Skip to content

Commit b7225e7

Browse files
authored
Sync store setup with the Transact Platform (#11077)
1 parent 30af7f0 commit b7225e7

9 files changed

+294
-21
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: add
3+
4+
Sync store setup details with the Transact Platform.

includes/admin/class-wc-rest-payments-settings-controller.php

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use WCPay\Fraud_Prevention\Fraud_Risk_Tools;
1111
use WCPay\Constants\Track_Events;
1212
use WCPay\Fraud_Prevention\Models\Rule;
13+
use WCPay\Logger;
1314

1415
defined( 'ABSPATH' ) || exit;
1516

@@ -563,6 +564,9 @@ public function update_settings( WP_REST_Request $request ) {
563564
return new WP_REST_Response( [ 'server_error' => $update_account_result->get_error_message() ], 400 );
564565
}
565566

567+
// Sync the store setup with the Transact Platform.
568+
$this->account->schedule_store_setup_sync();
569+
566570
return new WP_REST_Response( $this->get_settings(), 200 );
567571
}
568572

@@ -641,24 +645,28 @@ function ( $payment_method ) use ( $available_payment_methods ) {
641645
$disabled_payment_methods = array_diff( $active_payment_methods, $payment_method_ids_to_enable );
642646
$enabled_payment_methods = array_diff( $payment_method_ids_to_enable, $active_payment_methods );
643647

644-
if ( function_exists( 'wc_admin_record_tracks_event' ) ) {
645-
foreach ( $disabled_payment_methods as $disabled_payment_method ) {
646-
wc_admin_record_tracks_event(
647-
Track_Events::PAYMENT_METHOD_DISABLED,
648-
[
649-
'payment_method_id' => $disabled_payment_method,
650-
]
651-
);
652-
}
653-
654-
foreach ( $enabled_payment_methods as $enabled_payment_method ) {
655-
wc_admin_record_tracks_event(
656-
Track_Events::PAYMENT_METHOD_ENABLED,
657-
[
658-
'payment_method_id' => $enabled_payment_method,
659-
]
660-
);
661-
}
648+
// Log Tracks events for each enabled/disabled payment method.
649+
// We log these events before actually enabling/disabling the payment methods
650+
// to ensure that if enabling/disabling fails, we still have a record of the
651+
// attempt.
652+
$pm_to_capability_key_map = $this->wcpay_gateway->get_payment_method_capability_key_map();
653+
foreach ( $disabled_payment_methods as $disabled_payment_method ) {
654+
$this->tracks_event(
655+
Track_Events::PAYMENT_METHOD_DISABLED,
656+
[
657+
'payment_method_id' => $disabled_payment_method,
658+
'capability_id' => $pm_to_capability_key_map[ $disabled_payment_method ] ?? null,
659+
]
660+
);
661+
}
662+
foreach ( $enabled_payment_methods as $enabled_payment_method ) {
663+
$this->tracks_event(
664+
Track_Events::PAYMENT_METHOD_ENABLED,
665+
[
666+
'payment_method_id' => $enabled_payment_method,
667+
'capability_id' => $pm_to_capability_key_map[ $enabled_payment_method ] ?? null,
668+
]
669+
);
662670
}
663671

664672
foreach ( $enabled_payment_methods as $payment_method_id ) {
@@ -1095,4 +1103,42 @@ private function get_avs_check_enabled( array $ruleset_config ) {
10951103

10961104
return $avs_check_enabled;
10971105
}
1106+
1107+
/**
1108+
* Send a Tracks event.
1109+
*
1110+
* By default Woo adds `url`, `blog_lang`, `blog_id`, `store_id`, `products_count`, and `wc_version`
1111+
* properties to every event.
1112+
*
1113+
* @todo This is a duplicate of the one in the WC_Payments_Account and WC_Payments_Onboarding_Service classes.
1114+
*
1115+
* @param string $name The event name.
1116+
* @param array $properties Optional. The event custom properties.
1117+
*
1118+
* @return void
1119+
*/
1120+
private function tracks_event( string $name, array $properties = [] ) {
1121+
if ( ! function_exists( 'wc_admin_record_tracks_event' ) ) {
1122+
return;
1123+
}
1124+
1125+
// Add default properties to every event.
1126+
$account_service = WC_Payments::get_account_service();
1127+
$tracking_info = $account_service ? $account_service->get_tracking_info() : [];
1128+
1129+
$properties = array_merge(
1130+
$properties,
1131+
[
1132+
'is_test_mode' => WC_Payments::mode()->is_test(),
1133+
'jetpack_connected' => $this->api_client->is_server_connected(),
1134+
'wcpay_version' => WCPAY_VERSION_NUMBER,
1135+
'woo_country_code' => WC()->countries->get_base_country(),
1136+
],
1137+
$tracking_info ?? []
1138+
);
1139+
1140+
wc_admin_record_tracks_event( $name, $properties );
1141+
1142+
Logger::info( 'Tracks event: ' . $name . ' with data: ' . wp_json_encode( WC_Payments_Utils::redact_array( $properties, [ 'woo_country_code' ] ) ) );
1143+
}
10981144
}

includes/class-wc-payment-gateway-wcpay.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3965,7 +3965,7 @@ public function get_upe_enabled_payment_method_ids() {
39653965
return $this->get_option(
39663966
'upe_enabled_payment_method_ids',
39673967
[
3968-
'card',
3968+
Payment_Method::CARD,
39693969
]
39703970
);
39713971
}

includes/class-wc-payments-account.php

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
exit; // Exit if accessed directly.
1010
}
1111

12+
use Automattic\WooCommerce\Utilities\PluginUtil;
1213
use WCPay\Constants\Country_Code;
1314
use WCPay\Constants\Currency_Code;
1415
use WCPay\Core\Server\Request\Get_Account;
@@ -44,6 +45,8 @@ class WC_Payments_Account implements MultiCurrencyAccountInterface {
4445
const NOX_PROFILE_OPTION_KEY = 'woocommerce_woopayments_nox_profile';
4546
const NOX_ONBOARDING_LOCKED_KEY = 'woocommerce_woopayments_nox_onboarding_locked';
4647

48+
const STORE_SETUP_SYNC_ACTION = 'wcpay_store_setup_sync';
49+
4750
/**
4851
* Client for making requests to the WooCommerce Payments API
4952
*
@@ -132,6 +135,10 @@ public function init_hooks() {
132135
add_action( 'jetpack_site_registered', [ $this, 'clear_cache' ] );
133136
add_action( 'updated_option', [ $this, 'possibly_update_wcpay_account_locale' ], 10, 3 );
134137
add_action( 'woocommerce_woocommerce_payments_updated', [ $this, 'clear_cache' ] );
138+
// Hook into the account refreshed action to schedule a store setup sync.
139+
add_action( 'woocommerce_payments_account_refreshed', [ $this, 'schedule_store_setup_sync' ] );
140+
// Hook into the store setup sync action (triggered by the scheduled job) and do the sync.
141+
add_action( self::STORE_SETUP_SYNC_ACTION, [ $this, 'store_setup_sync' ] );
135142
}
136143

137144
/**
@@ -2631,6 +2638,186 @@ public function is_card_testing_protection_eligible(): bool {
26312638
return $account['card_testing_protection_eligible'] ?? false;
26322639
}
26332640

2641+
/**
2642+
* Schedule a store setup sync to run in 5 minutes, if there isn't one already scheduled.
2643+
*
2644+
* @return void
2645+
*/
2646+
public function schedule_store_setup_sync() {
2647+
$action_hook = self::STORE_SETUP_SYNC_ACTION;
2648+
2649+
// If there is already a pending action, do nothing.
2650+
if ( $this->action_scheduler_service->pending_action_exists( $action_hook ) ) {
2651+
return;
2652+
}
2653+
2654+
// Schedule the action to run in 5 minutes.
2655+
// Further attempts to schedule the action will be ignored until it runs.
2656+
$run_time = time() + 5 * MINUTE_IN_SECONDS;
2657+
$this->action_scheduler_service->schedule_job( $run_time, $action_hook );
2658+
}
2659+
2660+
/**
2661+
* Gather the latest store setup state and send it to the Transact Platform.
2662+
*
2663+
* @return void
2664+
*/
2665+
public function store_setup_sync() {
2666+
if ( ! $this->payments_api_client->is_server_connected() ) {
2667+
return;
2668+
}
2669+
2670+
try {
2671+
// This is a fire-and-forget operation, so we don't care about the result.
2672+
$this->payments_api_client->send_store_setup( $this->get_store_setup_details() );
2673+
} catch ( Exception $e ) {
2674+
Logger::error( 'Failed to sync store setup state with the Transact Platform: ' . $e->getMessage() );
2675+
}
2676+
}
2677+
2678+
/**
2679+
* Gathers the current store setup details.
2680+
*
2681+
* This overlaps heavily with the extension settings, but it is not limited to it.
2682+
*
2683+
* @see WC_REST_Payments_Settings_Controller::get_settings().
2684+
*
2685+
* @return array Store setup details.
2686+
* @throws Exception In case things are not properly initialized yet.
2687+
*/
2688+
private function get_store_setup_details(): array {
2689+
$gateway = WC_Payments::get_gateway();
2690+
// If the gateway is not available, return an empty array.
2691+
// This should never happen, but better safe than sorry.
2692+
if ( empty( $gateway ) ) {
2693+
return [];
2694+
}
2695+
2696+
$gateway_form_fields = $gateway->get_form_fields();
2697+
2698+
$payment_methods_available = $gateway->get_upe_available_payment_methods();
2699+
$payment_methods_enabled = $gateway->get_upe_enabled_payment_method_ids();
2700+
$payment_methods_disabled = array_diff( $payment_methods_available, $payment_methods_enabled );
2701+
2702+
// Map enabled payment methods to capabilities.
2703+
// This is needed because the capabilities in the Transact Platform are named differently.
2704+
// E.g. 'card_payments' capability corresponds to 'card' payment method.
2705+
$provider_capabilities_enabled = [];
2706+
$provider_capabilities_disabled = [];
2707+
$pm_to_capability_key_map = $gateway->get_payment_method_capability_key_map();
2708+
foreach ( $payment_methods_enabled as $pm_id ) {
2709+
if ( isset( $pm_to_capability_key_map[ $pm_id ] ) ) {
2710+
$provider_capabilities_enabled[] = $pm_to_capability_key_map[ $pm_id ];
2711+
}
2712+
}
2713+
foreach ( $payment_methods_disabled as $pm_id ) {
2714+
if ( isset( $pm_to_capability_key_map[ $pm_id ] ) ) {
2715+
$provider_capabilities_disabled[] = $pm_to_capability_key_map[ $pm_id ];
2716+
}
2717+
}
2718+
$provider_capabilities_available = array_unique( array_merge( $provider_capabilities_enabled, $provider_capabilities_disabled ) );
2719+
2720+
// Get active plugins using the PluginUtil from WC, if available.
2721+
$wc_plugin_util = null;
2722+
try {
2723+
$wc_plugin_util = wc_get_container()->get( PluginUtil::class );
2724+
} catch ( Exception $e ) {
2725+
// If we can't get the PluginUtil, we won't be able to get the active plugins.
2726+
// This is not a critical failure, so we can log it and continue.
2727+
Logger::error( 'Failed to get PluginUtil: ' . $e->getMessage() );
2728+
}
2729+
2730+
return [
2731+
// The WooPayments setup details.
2732+
'gateway' => [
2733+
'enabled' => $gateway->is_enabled(),
2734+
'test_mode' => WC_Payments::mode()->is_test(),
2735+
'test_mode_onboarding' => WC_Payments::mode()->is_test_mode_onboarding(),
2736+
],
2737+
2738+
// Payment methods setup.
2739+
'payment_methods' => [
2740+
'available' => $payment_methods_available,
2741+
'enabled' => $payment_methods_enabled,
2742+
'disabled' => $payment_methods_disabled,
2743+
'duplicates' => $gateway->find_duplicates(),
2744+
],
2745+
// Payment methods mapped to capabilities, for flexibility with the Transact Platform.
2746+
// E.g. 'card_payments' capability corresponds to 'card' payment method.
2747+
'provider_capabilities' => [
2748+
'available' => $provider_capabilities_available,
2749+
'enabled' => $provider_capabilities_enabled,
2750+
'disabled' => $provider_capabilities_disabled,
2751+
],
2752+
'apple_google_pay_in_payment_methods_options_enabled' => $gateway->get_option( 'apple_google_pay_in_payment_methods_options' ),
2753+
2754+
'saved_cards_enabled' => $gateway->is_saved_cards_enabled(),
2755+
'manual_capture_enabled' => 'yes' === $gateway->get_option( 'manual_capture' ),
2756+
'debug_log_enabled' => 'yes' === $gateway->get_option( 'enable_logging' ),
2757+
2758+
'payment_request' => [
2759+
'enabled' => 'yes' === $gateway->get_option( 'payment_request' ),
2760+
'enabled_locations' => $gateway->get_option( 'payment_request_button_locations' ),
2761+
'button_type' => $gateway->get_option( 'payment_request_button_type' ),
2762+
'button_size' => $gateway->get_option( 'payment_request_button_size' ),
2763+
'button_theme' => $gateway->get_option( 'payment_request_button_theme' ),
2764+
'button_border_radius' => $gateway->get_option( 'payment_request_button_border_radius' ),
2765+
],
2766+
2767+
'woopay' => [
2768+
'enabled' => WC_Payments_Features::is_woopay_enabled(),
2769+
'enabled_locations' => $gateway->get_option(
2770+
'platform_checkout_button_locations',
2771+
array_keys( $gateway_form_fields['payment_request_button_locations']['options'] )
2772+
),
2773+
'store_logo' => $gateway->get_option( 'platform_checkout_store_logo' ),
2774+
'custom_message' => $gateway->get_option( 'platform_checkout_custom_message' ),
2775+
'invalid_extension_found' => (bool) get_option( 'woopay_invalid_extension_found', false ),
2776+
],
2777+
2778+
// WooPayments features.
2779+
'multi_currency_enabled' => WC_Payments_Features::is_customer_multi_currency_enabled(),
2780+
'stripe_billing_enabled' => WC_Payments_Features::is_stripe_billing_enabled(),
2781+
2782+
// Other store setup details.
2783+
'wp_setup' => [
2784+
'name' => get_bloginfo( 'name' ),
2785+
'url' => home_url(),
2786+
'active_theme' => $this->get_store_theme_details(),
2787+
'active_plugins' => ! empty( $wc_plugin_util ) ? $wc_plugin_util->get_all_active_valid_plugins() : [],
2788+
'version' => get_bloginfo( 'version' ),
2789+
'locale' => get_locale(),
2790+
],
2791+
'wc_setup' => [
2792+
'version' => defined( 'WC_VERSION' ) ? explode( '-', WC_VERSION, 2 )[0] : '',
2793+
'store_id' => get_option( 'woocommerce_store_id', null ),
2794+
'currency' => get_woocommerce_currency(),
2795+
'tracking_enabled' => WC_Site_Tracking::is_tracking_enabled(),
2796+
'wc_subscriptions_active' => $gateway->is_subscriptions_plugin_active(),
2797+
'wc_subscriptions_version' => $gateway->get_subscriptions_plugin_version(),
2798+
],
2799+
];
2800+
}
2801+
2802+
/**
2803+
* Gathers the current store theme details.
2804+
*
2805+
* @return array Store theme details.
2806+
*/
2807+
private function get_store_theme_details(): array {
2808+
$theme_data = wp_get_theme();
2809+
$theme_child_theme = wc_bool_to_string( is_child_theme() );
2810+
$theme_wc_support = wc_bool_to_string( current_theme_supports( 'woocommerce' ) );
2811+
$theme_is_block_theme = wc_bool_to_string( wp_is_block_theme() );
2812+
2813+
return [
2814+
'name' => $theme_data->Name, // @phpcs:ignore
2815+
'version' => $theme_data->Version, // @phpcs:ignore
2816+
'child_theme' => $theme_child_theme,
2817+
'wc_support' => $theme_wc_support,
2818+
'block_theme' => $theme_is_block_theme,
2819+
];
2820+
}
26342821

26352822
/**
26362823
* Gets tracking info from the server and caches it.

includes/class-wc-payments-onboarding-service.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,9 @@ private function tracks_event( string $name, array $properties = [] ) {
15201520
}
15211521

15221522
// Add default properties to every event.
1523+
$account_service = WC_Payments::get_account_service();
1524+
$tracking_info = $account_service ? $account_service->get_tracking_info() : [];
1525+
15231526
$properties = array_merge(
15241527
$properties,
15251528
[
@@ -1528,7 +1531,7 @@ private function tracks_event( string $name, array $properties = [] ) {
15281531
'wcpay_version' => WCPAY_VERSION_NUMBER,
15291532
'woo_country_code' => WC()->countries->get_base_country(),
15301533
],
1531-
WC_Payments::get_account_service()->get_tracking_info() ?? []
1534+
$tracking_info ?? []
15321535
);
15331536

15341537
wc_admin_record_tracks_event( $name, $properties );

includes/class-wc-payments-status.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ function ( $rule ) {
246246
<td>
247247
<?php
248248
$manual_capture_enabled = 'yes' === $this->gateway->get_option( 'manual_capture' );
249-
echo $manual_capture_enabled ? esc_html_e( 'Enabled', 'woocommerce-payments' ) : esc_html_e( 'Disabled', 'woocommerce-payments' );
249+
echo $manual_capture_enabled ? esc_html__( 'Enabled', 'woocommerce-payments' ) : esc_html__( 'Disabled', 'woocommerce-payments' );
250250
?>
251251
</td>
252252
</tr>

includes/wc-payment-api/class-wc-payments-api-client.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class WC_Payments_API_Client implements MultiCurrencyApiClientInterface {
8484
const COMPATIBILITY_API = 'compatibility';
8585
const RECOMMENDED_PAYMENT_METHODS = 'payment_methods/recommended';
8686
const ADDRESS_AUTOCOMPLETE_TOKEN = 'address-autocomplete-token';
87+
const STORE_SETUP_API = 'accounts/store_setup';
8788

8889
/**
8990
* Common keys in API requests/responses that we might want to redact.
@@ -2463,6 +2464,26 @@ public function delete_account( bool $test_mode = false ) {
24632464
);
24642465
}
24652466

2467+
/**
2468+
* Send store setup data to the Transact Platform.
2469+
*
2470+
* @param array $store_setup The store setup data.
2471+
*
2472+
* @return array Response from the API.
2473+
* @throws API_Exception
2474+
*/
2475+
public function send_store_setup( array $store_setup ): array {
2476+
return $this->request(
2477+
[
2478+
'snapshot' => $store_setup,
2479+
'test_mode' => \WC_Payments::mode()->is_test_mode_onboarding(),
2480+
],
2481+
self::STORE_SETUP_API,
2482+
self::POST,
2483+
true
2484+
);
2485+
}
2486+
24662487
/**
24672488
* Send the request to the WooCommerce Payment API
24682489
*

0 commit comments

Comments
 (0)