From 2d2aeb002fa3a100ea861e4cb6fb8ce6f9865a9c Mon Sep 17 00:00:00 2001 From: Matt Allan Date: Mon, 15 Apr 2024 08:02:20 +1000 Subject: [PATCH] Fixes "Invalid recurring shipping method" errors when purchasing multiple subscriptions with Apple / Google Pay (#8618) --- ...8029-prb-invalid-recurring-shipping-method | 4 ++ ...ayments-payment-request-button-handler.php | 40 ++++++++++++++++++- psalm-baseline.xml | 4 +- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 changelog/fix-8029-prb-invalid-recurring-shipping-method diff --git a/changelog/fix-8029-prb-invalid-recurring-shipping-method b/changelog/fix-8029-prb-invalid-recurring-shipping-method new file mode 100644 index 00000000000..2dd0f954958 --- /dev/null +++ b/changelog/fix-8029-prb-invalid-recurring-shipping-method @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Resolves "Invalid recurring shipping method" errors when purchasing multiple subscriptions with Apple Pay and Google Pay. diff --git a/includes/class-wc-payments-payment-request-button-handler.php b/includes/class-wc-payments-payment-request-button-handler.php index 92ce89c7b94..d4f8c568a87 100644 --- a/includes/class-wc-payments-payment-request-button-handler.php +++ b/includes/class-wc-payments-payment-request-button-handler.php @@ -893,7 +893,7 @@ public function get_shipping_options( $shipping_address, $itemized_display_items $data = []; // Remember current shipping method before resetting. - $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); + $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', [] ); $this->calculate_shipping( apply_filters( 'wcpay_payment_request_shipping_posted_values', $shipping_address ) ); $packages = WC()->shipping->get_packages(); @@ -943,6 +943,8 @@ public function get_shipping_options( $shipping_address, $itemized_display_items WC()->cart->calculate_totals(); + $this->maybe_restore_recurring_chosen_shipping_methods( $chosen_shipping_methods ); + $data += $this->express_checkout_helper->build_display_items( $itemized_display_items ); $data['result'] = 'success'; } catch ( Exception $e ) { @@ -1507,4 +1509,40 @@ private function get_taxes_like_cart( $product, $price ) { // Normally there should be a single tax, but `calc_tax` returns an array, let's use it. return WC_Tax::calc_tax( $price, $rates, false ); } + + /** + * Restores the shipping methods previously chosen for each recurring cart after shipping was reset and recalculated + * during the Payment Request get_shipping_options flow. + * + * When the cart contains multiple subscriptions with different billing periods, customers are able to select different shipping + * methods for each subscription, however, this is not supported when purchasing with Apple Pay and Google Pay as it's + * only concerned about handling the initial purchase. + * + * In order to avoid Woo Subscriptions's `WC_Subscriptions_Cart::validate_recurring_shipping_methods` throwing an error, we need to restore + * the previously chosen shipping methods for each recurring cart. + * + * This function needs to be called after `WC()->cart->calculate_totals()` is run, otherwise `WC()->cart->recurring_carts` won't exist yet. + * + * @param array $previous_chosen_methods The previously chosen shipping methods. + */ + private function maybe_restore_recurring_chosen_shipping_methods( $previous_chosen_methods = [] ) { + if ( empty( WC()->cart->recurring_carts ) || ! method_exists( 'WC_Subscriptions_Cart', 'get_recurring_shipping_package_key' ) ) { + return; + } + + $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', [] ); + + foreach ( WC()->cart->recurring_carts as $recurring_cart_key => $recurring_cart ) { + foreach ( $recurring_cart->get_shipping_packages() as $recurring_cart_package_index => $recurring_cart_package ) { + $package_key = WC_Subscriptions_Cart::get_recurring_shipping_package_key( $recurring_cart_key, $recurring_cart_package_index ); + + // If the recurring cart package key is found in the previous chosen methods, but not in the current chosen methods, restore it. + if ( isset( $previous_chosen_methods[ $package_key ] ) && ! isset( $chosen_shipping_methods[ $package_key ] ) ) { + $chosen_shipping_methods[ $package_key ] = $previous_chosen_methods[ $package_key ]; + } + } + } + + WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); + } } diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ecb5ea1088e..17038230df6 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -25,11 +25,9 @@ - + WC_Pre_Orders_Product WC_Subscriptions_Product - WC_Subscriptions_Product - WC_Subscriptions_Cart WC_Subscriptions_Cart