|
9 | 9 | exit; // Exit if accessed directly. |
10 | 10 | } |
11 | 11 |
|
| 12 | +use Automattic\WooCommerce\Utilities\PluginUtil; |
12 | 13 | use WCPay\Constants\Country_Code; |
13 | 14 | use WCPay\Constants\Currency_Code; |
14 | 15 | use WCPay\Core\Server\Request\Get_Account; |
@@ -44,6 +45,8 @@ class WC_Payments_Account implements MultiCurrencyAccountInterface { |
44 | 45 | const NOX_PROFILE_OPTION_KEY = 'woocommerce_woopayments_nox_profile'; |
45 | 46 | const NOX_ONBOARDING_LOCKED_KEY = 'woocommerce_woopayments_nox_onboarding_locked'; |
46 | 47 |
|
| 48 | + const STORE_SETUP_SYNC_ACTION = 'wcpay_store_setup_sync'; |
| 49 | + |
47 | 50 | /** |
48 | 51 | * Client for making requests to the WooCommerce Payments API |
49 | 52 | * |
@@ -132,6 +135,10 @@ public function init_hooks() { |
132 | 135 | add_action( 'jetpack_site_registered', [ $this, 'clear_cache' ] ); |
133 | 136 | add_action( 'updated_option', [ $this, 'possibly_update_wcpay_account_locale' ], 10, 3 ); |
134 | 137 | 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' ] ); |
135 | 142 | } |
136 | 143 |
|
137 | 144 | /** |
@@ -2631,6 +2638,186 @@ public function is_card_testing_protection_eligible(): bool { |
2631 | 2638 | return $account['card_testing_protection_eligible'] ?? false; |
2632 | 2639 | } |
2633 | 2640 |
|
| 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 | + } |
2634 | 2821 |
|
2635 | 2822 | /** |
2636 | 2823 | * Gets tracking info from the server and caches it. |
|
0 commit comments