From e2fa15d1524a36d92bae8af797a62d64c590791b Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 24 Jan 2024 15:01:33 +1000 Subject: [PATCH 01/14] Fix subscription renewals when an existing is used to purchase subscription --- .../class-wc-stripe-upe-payment-gateway.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 7ec4952702..653db67b15 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -712,6 +712,14 @@ private function process_payment_with_deferred_intent( int $order_id ) { $payment_information['payment_method'], $selected_payment_type ); + } else if ( $payment_information['is_using_saved_payment_method'] ) { + $this->maybe_update_source_on_subscription_order( + $order, + (object) [ + 'payment_method' => $payment_information['payment_method'], + 'customer' => $payment_information['customer'], + ] + ); } // Use the last charge within the intent to proceed. From 9bc5fc67d5abf7e7848c06d89bc286ba4dc5567b Mon Sep 17 00:00:00 2001 From: mattallan Date: Thu, 25 Jan 2024 15:23:44 +1000 Subject: [PATCH 02/14] Update deferred intent process_payment function to support setup intents --- client/api/index.js | 7 +- client/classic/upe/payment-processing.js | 2 +- .../class-wc-stripe-intent-controller.php | 84 ++++++++++----- .../class-wc-stripe-upe-payment-gateway.php | 100 ++++++++++++------ 4 files changed, 133 insertions(+), 60 deletions(-) diff --git a/client/api/index.js b/client/api/index.js index d03b7f3e6e..63941a6857 100644 --- a/client/api/index.js +++ b/client/api/index.js @@ -166,16 +166,17 @@ export default class WCStripeAPI { /** * Creates and confirms a setup intent. * - * @param {string} paymentMethodId The id of the payment method. + * @param {Object} paymentMethod The id of the payment method. * * @return {Promise} Promise containing the setup intent. */ - setupIntent( paymentMethodId ) { + setupIntent( paymentMethod ) { return this.request( this.getAjaxUrl( 'create_and_confirm_setup_intent' ), { action: 'create_and_confirm_setup_intent', - 'wc-stripe-payment-method': paymentMethodId, + 'wc-stripe-payment-method': paymentMethod.id, + 'wc-stripe-payment-type': paymentMethod.type, _ajax_nonce: this.options?.createAndConfirmSetupIntentNonce, } ).then( ( response ) => { diff --git a/client/classic/upe/payment-processing.js b/client/classic/upe/payment-processing.js index 8bf7d3b89a..bea96f93e5 100644 --- a/client/classic/upe/payment-processing.js +++ b/client/classic/upe/payment-processing.js @@ -269,7 +269,7 @@ export const createAndConfirmSetupIntent = ( api ) => { return api - .setupIntent( paymentMethod.id ) + .setupIntent( paymentMethod ) .then( function ( confirmedSetupIntent ) { appendSetupIntentToForm( jQueryForm, confirmedSetupIntent ); return confirmedSetupIntent; diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index 1fa2c27c7e..5db8f50faa 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -713,16 +713,8 @@ public function create_and_confirm_payment_intent( $payment_information ) { ]; // For Stripe Link & SEPA with deferred intent UPE, we must create mandate to acknowledge that terms have been shown to customer. - if ( $this->is_mandate_data_required( $selected_payment_type ) ) { - $request['mandate_data'] = [ - 'customer_acceptance' => [ - 'type' => 'online', - 'online' => [ - 'ip_address' => WC_Geolocation::get_ip_address(), - 'user_agent' => 'WooCommerce Stripe Gateway' . WC_STRIPE_VERSION . '; ' . get_bloginfo( 'url' ), - ], - ], - ]; + if ( ! $this->is_mandate_data_required( $selected_payment_type ) ) { + $request = $this->add_mandate_data( $request ); } if ( $this->request_needs_redirection( $payment_method_types ) ) { @@ -762,6 +754,27 @@ public function create_and_confirm_payment_intent( $payment_information ) { return $payment_intent; } + /** + * Adds mandate data to the request. + * + * @param array $request The request to add mandate data to. + * + * @return array The request with mandate data added. + */ + private function add_mandate_data( $request ) { + $request['mandate_data'] = [ + 'customer_acceptance' => [ + 'type' => 'online', + 'online' => [ + 'ip_address' => WC_Geolocation::get_ip_address(), + 'user_agent' => 'WooCommerce Stripe Gateway' . WC_STRIPE_VERSION . '; ' . get_bloginfo( 'url' ), + ], + ], + ]; + + return $request; + } + /** * Validate the provided information for creating and confirming a payment intent. * @@ -891,26 +904,35 @@ public function is_mandate_data_required( $selected_payment_type ) { /** * Creates and confirm a setup intent with the given payment method ID. * - * @param string $payment_method The payment method ID (pm_). + * @param array $payment_information The payment information to be used for the setup intent. * * @throws WC_Stripe_Exception If the create intent call returns with an error. * * @return array */ - public function create_and_confirm_setup_intent( $payment_method ) { - // Determine the customer managing the payment methods, create one if we don't have one already. - $user = wp_get_current_user(); - $customer = new WC_Stripe_Customer( $user->ID ); - $customer_id = $customer->update_or_create_customer(); + public function create_and_confirm_setup_intent( $payment_information ) { + $request = [ + 'payment_method' => $payment_information['payment_method'], + 'payment_method_types' => [ $payment_information['selected_payment_type'] ], + 'customer' => $payment_information['customer'], + 'confirm' => 'true', + ]; - $setup_intent = WC_Stripe_API::request( - [ - 'customer' => $customer_id, - 'confirm' => 'true', - 'payment_method' => $payment_method, - ], - 'setup_intents' - ); + // SEPA setup intents require mandate data. + if ( ! $this->is_mandate_data_required( $selected_payment_type ) ) { + $request = $this->add_mandate_data( $request ); + } + + if ( isset( $payment_information['level3'], $payment_information['order'] ) ) { + $setup_intent = WC_Stripe_API::request_with_level3_data( + $request, + 'setup_intents', + $payment_information['level3'], + $payment_information['order'] + ); + } else { + $setup_intent = WC_Stripe_API::request( $request, 'setup_intents' ); + } if ( ! empty( $setup_intent->error ) ) { throw new WC_Stripe_Exception( print_r( $setup_intent->error, true ), $setup_intent->error->message ); @@ -935,12 +957,24 @@ public function create_and_confirm_setup_intent_ajax() { } $payment_method = sanitize_text_field( wp_unslash( $_POST['wc-stripe-payment-method'] ?? '' ) ); + $payment_type = sanitize_text_field( wp_unslash( $_POST['wc-stripe-payment-type'] ?? 'card' ) ); if ( ! $payment_method ) { throw new WC_Stripe_Exception( 'Payment method missing from request.', __( "We're not able to add this payment method. Please refresh the page and try again.", 'woocommerce-gateway-stripe' ) ); } - $setup_intent = $this->create_and_confirm_setup_intent( $payment_method ); + // Determine the customer managing the payment methods, create one if we don't have one already. + $user = wp_get_current_user(); + $customer = new WC_Stripe_Customer( $user->ID ); + + // Manually create the payment information array to create & confirm the setup intent. + $payment_information = [ + 'payment_method' => $payment_method, + 'customer' => $customer->update_or_create_customer(), + 'selected_payment_type' => $payment_type, + ]; + + $setup_intent = $this->create_and_confirm_setup_intent( $payment_information ); if ( empty( $setup_intent->status ) || ! in_array( $setup_intent->status, [ 'succeeded', 'processing', 'requires_action' ], true ) ) { throw new WC_Stripe_Exception( 'Response from Stripe: ' . print_r( $setup_intent, true ), __( 'There was an error adding this payment method. Please refresh the page and try again', 'woocommerce-gateway-stripe' ) ); diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 653db67b15..270b660fad 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -703,40 +703,31 @@ private function process_payment_with_deferred_intent( int $order_id ) { // Create a payment intent, or update an existing one associated with the order. $payment_intent = $this->process_payment_intent_for_order( $order, $payment_information ); - - // Handle saving the payment method in the store. - // It's already attached to the Stripe customer at this point. - if ( $payment_information['save_payment_method_to_store'] ) { - $this->handle_saving_payment_method( - $order, - $payment_information['payment_method'], - $selected_payment_type - ); - } else if ( $payment_information['is_using_saved_payment_method'] ) { - $this->maybe_update_source_on_subscription_order( - $order, - (object) [ - 'payment_method' => $payment_information['payment_method'], - 'customer' => $payment_information['customer'], - ] - ); - } - - // Use the last charge within the intent to proceed. - $charge = end( $payment_intent->charges->data ); - - // Only process the response if it contains a charge object. Intents with no charge require further action like 3DS and will be processed later. - if ( $charge ) { - $this->process_response( $charge, $order ); - } - - // Set the selected UPE payment method type title in the WC order. - $this->set_payment_method_title_for_order( $order, $selected_payment_type ); } else { - // It's a setup intent. To be handled. - return [ 'result' => 'failure' ]; + $payment_intent = $this->process_setup_intent_for_order( $order, $payment_information ); } + // Handle saving the payment method in the store. + // It's already attached to the Stripe customer at this point. + if ( $payment_information['save_payment_method_to_store'] ) { + $this->handle_saving_payment_method( + $order, + $payment_information['payment_method'], + $selected_payment_type + ); + } else if ( $payment_information['is_using_saved_payment_method'] ) { + $this->maybe_update_source_on_subscription_order( + $order, + (object) [ + 'payment_method' => $payment_information['payment_method'], + 'customer' => $payment_information['customer'], + ] + ); + } + + // Set the selected UPE payment method type title in the WC order. + $this->set_payment_method_title_for_order( $order, $selected_payment_type ); + $redirect = $this->get_return_url( $order ); /** @@ -769,6 +760,18 @@ private function process_payment_with_deferred_intent( int $order_id ) { } } + if ( $payment_needed ) { + // Use the last charge within the intent to proceed. + $charge = end( $payment_intent->charges->data ); + + // Only process the response if it contains a charge object. Intents with no charge require further action like 3DS and will be processed later. + if ( $charge ) { + $this->process_response( $charge, $order ); + } + } else { + $order->payment_complete(); + } + return [ 'result' => 'success', 'redirect' => $redirect, @@ -1708,6 +1711,41 @@ private function process_payment_intent_for_order( WC_Order $order, array $payme return $payment_intent; } + /** + * Create a setup intent for the order. + * + * @param WC_Order $order The WC Order for which we're handling a setup intent. + * @param array $payment_information The payment information to be used for the setup intent. + * + * @throws WC_Stripe_Exception When there's an error creating the setup intent. + * + * @return stdClass + */ + private function process_setup_intent_for_order( WC_Order $order, array $payment_information ) { + $setup_intent = $this->intent_controller->create_and_confirm_setup_intent( $payment_information ); + + if ( ! empty( $setup_intent->error ) ) { + + // Add the setup intent information to the order meta, if one was created despite the error. + if ( ! empty( $setup_intent->error->payment_intent ) ) { + $this->save_intent_to_order( $order, $setup_intent->error->payment_intent ); + } + + $this->maybe_remove_non_existent_customer( $setup_intent->error, $order ); + + throw new WC_Stripe_Exception( + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r + print_r( $setup_intent, true ), + __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' ) + ); + } + + // Add the payment intent information to the order meta. + $this->save_intent_to_order( $order, $setup_intent ); + + return $setup_intent; + } + /** * Collects the payment information needed for processing a payment intent. * From 86d3d62ead7f3a28970b7c323a786f122da318d9 Mon Sep 17 00:00:00 2001 From: mattallan Date: Thu, 25 Jan 2024 15:56:31 +1000 Subject: [PATCH 03/14] Fixes changing a subscriptions payment method to an existing saved card --- client/classic/upe/deferred-intent.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/client/classic/upe/deferred-intent.js b/client/classic/upe/deferred-intent.js index fefd84d84b..c4628e8613 100644 --- a/client/classic/upe/deferred-intent.js +++ b/client/classic/upe/deferred-intent.js @@ -63,11 +63,14 @@ jQuery( function ( $ ) { // Pay for Order page submit. $( '#order_review' ).on( 'submit', () => { - return processPayment( - api, - $( '#order_review' ), - getSelectedUPEGatewayPaymentMethod() - ); + const paymentMethodType = getSelectedUPEGatewayPaymentMethod(); + if ( ! isUsingSavedPaymentMethod( paymentMethodType ) ) { + return processPayment( + api, + $( '#order_review' ), + paymentMethodType + ); + } } ); // If the card element selector doesn't exist, then do nothing. From 50a2af671c437182184df103eace000ad7ad3e18 Mon Sep 17 00:00:00 2001 From: mattallan Date: Mon, 29 Jan 2024 14:55:45 +1000 Subject: [PATCH 04/14] Add inline comments to explain why level3 data might be missing --- includes/class-wc-stripe-intent-controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index 5db8f50faa..4fda6cfa8c 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -923,6 +923,13 @@ public function create_and_confirm_setup_intent( $payment_information ) { $request = $this->add_mandate_data( $request ); } + /** + * When the setup intent is being created from a checkout request, i.e. purchasing a subscription product + * with a free trial, send the setup_intents request with level3 and order data. + * + * If the given $payment_information doesn't have the level3 or order data i.e. creating a setup intent from the Add Payment Method + * page, send off a generic Stripe request. + */ if ( isset( $payment_information['level3'], $payment_information['order'] ) ) { $setup_intent = WC_Stripe_API::request_with_level3_data( $request, From 6a589487da392b3c902cc60a0e9975fc23389809 Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 31 Jan 2024 13:40:24 +1000 Subject: [PATCH 05/14] Don't send level3 data with setup_intent requests --- includes/class-wc-stripe-intent-controller.php | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index 4fda6cfa8c..f32693e409 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -923,23 +923,7 @@ public function create_and_confirm_setup_intent( $payment_information ) { $request = $this->add_mandate_data( $request ); } - /** - * When the setup intent is being created from a checkout request, i.e. purchasing a subscription product - * with a free trial, send the setup_intents request with level3 and order data. - * - * If the given $payment_information doesn't have the level3 or order data i.e. creating a setup intent from the Add Payment Method - * page, send off a generic Stripe request. - */ - if ( isset( $payment_information['level3'], $payment_information['order'] ) ) { - $setup_intent = WC_Stripe_API::request_with_level3_data( - $request, - 'setup_intents', - $payment_information['level3'], - $payment_information['order'] - ); - } else { - $setup_intent = WC_Stripe_API::request( $request, 'setup_intents' ); - } + $setup_intent = WC_Stripe_API::request( $request, 'setup_intents' ); if ( ! empty( $setup_intent->error ) ) { throw new WC_Stripe_Exception( print_r( $setup_intent->error, true ), $setup_intent->error->message ); From 75b83294fab4cfb0b4cae639b50fcfe3812f3491 Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 31 Jan 2024 13:41:40 +1000 Subject: [PATCH 06/14] Fix undefined variable --- includes/class-wc-stripe-intent-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index f32693e409..06a3fe967c 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -919,7 +919,7 @@ public function create_and_confirm_setup_intent( $payment_information ) { ]; // SEPA setup intents require mandate data. - if ( ! $this->is_mandate_data_required( $selected_payment_type ) ) { + if ( ! $this->is_mandate_data_required( $payment_information['selected_payment_type'] ) ) { $request = $this->add_mandate_data( $request ); } From 7e4fbcede518ae38858866dfc8d927832004673e Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 31 Jan 2024 13:42:21 +1000 Subject: [PATCH 07/14] fix docblock --- includes/class-wc-stripe-intent-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index 06a3fe967c..82e1b195cf 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -888,7 +888,7 @@ private function get_payment_method_types_for_intent_creation( string $selected_ * This applies to SEPA and Link payment methods. * https://stripe.com/docs/payments/finalize-payments-on-the-server * - * @param $selected_payment_type The name of the selected UPE payment type. + * @param string $selected_payment_type The name of the selected UPE payment type. * * @return bool True if a mandate must be shown and acknowledged by customer before deferred intent UPE payment can be processed, false otherwise. */ From 10591299491085df343f37f2211d00c24d7c1490 Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 31 Jan 2024 16:23:25 +1000 Subject: [PATCH 08/14] For change payment requests, call backwards compatible do_action hook and don't call payment_complete() --- .../trait-wc-stripe-subscriptions-utilities.php | 2 +- .../class-wc-stripe-upe-payment-gateway.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/includes/compat/trait-wc-stripe-subscriptions-utilities.php b/includes/compat/trait-wc-stripe-subscriptions-utilities.php index dded80f95f..b5cafd17b0 100644 --- a/includes/compat/trait-wc-stripe-subscriptions-utilities.php +++ b/includes/compat/trait-wc-stripe-subscriptions-utilities.php @@ -42,7 +42,7 @@ public function has_subscription( $order_id ) { * @return bool */ public function is_changing_payment_method_for_subscription() { - if ( isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( isset( $_GET['change_payment_method'] ) && function_exists( 'wcs_is_subscription' ) ) { // phpcs:ignore WordPress.Security.NonceVerification return wcs_is_subscription( wc_clean( wp_unslash( $_GET['change_payment_method'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification } return false; diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 270b660fad..f1b81ac4ba 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -768,6 +768,19 @@ private function process_payment_with_deferred_intent( int $order_id ) { if ( $charge ) { $this->process_response( $charge, $order ); } + } elseif ( $this->is_changing_payment_method_for_subscription() ) { + // Trigger wc_stripe_change_subs_payment_method_success action hook to preserve backwards compatibility, see process_change_subscription_payment_method(). + do_action( + 'wc_stripe_change_subs_payment_method_success', + $payment_information['payment_method'], + (object) [ + 'token_id' => false !== $payment_information['token'] ? $payment_information['token']->get_id() : false, + 'customer' => $payment_information['customer'], + 'source' => null, + 'source_object' => $payment_method, + 'payment_method' => $payment_information['payment_method'], + ] + ); } else { $order->payment_complete(); } @@ -1758,6 +1771,7 @@ private function prepare_payment_information_from_request( WC_Order $order ) { $currency = strtolower( $order->get_currency() ); $amount = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $currency ); $shipping_details = null; + $token = false; $save_payment_method_to_store = $this->should_save_payment_method_from_request( $order->get_id(), $selected_payment_type ); $is_using_saved_payment_method = $this->is_using_saved_payment_method(); @@ -1801,6 +1815,7 @@ private function prepare_payment_information_from_request( WC_Order $order ) { 'selected_payment_type' => $selected_payment_type, 'shipping' => $shipping_details, 'statement_descriptor' => $this->get_statement_descriptor( $order, $selected_payment_type ), + 'token' => $token, 'return_url' => $this->get_return_url_for_redirect( $order, $save_payment_method_to_store ), ]; From cfa2cd7fdcf3ae314e8ab16c7854c87ff2d9ddf4 Mon Sep 17 00:00:00 2001 From: mattallan Date: Wed, 31 Jan 2024 17:51:31 +1000 Subject: [PATCH 09/14] Fix is_is_mandate_data_required() logic --- includes/class-wc-stripe-intent-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index 82e1b195cf..ce967a7660 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -713,7 +713,7 @@ public function create_and_confirm_payment_intent( $payment_information ) { ]; // For Stripe Link & SEPA with deferred intent UPE, we must create mandate to acknowledge that terms have been shown to customer. - if ( ! $this->is_mandate_data_required( $selected_payment_type ) ) { + if ( $this->is_mandate_data_required( $selected_payment_type ) ) { $request = $this->add_mandate_data( $request ); } @@ -919,7 +919,7 @@ public function create_and_confirm_setup_intent( $payment_information ) { ]; // SEPA setup intents require mandate data. - if ( ! $this->is_mandate_data_required( $payment_information['selected_payment_type'] ) ) { + if ( $this->is_mandate_data_required( $payment_information['selected_payment_type'] ) ) { $request = $this->add_mandate_data( $request ); } From 78feb0b45920e40f731c539eaedca84397d43f15 Mon Sep 17 00:00:00 2001 From: Matt Allan Date: Mon, 5 Feb 2024 10:49:10 +1000 Subject: [PATCH 10/14] Update includes/payment-methods/class-wc-stripe-upe-payment-gateway.php Co-authored-by: James Allan --- .../payment-methods/class-wc-stripe-upe-payment-gateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index f1b81ac4ba..27ec8649ba 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -715,7 +715,7 @@ private function process_payment_with_deferred_intent( int $order_id ) { $payment_information['payment_method'], $selected_payment_type ); - } else if ( $payment_information['is_using_saved_payment_method'] ) { + } elseif ( $payment_information['is_using_saved_payment_method'] ) { $this->maybe_update_source_on_subscription_order( $order, (object) [ From b6d99e7447a72d525cb0ff1b24cbf4c22bfadaa2 Mon Sep 17 00:00:00 2001 From: Matt Allan Date: Mon, 5 Feb 2024 10:50:59 +1000 Subject: [PATCH 11/14] Update client/api/index.js Co-authored-by: James Allan --- client/api/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/api/index.js b/client/api/index.js index 63941a6857..f64502f1a6 100644 --- a/client/api/index.js +++ b/client/api/index.js @@ -166,7 +166,9 @@ export default class WCStripeAPI { /** * Creates and confirms a setup intent. * - * @param {Object} paymentMethod The id of the payment method. + * @param {Object} paymentMethod Payment method data. + * @param {string} paymentMethod.id The ID of the payment method. + * @param {string} paymentMethod.type The type of the payment method. * * @return {Promise} Promise containing the setup intent. */ From 3eecfc42f12d865c33b0950e1ca1853436ca9536 Mon Sep 17 00:00:00 2001 From: mattallan Date: Mon, 5 Feb 2024 16:12:51 +1000 Subject: [PATCH 12/14] Only mark orders as complete if the intent has a success status and payment is not needed --- .../payment-methods/class-wc-stripe-upe-payment-gateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 27ec8649ba..73c2f61c16 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -781,7 +781,7 @@ private function process_payment_with_deferred_intent( int $order_id ) { 'payment_method' => $payment_information['payment_method'], ] ); - } else { + } elseif ( in_array( $payment_intent->status, self::SUCCESSFUL_INTENT_STATUS, true ) ) { $order->payment_complete(); } From 1668359db3299b988a30efc2935a935841dc9bbc Mon Sep 17 00:00:00 2001 From: mattallan Date: Mon, 5 Feb 2024 16:17:00 +1000 Subject: [PATCH 13/14] Fix purchasing a subscription with no upfront payment with a 3DS card --- includes/class-wc-stripe-helper.php | 17 +++++++++++++++++ includes/class-wc-stripe-intent-controller.php | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-stripe-helper.php b/includes/class-wc-stripe-helper.php index d50db2c6d6..8496d7a152 100644 --- a/includes/class-wc-stripe-helper.php +++ b/includes/class-wc-stripe-helper.php @@ -880,4 +880,21 @@ public static function get_payment_method_from_intent( $intent ) { return null; } + + /** + * Returns the payment intent or setup intent id method ID from a given intent object. + * + * @param WC_Order $order The order to fetch the Stripe intent from. + * + * @return string|bool The intent ID if found, false otherwise. + */ + public static function get_intent_id_from_order( $order ) { + $intent_id = $order->get_meta( '_stripe_intent_id' ); + + if ( ! $intent_id ) { + $intent_id = $order->get_meta( '_stripe_setup_intent' ); + } + + return $intent_id ?? false; + } } diff --git a/includes/class-wc-stripe-intent-controller.php b/includes/class-wc-stripe-intent-controller.php index ce967a7660..ecc703f478 100644 --- a/includes/class-wc-stripe-intent-controller.php +++ b/includes/class-wc-stripe-intent-controller.php @@ -562,7 +562,7 @@ public function update_order_status_ajax() { throw new WC_Stripe_Exception( 'order_not_found', __( "We're not able to process this payment. Please try again later.", 'woocommerce-gateway-stripe' ) ); } - $intent_id = $order->get_meta( '_stripe_intent_id' ); + $intent_id = WC_Stripe_Helper::get_intent_id_from_order( $order ); $intent_id_received = isset( $_POST['intent_id'] ) ? wc_clean( wp_unslash( $_POST['intent_id'] ) ) : null; if ( empty( $intent_id_received ) || $intent_id_received !== $intent_id ) { $note = sprintf( From 96b8222fc6976451b6c053445a2f640cfd446037 Mon Sep 17 00:00:00 2001 From: Matt Allan Date: Tue, 6 Feb 2024 15:13:46 +1000 Subject: [PATCH 14/14] Update includes/class-wc-stripe-helper.php Co-authored-by: James Allan --- includes/class-wc-stripe-helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-stripe-helper.php b/includes/class-wc-stripe-helper.php index 8496d7a152..1033800f23 100644 --- a/includes/class-wc-stripe-helper.php +++ b/includes/class-wc-stripe-helper.php @@ -882,7 +882,7 @@ public static function get_payment_method_from_intent( $intent ) { } /** - * Returns the payment intent or setup intent id method ID from a given intent object. + * Returns the payment intent or setup intent ID from a given order object. * * @param WC_Order $order The order to fetch the Stripe intent from. *