From 0ef50605d4ced2bdcd0feaa561dbda6983a35ad9 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Fri, 5 Jul 2024 13:30:48 -0600 Subject: [PATCH 01/10] Update the payment method used for renewals to the migrated pm_ if migrated already --- ...subscriptions-legacy-sepa-token-update.php | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 660ee5d38..411fabff6 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -63,12 +63,15 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i $source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); $user_id = $subscription->get_user_id(); + // Update the subscription with the updated SEPA gateway ID. + $this->set_subscription_updated_payment_gateway_id( $subscription ); + // Try to create an updated SEPA gateway token if none exists. // We don't need this to update the subscription, but creating one for consistency. // It could be confusing for a merchant to see the subscription renewing but no saved token in the store. $this->maybe_create_updated_sepa_token_by_source_id( $source_id, $user_id ); - // Update the subscription with the updated SEPA gateway ID. + // Update the payment method used for renewals to the migrated pm_, if any. $this->set_subscription_updated_payment_method( $subscription ); } @@ -107,6 +110,35 @@ private function get_subscription_to_migrate( $subscription_id ) { return $subscription; } + /** + * Updates the payment method used for renewals to the migrated pm_, if any. + * + * The subscription is using a source for renewals at this point. + * When the migration runs on the Stripe account, there will be a payment method (pm_) migrated from the source (src_). + * This method updates the subscription to use the migrated payment method (pm_) for renewals, if it exists. + * + * @param WC_Subscription $subscription The subscription to update. + */ + private function set_subscription_updated_payment_method( WC_Subscription $subscription ) { + $source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); + + // Retrieve the source object from the API. + $source_object = WC_Stripe_API::get_payment_method( $source_id ); + + // Bail out if the src_ hasn't been migrated to pm_ yet. + if ( ! isset( $source_object->metadata->migrated_payment_method ) ) { + // TODO: Maybe log something on the subscription? + return; + } + + // Get the payment method ID that was migrated from the source. + $migrated_payment_method_id = $source_object->metadata->migrated_payment_method; + + // And set it as the payment method for the subscription. + $subscription->update_meta_data( self::SOURCE_ID_META_KEY, $migrated_payment_method_id ); + $subscription->save(); + } + /** * Returns an updated token to be used for the subscription, given the source ID. * @@ -178,7 +210,7 @@ private function create_updated_sepa_token( string $source_id, int $user_id ) { * * @param WC_Subscription $subscription Subscription for which the payment method must be updated. */ - private function set_subscription_updated_payment_method( WC_Subscription $subscription ) { + private function set_subscription_updated_payment_gateway_id( WC_Subscription $subscription ) { // Add a meta to the subscription to flag that its token got updated. $subscription->update_meta_data( self::LEGACY_TOKEN_PAYMENT_METHOD_META_KEY, WC_Gateway_Stripe_Sepa::ID ); $subscription->set_payment_method( $this->updated_sepa_gateway_id ); From fde964143ec24f5e2c48355068fb299f9eea888e Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Fri, 5 Jul 2024 13:36:57 -0600 Subject: [PATCH 02/10] Stop trying to create a token for the payment method used for the subscription The renewal will use the payment method ID stored in the subscription metadata, no token is needed. The token will be created from the payment methods in the Stripe account when we list the tokens for the shopper. --- ...subscriptions-legacy-sepa-token-update.php | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 411fabff6..3c5a9ea06 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -60,17 +60,10 @@ class WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update { */ public function maybe_update_subscription_legacy_payment_method( $subscription_id ) { $subscription = $this->get_subscription_to_migrate( $subscription_id ); - $source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); - $user_id = $subscription->get_user_id(); // Update the subscription with the updated SEPA gateway ID. $this->set_subscription_updated_payment_gateway_id( $subscription ); - // Try to create an updated SEPA gateway token if none exists. - // We don't need this to update the subscription, but creating one for consistency. - // It could be confusing for a merchant to see the subscription renewing but no saved token in the store. - $this->maybe_create_updated_sepa_token_by_source_id( $source_id, $user_id ); - // Update the payment method used for renewals to the migrated pm_, if any. $this->set_subscription_updated_payment_method( $subscription ); } @@ -139,72 +132,6 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc $subscription->save(); } - /** - * Returns an updated token to be used for the subscription, given the source ID. - * - * If no updated token is found, we create a new one based on the legacy one. - * - * @param string $source_id The Source or Payment Method ID associated with the subscription. - * @param integer $user_id The WordPress User ID to whom the subscription belongs. - */ - private function maybe_create_updated_sepa_token_by_source_id( string $source_id, int $user_id ) { - - // Retrieve the updated SEPA tokens for the user. - $replacement_token = $this->get_customer_token_by_source_id( $source_id, $user_id, $this->updated_sepa_gateway_id ); - - // If no updated SEPA token was found, create a new one based on the source ID. - if ( ! $replacement_token ) { - $replacement_token = $this->create_updated_sepa_token( $source_id, $user_id ); - } - } - - /** - * Get the token for the user by its source ID and gateway ID. s - * - * @param string $source_id The ID of the source we're looking for. - * @param integer $user_id The ID of the user we're retrieving tokens for. - * @param string $gateway_id The ID of the gateway of the tokens we want to check. - * - * @return WC_Payment_Token|false - */ - private function get_customer_token_by_source_id( string $source_id, int $user_id, string $gateway_id ) { - $customer_tokens = WC_Payment_Tokens::get_customer_tokens( $user_id, $gateway_id ); - - foreach ( $customer_tokens as $token ) { - if ( $source_id === $token->get_token() ) { - return $token; - } - } - - return false; - } - - /** - * Creates an updated SEPA token given the source ID. - * - * @param string $source_id Source ID from which to create the new token. - * @param integer $user_id - * @return WC_Payment_Token_SEPA|bool The new SEPA token, or false if no legacy token was found. - */ - private function create_updated_sepa_token( string $source_id, int $user_id ) { - $legacy_token = $this->get_customer_token_by_source_id( $source_id, $user_id, WC_Gateway_Stripe_Sepa::ID ); - - // Bail out if we don't have a token from which to create an updated one. - if ( ! $legacy_token ) { - return false; - } - - $token = new WC_Payment_Token_SEPA(); - $token->set_last4( $legacy_token->get_last4() ); - $token->set_payment_method_type( $legacy_token->get_payment_method_type() ); - $token->set_gateway_id( $this->updated_sepa_gateway_id ); - $token->set_token( $source_id ); - $token->set_user_id( $user_id ); - $token->save(); - - return $token; - } - /** * Sets the updated SEPA gateway ID for the subscription. * From 70060c4c1f6087fcaffe69c1099811fbdb525308 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Fri, 5 Jul 2024 21:09:37 -0600 Subject: [PATCH 03/10] Remove obsolete php unit tests --- ...scriptions-repairer-legacy-sepa-tokens.php | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php index e144fe0b8..895c18b54 100644 --- a/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php +++ b/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php @@ -237,70 +237,6 @@ function ( $id ) { $this->updater->repair_item( $subscription_id ); } - public function test_get_updated_sepa_token_by_source_id_creates_an_updated_token() { - $this->upe_helper->enable_upe_feature_flag(); - $this->upe_helper->enable_upe(); - $this->upe_helper->reload_payment_gateways(); - - $stripe_payment_tokens_instance = WC_Stripe_Payment_Tokens::get_instance(); - - // The SEPA token we create below gets deleted by the method we hook in this filter because it's not found in Stripe. - remove_filter( 'woocommerce_get_customer_payment_tokens', [ $stripe_payment_tokens_instance, 'woocommerce_get_customer_payment_tokens' ], 10, 3 ); - - // Retrieve the actual subscription. - WC_Subscriptions::set_wcs_get_subscription( - function ( $id ) { - return new WC_Subscription( $id ); - } - ); - - $ids_to_migrate = $this->get_subs_ids_to_migrate(); - $subscription_id = $ids_to_migrate[0]; - $subscription = new WC_Subscription( $subscription_id ); - $customer_id = $subscription->get_user_id(); - - // Create the legacy token associated with the subscription. - $original_source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); - $original_token = WC_Helper_Token::create_sepa_token( $original_source_id, $customer_id, $this->legacy_sepa_gateway_id ); - - // Confirm the user doesn't have any updated tokens. - $customer_updated_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, $this->updated_sepa_gateway_id ); - $this->assertEmpty( $customer_updated_tokens ); - - $this->logger_mock - ->expects( $this->at( 0 ) ) - ->method( 'add' ) - ->with( - $this->equalTo( 'woocommerce-gateway-stripe-subscriptions-legacy-sepa-tokens-repairs' ), - $this->equalTo( sprintf( 'Migrating subscription #%1$d.', $subscription_id ) ) - ); - - $this->logger_mock - ->expects( $this->at( 1 ) ) - ->method( 'add' ) - ->with( - $this->equalTo( 'woocommerce-gateway-stripe-subscriptions-legacy-sepa-tokens-repairs' ), - $this->equalTo( sprintf( 'Successful migration of subscription #%1$d.', $subscription_id ) ) - ); - - $this->updater->repair_item( $subscription_id ); - - $subscription = new WC_Subscription( $subscription_id ); - - // Confirm the user has an updated token. - $customer_updated_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, $this->updated_sepa_gateway_id ); - $this->assertEquals( 1, count( $customer_updated_tokens ) ); - - // Confirm the subscription's payment method was updated. - $this->assertEquals( $this->updated_sepa_gateway_id, $subscription->get_payment_method() ); - - // Confirm the subscription's source ID was updated to use the default token. - $this->assertEquals( $original_source_id, $subscription->get_meta( self::SOURCE_ID_META_KEY ) ); - - // Confirm the flag for the migration was set. - $this->assertEquals( $this->legacy_sepa_gateway_id, $subscription->get_meta( '_migrated_sepa_payment_method' ) ); - } - public function test_get_updated_sepa_token_by_source_id_returns_the_updated_token() { $this->upe_helper->enable_upe_feature_flag(); $this->upe_helper->enable_upe(); @@ -330,10 +266,6 @@ function ( $id ) { // Create the updated token we expect the subscription to be updated with. $updated_token = WC_Helper_Token::create_sepa_token( $original_source_id, $customer_id, $this->updated_sepa_gateway_id ); - // Create default updated token we don't expect the subscription to use. - $default_token = WC_Helper_Token::create_sepa_token( 'src_999', $customer_id, $this->updated_sepa_gateway_id ); - WC_Payment_Tokens::set_users_default( $customer_id, $default_token->get_id() ); - $this->logger_mock ->expects( $this->at( 0 ) ) ->method( 'add' ) From fd287a8b7040bf8318ae36555d1fe8ea671ea196 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Fri, 5 Jul 2024 21:33:14 -0600 Subject: [PATCH 04/10] Update outdated inline documentation --- ...stripe-subscriptions-legacy-sepa-token-update.php | 12 +++--------- ...ipe-subscriptions-repairer-legacy-sepa-tokens.php | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 3c5a9ea06..0287b90da 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -19,15 +19,9 @@ * WooCommerce detects that the stripe_sepa payment gateway as no longer available. * This causes the Subscription to change to Manual renewal, and automatic renewals to fail. * - * This class fixes failing automatic renewals by: - * - Retrieving all the subscriptions that are using the stripe_sepa payment gateway. - * - Iterating over each subscription. - * - Retrieving an Updated (Payment Methods API) token based on the Legacy (Sources API) token associated with the subscription. - * - If none is found, we create a new Updated (Payment Methods API) token based on the Legacy (Sources API) token. - * - If it can't be created, we skip the migration. - * - Associating this replacement token to the subscription. - * - * This class extends the WCS_Background_Repairer for scheduling and running the individual migration actions. + * This class updates the following for the given subscription: + * - Setting the associated gateway ID to the one used for the updated checkout experience `stripe_sepa_debit`, so it doesn't switch to Manual Renewal. + * - Updating the payment method used for renewals to the migrated pm_, if any. */ class WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update { diff --git a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php index e4d9f8371..ea2d7a010 100644 --- a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php +++ b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php @@ -6,7 +6,7 @@ defined( 'ABSPATH' ) || exit; /** - * Handles migrating the tokens of Subscriptions using SEPA's Legacy gateway ID. + * Handles repairing the Subscriptions using SEPA's Legacy payment method. * * This class extends the WCS_Background_Repairer for scheduling and running the individual migration actions. */ From b277b48ff098de36483bc258b34dbf1decef4310 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Fri, 5 Jul 2024 23:23:09 -0600 Subject: [PATCH 05/10] Move throwing the exceptions within the methods that update the subscription --- ...subscriptions-legacy-sepa-token-update.php | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 0287b90da..99473027a 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -20,8 +20,8 @@ * This causes the Subscription to change to Manual renewal, and automatic renewals to fail. * * This class updates the following for the given subscription: - * - Setting the associated gateway ID to the one used for the updated checkout experience `stripe_sepa_debit`, so it doesn't switch to Manual Renewal. - * - Updating the payment method used for renewals to the migrated pm_, if any. + * - The associated gateway ID to the one used for the updated checkout experience `stripe_sepa_debit`, so it doesn't switch to Manual Renewal. + * - The payment method used for renewals to the migrated pm_, if any. */ class WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update { @@ -58,7 +58,7 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i // Update the subscription with the updated SEPA gateway ID. $this->set_subscription_updated_payment_gateway_id( $subscription ); - // Update the payment method used for renewals to the migrated pm_, if any. + // Update the payment method to the migrated pm_. $this->set_subscription_updated_payment_method( $subscription ); } @@ -69,11 +69,10 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i * - The Legacy experience is disabled * - The WooCommerce Subscription extension is active * - The subscription ID is a valid subscription - * - The payment method associated with the subscription is the legacy SEPA gateway, `stripe_sepa` * * @param int $subscription_id The ID of the subscription to update. * @return WC_Subscription An instance of the subscription to be updated. - * @throws \Exception When the subscription can't or doesn't need to be updated. + * @throws \Exception When the subscription can't be updated. */ private function get_subscription_to_migrate( $subscription_id ) { if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) { @@ -90,10 +89,6 @@ private function get_subscription_to_migrate( $subscription_id ) { throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. Subscription not found.', $subscription_id ) ); } - if ( WC_Gateway_Stripe_Sepa::ID !== $subscription->get_payment_method() ) { - throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. Subscription is not using the legacy SEPA payment method.', $subscription_id ) ); - } - return $subscription; } @@ -105,17 +100,22 @@ private function get_subscription_to_migrate( $subscription_id ) { * This method updates the subscription to use the migrated payment method (pm_) for renewals, if it exists. * * @param WC_Subscription $subscription The subscription to update. + * @throws \Exception When the subscription is already using a pm_ or its src_ hasn't been migrated to a pm_. */ private function set_subscription_updated_payment_method( WC_Subscription $subscription ) { $source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); + // Bail out if the subscription is already using a pm_. + if ( ! str_starts_with( $source_id, 'src_' ) ) { + throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. The subscription is not using a source.', $subscription_id ) ); + } + // Retrieve the source object from the API. $source_object = WC_Stripe_API::get_payment_method( $source_id ); // Bail out if the src_ hasn't been migrated to pm_ yet. if ( ! isset( $source_object->metadata->migrated_payment_method ) ) { - // TODO: Maybe log something on the subscription? - return; + throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. The subscription is using a source that was not migrated to a payment method on the Stripe account.', $subscription_id ) ); } // Get the payment method ID that was migrated from the source. @@ -132,6 +132,11 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc * @param WC_Subscription $subscription Subscription for which the payment method must be updated. */ private function set_subscription_updated_payment_gateway_id( WC_Subscription $subscription ) { + // The subscription is not using the legacy SEPA gateway ID. + if ( WC_Gateway_Stripe_Sepa::ID !== $subscription->get_payment_method() ) { + throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. Subscription is not using the legacy SEPA payment method.', $subscription_id ) ); + } + // Add a meta to the subscription to flag that its token got updated. $subscription->update_meta_data( self::LEGACY_TOKEN_PAYMENT_METHOD_META_KEY, WC_Gateway_Stripe_Sepa::ID ); $subscription->set_payment_method( $this->updated_sepa_gateway_id ); From 00ec1ce833e283e8cecfcc74f46082564d98c28e Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Sun, 7 Jul 2024 11:03:52 -0600 Subject: [PATCH 06/10] Update the source on renewal when the payment gateway ID was already repaired --- ...subscriptions-legacy-sepa-token-update.php | 26 ++++++++++++++++--- ...scriptions-repairer-legacy-sepa-tokens.php | 21 ++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 99473027a..7118cdb89 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -59,7 +59,25 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i $this->set_subscription_updated_payment_gateway_id( $subscription ); // Update the payment method to the migrated pm_. - $this->set_subscription_updated_payment_method( $subscription ); + $this->maybe_update_subscription_source( $subscription ); + } + + /** + * Attempts to to update the payment method for renewals from Sources to PaymentMethods. + * + * @param WC_Subscription $subscription The subscription for which the payment method must be updated. + */ + public function maybe_update_subscription_source( WC_Subscription $subscription ) { + try { + $this->set_subscription_updated_payment_method( $subscription ); + + $order_note = __( 'Stripe Gateway: The payment method used for renewals was updated from Sources to PaymentMethods.', 'woocommerce-gateway-stripe' ); + } catch ( \Exception $e ) { + /* translators: Reason why the subscription payment method wasn't updated */ + $order_note = sprintf( __( 'Stripe Gateway: A Source is used for renewals but could not be updated to PaymentMethods. Reason: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ); + } + + $subscription->add_order_note( $order_note ); } /** @@ -107,7 +125,7 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc // Bail out if the subscription is already using a pm_. if ( ! str_starts_with( $source_id, 'src_' ) ) { - throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. The subscription is not using a source.', $subscription_id ) ); + throw new \Exception( sprintf( 'The subscription is not using a Stripe Source for renewals.', $subscription->get_id() ) ); } // Retrieve the source object from the API. @@ -115,7 +133,7 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc // Bail out if the src_ hasn't been migrated to pm_ yet. if ( ! isset( $source_object->metadata->migrated_payment_method ) ) { - throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. The subscription is using a source that was not migrated to a payment method on the Stripe account.', $subscription_id ) ); + throw new \Exception( sprintf( 'The Source has not been migrated to PaymentMethods on the Stripe account.', $subscription->get_id() ) ); } // Get the payment method ID that was migrated from the source. @@ -134,7 +152,7 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc private function set_subscription_updated_payment_gateway_id( WC_Subscription $subscription ) { // The subscription is not using the legacy SEPA gateway ID. if ( WC_Gateway_Stripe_Sepa::ID !== $subscription->get_payment_method() ) { - throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. Subscription is not using the legacy SEPA payment method.', $subscription_id ) ); + throw new \Exception( sprintf( '---- Skipping migration of subscription #%d. Subscription is not using the legacy SEPA payment method.', $subscription->get_id() ) ); } // Add a meta to the subscription to flag that its token got updated. diff --git a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php index ea2d7a010..6bf793630 100644 --- a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php +++ b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php @@ -115,10 +115,29 @@ public function maybe_migrate_before_renewal( $subscription_id ) { $subscription = wcs_get_subscription( $subscription_id ); - if ( $subscription && $subscription->get_payment_method() === WC_Gateway_Stripe_Sepa::ID ) { + if ( ! $subscription ) { + return; + } + + // Run the full repair if the subscription is using the Legacy SEPA gateway ID. + if ( $subscription->get_payment_method() === WC_Gateway_Stripe_Sepa::ID ) { $this->repair_item( $subscription_id ); + // Unschedule the repair action as it's no longer needed. as_unschedule_action( $this->repair_hook, [ 'repair_object' => $subscription_id ] ); + + // Returning at this point because the source will be updated by the repair_item method called above. + return; + } + + // It's possible that the Legacy SEPA gateway ID was updated by the reparing above, but that the Stripe account + // hadn't been migrated from src_ to pm_ at the time. + // Thus, we keep checking if the associated payment method is a source in subsequent renewals. + $subscription_source = $subscription->get_meta( '_stripe_source_id' ); + + if ( str_starts_with( $subscription_source, 'src_' ) ) { + $token_updater = new WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update(); + $token_updater->maybe_update_subscription_source( $subscription ); } } } From c83f45b154341589281f21be0082a28ef7f56128 Mon Sep 17 00:00:00 2001 From: Danae Millan <41606954+a-danae@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:42:01 +0200 Subject: [PATCH 07/10] Update includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php Co-authored-by: James Allan --- .../class-wc-stripe-subscriptions-legacy-sepa-token-update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 7118cdb89..9f0c9509b 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -63,7 +63,7 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i } /** - * Attempts to to update the payment method for renewals from Sources to PaymentMethods. + * Attempts to update the payment method for renewals from Sources to PaymentMethods. * * @param WC_Subscription $subscription The subscription for which the payment method must be updated. */ From b13d3a958ee06bd8bd1edd56cc8d55c91c1ec139 Mon Sep 17 00:00:00 2001 From: Danae Millan <41606954+a-danae@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:49:03 +0200 Subject: [PATCH 08/10] Replace str_starts_with with strpos to maintain compatibility with PHP < 8.0 Co-authored-by: James Allan --- .../class-wc-stripe-subscriptions-legacy-sepa-token-update.php | 2 +- ...lass-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php index 9f0c9509b..a3d65b89e 100644 --- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php +++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php @@ -124,7 +124,7 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc $source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY ); // Bail out if the subscription is already using a pm_. - if ( ! str_starts_with( $source_id, 'src_' ) ) { + if ( 0 === strpos( $source_id, 'src_' ) ) { throw new \Exception( sprintf( 'The subscription is not using a Stripe Source for renewals.', $subscription->get_id() ) ); } diff --git a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php index 6bf793630..0cc138621 100644 --- a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php +++ b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php @@ -135,7 +135,7 @@ public function maybe_migrate_before_renewal( $subscription_id ) { // Thus, we keep checking if the associated payment method is a source in subsequent renewals. $subscription_source = $subscription->get_meta( '_stripe_source_id' ); - if ( str_starts_with( $subscription_source, 'src_' ) ) { + if ( 0 === strpos( $subscription_source, 'src_' ) ) { $token_updater = new WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update(); $token_updater->maybe_update_subscription_source( $subscription ); } From c239d56abac5d1f5e979bd60cbeebc97ff1fa012 Mon Sep 17 00:00:00 2001 From: Danae Millan <41606954+a-danae@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:50:49 +0200 Subject: [PATCH 09/10] Fix typo Co-authored-by: James Allan --- ...lass-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php index 0cc138621..763342a55 100644 --- a/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php +++ b/includes/migrations/class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php @@ -130,7 +130,7 @@ public function maybe_migrate_before_renewal( $subscription_id ) { return; } - // It's possible that the Legacy SEPA gateway ID was updated by the reparing above, but that the Stripe account + // It's possible that the Legacy SEPA gateway ID was updated by the repairing above, but that the Stripe account // hadn't been migrated from src_ to pm_ at the time. // Thus, we keep checking if the associated payment method is a source in subsequent renewals. $subscription_source = $subscription->get_meta( '_stripe_source_id' ); From 7631da239fcd1c9e0e88e59c760c2550a1e33932 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Mon, 8 Jul 2024 12:48:31 -0600 Subject: [PATCH 10/10] Add changelog entries --- changelog.txt | 3 +-- readme.txt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3093abd4b..f1016bee0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ = 8.5.0 - 2024-xx-xx = * Tweak - Additional visual improvement for the webhook configuration notice. * Add - Allow changing display order of payment methods in the new checkout experience. +* Add - Update the payment method associated with a subscription to a PaymentMethod when it's using a Stripe Source that was migrated to PaymentMethods. * Fix - Prevent subscriptions using Legacy SEPA from switching to Manual Renewal when disabling the Legacy experience. * Tweak - Add a notice in checkout for Cash App transactions above 2000 USD to inform customers about the decline risk. * Tweak - Improve the display of warning messages related to webhook configuration. @@ -13,8 +14,6 @@ * Fix - Ensure subscriptions purchased with iDEAL or Bancontact are correctly set to SEPA debit prior to processing the intitial payment. * Tweak - Stripe API version updated to support 2024-06-20. * Fix - Ensure SEPA tokens are attached to customers in the legacy checkout experience when the payment method is saved. This addresses subscription recurring payment "off-session" errors with SEPA. -* Tweak - Limit the configure webhooks button to 1 click per minute to prevent multiple webhook creations. -* Fix - Address Klarna currency rules to ensure correct presentment and availability based on merchant and customer locations. = 8.4.0 - 2024-06-13 = * Tweak - Resets the list of payment methods when any Stripe key is updated. diff --git a/readme.txt b/readme.txt index 097864378..4c617aa3d 100644 --- a/readme.txt +++ b/readme.txt @@ -131,6 +131,7 @@ If you get stuck, you can ask for help in the Plugin Forum. = 8.5.0 - 2024-xx-xx = * Tweak - Additional visual improvement for the webhook configuration notice. * Add - Allow changing display order of payment methods in the new checkout experience. +* Add - Update the payment method associated with a subscription to a PaymentMethod when it's using a Stripe Source that was migrated to PaymentMethods. * Fix - Prevent subscriptions using Legacy SEPA from switching to Manual Renewal when disabling the Legacy experience. * Tweak - Add a notice in checkout for Cash App transactions above 2000 USD to inform customers about the decline risk. * Tweak - Improve the display of warning messages related to webhook configuration.