Skip to content

Commit

Permalink
Add support for utilizing NOX capabilities as URL parameters during a…
Browse files Browse the repository at this point in the history
…ccount creation (#9947)

Co-authored-by: oaratovskyi <[email protected]>
Co-authored-by: Oleksandr Aratovskyi <[email protected]>
Co-authored-by: Vlad Olaru <[email protected]>
  • Loading branch information
4 people authored Dec 15, 2024
1 parent 009bc44 commit cb65e92
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: dev

Add support for utilizing NOX capabilities as URL parameters during account creation.
1 change: 1 addition & 0 deletions client/connect-account-page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ const ConnectAccountPage: React.FC = () => {

const customizedConnectUrl = addQueryArgs( connectUrl, {
test_drive: 'true',
capabilities: urlParams.get( 'capabilities' ) || '',
} );

const updateProgress = setInterval( updateLoaderProgress, 2500, 40, 5 );
Expand Down
2 changes: 2 additions & 0 deletions client/onboarding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ export const createAccountSession = async (
data: OnboardingFields,
isPoEligible: boolean
): Promise< AccountKycSession > => {
const urlParams = new URLSearchParams( window.location.search );
return await apiFetch< AccountKycSession >( {
path: addQueryArgs( `${ NAMESPACE }/onboarding/kyc/session`, {
self_assessment: fromDotNotation( data ),
capabilities: urlParams.get( 'capabilities' ) || '',
progressive: isPoEligible,
} ),
method: 'GET',
Expand Down
44 changes: 34 additions & 10 deletions includes/class-wc-payments-account.php
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ public function maybe_handle_onboarding() {
// If there is a working one, we can proceed with the Stripe account handling.
try {
$this->maybe_init_jetpack_connection(
// Carry over all the important GET params, so we have them after the Jetpack connection setup.
// Carry over all the important GET params, so we have them after the Jetpack connection setup.
add_query_arg(
[
'promo' => ! empty( $incentive_id ) ? $incentive_id : false,
Expand All @@ -1506,6 +1506,10 @@ public function maybe_handle_onboarding() {
'test_mode' => $should_onboard_in_test_mode ? 'true' : false,
'test_drive' => $create_test_drive_account ? 'true' : false,
'auto_start_test_drive_onboarding' => $auto_start_test_drive_onboarding ? 'true' : false,
// These are starting capabilities for the account.
// They are collected by the payment method step of the
// WC Payments settings page native onboarding experience.
'capabilities' => rawurlencode( wp_json_encode( $this->onboarding_service->get_capabilities_from_request() ) ),
'from' => WC_Payments_Onboarding_Service::FROM_WPCOM_CONNECTION,
'source' => $onboarding_source,
'redirect_to_settings_page' => $redirect_to_settings_page ? 'true' : false,
Expand Down Expand Up @@ -1534,13 +1538,19 @@ public function maybe_handle_onboarding() {
&& WC_Payments_Onboarding_Service::FROM_ONBOARDING_WIZARD !== $from
&& ! $this->is_stripe_connected() ) {

$additional_params = [
'source' => $onboarding_source,
];

if ( $this->onboarding_service->get_capabilities_from_request() ) {
$additional_params['capabilities'] = rawurlencode( wp_json_encode( $this->onboarding_service->get_capabilities_from_request() ) );
}

$this->redirect_service->redirect_to_onboarding_wizard(
// When we redirect to the onboarding wizard, we carry over the `from`, if we have it.
// This is because there is no interim step between the user clicking the connect link and the onboarding wizard.
! empty( $from ) ? $from : $next_step_from,
[
'source' => $onboarding_source,
]
$additional_params
);
return;
}
Expand Down Expand Up @@ -1573,11 +1583,15 @@ public function maybe_handle_onboarding() {
null,
$from, // Carry over `from` since we are doing a short-circuit.
[
'promo' => ! empty( $incentive_id ) ? $incentive_id : false,
'test_drive' => 'true',
'promo' => ! empty( $incentive_id ) ? $incentive_id : false,
'test_drive' => 'true',
'auto_start_test_drive_onboarding' => 'true', // This is critical.
'test_mode' => $should_onboard_in_test_mode ? 'true' : false,
'source' => $onboarding_source,
// These are starting capabilities for the account.
// They are collected by the payment method step of the
// WC Payments settings page native onboarding experience.
'capabilities' => rawurlencode( wp_json_encode( $this->onboarding_service->get_capabilities_from_request() ) ),
'test_mode' => $should_onboard_in_test_mode ? 'true' : false,
'source' => $onboarding_source,
'redirect_to_settings_page' => $redirect_to_settings_page ? 'true' : false,
]
);
Expand Down Expand Up @@ -1982,6 +1996,7 @@ private function init_stripe_onboarding( string $setup_mode, string $wcpay_conne
}

$self_assessment_data = isset( $_GET['self_assessment'] ) ? wc_clean( wp_unslash( $_GET['self_assessment'] ) ) : [];

if ( 'test_drive' === $setup_mode ) {
// If we get to the overview page, we want to show the success message.
$return_url = add_query_arg( 'wcpay-sandbox-success', 'true', $return_url );
Expand All @@ -1996,7 +2011,14 @@ private function init_stripe_onboarding( string $setup_mode, string $wcpay_conne
];

$user_data = $this->onboarding_service->get_onboarding_user_data();
$account_data = $this->onboarding_service->get_account_data( $setup_mode, $self_assessment_data );
$account_data = $this->onboarding_service->get_account_data(
$setup_mode,
$self_assessment_data,
// These are starting capabilities for the account.
// They are collected by the payment method step of the
// WC Payments settings page native onboarding experience.
$this->onboarding_service->get_capabilities_from_request()
);

$onboarding_data = $this->payments_api_client->get_onboarding_data(
'live' === $setup_mode,
Expand Down Expand Up @@ -2594,7 +2616,9 @@ public function get_lifetime_total_payment_volume(): int {
}

/**
* Extract the test drive settings from the account data that we want to store for the live account.
* Extract the useful test drive settings from the account data.
*
* We will use this data to migrate the test drive settings when onboarding the live account.
* ATM we only store the enabled payment methods.
*
* @return array The test drive settings for the live account.
Expand Down
108 changes: 103 additions & 5 deletions includes/class-wc-payments-onboarding-service.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,69 @@ function () use ( $country_code, $locale ) {
);
}

/**
* Get the onboarding capabilities from the request.
*
* The capabilities are expected to be passed as an array of capabilities keyed by the capability ID and
* with boolean values. If the value is true, the capability is requested when the account is created.
*
* @return array The standardized capabilities that were passed in the request.
* Empty array if no capabilities were passed or none were valid.
*/
public function get_capabilities_from_request(): array {
$capabilities = [];

if ( empty( $_REQUEST['capabilities'] ) ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended
return $capabilities;
}

// Try to extract the capabilities.
// They might be already decoded or not, so we need to handle both cases.
// We expect them to be an array.
// We disable the warning because we have our own sanitization and validation.
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$capabilities = wp_unslash( $_REQUEST['capabilities'] );
if ( ! is_array( $capabilities ) ) {
$capabilities = json_decode( $capabilities, true ) ?? [];
}

if ( empty( $capabilities ) ) {
return [];
}

// Sanitize and validate.
$capabilities = array_combine(
array_map(
function ( $key ) {
// Keep numeric keys as integers so we can remove them later.
if ( is_numeric( $key ) ) {
return intval( $key );
}

return sanitize_text_field( $key );
},
array_keys( $capabilities )
),
array_map(
function ( $value ) {
return filter_var( $value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
},
$capabilities
)
);

// Filter out any invalid entries.
$capabilities = array_filter(
$capabilities,
function ( $value, $key ) {
return is_string( $key ) && is_bool( $value );
},
ARRAY_FILTER_USE_BOTH
);

return $capabilities;
}

/**
* Retrieve the embedded KYC session and handle initial account creation (if necessary).
*
Expand Down Expand Up @@ -207,15 +270,19 @@ public function create_embedded_kyc_session( array $self_assessment_data, bool $
'site_locale' => get_locale(),
];
$user_data = $this->get_onboarding_user_data();
$account_data = $this->get_account_data( $setup_mode, $self_assessment_data );
$account_data = $this->get_account_data(
$setup_mode,
$self_assessment_data,
$this->get_capabilities_from_request()
);
$actioned_notes = self::get_actioned_notes();

try {
$account_session = $this->payments_api_client->initialize_onboarding_embedded_kyc(
'live' === $setup_mode,
$site_data,
array_filter( $user_data ), // nosemgrep: audit.php.lang.misc.array-filter-no-callback -- output of array_filter is escaped.
array_filter( $account_data ), // nosemgrep: audit.php.lang.misc.array-filter-no-callback -- output of array_filter is escaped.
WC_Payments_Utils::array_filter_recursive( $user_data ), // nosemgrep: audit.php.lang.misc.array-filter-no-callback -- output of array_filter is escaped.
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
);
Expand Down Expand Up @@ -365,12 +432,15 @@ public function add_admin_body_classes( string $classes = '' ): string {
/**
* Get account data for onboarding from self assessment data.
*
* @param string $setup_mode Setup mode.
* @param string $setup_mode Setup mode.
* @param array $self_assessment_data Self assessment data.
* @param array $capabilities Optional. List keyed by capabilities IDs (payment methods) with boolean values.
* If the value is true, the capability is requested when the account is created.
* If the value is false, the capability is not requested when the account is created.
*
* @return array Account data.
*/
public function get_account_data( string $setup_mode, array $self_assessment_data ): array {
public function get_account_data( string $setup_mode, array $self_assessment_data, array $capabilities = [] ): array {
$home_url = get_home_url();
// If the site is running on localhost, use a bogus URL. This is to avoid Stripe's errors.
// wp_http_validate_url does not check that, unfortunately.
Expand All @@ -387,6 +457,33 @@ public function get_account_data( string $setup_mode, array $self_assessment_dat
'business_name' => get_bloginfo( 'name' ),
];

foreach ( $capabilities as $capability => $should_request ) {
// Remove the `_payments` suffix from the capability, if present.
if ( strpos( $capability, '_payments' ) === strlen( $capability ) - 9 ) {
$capability = str_replace( '_payments', '', $capability );
}

// Skip the special 'apple_google' because it is not a payment method.
// Skip the 'woopay' because it is automatically handled by the API.
if ( 'apple_google' === $capability || 'woopay' === $capability ) {
continue;
}

if ( 'card' === $capability ) {
// Card is always requested.
$account_data['capabilities']['card_payments'] = [ 'requested' => 'true' ];
// When requesting card, we also need to request transfers.
// The platform should handle this automatically, but it is best to be thorough.
$account_data['capabilities']['transfers'] = [ 'requested' => 'true' ];
continue;
}

// We only request, not unrequest capabilities.
if ( $should_request ) {
$account_data['capabilities'][ $capability . '_payments' ] = [ 'requested' => 'true' ];
}
}

if ( ! empty( $self_assessment_data ) ) {
$business_type = $self_assessment_data['business_type'] ?? null;
$account_data = WC_Payments_Utils::array_merge_recursive_distinct(
Expand Down Expand Up @@ -436,6 +533,7 @@ public function get_account_data( string $setup_mode, array $self_assessment_dat
]
);
}

return $account_data;
}

Expand Down

0 comments on commit cb65e92

Please sign in to comment.