onboarding_service = $onboarding_service; } /** * Configure REST API routes. */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base . '/kyc/session', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'create_embedded_kyc_session' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ 'self_assessment' => [ 'required' => false, 'description' => 'The self-assessment data.', 'type' => 'object', 'properties' => [ 'country' => [ 'type' => 'string', 'description' => 'The country code where the company is legally registered.', ], 'business_type' => [ 'type' => 'string', 'description' => 'The company incorporation type.', ], 'mcc' => [ 'type' => 'string', 'description' => 'The merchant category code. This can either be a true MCC or an MCCs tree item id from the onboarding form.', ], 'site' => [ 'type' => 'string', 'description' => 'The URL of the site.', ], ], ], 'capabilities' => [ 'description' => 'The capabilities to request and enable for the test-drive account. Leave empty to use the default capabilities.', 'type' => 'object', 'default' => [], 'required' => false, 'properties' => [ '*' => [ 'type' => 'boolean', ], ], ], 'mode' => [ 'description' => 'The account mode the user selected: live or test. Overrides environment-based auto-detection (except dev mode).', 'type' => 'string', 'required' => false, 'enum' => [ 'live', 'test' ], ], ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/kyc/finalize', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'finalize_embedded_kyc' ], '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', ], ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/reset', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'reset_onboarding' ], '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', ], ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/fields', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_fields' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/business_types', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_business_types' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/test_drive_account/init', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'init_test_drive_account' ], 'permission_callback' => [ $this, 'check_permission' ], 'args' => [ 'country' => [ 'type' => 'string', 'description' => 'The country code for which to create the test-drive account.', 'required' => false, ], 'capabilities' => [ 'description' => 'The capabilities to request and enable for the test-drive account. Leave empty to use the default capabilities.', 'type' => 'object', 'default' => [], 'required' => false, 'properties' => [ '*' => [ 'type' => 'boolean', ], ], ], 'account_data' => [ 'description' => 'Additional account data to pass to the platform during account creation.', 'type' => 'object', 'default' => [], 'required' => false, 'properties' => [ 'extra_bootstrapping' => [ 'type' => 'boolean' ], ], 'additionalProperties' => false, ], ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/test_drive_account/disable', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'disable_test_drive_account' ], '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', ], ], ] ); } /** * Create an account embedded KYC session via the API. * * @param WP_REST_Request $request Request object. * * @return WP_Error|WP_REST_Response Response object containing the account session, or an error if session creation failed. */ 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' ) ) ) : []; $capabilities = ! empty( $request->get_param( 'capabilities' ) ) ? wc_clean( wp_unslash( $request->get_param( 'capabilities' ) ) ) : []; $mode = ! empty( $request->get_param( 'mode' ) ) ? sanitize_text_field( $request->get_param( 'mode' ) ) : null; try { $account_session = $this->onboarding_service->create_embedded_kyc_session( $self_assessment_data, $capabilities, $mode ); } catch ( Exception $e ) { Logger::error( 'Failed to create embedded KYC session: ' . $e->getMessage() ); return new WP_Error( 'wcpay_onboarding_duplicate_session', $e->getMessage(), [ 'status' => 409 ] ); } if ( empty( $account_session ) ) { WC_Payments_Utils::log_to_wc( 'Failed to create embedded KYC session: Empty response from onboarding service.' ); } elseif ( empty( $account_session['publishableKey'] ) ) { WC_Payments_Utils::log_to_wc( sprintf( 'Embedded KYC session missing publishableKey. Session keys: %s.', implode( ', ', array_keys( $account_session ) ) ), 'warning' ); } if ( $account_session ) { $account_session['locale'] = get_user_locale(); } return rest_ensure_response( $account_session ); } /** * Finalize the embedded KYC session via the API. * * @param WP_REST_Request $request Request object. * * @return WP_REST_Response|WP_Error */ public function finalize_embedded_kyc( WP_REST_Request $request ) { $source = $request->get_param( 'source' ) ?? ''; $from = $request->get_param( 'from' ) ?? ''; $actioned_notes = WC_Payments_Onboarding_Service::get_actioned_notes(); // Call the API to finalize the onboarding. try { $response = $this->onboarding_service->finalize_embedded_kyc( get_user_locale(), $source, $actioned_notes ); } catch ( Exception $e ) { return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); } // Handle some post-onboarding tasks and get the redirect params. $finalize = WC_Payments::get_account_service()->finalize_embedded_connection( $response['mode'], [ 'promo' => $response['promotion_id'] ?? '', 'from' => $from, 'source' => $source, ] ); // Return the response, the client will handle the redirect. return rest_ensure_response( array_merge( $response, $finalize ) ); } /** * Reset the onboarding via the API. * * @param WP_REST_Request $request Request object. * * @return WP_REST_Response|WP_Error */ public function reset_onboarding( WP_REST_Request $request ) { $context = [ 'from' => $request->get_param( 'from' ) ?? '', 'source' => $request->get_param( 'source' ) ?? '', ]; try { $result = $this->onboarding_service->reset_onboarding( $context ); } catch ( Exception $e ) { return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); } return rest_ensure_response( [ 'success' => $result ] ); } /** * Get fields data via API. * * @return WP_REST_Response|WP_Error */ public function get_fields() { $fields = $this->onboarding_service->get_fields_data( get_user_locale() ); if ( is_null( $fields ) ) { return new WP_Error( self::RESULT_BAD_REQUEST, 'Failed to retrieve the onboarding fields.', [ 'status' => 400 ] ); } return rest_ensure_response( [ 'data' => $fields ] ); } /** * Get business types via API. * * @return WP_REST_Response|WP_Error */ public function get_business_types() { $business_types = $this->onboarding_service->get_cached_business_types(); return rest_ensure_response( [ 'data' => $business_types ] ); } /** * Initialize a test-drive account. * * @param WP_REST_Request $request Request object. * * @return WP_REST_Response|WP_Error */ public function init_test_drive_account( WP_REST_Request $request ) { $country = $request->get_param( 'country' ); if ( empty( $country ) ) { // Fall back to the store's base country if no country is provided. $country = WC()->countries->get_base_country() ?? 'US'; } try { $success = $this->onboarding_service->init_test_drive_account( $country, $request->get_param( 'capabilities' ) ?? [], $request->get_param( 'account_data' ) ?? [] ); } catch ( Exception $e ) { return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); } return rest_ensure_response( [ 'success' => $success, ] ); } /** * Disable Test Drive account API. * * @param WP_REST_Request $request Request object. * * @return WP_REST_Response|WP_Error */ public function disable_test_drive_account( WP_REST_Request $request ) { $context = [ 'from' => $request->get_param( 'from' ) ?? '', 'source' => $request->get_param( 'source' ) ?? '', ]; try { $result = $this->onboarding_service->disable_test_drive_account( $context ); } catch ( Exception $e ) { return new WP_Error( self::RESULT_BAD_REQUEST, $e->getMessage(), [ 'status' => 400 ] ); } return rest_ensure_response( [ 'success' => $result ] ); } }