From 04f162c4919d4aba5e2023373588a38b1c1b1f47 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 4 Jul 2025 10:13:32 +0100 Subject: [PATCH 01/25] Add delete_test_account boolean to the route config --- ...ss-wc-rest-payments-onboarding-controller.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index 9062e43283f..78efd7735ec 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -56,14 +56,14 @@ public function register_routes() { 'callback' => [ $this, 'create_embedded_kyc_session' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ - 'progressive' => [ + 'progressive' => [ 'required' => false, 'description' => 'Whether the session is for progressive onboarding.', // phpcs:ignore Squiz.PHP.CommentedOutCode.Found // We expect a boolean (true, false, 0, 1, '0', '1', 'true', or 'false'), but will also accept `yes`/`no`. 'type' => [ 'boolean', 'string' ], ], - 'self_assessment' => [ + 'self_assessment' => [ 'required' => false, 'description' => 'The self-assessment data.', 'type' => 'object', @@ -86,7 +86,7 @@ public function register_routes() { ], ], ], - 'capabilities' => [ + 'capabilities' => [ 'description' => 'The capabilities to request and enable for the test-drive account. Leave empty to use the default capabilities.', 'type' => 'object', 'default' => [], @@ -97,6 +97,12 @@ public function register_routes() { ], ], ], + 'delete_test_account' => [ + 'description' => 'Whether to delete the test account if it exists.', + 'type' => 'boolean', + 'default' => false, + 'required' => false, + ], ], ] ); @@ -227,11 +233,13 @@ public function create_embedded_kyc_session( WP_REST_Request $request ) { $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; $progressive = ! empty( $request->get_param( 'progressive' ) ) && filter_var( $request->get_param( 'progressive' ), FILTER_VALIDATE_BOOLEAN ); $capabilities = ! empty( $request->get_param( 'capabilities' ) ) ? wc_clean( wp_unslash( $request->get_param( 'capabilities' ) ) ) : []; + $delete_test_account = ! empty( $request->get_param( 'delete_test_account' ) ) && filter_var( $request->get_param( 'delete_test_account' ), FILTER_VALIDATE_BOOLEAN ); $account_session = $this->onboarding_service->create_embedded_kyc_session( $self_assessment_data, $progressive, - $capabilities + $capabilities, + $delete_test_account ); if ( $account_session ) { From 857fea38b7c9b7aa638460d65e7ded761260f32e Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 4 Jul 2025 10:13:51 +0100 Subject: [PATCH 02/25] Delete test account if needed --- includes/class-wc-payments-onboarding-service.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 33c81b9e642..25e4d0c7c75 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -281,12 +281,13 @@ public function should_enable_woopay( bool $default_value, array $capabilities ) * @param array $capabilities Optional. List keyed by capabilities IDs (payment methods) with boolean values * indicating whether the capability should be requested when the account is created * and enabled in the settings. + * @param boolean $delete_test_account Whether to delete the test account before creating an embedded KYC session. * * @return array Session data. * * @throws API_Exception|Exception */ - public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [] ): array { + public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [], bool $delete_test_account = false ): array { if ( ! $this->payments_api_client->is_server_connected() ) { return []; } @@ -331,6 +332,10 @@ public function create_embedded_kyc_session( array $self_assessment_data, bool $ $this->update_enabled_payment_methods_ids( $gateway, $capabilities ); } + if ( $delete_test_account ) { + $this->payments_api_client->delete_account( true ); + } + try { $account_session = $this->payments_api_client->initialize_onboarding_embedded_kyc( 'live' === $setup_mode, From 9640e206b8d955bb92cdb98d69f2c89a570a7d49 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 09:52:37 +0100 Subject: [PATCH 03/25] Add /test_drive_account/migrate_to_live route --- ...wc-rest-payments-onboarding-controller.php | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index 78efd7735ec..d94653668bc 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -220,6 +220,28 @@ public function register_routes() { ], ] ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/test_drive_account/migrate_to_live', + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'migrate_test_drive_account_to_live' ], + 'permission_callback' => [ $this, 'check_permission' ], + 'args' => [ + 'source' => [ + 'required' => false, + 'description' => 'The very first entry point the merchant entered our onboarding flow.', + 'type' => 'string', + ], + 'from' => [ + 'required' => false, + 'description' => 'The previous step in the onboarding flow leading the merchant to arrive at the current step.', + 'type' => 'string', + ], + ], + ] + ); } /** @@ -385,4 +407,26 @@ public function disable_test_drive_account( WP_REST_Request $request ) { return rest_ensure_response( [ 'success' => $result ] ); } + + /** + * Migrate test-drive account to live account. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response|WP_Error + */ + public function migrate_test_drive_account_to_live( WP_REST_Request $request ) { + $context = [ + 'from' => $request->get_param( 'from' ) ?? '', + 'source' => $request->get_param( 'source' ) ?? '', + ]; + + try { + $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context ); + } catch ( Exception $e ) { + return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); + } + + return rest_ensure_response( [ 'success' => $result ] ); + } } From f93964dd0d9f6b8d64100749f1579842d0d03db9 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 09:53:21 +0100 Subject: [PATCH 04/25] Add transients management for onboarding to live process --- .../class-wc-payments-onboarding-service.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 25e4d0c7c75..35ea061660e 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -25,6 +25,8 @@ class WC_Payments_Onboarding_Service { const ONBOARDING_CONNECTION_SUCCESS_MODAL_OPTION = 'wcpay_connection_success_modal_dismissed'; const ONBOARDING_INIT_IN_PROGRESS_TRANSIENT = 'wcpay_onboarding_init_in_progress'; + const ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT = 'wcpay_onboarding_migrate_to_live'; + // Onboarding flow sources. // We use these to identify the originating place for the current onboarding flow. // This should be very sticky as opposed to the `from` value which is meant to represent the immediately previous step. @@ -496,6 +498,33 @@ public function clear_onboarding_init_in_progress(): void { delete_transient( self::ONBOARDING_INIT_IN_PROGRESS_TRANSIENT ); } + /** + * Check whether the onboarding migration to live is in progress. + * + * @return bool Whether the onboarding migration to live is in progress. + */ + public function is_onboarding_migrate_to_live_in_progress(): bool { + return filter_var( get_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT ), FILTER_VALIDATE_BOOLEAN ); + } + + /** + * Mark the onboarding migration to live as in progress. + * + * @return void + */ + public function set_onboarding_migrate_to_live_in_progress(): void { + // Default to 3 minutes expiration in case the migration takes longer than expected, or errored. + set_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT, 'yes', 3 * MINUTE_IN_SECONDS ); + } + + /** + * Clear the onboarding migration to live in progress transient. + * + * @return void + */ + public function clear_onboarding_migrate_to_live_in_progress(): void { + delete_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT ); + } /** * Check whether the business types fetched from the cache are valid. * From cffeccbd8ccec4055d6e36c28f848ecb9873df02 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 09:54:06 +0100 Subject: [PATCH 05/25] Add early exploration for migrate_test_drive_account_to_live function --- .../class-wc-payments-onboarding-service.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 35ea061660e..5b742d61381 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -909,6 +909,41 @@ public function disable_test_drive_account( array $context ): bool { return true; } + /** + * Migrate test-drive account to live account. + * + * @param array $context Context for the migrate test drive account to live request. + * - 'from' (string) The source of the request. + * - 'source' (string) The source of the onboarding flow. + */ + public function migrate_test_drive_account_to_live( array $context ): bool { + try { + // If the account does not exist, there's nothing to migrate. + if ( ! WC_Payments::get_account_service()->is_stripe_connected() ) { + throw new API_Exception( __( 'Failed to migrate the account: account does not exist.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); + } + + // First, set the migration transient. + $this->set_onboarding_migrate_to_live_in_progress(); + + // Second, disconnect the account by inserting an empty array into the account cache. + WC_Payments::get_account_service()->overwrite_cache_with_no_account(); + + // TODO: Call the Transact Platform to do the deletion, migration, and live account creation. + + // Upon finishing, remove the lock, and refresh the account cache. + WC_Payments::get_account_service()->refresh_account_data(); + + } catch ( Exception $e ) { + throw new API_Exception( __( 'Failed to migrate the account.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); + } finally { + // Clear the migration transient. + $this->clear_onboarding_migrate_to_live_in_progress(); + } + + return true; + } + /** * Sets things up for a fresh onboarding flow. * From 3f861b8db2a638cf21d9dd082ce67b0ef96cfa76 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 09:54:11 +0100 Subject: [PATCH 06/25] Revert "Delete test account if needed" This reverts commit 857fea38b7c9b7aa638460d65e7ded761260f32e. --- includes/class-wc-payments-onboarding-service.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 5b742d61381..2929c9e9260 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -283,13 +283,12 @@ public function should_enable_woopay( bool $default_value, array $capabilities ) * @param array $capabilities Optional. List keyed by capabilities IDs (payment methods) with boolean values * indicating whether the capability should be requested when the account is created * and enabled in the settings. - * @param boolean $delete_test_account Whether to delete the test account before creating an embedded KYC session. * * @return array Session data. * * @throws API_Exception|Exception */ - public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [], bool $delete_test_account = false ): array { + public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [] ): array { if ( ! $this->payments_api_client->is_server_connected() ) { return []; } @@ -334,10 +333,6 @@ public function create_embedded_kyc_session( array $self_assessment_data, bool $ $this->update_enabled_payment_methods_ids( $gateway, $capabilities ); } - if ( $delete_test_account ) { - $this->payments_api_client->delete_account( true ); - } - try { $account_session = $this->payments_api_client->initialize_onboarding_embedded_kyc( 'live' === $setup_mode, From 4ac293932feaefce4d534b8bd250ea92df307096 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 09:54:14 +0100 Subject: [PATCH 07/25] Revert "Add delete_test_account boolean to the route config" This reverts commit 04f162c4919d4aba5e2023373588a38b1c1b1f47. --- ...ss-wc-rest-payments-onboarding-controller.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index d94653668bc..f846218cdc4 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -56,14 +56,14 @@ public function register_routes() { 'callback' => [ $this, 'create_embedded_kyc_session' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ - 'progressive' => [ + 'progressive' => [ 'required' => false, 'description' => 'Whether the session is for progressive onboarding.', // phpcs:ignore Squiz.PHP.CommentedOutCode.Found // We expect a boolean (true, false, 0, 1, '0', '1', 'true', or 'false'), but will also accept `yes`/`no`. 'type' => [ 'boolean', 'string' ], ], - 'self_assessment' => [ + 'self_assessment' => [ 'required' => false, 'description' => 'The self-assessment data.', 'type' => 'object', @@ -86,7 +86,7 @@ public function register_routes() { ], ], ], - 'capabilities' => [ + 'capabilities' => [ 'description' => 'The capabilities to request and enable for the test-drive account. Leave empty to use the default capabilities.', 'type' => 'object', 'default' => [], @@ -97,12 +97,6 @@ public function register_routes() { ], ], ], - 'delete_test_account' => [ - 'description' => 'Whether to delete the test account if it exists.', - 'type' => 'boolean', - 'default' => false, - 'required' => false, - ], ], ] ); @@ -255,13 +249,11 @@ public function create_embedded_kyc_session( WP_REST_Request $request ) { $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; $progressive = ! empty( $request->get_param( 'progressive' ) ) && filter_var( $request->get_param( 'progressive' ), FILTER_VALIDATE_BOOLEAN ); $capabilities = ! empty( $request->get_param( 'capabilities' ) ) ? wc_clean( wp_unslash( $request->get_param( 'capabilities' ) ) ) : []; - $delete_test_account = ! empty( $request->get_param( 'delete_test_account' ) ) && filter_var( $request->get_param( 'delete_test_account' ), FILTER_VALIDATE_BOOLEAN ); $account_session = $this->onboarding_service->create_embedded_kyc_session( $self_assessment_data, $progressive, - $capabilities, - $delete_test_account + $capabilities ); if ( $account_session ) { From f3779fcc217fc57bd7c0f442241ce7f46d6dcf9b Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 11:09:46 +0100 Subject: [PATCH 08/25] Pass self assessment data to migration function --- .../admin/class-wc-rest-payments-onboarding-controller.php | 2 ++ includes/class-wc-payments-onboarding-service.php | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index f846218cdc4..3a94c06c357 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -413,6 +413,8 @@ public function migrate_test_drive_account_to_live( WP_REST_Request $request ) { 'source' => $request->get_param( 'source' ) ?? '', ]; + $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; + try { $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context ); } catch ( Exception $e ) { diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 2929c9e9260..9947595bec1 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -910,8 +910,10 @@ public function disable_test_drive_account( array $context ): bool { * @param array $context Context for the migrate test drive account to live request. * - 'from' (string) The source of the request. * - 'source' (string) The source of the onboarding flow. + * @param array $self_assessment_data Self assessment data. + * @return array The account session. */ - public function migrate_test_drive_account_to_live( array $context ): bool { + public function migrate_test_drive_account_to_live( array $context, array $self_assessment_data ): array { try { // If the account does not exist, there's nothing to migrate. if ( ! WC_Payments::get_account_service()->is_stripe_connected() ) { From 10febdf8d4183be3b1ef023e781fffc22bb23126 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 11:10:41 +0100 Subject: [PATCH 09/25] Get current capabilities and use the create_embedded_kyc_session function --- includes/class-wc-payments-onboarding-service.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 9947595bec1..179378f8e76 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -920,6 +920,12 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ throw new API_Exception( __( 'Failed to migrate the account: account does not exist.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); } + $account_data = WC_Payments::get_account_service()->get_cached_account_data(); + $capabilities = array_map(function($status) { + // Consider 'active' status as true, all other statuses as false + return $status === 'active'; + }, $account_data['capabilities'] ?? []); + // First, set the migration transient. $this->set_onboarding_migrate_to_live_in_progress(); @@ -927,10 +933,14 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ WC_Payments::get_account_service()->overwrite_cache_with_no_account(); // TODO: Call the Transact Platform to do the deletion, migration, and live account creation. + // Third, disable the test drive account. + $this->disable_test_drive_account( $context ); // Upon finishing, remove the lock, and refresh the account cache. WC_Payments::get_account_service()->refresh_account_data(); + // Fourth, create an embedded KYC session. + $this->create_embedded_kyc_session( $self_assessment_data, false, $capabilities ); } catch ( Exception $e ) { throw new API_Exception( __( 'Failed to migrate the account.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); } finally { From 2f80e2a8ab3bdfe8b6834299427f96fa6506dee8 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 11:10:55 +0100 Subject: [PATCH 10/25] Move the refresh of the account data to finally --- includes/class-wc-payments-onboarding-service.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 179378f8e76..90e09db3b94 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -936,8 +936,6 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ // Third, disable the test drive account. $this->disable_test_drive_account( $context ); - // Upon finishing, remove the lock, and refresh the account cache. - WC_Payments::get_account_service()->refresh_account_data(); // Fourth, create an embedded KYC session. $this->create_embedded_kyc_session( $self_assessment_data, false, $capabilities ); @@ -946,6 +944,9 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ } finally { // Clear the migration transient. $this->clear_onboarding_migrate_to_live_in_progress(); + + // Refresh the account cache. + WC_Payments::get_account_service()->refresh_account_data(); } return true; From 5e03bda90931ebb67617c29b27c47a21f4d4ba92 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 11:11:48 +0100 Subject: [PATCH 11/25] Fix function response type in the controller --- .../class-wc-rest-payments-onboarding-controller.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index 3a94c06c357..5a7de3aeb38 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -416,11 +416,15 @@ public function migrate_test_drive_account_to_live( WP_REST_Request $request ) { $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; try { - $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context ); + $account_session = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); } catch ( Exception $e ) { return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); } - return rest_ensure_response( [ 'success' => $result ] ); + if ( $account_session ) { + $account_session['locale'] = get_user_locale(); + } + + return rest_ensure_response( $account_session ); } } From 8f28e350da9a109caae3aeed5abcbcdb4deb9381 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 14:48:13 +0100 Subject: [PATCH 12/25] If there is a migration happening, return an empty array --- includes/class-wc-payments-account.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/includes/class-wc-payments-account.php b/includes/class-wc-payments-account.php index d3320a59071..3f6b80f3665 100644 --- a/includes/class-wc-payments-account.php +++ b/includes/class-wc-payments-account.php @@ -2327,6 +2327,12 @@ public function get_cached_account_data( bool $force_refresh = false ) { return []; } + // Check if there's an ongoing migration to live payments. + if ( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ) { + // Return empty array to indicate no account is connected yet. + return []; + } + $refreshed = false; $account = $this->database_cache->get_or_add( From 1f514b688279c56c6f5038862ef874cc5b50c062 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 15:40:56 +0100 Subject: [PATCH 13/25] Update the migrate_test_drive_account_to_live function to return the account session --- .../class-wc-payments-onboarding-service.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 90e09db3b94..f54165885ca 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -920,11 +920,11 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ throw new API_Exception( __( 'Failed to migrate the account: account does not exist.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); } - $account_data = WC_Payments::get_account_service()->get_cached_account_data(); - $capabilities = array_map(function($status) { + $cached_account_data = WC_Payments::get_account_service()->get_cached_account_data(); + $capabilities = array_map(function($status) { // Consider 'active' status as true, all other statuses as false return $status === 'active'; - }, $account_data['capabilities'] ?? []); + }, $cached_account_data['capabilities'] ?? []); // First, set the migration transient. $this->set_onboarding_migrate_to_live_in_progress(); @@ -932,13 +932,11 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ // Second, disconnect the account by inserting an empty array into the account cache. WC_Payments::get_account_service()->overwrite_cache_with_no_account(); - // TODO: Call the Transact Platform to do the deletion, migration, and live account creation. - // Third, disable the test drive account. + // Third, disable the test drive account and save account settings for the live account. $this->disable_test_drive_account( $context ); - - // Fourth, create an embedded KYC session. - $this->create_embedded_kyc_session( $self_assessment_data, false, $capabilities ); + // Fourth, create an embedded KYC session using data saved while disabling the test-drive account. + $account_session = $this->create_embedded_kyc_session( $self_assessment_data, false, $capabilities ); } catch ( Exception $e ) { throw new API_Exception( __( 'Failed to migrate the account.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); } finally { @@ -949,7 +947,7 @@ public function migrate_test_drive_account_to_live( array $context, array $self_ WC_Payments::get_account_service()->refresh_account_data(); } - return true; + return $account_session; } /** From c14138b1b8cf4f4d40b3e8caeae265b0eff4a54c Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 15:43:38 +0100 Subject: [PATCH 14/25] remove extra line --- includes/class-wc-payments-onboarding-service.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index f54165885ca..2394fcac798 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -24,7 +24,6 @@ class WC_Payments_Onboarding_Service { const ONBOARDING_ELIGIBILITY_MODAL_OPTION = 'wcpay_onboarding_eligibility_modal_dismissed'; const ONBOARDING_CONNECTION_SUCCESS_MODAL_OPTION = 'wcpay_connection_success_modal_dismissed'; const ONBOARDING_INIT_IN_PROGRESS_TRANSIENT = 'wcpay_onboarding_init_in_progress'; - const ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT = 'wcpay_onboarding_migrate_to_live'; // Onboarding flow sources. From c079f800ceb9e9f640a3170c2b512ce09f073cc5 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 7 Jul 2025 16:16:45 +0100 Subject: [PATCH 15/25] add test cases --- ...t-class-wc-payments-onboarding-service.php | 194 +++++++++++++++++- 1 file changed, 192 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-class-wc-payments-onboarding-service.php b/tests/unit/test-class-wc-payments-onboarding-service.php index d65bb02653d..08749872307 100644 --- a/tests/unit/test-class-wc-payments-onboarding-service.php +++ b/tests/unit/test-class-wc-payments-onboarding-service.php @@ -31,14 +31,14 @@ class WC_Payments_Onboarding_Service_Test extends WCPAY_UnitTestCase { /** * Mock Database_Cache * - * @var MockObject + * @var Database_Cache|MockObject */ private $mock_database_cache; /** * Mock WC_Payments_Session_Service * - * @var MockObject + * @var WC_Payments_Session_Service|MockObject */ private $mock_session_service; @@ -642,4 +642,194 @@ public function data_get_source(): array { ], ]; } + + /** + * Test successful migration from test drive account to live account. + */ + public function test_migrate_test_drive_account_to_live_success() { + // Arrange. + $context = [ + 'from' => 'test_from', + 'source' => 'test_source', + ]; + $self_assessment_data = [ + 'business_type' => 'individual', + ]; + $capabilities = [ + 'card_payments' => true, + 'transfers' => false, + ]; + + // Mock account service methods + $mock_account = $this->createMock( WC_Payments_Account::class ); + WC_Payments::set_account_service( $mock_account ); + + $mock_account->expects( $this->once() ) + ->method( 'is_stripe_connected' ) + ->willReturn( true ); + + $mock_account->expects( $this->once() ) + ->method( 'get_cached_account_data' ) + ->willReturn([ + 'capabilities' => [ + 'card_payments' => 'active', + 'transfers' => 'inactive', + ], + ]); + + $mock_account->expects( $this->once() ) + ->method( 'overwrite_cache_with_no_account' ); + + // Mock expected account session response + $expected_account_session = [ + 'client_secret' => 'test_secret', + 'expires_at' => time() + 3600, + 'account_id' => 'acct_test123', + 'is_live' => true, + ]; + + // Mock create_embedded_kyc_session + $this->mock_api_client + ->method( 'initialize_onboarding_embedded_kyc' ) + ->willReturn( $expected_account_session ); + + // Act. + $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); + + // Assert. + $this->assertEquals( $expected_account_session['client_secret'], $result['clientSecret'] ); + $this->assertEquals( $expected_account_session['expires_at'], $result['expiresAt'] ); + $this->assertEquals( $expected_account_session['account_id'], $result['accountId'] ); + $this->assertEquals( $expected_account_session['is_live'], $result['isLive'] ); + $this->assertFalse( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ); + } + + /** + * Test migration failure when no account exists. + */ + public function test_migrate_test_drive_account_to_live_no_account() { + // Arrange. + $context = [ + 'from' => 'test_from', + 'source' => 'test_source', + ]; + $self_assessment_data = []; + + // Mock account service methods + $mock_account = $this->createMock( WC_Payments_Account::class ); + $mock_account->expects( $this->once() ) + ->method( 'is_stripe_connected' ) + ->willReturn( false ); + + WC_Payments::set_account_service( $mock_account ); + + // Assert. + $this->expectException( API_Exception::class ); + $this->expectExceptionMessage( 'Failed to migrate the account: account does not exist.' ); + + // Act. + $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); + } + + /** + * Test migration failure during the process. + */ + public function test_migrate_test_drive_account_to_live_failure() { + // Arrange. + $context = [ + 'from' => 'test_from', + 'source' => 'test_source', + ]; + $self_assessment_data = []; + + // Mock account service methods + $mock_account = $this->createMock( WC_Payments_Account::class ); + WC_Payments::set_account_service( $mock_account ); + + $mock_account->expects( $this->once() ) + ->method( 'is_stripe_connected' ) + ->willReturn( true ); + + $mock_account->expects( $this->once() ) + ->method( 'get_cached_account_data' ) + ->willReturn([ + 'capabilities' => [ + 'card_payments' => 'active', + ], + ]); + + $mock_account->expects( $this->once() ) + ->method( 'overwrite_cache_with_no_account' ) + ->willThrowException( new Exception( 'Test error' ) ); + + // Assert. + $this->expectException( API_Exception::class ); + $this->expectExceptionMessage( 'Failed to migrate the account.' ); + + // Act. + $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); + + // Verify migration flag is cleared even on failure + $this->assertFalse( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ); + } + + /** + * Test that capabilities are correctly mapped from account data. + */ + public function test_migrate_test_drive_account_to_live_capabilities_mapping() { + // Arrange. + $context = [ + 'from' => 'test_from', + 'source' => 'test_source', + ]; + $self_assessment_data = []; + + // Mock account service methods + $mock_account = $this->createMock( WC_Payments_Account::class ); + WC_Payments::set_account_service( $mock_account ); + + $mock_account->expects( $this->once() ) + ->method( 'is_stripe_connected' ) + ->willReturn( true ); + + $mock_account->expects( $this->once() ) + ->method( 'get_cached_account_data' ) + ->willReturn([ + 'capabilities' => [ + 'card_payments' => 'active', + 'transfers' => 'inactive', + 'sepa_debit_payments' => 'pending', + 'sofort_payments' => 'active', + ], + ]); + + $mock_account->expects( $this->once() ) + ->method( 'overwrite_cache_with_no_account' ); + + // Mock expected account session response with mapped capabilities + $expected_account_session = [ + 'client_secret' => 'test_secret', + 'expires_at' => time() + 3600, + 'account_id' => 'acct_test123', + 'is_live' => true, + ]; + + // Mock create_embedded_kyc_session and verify capabilities are correctly mapped + $this->mock_api_client + ->method( 'initialize_onboarding_embedded_kyc' ) + ->willReturnCallback(function($data, $progressive, $capabilities) use ($expected_account_session) { + // Verify capabilities are correctly mapped from status to boolean + $this->assertEquals(true, $capabilities['card_payments']); + $this->assertEquals(false, $capabilities['transfers']); + $this->assertEquals(false, $capabilities['sepa_debit_payments']); + $this->assertEquals(true, $capabilities['sofort_payments']); + return $expected_account_session; + }); + + // Act. + $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); + + // Assert basic response + $this->assertEquals( $expected_account_session['client_secret'], $result['clientSecret'] ); + } } From c758131a1b35f46636b8bcba0aba2ef6b88ee414 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:55:08 +0100 Subject: [PATCH 16/25] Revert new route definition --- ...wc-rest-payments-onboarding-controller.php | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index 5a7de3aeb38..9062e43283f 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -214,28 +214,6 @@ public function register_routes() { ], ] ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/test_drive_account/migrate_to_live', - [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'migrate_test_drive_account_to_live' ], - 'permission_callback' => [ $this, 'check_permission' ], - 'args' => [ - 'source' => [ - 'required' => false, - 'description' => 'The very first entry point the merchant entered our onboarding flow.', - 'type' => 'string', - ], - 'from' => [ - 'required' => false, - 'description' => 'The previous step in the onboarding flow leading the merchant to arrive at the current step.', - 'type' => 'string', - ], - ], - ] - ); } /** @@ -399,32 +377,4 @@ public function disable_test_drive_account( WP_REST_Request $request ) { return rest_ensure_response( [ 'success' => $result ] ); } - - /** - * Migrate test-drive account to live account. - * - * @param WP_REST_Request $request Request object. - * - * @return WP_REST_Response|WP_Error - */ - public function migrate_test_drive_account_to_live( WP_REST_Request $request ) { - $context = [ - 'from' => $request->get_param( 'from' ) ?? '', - 'source' => $request->get_param( 'source' ) ?? '', - ]; - - $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; - - try { - $account_session = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); - } catch ( Exception $e ) { - return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); - } - - if ( $account_session ) { - $account_session['locale'] = get_user_locale(); - } - - return rest_ensure_response( $account_session ); - } } From fa834a2b94a88cb64b494d2a289c2e3cdb342a83 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:55:37 +0100 Subject: [PATCH 17/25] Add migrate to live query param --- .../class-wc-rest-payments-onboarding-controller.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/includes/admin/class-wc-rest-payments-onboarding-controller.php b/includes/admin/class-wc-rest-payments-onboarding-controller.php index 9062e43283f..6df13680dff 100644 --- a/includes/admin/class-wc-rest-payments-onboarding-controller.php +++ b/includes/admin/class-wc-rest-payments-onboarding-controller.php @@ -97,6 +97,12 @@ public function register_routes() { ], ], ], + 'migrate_to_live' => [ + 'required' => false, + 'description' => 'Whether to migrate the account to live.', + 'type' => 'boolean', + 'default' => false, + ], ], ] ); @@ -227,11 +233,13 @@ public function create_embedded_kyc_session( WP_REST_Request $request ) { $self_assessment_data = ! empty( $request->get_param( 'self_assessment' ) ) ? wc_clean( wp_unslash( $request->get_param( 'self_assessment' ) ) ) : []; $progressive = ! empty( $request->get_param( 'progressive' ) ) && filter_var( $request->get_param( 'progressive' ), FILTER_VALIDATE_BOOLEAN ); $capabilities = ! empty( $request->get_param( 'capabilities' ) ) ? wc_clean( wp_unslash( $request->get_param( 'capabilities' ) ) ) : []; + $migrate_to_live = ! empty( $request->get_param( 'migrate_to_live' ) ) && filter_var( $request->get_param( 'migrate_to_live' ), FILTER_VALIDATE_BOOLEAN ); $account_session = $this->onboarding_service->create_embedded_kyc_session( $self_assessment_data, $progressive, - $capabilities + $capabilities, + $migrate_to_live ); if ( $account_session ) { From e7f66ac1fe83efc6d723538b215f3414f27d6d1d Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:56:24 +0100 Subject: [PATCH 18/25] Add migrate_to_live to onboarding request --- includes/wc-payment-api/class-wc-payments-api-client.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/wc-payment-api/class-wc-payments-api-client.php b/includes/wc-payment-api/class-wc-payments-api-client.php index 9d1233bd0e9..7b2a51ceceb 100644 --- a/includes/wc-payment-api/class-wc-payments-api-client.php +++ b/includes/wc-payment-api/class-wc-payments-api-client.php @@ -1107,7 +1107,8 @@ public function initialize_onboarding_embedded_kyc( array $account_data = [], array $actioned_notes = [], bool $progressive = false, - ?string $referral_code = null + ?string $referral_code = null, + ?bool $migrate_to_live = false ): array { $request_args = apply_filters( 'wc_payments_get_onboarding_data_args', @@ -1118,6 +1119,7 @@ public function initialize_onboarding_embedded_kyc( 'actioned_notes' => $actioned_notes, 'create_live_account' => $live_account, 'progressive' => $progressive, + 'migrate_to_live' => $migrate_to_live, ] ); From 4e5e93ba0caf8fc9985ea5e6559978d25cbccbcf Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:56:38 +0100 Subject: [PATCH 19/25] remove unnecessary tests --- ...t-class-wc-payments-onboarding-service.php | 190 ------------------ 1 file changed, 190 deletions(-) diff --git a/tests/unit/test-class-wc-payments-onboarding-service.php b/tests/unit/test-class-wc-payments-onboarding-service.php index 08749872307..d03eea595c6 100644 --- a/tests/unit/test-class-wc-payments-onboarding-service.php +++ b/tests/unit/test-class-wc-payments-onboarding-service.php @@ -642,194 +642,4 @@ public function data_get_source(): array { ], ]; } - - /** - * Test successful migration from test drive account to live account. - */ - public function test_migrate_test_drive_account_to_live_success() { - // Arrange. - $context = [ - 'from' => 'test_from', - 'source' => 'test_source', - ]; - $self_assessment_data = [ - 'business_type' => 'individual', - ]; - $capabilities = [ - 'card_payments' => true, - 'transfers' => false, - ]; - - // Mock account service methods - $mock_account = $this->createMock( WC_Payments_Account::class ); - WC_Payments::set_account_service( $mock_account ); - - $mock_account->expects( $this->once() ) - ->method( 'is_stripe_connected' ) - ->willReturn( true ); - - $mock_account->expects( $this->once() ) - ->method( 'get_cached_account_data' ) - ->willReturn([ - 'capabilities' => [ - 'card_payments' => 'active', - 'transfers' => 'inactive', - ], - ]); - - $mock_account->expects( $this->once() ) - ->method( 'overwrite_cache_with_no_account' ); - - // Mock expected account session response - $expected_account_session = [ - 'client_secret' => 'test_secret', - 'expires_at' => time() + 3600, - 'account_id' => 'acct_test123', - 'is_live' => true, - ]; - - // Mock create_embedded_kyc_session - $this->mock_api_client - ->method( 'initialize_onboarding_embedded_kyc' ) - ->willReturn( $expected_account_session ); - - // Act. - $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); - - // Assert. - $this->assertEquals( $expected_account_session['client_secret'], $result['clientSecret'] ); - $this->assertEquals( $expected_account_session['expires_at'], $result['expiresAt'] ); - $this->assertEquals( $expected_account_session['account_id'], $result['accountId'] ); - $this->assertEquals( $expected_account_session['is_live'], $result['isLive'] ); - $this->assertFalse( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ); - } - - /** - * Test migration failure when no account exists. - */ - public function test_migrate_test_drive_account_to_live_no_account() { - // Arrange. - $context = [ - 'from' => 'test_from', - 'source' => 'test_source', - ]; - $self_assessment_data = []; - - // Mock account service methods - $mock_account = $this->createMock( WC_Payments_Account::class ); - $mock_account->expects( $this->once() ) - ->method( 'is_stripe_connected' ) - ->willReturn( false ); - - WC_Payments::set_account_service( $mock_account ); - - // Assert. - $this->expectException( API_Exception::class ); - $this->expectExceptionMessage( 'Failed to migrate the account: account does not exist.' ); - - // Act. - $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); - } - - /** - * Test migration failure during the process. - */ - public function test_migrate_test_drive_account_to_live_failure() { - // Arrange. - $context = [ - 'from' => 'test_from', - 'source' => 'test_source', - ]; - $self_assessment_data = []; - - // Mock account service methods - $mock_account = $this->createMock( WC_Payments_Account::class ); - WC_Payments::set_account_service( $mock_account ); - - $mock_account->expects( $this->once() ) - ->method( 'is_stripe_connected' ) - ->willReturn( true ); - - $mock_account->expects( $this->once() ) - ->method( 'get_cached_account_data' ) - ->willReturn([ - 'capabilities' => [ - 'card_payments' => 'active', - ], - ]); - - $mock_account->expects( $this->once() ) - ->method( 'overwrite_cache_with_no_account' ) - ->willThrowException( new Exception( 'Test error' ) ); - - // Assert. - $this->expectException( API_Exception::class ); - $this->expectExceptionMessage( 'Failed to migrate the account.' ); - - // Act. - $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); - - // Verify migration flag is cleared even on failure - $this->assertFalse( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ); - } - - /** - * Test that capabilities are correctly mapped from account data. - */ - public function test_migrate_test_drive_account_to_live_capabilities_mapping() { - // Arrange. - $context = [ - 'from' => 'test_from', - 'source' => 'test_source', - ]; - $self_assessment_data = []; - - // Mock account service methods - $mock_account = $this->createMock( WC_Payments_Account::class ); - WC_Payments::set_account_service( $mock_account ); - - $mock_account->expects( $this->once() ) - ->method( 'is_stripe_connected' ) - ->willReturn( true ); - - $mock_account->expects( $this->once() ) - ->method( 'get_cached_account_data' ) - ->willReturn([ - 'capabilities' => [ - 'card_payments' => 'active', - 'transfers' => 'inactive', - 'sepa_debit_payments' => 'pending', - 'sofort_payments' => 'active', - ], - ]); - - $mock_account->expects( $this->once() ) - ->method( 'overwrite_cache_with_no_account' ); - - // Mock expected account session response with mapped capabilities - $expected_account_session = [ - 'client_secret' => 'test_secret', - 'expires_at' => time() + 3600, - 'account_id' => 'acct_test123', - 'is_live' => true, - ]; - - // Mock create_embedded_kyc_session and verify capabilities are correctly mapped - $this->mock_api_client - ->method( 'initialize_onboarding_embedded_kyc' ) - ->willReturnCallback(function($data, $progressive, $capabilities) use ($expected_account_session) { - // Verify capabilities are correctly mapped from status to boolean - $this->assertEquals(true, $capabilities['card_payments']); - $this->assertEquals(false, $capabilities['transfers']); - $this->assertEquals(false, $capabilities['sepa_debit_payments']); - $this->assertEquals(true, $capabilities['sofort_payments']); - return $expected_account_session; - }); - - // Act. - $result = $this->onboarding_service->migrate_test_drive_account_to_live( $context, $self_assessment_data ); - - // Assert basic response - $this->assertEquals( $expected_account_session['client_secret'], $result['clientSecret'] ); - } } From 4846ae5e8ea32eb494b7f79229979b1cd4787fd2 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:57:06 +0100 Subject: [PATCH 20/25] revert earlier changes around new endpoint and lockers --- .../class-wc-payments-onboarding-service.php | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 2394fcac798..33c81b9e642 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -24,7 +24,6 @@ class WC_Payments_Onboarding_Service { const ONBOARDING_ELIGIBILITY_MODAL_OPTION = 'wcpay_onboarding_eligibility_modal_dismissed'; const ONBOARDING_CONNECTION_SUCCESS_MODAL_OPTION = 'wcpay_connection_success_modal_dismissed'; const ONBOARDING_INIT_IN_PROGRESS_TRANSIENT = 'wcpay_onboarding_init_in_progress'; - const ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT = 'wcpay_onboarding_migrate_to_live'; // Onboarding flow sources. // We use these to identify the originating place for the current onboarding flow. @@ -492,33 +491,6 @@ public function clear_onboarding_init_in_progress(): void { delete_transient( self::ONBOARDING_INIT_IN_PROGRESS_TRANSIENT ); } - /** - * Check whether the onboarding migration to live is in progress. - * - * @return bool Whether the onboarding migration to live is in progress. - */ - public function is_onboarding_migrate_to_live_in_progress(): bool { - return filter_var( get_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT ), FILTER_VALIDATE_BOOLEAN ); - } - - /** - * Mark the onboarding migration to live as in progress. - * - * @return void - */ - public function set_onboarding_migrate_to_live_in_progress(): void { - // Default to 3 minutes expiration in case the migration takes longer than expected, or errored. - set_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT, 'yes', 3 * MINUTE_IN_SECONDS ); - } - - /** - * Clear the onboarding migration to live in progress transient. - * - * @return void - */ - public function clear_onboarding_migrate_to_live_in_progress(): void { - delete_transient( self::ONBOARDING_MIGRATE_TO_LIVE_TRANSIENT ); - } /** * Check whether the business types fetched from the cache are valid. * @@ -903,52 +875,6 @@ public function disable_test_drive_account( array $context ): bool { return true; } - /** - * Migrate test-drive account to live account. - * - * @param array $context Context for the migrate test drive account to live request. - * - 'from' (string) The source of the request. - * - 'source' (string) The source of the onboarding flow. - * @param array $self_assessment_data Self assessment data. - * @return array The account session. - */ - public function migrate_test_drive_account_to_live( array $context, array $self_assessment_data ): array { - try { - // If the account does not exist, there's nothing to migrate. - if ( ! WC_Payments::get_account_service()->is_stripe_connected() ) { - throw new API_Exception( __( 'Failed to migrate the account: account does not exist.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); - } - - $cached_account_data = WC_Payments::get_account_service()->get_cached_account_data(); - $capabilities = array_map(function($status) { - // Consider 'active' status as true, all other statuses as false - return $status === 'active'; - }, $cached_account_data['capabilities'] ?? []); - - // First, set the migration transient. - $this->set_onboarding_migrate_to_live_in_progress(); - - // Second, disconnect the account by inserting an empty array into the account cache. - WC_Payments::get_account_service()->overwrite_cache_with_no_account(); - - // Third, disable the test drive account and save account settings for the live account. - $this->disable_test_drive_account( $context ); - - // Fourth, create an embedded KYC session using data saved while disabling the test-drive account. - $account_session = $this->create_embedded_kyc_session( $self_assessment_data, false, $capabilities ); - } catch ( Exception $e ) { - throw new API_Exception( __( 'Failed to migrate the account.', 'woocommerce-payments' ), 'wcpay-onboarding-account-error', 400 ); - } finally { - // Clear the migration transient. - $this->clear_onboarding_migrate_to_live_in_progress(); - - // Refresh the account cache. - WC_Payments::get_account_service()->refresh_account_data(); - } - - return $account_session; - } - /** * Sets things up for a fresh onboarding flow. * From 83dde5a03bac8c17eb6943edc453e09638849f3c Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:57:27 +0100 Subject: [PATCH 21/25] accept boolean for create_embedded_kyc_session and pass it down the chain --- includes/class-wc-payments-onboarding-service.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index 33c81b9e642..ebf3b492755 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -286,7 +286,7 @@ public function should_enable_woopay( bool $default_value, array $capabilities ) * * @throws API_Exception|Exception */ - public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [] ): array { + public function create_embedded_kyc_session( array $self_assessment_data, bool $progressive = false, array $capabilities = [], bool $migrate_to_live = false ): array { if ( ! $this->payments_api_client->is_server_connected() ) { return []; } @@ -339,7 +339,8 @@ public function create_embedded_kyc_session( array $self_assessment_data, bool $ WC_Payments_Utils::array_filter_recursive( $account_data ), // nosemgrep: audit.php.lang.misc.array-filter-no-callback -- output of array_filter is escaped. $actioned_notes, $progressive, - $this->get_referral_code() + $this->get_referral_code(), + $migrate_to_live ); } catch ( API_Exception $e ) { $this->clear_onboarding_init_in_progress(); From e93b576bb0be1037112d6c26318d41c62a1b70ec Mon Sep 17 00:00:00 2001 From: Ahmed Date: Wed, 9 Jul 2025 14:57:49 +0100 Subject: [PATCH 22/25] Check for is_onboarding_init_in_progress instead of is_onboarding_migrate_to_live_in_progress --- includes/class-wc-payments-account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-payments-account.php b/includes/class-wc-payments-account.php index 3f6b80f3665..c9836fa1da7 100644 --- a/includes/class-wc-payments-account.php +++ b/includes/class-wc-payments-account.php @@ -2328,7 +2328,7 @@ public function get_cached_account_data( bool $force_refresh = false ) { } // Check if there's an ongoing migration to live payments. - if ( $this->onboarding_service->is_onboarding_migrate_to_live_in_progress() ) { + if ( $this->onboarding_service->is_onboarding_init_in_progress() ) { // Return empty array to indicate no account is connected yet. return []; } From 84f62023bdef05212e41483520647b8358ee31b4 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 11 Jul 2025 11:05:08 +0100 Subject: [PATCH 23/25] Fix failing test --- tests/unit/test-class-wc-payments-account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test-class-wc-payments-account.php b/tests/unit/test-class-wc-payments-account.php index 7d42386e11f..bbdf887a345 100644 --- a/tests/unit/test-class-wc-payments-account.php +++ b/tests/unit/test-class-wc-payments-account.php @@ -1154,7 +1154,7 @@ public function test_maybe_handle_onboarding_init_stripe_onboarding_another_onbo // There is another onboarding started. $this->mock_onboarding_service - ->expects( $this->once() ) + ->expects( $this->atLeastOnce() ) ->method( 'is_onboarding_init_in_progress' ) ->willReturn( true ); From 91eb1e5a13d80b53423d4cb1d1484a08ea10d71f Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 11 Jul 2025 11:43:07 +0100 Subject: [PATCH 24/25] Add migrate_to_live flag --- includes/class-wc-payments-onboarding-service.php | 1 + includes/wc-payment-api/class-wc-payments-api-client.php | 1 + 2 files changed, 2 insertions(+) diff --git a/includes/class-wc-payments-onboarding-service.php b/includes/class-wc-payments-onboarding-service.php index ebf3b492755..aa69d6081da 100644 --- a/includes/class-wc-payments-onboarding-service.php +++ b/includes/class-wc-payments-onboarding-service.php @@ -281,6 +281,7 @@ public function should_enable_woopay( bool $default_value, array $capabilities ) * @param array $capabilities Optional. List keyed by capabilities IDs (payment methods) with boolean values * indicating whether the capability should be requested when the account is created * and enabled in the settings. + * @param bool $migrate_to_live Whether to migrate the account to live. * * @return array Session data. * diff --git a/includes/wc-payment-api/class-wc-payments-api-client.php b/includes/wc-payment-api/class-wc-payments-api-client.php index 7b2a51ceceb..327c2fba1d1 100644 --- a/includes/wc-payment-api/class-wc-payments-api-client.php +++ b/includes/wc-payment-api/class-wc-payments-api-client.php @@ -1095,6 +1095,7 @@ public function get_onboarding_data( * @param array $actioned_notes Actioned notes to be sent. * @param bool $progressive Whether progressive onboarding should be enabled for this onboarding. * @param ?string $referral_code Referral code to be used for onboarding. + * @param ?bool $migrate_to_live Whether to migrate the account to live. * * @return array * From 39d91f7639d951cacb66923d9381abec7f09cb0d Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 11 Jul 2025 13:09:17 +0100 Subject: [PATCH 25/25] add changelog --- changelog/account-migration-feature | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/account-migration-feature diff --git a/changelog/account-migration-feature b/changelog/account-migration-feature new file mode 100644 index 00000000000..673d2362b31 --- /dev/null +++ b/changelog/account-migration-feature @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Make it possible to migrate a test account to a live account