diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 272bfdaf7..7fb75327b 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -80,12 +80,11 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer; use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; return array( 'api.host' => static function( ContainerInterface $container ) : string { - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); if ( $environment->is_sandbox() ) { diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 53068c506..6fcbc12f4 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -42,7 +42,7 @@ return array( assert( $display_manager instanceof DisplayManager ); // Domain registration. - $env = $container->get( 'onboarding.environment' ); + $env = $container->get( 'settings.environment' ); assert( $env instanceof Environment ); $domain_registration_url = 'https://www.paypal.com/uccservicing/apm/applepay'; diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 586a300ae..1bf5dd311 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -20,7 +20,6 @@ use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice; use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( @@ -260,15 +259,15 @@ return array( }, 'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() < State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return ''; } $product_status = $container->get( 'applepay.apple-product-status' ); assert( $product_status instanceof AppleProductStatus ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); $enabled = $product_status->is_active(); diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index 8015e0a4d..df0d06adf 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -368,7 +368,7 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule if ( ! $button->is_enabled() ) { return; } - $env = $c->get( 'onboarding.environment' ); + $env = $c->get( 'settings.environment' ); assert( $env instanceof Environment ); $is_sandobx = $env->current_environment_is( Environment::SANDBOX ); $this->load_domain_association_file( $is_sandobx ); diff --git a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php index 61668c26d..c7ff102fe 100644 --- a/modules/ppcp-applepay/src/Assets/AppleProductStatus.php +++ b/modules/ppcp-applepay/src/Assets/AppleProductStatus.php @@ -89,6 +89,7 @@ class AppleProductStatus extends ProductStatus { } } + // Settings used as a cache; `settings->set` is compatible with new UI. if ( $has_capability ) { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); } else { diff --git a/modules/ppcp-axo-block/services.php b/modules/ppcp-axo-block/services.php index 6945215ba..b93513148 100644 --- a/modules/ppcp-axo-block/services.php +++ b/modules/ppcp-axo-block/services.php @@ -36,7 +36,7 @@ return array( fn(): SmartButtonInterface => $container->get( 'button.smart-button' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.configuration.dcc' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'wcgateway.url' ), $container->get( 'axo.payment_method_selected_map' ), $container->get( 'axo.supported-country-card-type-matrix' ) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index d41829726..a834dcb0d 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; use WooCommerce\PayPalCommerce\Axo\Helper\NoticeRenderer; -use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index ca427700d..294558be9 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -66,7 +66,7 @@ return array( $container->get( 'ppcp.asset-version' ), $container->get( 'session.handler' ), $container->get( 'wcgateway.settings' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'axo.insights' ), $container->get( 'wcgateway.settings.status' ), $container->get( 'api.shop.currency.getter' ), @@ -89,7 +89,7 @@ return array( $container->get( 'api.factory.purchase-unit' ), $container->get( 'api.factory.shipping-preference' ), $container->get( 'wcgateway.transaction-url-provider' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 5a489d330..44e9a52ac 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -36,7 +36,6 @@ use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; @@ -49,7 +48,7 @@ return array( return $client_id; } - $env = $container->get( 'onboarding.environment' ); + $env = $container->get( 'settings.environment' ); /** * The environment. * @@ -125,8 +124,8 @@ return array( } } - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() !== State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return new DisabledSmartButton(); } @@ -142,7 +141,7 @@ return array( $dcc_applies = $container->get( 'api.helpers.dccapplies' ); $subscription_helper = $container->get( 'wc-subscriptions.helper' ); $messages_apply = $container->get( 'button.helper.messages-apply' ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); $payment_token_repository = $container->get( 'vaulting.repository.payment-token' ); return new SmartButton( $container->get( 'button.url' ), @@ -241,11 +240,11 @@ return array( ); }, 'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler { - - $state = $container->get( 'onboarding.state' ); - $order_processor = $container->get( 'wcgateway.order-processor' ); - $session_handler = $container->get( 'session.handler' ); - return new EarlyOrderHandler( $state, $order_processor, $session_handler ); + return new EarlyOrderHandler( + $container->get( 'settings.flag.is-connected' ), + $container->get( 'wcgateway.order-processor' ), + $container->get( 'session.handler' ) + ); }, 'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint { $request_data = $container->get( 'button.request-data' ); diff --git a/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php index 775be302e..2e8cc4c0b 100644 --- a/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php @@ -272,8 +272,9 @@ class ApproveOrderEndpoint implements EndpointInterface { * @return void */ protected function toggle_final_review_enabled_setting(): void { + // TODO new-ux: This flag must also be updated in the new settings. $final_review_enabled_setting = $this->settings->has( 'blocks_final_review_enabled' ) && $this->settings->get( 'blocks_final_review_enabled' ); - $final_review_enabled_setting ? $this->settings->set( 'blocks_final_review_enabled', false ) : $this->settings->set( 'blocks_final_review_enabled', true ); + $this->settings->set( 'blocks_final_review_enabled', ! $final_review_enabled_setting ); $this->settings->persist(); } } diff --git a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php index 46b4ddf41..393ecab45 100644 --- a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php +++ b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php @@ -11,8 +11,6 @@ namespace WooCommerce\PayPalCommerce\Button\Helper; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; -use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; @@ -23,11 +21,11 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; class EarlyOrderHandler { /** - * The State. + * Whether the merchant is connected to PayPal (onboarding completed). * - * @var State + * @var bool */ - private $state; + private bool $is_connected; /** * The Order Processor. @@ -46,17 +44,17 @@ class EarlyOrderHandler { /** * EarlyOrderHandler constructor. * - * @param State $state The State. + * @param bool $is_connected Whether onboarding was completed. * @param OrderProcessor $order_processor The Order Processor. * @param SessionHandler $session_handler The Session Handler. */ public function __construct( - State $state, + bool $is_connected, OrderProcessor $order_processor, SessionHandler $session_handler ) { - $this->state = $state; + $this->is_connected = $is_connected; $this->order_processor = $order_processor; $this->session_handler = $session_handler; } @@ -67,7 +65,7 @@ class EarlyOrderHandler { * @return bool */ public function should_create_early_order(): bool { - return $this->state->current_state() === State::STATE_ONBOARDED; + return $this->is_connected; } //phpcs:disable WordPress.Security.NonceVerification.Recommended diff --git a/modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php b/modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php index d3ce17290..fddb31f99 100644 --- a/modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php +++ b/modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php @@ -30,13 +30,17 @@ class SettingsTabMapHelper { */ public function map(): array { return array( - 'disable_cards' => 'disabled_cards', - 'brand_name' => 'brand_name', - 'soft_descriptor' => 'soft_descriptor', - 'payee_preferred' => 'instant_payments_only', - 'subtotal_mismatch_behavior' => 'subtotal_adjustment', - 'landing_page' => 'landing_page', - 'smart_button_language' => 'button_language', + 'disable_cards' => 'disabled_cards', + 'brand_name' => 'brand_name', + 'soft_descriptor' => 'soft_descriptor', + 'payee_preferred' => 'instant_payments_only', + 'subtotal_mismatch_behavior' => 'subtotal_adjustment', + 'landing_page' => 'landing_page', + 'smart_button_language' => 'button_language', + 'prefix' => 'invoice_prefix', + 'intent' => '', + 'vault_enabled_dcc' => 'save_card_details', + 'blocks_final_review_enabled' => 'enable_pay_now', ); } @@ -57,6 +61,12 @@ class SettingsTabMapHelper { case 'landing_page': return $this->mapped_landing_page_value( $settings_model ); + case 'intent': + return $this->mapped_intent_value( $settings_model ); + + case 'blocks_final_review_enabled': + return $this->mapped_pay_now_value( $settings_model ); + default: return $settings_model[ $new_key ] ?? null; } @@ -98,4 +108,37 @@ class SettingsTabMapHelper { : ApplicationContext::LANDING_PAGE_NO_PREFERENCE ); } + + /** + * Retrieves the mapped value for the order intent from the new settings. + * + * @param array $settings_model The new settings model data as an array. + * @return 'AUTHORIZE'|'CAPTURE'|null The mapped 'intent' setting value. + */ + protected function mapped_intent_value( array $settings_model ): ?string { + $authorize_only = $settings_model['authorize_only'] ?? null; + $capture_virtual_orders = $settings_model['capture_virtual_orders'] ?? null; + + if ( is_null( $authorize_only ) && is_null( $capture_virtual_orders ) ) { + return null; + } + + return $authorize_only ? 'AUTHORIZE' : 'CAPTURE'; + } + + /** + * Retrieves the mapped value for the "Pay Now Experience" from the new settings. + * + * @param array $settings_model The new settings model data as an array. + * @return bool|null The mapped 'Pay Now Experience' setting value. + */ + protected function mapped_pay_now_value( array $settings_model ): ?bool { + $enable_pay_now = $settings_model['enable_pay_now'] ?? null; + + if ( is_null( $enable_pay_now ) ) { + return null; + } + + return ! $enable_pay_now; + } } diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index f92aa0bd8..b26e4dfe9 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus; use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( @@ -174,7 +173,7 @@ return array( $container->get( 'session.handler' ), $container->get( 'wc-subscriptions.helper' ), $container->get( 'wcgateway.settings' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'wcgateway.settings.status' ), $container->get( 'woocommerce.logger.woocommerce' ) ); @@ -221,15 +220,15 @@ return array( }, 'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string { - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() < State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return ''; } $product_status = $container->get( 'googlepay.helpers.apm-product-status' ); assert( $product_status instanceof ApmProductStatus ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); $enabled = $product_status->is_active(); diff --git a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php index dbe812837..20917eac7 100644 --- a/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php +++ b/modules/ppcp-googlepay/src/Helper/ApmProductStatus.php @@ -89,6 +89,7 @@ class ApmProductStatus extends ProductStatus { } } + // Settings used as a cache; `settings->set` is compatible with new UI. if ( $has_capability ) { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); } else { diff --git a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php index 1fac77a6e..a65e49317 100644 --- a/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php +++ b/modules/ppcp-local-alternative-payment-methods/src/LocalAlternativePaymentMethodsModule.php @@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods; use WC_Order; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; @@ -56,8 +55,9 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo if ( ! self::should_add_local_apm_gateways( $c ) ) { return $methods; } - $onboarding_state = $c->get( 'onboarding.state' ); - if ( $onboarding_state->current_state() === State::STATE_START ) { + + $is_connected = $c->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return $methods; } diff --git a/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php b/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php index 2a89146be..546a94d15 100644 --- a/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php +++ b/modules/ppcp-local-alternative-payment-methods/src/LocalApmProductStatus.php @@ -71,6 +71,7 @@ class LocalApmProductStatus extends ProductStatus { } } + // Settings used as a cache; `settings->set` is compatible with new UI. if ( $has_capability ) { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); } else { diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index cbbd02da5..64d136092 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -18,10 +18,11 @@ use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingSendOnlyNoticeRenderer; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; return array( 'api.paypal-host' => function( ContainerInterface $container ) : string { - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); /** * The current environment. * @@ -34,7 +35,7 @@ return array( }, 'api.paypal-website-url' => function( ContainerInterface $container ) : string { - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); if ( $environment->current_environment_is( Environment::SANDBOX ) ) { return $container->get( 'api.paypal-website-url-sandbox' ); @@ -56,9 +57,16 @@ return array( return $state->current_state() >= State::STATE_ONBOARDED; }, - 'onboarding.environment' => function( ContainerInterface $container ) : Environment { + 'settings.flag.is-sandbox' => static function ( ContainerInterface $container ) : bool { $settings = $container->get( 'wcgateway.settings' ); - return new Environment( $settings ); + assert( $settings instanceof Settings ); + + return $settings->has( 'sandbox_on' ) && $settings->get( 'sandbox_on' ); + }, + 'settings.environment' => function ( ContainerInterface $container ) : Environment { + return new Environment( + $container->get( 'settings.flag.is-sandbox' ) + ); }, 'onboarding.assets' => function( ContainerInterface $container ) : OnboardingAssets { @@ -68,7 +76,7 @@ return array( $container->get( 'onboarding.url' ), $container->get( 'ppcp.asset-version' ), $state, - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $login_seller_endpoint, $container->get( 'wcgateway.current-ppcp-settings-page-id' ) ); diff --git a/modules/ppcp-onboarding/src/OnboardingRESTController.php b/modules/ppcp-onboarding/src/OnboardingRESTController.php index d95b0a9d3..95d47b8f8 100644 --- a/modules/ppcp-onboarding/src/OnboardingRESTController.php +++ b/modules/ppcp-onboarding/src/OnboardingRESTController.php @@ -133,9 +133,11 @@ class OnboardingRESTController { * @return array */ public function get_status( $request ) { - $environment = $this->container->get( 'onboarding.environment' ); + $environment = $this->container->get( 'settings.environment' ); $state = $this->container->get( 'onboarding.state' ); + // Legacy onboarding module; using `State::STATE_ONBOARDED` checks is valid here. + return array( 'environment' => $environment->current_environment(), 'onboarded' => ( $state->current_state() >= State::STATE_ONBOARDED ), diff --git a/modules/ppcp-paylater-configurator/src/Endpoint/SaveConfig.php b/modules/ppcp-paylater-configurator/src/Endpoint/SaveConfig.php index b1f77de85..6c1dc4b41 100644 --- a/modules/ppcp-paylater-configurator/src/Endpoint/SaveConfig.php +++ b/modules/ppcp-paylater-configurator/src/Endpoint/SaveConfig.php @@ -95,6 +95,7 @@ class SaveConfig { * @param array $config The configurator config. */ public function save_config( array $config ): void { + // TODO new-ux: We should convert this to a new AbstractDataModel class in the settings folder! $this->settings->set( 'pay_later_enable_styling_per_messaging_location', true ); $this->settings->set( 'pay_later_messaging_enabled', true ); diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 5008ac67c..768668a77 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -365,7 +365,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu function( $subscription ) use ( $c ) { $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id ) { - $environment = $c->get( 'onboarding.environment' ); + $environment = $c->get( 'settings.environment' ); $host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com'; ?> @@ -476,7 +476,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu return; } - $environment = $c->get( 'onboarding.environment' ); + $environment = $c->get( 'settings.environment' ); echo ''; @@ -507,7 +507,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu return; } - $environment = $c->get( 'onboarding.environment' ); + $environment = $c->get( 'settings.environment' ); $this->render_paypal_subscription_fields( $product, $environment ); } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-block.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-block.scss index 5b1d04dd4..2ab6c9311 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-block.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-block.scss @@ -24,6 +24,10 @@ padding-top: var(--block-separator-gap, 32px); border-top: var(--block-separator-size, 1px) solid var(--block-separator-color); } + + &.ppcp--pull-right { + float: right; + } } .ppcp-r-settings-block { diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-card.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-card.scss index 30826351f..59d897132 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-card.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-card.scss @@ -70,16 +70,33 @@ $width_gap: 24px; margin: 0; } + + .ppcp-r-settings-card { + margin-top: $card-vertical-gap; + padding-top: $card-vertical-gap; + border-top: 1px solid $color-gray-200; + } + .ppcp--card-actions { - opacity: 0.5; transition: opacity 0.3s; - &:hover { - opacity: 1; + &.ppcp--dimmed { + opacity: 0.5; + + &:hover { + opacity: 1; + } } - .components-button.is-tertiary:first-child { - padding-left: 0; + .components-button.is-tertiary { + transition: color 0.3s, background 0.3s; + + &:first-child { + padding-left: 0; + } + + svg { + margin-right: 4px; + } } } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss index 90c14d734..e2f091966 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss @@ -13,14 +13,3 @@ padding-bottom: 36px; } } - -.ppcp-r-settings { - > * { - margin-bottom: $card-vertical-gap; - } - - > *:not(:last-child) { - padding-bottom: $card-vertical-gap; - border-bottom: 1px solid $color-gray-200; - } -} diff --git a/modules/ppcp-settings/resources/css/components/screens/_settings.scss b/modules/ppcp-settings/resources/css/components/screens/_settings.scss index 0268bc1ca..901009e6c 100644 --- a/modules/ppcp-settings/resources/css/components/screens/_settings.scss +++ b/modules/ppcp-settings/resources/css/components/screens/_settings.scss @@ -227,11 +227,6 @@ } // Payment Methods -.ppcp-r-payment-methods { - display: flex; - flex-direction: column; - gap: 48px; -} .ppcp-highlight { animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1); diff --git a/modules/ppcp-settings/resources/css/components/screens/settings/_tab-paylater-configurator.scss b/modules/ppcp-settings/resources/css/components/screens/settings/_tab-paylater-configurator.scss index 8e76d0028..e37fce07a 100644 --- a/modules/ppcp-settings/resources/css/components/screens/settings/_tab-paylater-configurator.scss +++ b/modules/ppcp-settings/resources/css/components/screens/settings/_tab-paylater-configurator.scss @@ -13,7 +13,7 @@ #configurator-eligibleContainer.css-4nclxm.e1vy3g880 { width: 100%; max-width: 100%; - padding: 48px 0px 48px 48px; + padding: 16px 0px 16px 16px; #configurator-controlPanelContainer.css-5urmrq.e1vy3g880 { width: 374px; @@ -25,6 +25,7 @@ .css-7xkxom, .css-8tvj6u { height: auto; + width: 1.2rem; } .css-10nkerk.ej6n7t60 { @@ -37,14 +38,19 @@ } .css-1vc34jy-handler { - height: 1.6rem; - width: 1.6rem; + height: 1.7em; + width: 1.5rem; } .css-8vwtr6-state { - height: 1.6rem; + height: 1.4rem; + width: 3rem; } } + + .css-1s8clkf.etu8a6w2 { + width: 374px; + } } &__subheader, #configurator-controlPanelSubHeader { @@ -68,6 +74,7 @@ .css-rok10q, .css-dfgbdq-text_body_strong { margin-top: 0; + margin-bottom: 0; } &__publish-button { @@ -110,4 +117,30 @@ width: 100%; } } + + .css-n4cwz8 { + margin-top: 20px; + } + + .css-1ce6bcu-container { + width: 3rem; + height: 1.8rem; + } + + #configurator-previewSectionSubHeaderText { + margin-right: 10px; + } + + .css-zcyvrz.ej6n7t60 { + margin-bottom: 5px; + + .css-3xbhoy-svg-size_md-icon { + width: 1.5rem; + height: 1.5rem; + } + + .css-7i5kpm-icon-button_base-size_xl-size_sm-secondary { + padding: 0.5rem; + } + } } diff --git a/modules/ppcp-settings/resources/css/components/screens/settings/_tab-styling.scss b/modules/ppcp-settings/resources/css/components/screens/settings/_tab-styling.scss index 488aef61d..5c171a94b 100644 --- a/modules/ppcp-settings/resources/css/components/screens/settings/_tab-styling.scss +++ b/modules/ppcp-settings/resources/css/components/screens/settings/_tab-styling.scss @@ -3,8 +3,8 @@ --block-separator-gap: 24px; --block-header-gap: 18px; --panel-width: 422px; - --sticky-offset-top: 92px; // 32px admin-bar + 60px TopNavigation height - --preview-height-reduction: 236px; // 32px admin-bar + 60px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin + --sticky-offset-top: 132px; // 32px admin-bar + 100px TopNavigation height + --preview-height-reduction: 276px; // 32px admin-bar + 100px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin display: flex; border: 1px solid var(--color-separators); diff --git a/modules/ppcp-settings/resources/js/Components/App.js b/modules/ppcp-settings/resources/js/Components/App.js index 1febe8674..6441d0a51 100644 --- a/modules/ppcp-settings/resources/js/Components/App.js +++ b/modules/ppcp-settings/resources/js/Components/App.js @@ -11,8 +11,8 @@ import { getQuery } from '../utils/navigation'; const SettingsApp = () => { const { isReady: onboardingIsReady, completed: onboardingCompleted } = OnboardingHooks.useSteps(); + const { isReady: merchantIsReady } = CommonHooks.useStore(); const { - isReady: merchantIsReady, merchant: { isSendOnlyCountry }, } = CommonHooks.useMerchantInfo(); diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js index 2d648e20b..cbef51f5d 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Controls/ControlToggleButton.js @@ -1,7 +1,13 @@ import { ToggleControl } from '@wordpress/components'; import { Action, Description } from '../Elements'; -const ControlToggleButton = ( { label, description, value, onChange } ) => ( +const ControlToggleButton = ( { + label, + description, + value, + onChange, + disabled = false, +} ) => ( ( help={ description ? { description } : null } + disabled={ disabled } /> ); diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/CardActions.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/CardActions.js new file mode 100644 index 000000000..6f5ff523f --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/CardActions.js @@ -0,0 +1,11 @@ +import classNames from 'classnames'; + +const CardActions = ( { isDimmed = false, children } ) => { + const className = classNames( 'ppcp--card-actions', { + 'ppcp--dimmed': isDimmed, + } ); + + return
{ children }
; +}; + +export default CardActions; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/index.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/index.js index 94c100e41..c56a70b8a 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/index.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Elements/index.js @@ -3,6 +3,7 @@ */ export { default as Action } from './Action'; +export { default as CardActions } from './CardActions'; export { default as Content } from './Content'; export { default as ContentWrapper } from './ContentWrapper'; export { default as Description } from './Description'; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PricingDescription.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PricingDescription.js index b3bcfdea4..6d5069d91 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PricingDescription.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/PricingDescription.js @@ -11,7 +11,7 @@ const PricingDescription = () => { return null; } - const lastDate = 'October 25th, 2024'; // TODO -- needs to be the last plugin update date. + const lastDate = 'February 1st, 2025'; // TODO -- needs to be the last plugin update date. const countryLinks = learnMoreLinks[ storeCountry ] || learnMoreLinks.US; const label = sprintf( diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepProducts.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepProducts.js index 9f7023850..a697e759a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepProducts.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/StepProducts.js @@ -76,7 +76,17 @@ const StepProducts = () => { 'woocommerce-paypal-payments' ), isDisabled: isCasualSeller, - contents: , + contents: ( + /* + * Note: The link should be only displayed if the subscriptions plugin is not installed. + * But when the plugin is not active, this option is completely hidden; + * This means: In the current configuration, we never show the link. + */ + + ), }, ]; return ( @@ -117,14 +127,19 @@ const DetailsPhysical = () => ( ); -const DetailsSubscriptions = ( { showNotice } ) => ( +const DetailsSubscriptions = ( { showLink, showNotice } ) => ( <> - - { __( 'WooCommerce Subscriptions', 'woocommerce-paypal-payments' ) } - + { showLink && ( + + { __( + 'WooCommerce Subscriptions', + 'woocommerce-paypal-payments' + ) } + + ) } { showNotice && (

{ __( diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js index 6ea843c67..1b06eb222 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Navigation.js @@ -3,7 +3,7 @@ import { __ } from '@wordpress/i18n'; import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; import TopNavigation from '../../../ReusableComponents/TopNavigation'; -import { useSaveSettings } from '../../../../hooks/useSaveSettings'; +import { useStoreManager } from '../../../../hooks/useStoreManager'; import { CommonHooks } from '../../../../data'; import TabBar from '../../../ReusableComponents/TabBar'; import classNames from 'classnames'; @@ -20,7 +20,7 @@ const SettingsNavigation = ( { activePanel, setActivePanel, } ) => { - const { persistAll } = useSaveSettings(); + const { persistAll } = useStoreManager(); const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' ); diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/Blocks/OrderIntent.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/Blocks/OrderIntent.js index 77fab7362..c67c8a768 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/Blocks/OrderIntent.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/Blocks/OrderIntent.js @@ -1,4 +1,5 @@ import { __ } from '@wordpress/i18n'; +import { useEffect } from 'react'; import { ControlToggleButton } from '../../../../../ReusableComponents/Controls'; import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock'; @@ -12,6 +13,12 @@ const OrderIntent = () => { setCaptureVirtualOnlyOrders, } = SettingsHooks.useSettings(); + useEffect( () => { + if ( ! authorizeOnly && captureVirtualOnlyOrders ) { + setCaptureVirtualOnlyOrders( false ); + } + }, [ authorizeOnly ] ); + return ( { ) } onChange={ setCaptureVirtualOnlyOrders } value={ captureVirtualOnlyOrders } + disabled={ ! authorizeOnly } /> ); diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ConnectionStatus.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ConnectionStatus.js index 341f11a3d..172e1cd40 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ConnectionStatus.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ConnectionStatus.js @@ -7,6 +7,7 @@ import ConnectionStatusBadge from './Parts/ConnectionStatusBadge'; import DisconnectButton from './Parts/DisconnectButton'; import SettingsBlock from '../../../../ReusableComponents/SettingsBlock'; import { ControlStaticValue } from '../../../../ReusableComponents/Controls'; +import { CardActions } from '../../../../ReusableComponents/Elements'; const ConnectionStatus = () => { const merchant = CommonHooks.useMerchant(); @@ -21,18 +22,20 @@ const ConnectionStatus = () => { title={ __( 'Connection status', 'woocommerce-paypal-payments' ) } description={ } > - + } /> @@ -59,9 +62,9 @@ const ConnectionDescription = () => { 'Your PayPal account connection details.', 'woocommerce-paypal-payments' ) } -

+ -
+ ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ExpertSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ExpertSettings.js index 13f8e0928..495bb81a4 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ExpertSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Components/Settings/ExpertSettings.js @@ -4,7 +4,6 @@ import { Content, ContentWrapper, } from '../../../../ReusableComponents/Elements'; -import ConnectionDetails from './Blocks/ConnectionDetails'; import Troubleshooting from './Blocks/Troubleshooting'; import PaypalSettings from './Blocks/PaypalSettings'; import OtherSettings from './Blocks/OtherSettings'; @@ -29,12 +28,12 @@ const ExpertSettings = () => { contentContainer={ false } > - + { /* - + */ } { +const ConnectionStatusBadge = ( { isActive, isSandbox, isBusinessSeller } ) => { if ( isActive ) { - const label = isSandbox - ? __( 'Sandbox Mode', 'woocommerce-paypal-payments' ) - : __( 'Active', 'woocommerce-paypal-payments' ); + let label; + + if ( isBusinessSeller ) { + label = isSandbox + ? __( 'Business | Sandbox', 'woocommerce-paypal-payments' ) + : __( 'Business | Live', 'woocommerce-paypal-payments' ); + } else { + label = isSandbox + ? __( 'Sandbox', 'woocommerce-paypal-payments' ) + : __( 'Active', 'woocommerce-paypal-payments' ); + } return ; } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js index a5a1014e6..1dbad3cad 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings/Tabs/TabOverview.js @@ -9,14 +9,19 @@ import { TodoSettingsBlock, FeatureSettingsBlock, } from '../../../ReusableComponents/SettingsBlocks'; -import { Content, ContentWrapper } from '../../../ReusableComponents/Elements'; +import { + Content, + ContentWrapper, + CardActions, +} from '../../../ReusableComponents/Elements'; import SettingsCard from '../../../ReusableComponents/SettingsCard'; import { TITLE_BADGE_POSITIVE } from '../../../ReusableComponents/TitleBadge'; -import { useTodos } from '../../../../data/todos/hooks'; -import { useMerchantInfo } from '../../../../data/common/hooks'; -import { STORE_NAME as COMMON_STORE_NAME } from '../../../../data/common'; -import { STORE_NAME as TODOS_STORE_NAME } from '../../../../data/todos'; -import { CommonHooks, TodosHooks } from '../../../../data'; +import { + CommonStoreName, + TodosStoreName, + CommonHooks, + TodosHooks, +} from '../../../../data'; import { getFeatures } from '../Components/Overview/features-config'; @@ -27,8 +32,8 @@ import { import SpinnerOverlay from '../../../ReusableComponents/SpinnerOverlay'; const TabOverview = () => { - const { isReady: areTodosReady } = TodosHooks.useTodos(); - const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo(); + const { isReady: areTodosReady } = TodosHooks.useStore(); + const { isReady: merchantIsReady } = CommonHooks.useStore(); if ( ! areTodosReady || ! merchantIsReady ) { return ; @@ -47,11 +52,12 @@ export default TabOverview; const OverviewTodos = () => { const [ isResetting, setIsResetting ] = useState( false ); - const { todos, isReady: areTodosReady, dismissTodo } = useTodos(); + const { todos, dismissTodo } = TodosHooks.useTodos(); + const { isReady: areTodosReady } = TodosHooks.useStore(); const { setActiveModal, setActiveHighlight } = - useDispatch( COMMON_STORE_NAME ); + useDispatch( CommonStoreName ); const { resetDismissedTodos, setDismissedTodos } = - useDispatch( TODOS_STORE_NAME ); + useDispatch( TodosStoreName ); const { createSuccessNotice } = useDispatch( noticesStore ); const showTodos = areTodosReady && todos.length > 0; @@ -90,19 +96,24 @@ const OverviewTodos = () => { 'woocommerce-paypal-payments' ) }

- + + + } > @@ -118,9 +129,10 @@ const OverviewTodos = () => { const OverviewFeatures = () => { const [ isRefreshing, setIsRefreshing ] = useState( false ); - const { merchant, features: merchantFeatures } = useMerchantInfo(); + const { merchant, features: merchantFeatures } = + CommonHooks.useMerchantInfo(); const { refreshFeatureStatuses, setActiveModal } = - useDispatch( COMMON_STORE_NAME ); + useDispatch( CommonStoreName ); const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); @@ -281,14 +293,16 @@ const OverviewFeatureDescription = ( { refreshHandler, isRefreshing } ) => { 'woocommerce-paypal-payments' ) }

- + + + ); }; diff --git a/modules/ppcp-settings/resources/js/data/_example/actions.js b/modules/ppcp-settings/resources/js/data/_example/actions.js index f9cd9a556..51b516bb1 100644 --- a/modules/ppcp-settings/resources/js/data/_example/actions.js +++ b/modules/ppcp-settings/resources/js/data/_example/actions.js @@ -82,3 +82,16 @@ export function persist() { } ); }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/_example/hooks.js b/modules/ppcp-settings/resources/js/data/_example/hooks.js index 21253d137..4c88523b5 100644 --- a/modules/ppcp-settings/resources/js/data/_example/hooks.js +++ b/modules/ppcp-settings/resources/js/data/_example/hooks.js @@ -38,10 +38,16 @@ const useStoreData = () => { }; export const useStore = () => { - const { dispatch, useTransient } = useStoreData(); + const { select, dispatch, useTransient } = useStoreData(); + const { persist, refresh } = dispatch; const [ isReady ] = useTransient( 'isReady' ); - return { persist: dispatch.persist, isReady }; + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.persistentData(); + } + + return { persist, refresh, isReady }; }; // TODO: Replace with real hook. diff --git a/modules/ppcp-settings/resources/js/data/common/actions-thunk.js b/modules/ppcp-settings/resources/js/data/common/actions-thunk.js index 8e7448af8..d49aa914a 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions-thunk.js +++ b/modules/ppcp-settings/resources/js/data/common/actions-thunk.js @@ -27,6 +27,19 @@ export function persist() { }; } +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} + /** * Side effect. Fetches the ISU-login URL for a sandbox account. * diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index 76a12cd2b..fd6ba3157 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -13,8 +13,32 @@ import { useCallback, useEffect, useMemo, useState } from '@wordpress/element'; import { createHooksForStore } from '../utils'; import { STORE_NAME } from './constants'; -const useHooks = () => { +/** + * Single source of truth for access Redux details. + * + * This hook returns a stable API to access actions, selectors and special hooks to generate + * getter- and setters for transient or persistent properties. + * + * @return {{select, dispatch, useTransient, usePersistent}} Store data API. + */ +const useStoreData = () => { + const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] ); + const dispatch = useDispatch( STORE_NAME ); const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); + + return useMemo( + () => ( { + select, + dispatch, + useTransient, + usePersistent, + } ), + [ select, dispatch, useTransient, usePersistent ] + ); +}; + +const useHooks = () => { + const { useTransient, usePersistent, dispatch, select } = useStoreData(); const { persist, sandboxOnboardingUrl, @@ -23,10 +47,9 @@ const useHooks = () => { authenticateWithOAuth, startWebhookSimulation, checkWebhookSimulationState, - } = useDispatch( STORE_NAME ); + } = dispatch; // Transient accessors. - const [ isReady ] = useTransient( 'isReady' ); const [ activeModal, setActiveModal ] = useTransient( 'activeModal' ); const [ activeHighlight, setActiveHighlight ] = useTransient( 'activeHighlight' ); @@ -38,18 +61,9 @@ const useHooks = () => { ); // Read-only properties. - const wooSettings = useSelect( - ( select ) => select( STORE_NAME ).wooSettings(), - [] - ); - const features = useSelect( - ( select ) => select( STORE_NAME ).features(), - [] - ); - const webhooks = useSelect( - ( select ) => select( STORE_NAME ).webhooks(), - [] - ); + const wooSettings = select.wooSettings(); + const features = select.features(); + const webhooks = select.webhooks(); const savePersistent = async ( setter, value ) => { setter( value ); @@ -57,7 +71,6 @@ const useHooks = () => { }; return { - isReady, activeModal, setActiveModal, activeHighlight, @@ -82,6 +95,19 @@ const useHooks = () => { }; }; +export const useStore = () => { + const { select, dispatch, useTransient } = useStoreData(); + const { persist, refresh } = dispatch; + const [ isReady ] = useTransient( 'isReady' ); + + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.persistentData(); + } + + return { persist, refresh, isReady }; +}; + export const useSandbox = () => { const { isSandboxMode, setSandboxMode, sandboxOnboardingUrl } = useHooks(); @@ -139,7 +165,7 @@ export const useWebhooks = () => { }; export const useMerchantInfo = () => { - const { isReady, features } = useHooks(); + const { features } = useHooks(); const merchant = useMerchant(); const { refreshMerchantData, setMerchant } = useDispatch( STORE_NAME ); @@ -164,7 +190,6 @@ export const useMerchantInfo = () => { }, [ refreshMerchantData, setMerchant ] ); return { - isReady, merchant, // Merchant details features, // Eligible merchant features verifyLoginStatus, // Callback @@ -204,7 +229,9 @@ export const useActiveHighlight = () => { return { activeHighlight, setActiveHighlight }; }; -// -- Not using the `useHooks()` data provider -- +/* + * Busy state management hooks + */ export const useBusyState = () => { const { startActivity, stopActivity } = useDispatch( STORE_NAME ); diff --git a/modules/ppcp-settings/resources/js/data/debug.js b/modules/ppcp-settings/resources/js/data/debug.js index 88bdf40ae..286285892 100644 --- a/modules/ppcp-settings/resources/js/data/debug.js +++ b/modules/ppcp-settings/resources/js/data/debug.js @@ -18,10 +18,15 @@ export const addDebugTools = ( context, modules ) => { if ( ! context.debug ) { return } */ + const describe = ( fnName, fnInfo ) => { + // eslint-disable-next-line no-console + console.log( `\n%c${ fnName }:`, 'font-weight:bold', fnInfo, '\n\n' ); + }; + const debugApi = ( window.ppcpDebugger = window.ppcpDebugger || {} ); // Dump the current state of all our Redux stores. - debugApi.dumpStore = async () => { + debugApi.dumpStore = async ( cbFilter = null ) => { /* eslint-disable no-console */ if ( ! console?.groupCollapsed ) { console.error( 'console.groupCollapsed is not supported.' ); @@ -34,11 +39,19 @@ export const addDebugTools = ( context, modules ) => { console.group( `[STORE] ${ storeSelector }` ); const dumpStore = ( selector ) => { - const contents = wp.data.select( storeName )[ selector ](); + let contents = wp.data.select( storeName )[ selector ](); - console.groupCollapsed( `.${ selector }()` ); - console.table( contents ); - console.groupEnd(); + if ( cbFilter ) { + contents = cbFilter( contents, selector, storeName ); + + if ( undefined !== contents && null !== contents ) { + console.log( `.${ selector }() [filtered]`, contents ); + } + } else { + console.groupCollapsed( `.${ selector }()` ); + console.table( contents ); + console.groupEnd(); + } }; Object.keys( module.selectors ).forEach( dumpStore ); @@ -51,45 +64,89 @@ export const addDebugTools = ( context, modules ) => { // Reset all Redux stores to their initial state. debugApi.resetStore = () => { const stores = []; - const { isConnected } = wp.data.select( CommonStoreName ).merchant(); - if ( isConnected ) { - // Make sure the Onboarding wizard is "completed". - const onboarding = wp.data.dispatch( OnboardingStoreName ); - onboarding.setPersistent( 'completed', true ); - onboarding.persist(); + describe( + 'resetStore', + 'Reset all Redux stores to their DEFAULT state, without changing any server-side data. The default state is defined in the JS code.' + ); - // Reset all stores, except for the onboarding store. - stores.push( CommonStoreName ); - stores.push( PaymentStoreName ); - stores.push( SettingsStoreName ); - stores.push( StylingStoreName ); - stores.push( TodosStoreName ); - } else { - // Only reset the common & onboarding stores to restart the onboarding wizard. - stores.push( CommonStoreName ); + const { completed } = wp.data + .select( OnboardingStoreName ) + .persistentData(); + + // Reset all stores, except for the onboarding store. + stores.push( CommonStoreName ); + stores.push( PaymentStoreName ); + stores.push( SettingsStoreName ); + stores.push( StylingStoreName ); + stores.push( TodosStoreName ); + + // Only reset the onboarding store when the wizard is not completed. + if ( ! completed ) { stores.push( OnboardingStoreName ); } stores.forEach( ( storeName ) => { const store = wp.data.dispatch( storeName ); - // eslint-disable-next-line no-console - console.log( `Reset store: ${ storeName }...` ); - try { store.reset(); - store.persist(); + + // eslint-disable-next-line no-console + console.log( `Done: Store '${ storeName }' reset` ); } catch ( error ) { - console.error( ' ... Reset failed, skipping this store' ); + console.error( + `Failed: Could not reset store '${ storeName }'` + ); } } ); + + // eslint-disable-next-line no-console + console.log( '---- Complete ----\n\n' ); + }; + + debugApi.refreshStore = () => { + const stores = []; + + describe( + 'refreshStore', + 'Refreshes all Redux details with details provided by the server. This has a similar effect as reloading the page without saving' + ); + + stores.push( CommonStoreName ); + stores.push( PaymentStoreName ); + stores.push( SettingsStoreName ); + stores.push( StylingStoreName ); + stores.push( TodosStoreName ); + stores.push( OnboardingStoreName ); + + stores.forEach( ( storeName ) => { + const store = wp.data.dispatch( storeName ); + + try { + store.refresh(); + + // eslint-disable-next-line no-console + console.log( + `Done: Store '${ storeName }' refreshed from REST` + ); + } catch ( error ) { + console.error( + `Failed: Could not refresh store '${ storeName }' from REST` + ); + } + } ); + + // eslint-disable-next-line no-console + console.log( '---- Complete ----\n\n' ); }; // Disconnect the merchant and display the onboarding wizard. debugApi.disconnect = () => { const common = wp.data.dispatch( CommonStoreName ); + describe(); + common.disconnectMerchant(); // eslint-disable-next-line no-console @@ -102,6 +159,11 @@ export const addDebugTools = ( context, modules ) => { debugApi.onboardingMode = ( state ) => { const onboarding = wp.data.dispatch( OnboardingStoreName ); + describe( + 'onboardingMode', + 'Toggle between onboarding wizard and the settings screen.' + ); + onboarding.setPersistent( 'completed', ! state ); onboarding.persist(); }; diff --git a/modules/ppcp-settings/resources/js/data/onboarding/actions.js b/modules/ppcp-settings/resources/js/data/onboarding/actions.js index 0d2617095..70967168d 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/actions.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/actions.js @@ -87,3 +87,16 @@ export function persist() { } }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js b/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js index f9cd9a556..51b516bb1 100644 --- a/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js +++ b/modules/ppcp-settings/resources/js/data/pay-later-messaging/actions.js @@ -82,3 +82,16 @@ export function persist() { } ); }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/payment/actions.js b/modules/ppcp-settings/resources/js/data/payment/actions.js index 1107d5bfb..dfa100f76 100644 --- a/modules/ppcp-settings/resources/js/data/payment/actions.js +++ b/modules/ppcp-settings/resources/js/data/payment/actions.js @@ -94,3 +94,16 @@ export function persist() { } ); }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/payment/hooks.js b/modules/ppcp-settings/resources/js/data/payment/hooks.js index 253710580..6060041f4 100644 --- a/modules/ppcp-settings/resources/js/data/payment/hooks.js +++ b/modules/ppcp-settings/resources/js/data/payment/hooks.js @@ -7,22 +7,58 @@ * @file */ -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { STORE_NAME } from './constants'; import { createHooksForStore } from '../utils'; +import { useMemo } from '@wordpress/element'; -const useHooks = () => { +/** + * Single source of truth for access Redux details. + * + * This hook returns a stable API to access actions, selectors and special hooks to generate + * getter- and setters for transient or persistent properties. + * + * @return {{select, dispatch, useTransient, usePersistent}} Store data API. + */ +const useStoreData = () => { + const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] ); + const dispatch = useDispatch( STORE_NAME ); const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); - const { persist, setPersistent, changePaymentSettings } = - useDispatch( STORE_NAME ); - // Read-only flags and derived state. - // Nothing here yet. + return useMemo( + () => ( { + select, + dispatch, + useTransient, + usePersistent, + } ), + [ select, dispatch, useTransient, usePersistent ] + ); +}; - // Transient accessors. +export const useStore = () => { + const { select, useTransient, dispatch } = useStoreData(); + const { persist, refresh, setPersistent, changePaymentSettings } = dispatch; const [ isReady ] = useTransient( 'isReady' ); + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.persistentData(); + } + + return { + persist, + refresh, + setPersistent, + changePaymentSettings, + isReady, + }; +}; + +export const usePaymentMethods = () => { + const { usePersistent } = useStoreData(); + // PayPal checkout. const [ paypal ] = usePersistent( 'ppcp-gateway' ); const [ venmo ] = usePersistent( 'venmo' ); @@ -47,79 +83,6 @@ const useHooks = () => { const [ pui ] = usePersistent( 'ppcp-pay-upon-invoice-gateway' ); const [ oxxo ] = usePersistent( 'ppcp-oxxo-gateway' ); - // Custom modal data. - const [ paypalShowLogo ] = usePersistent( 'paypalShowLogo' ); - const [ threeDSecure ] = usePersistent( 'threeDSecure' ); - const [ fastlaneCardholderName ] = usePersistent( - 'fastlaneCardholderName' - ); - const [ fastlaneDisplayWatermark ] = usePersistent( - 'fastlaneDisplayWatermark' - ); - - return { - persist, - isReady, - setPersistent, - changePaymentSettings, - paypal, - venmo, - payLater, - creditCard, - advancedCreditCard, - fastlane, - applePay, - googlePay, - bancontact, - blik, - eps, - ideal, - mybank, - p24, - trustly, - multibanco, - pui, - oxxo, - paypalShowLogo, - threeDSecure, - fastlaneCardholderName, - fastlaneDisplayWatermark, - }; -}; - -export const useStore = () => { - const { persist, isReady, setPersistent, changePaymentSettings } = - useHooks(); - return { persist, isReady, setPersistent, changePaymentSettings }; -}; - -export const usePaymentMethods = () => { - const { - // PayPal Checkout. - paypal, - venmo, - payLater, - creditCard, - - // Online card payments. - advancedCreditCard, - fastlane, - applePay, - googlePay, - - // Local APMs. - bancontact, - blik, - eps, - ideal, - mybank, - p24, - trustly, - multibanco, - pui, - oxxo, - } = useHooks(); - const payPalCheckout = [ paypal, venmo, payLater, creditCard ]; const onlineCardPayments = [ advancedCreditCard, @@ -169,12 +132,16 @@ export const usePaymentMethods = () => { }; export const usePaymentMethodsModal = () => { - const { - paypalShowLogo, - threeDSecure, - fastlaneCardholderName, - fastlaneDisplayWatermark, - } = useHooks(); + const { usePersistent } = useStoreData(); + + const [ paypalShowLogo ] = usePersistent( 'paypalShowLogo' ); + const [ threeDSecure ] = usePersistent( 'threeDSecure' ); + const [ fastlaneCardholderName ] = usePersistent( + 'fastlaneCardholderName' + ); + const [ fastlaneDisplayWatermark ] = usePersistent( + 'fastlaneDisplayWatermark' + ); return { paypalShowLogo, diff --git a/modules/ppcp-settings/resources/js/data/payment/reducer.js b/modules/ppcp-settings/resources/js/data/payment/reducer.js index d46106f6b..4da85fa4e 100644 --- a/modules/ppcp-settings/resources/js/data/payment/reducer.js +++ b/modules/ppcp-settings/resources/js/data/payment/reducer.js @@ -19,6 +19,7 @@ const defaultTransient = Object.freeze( { // Persistent: Values that are loaded from the DB. const defaultPersistent = Object.freeze( { + // Payment methods. 'ppcp-gateway': {}, venmo: {}, 'pay-later': {}, @@ -37,6 +38,8 @@ const defaultPersistent = Object.freeze( { 'ppcp-multibanco': {}, 'ppcp-pay-upon-invoice-gateway': {}, 'ppcp-oxxo-gateway': {}, + + // Custom payment method properties. paypalShowLogo: false, threeDSecure: 'no-3d-secure', fastlaneCardholderName: false, diff --git a/modules/ppcp-settings/resources/js/data/settings/actions.js b/modules/ppcp-settings/resources/js/data/settings/actions.js index 6633516bb..f99adae5a 100644 --- a/modules/ppcp-settings/resources/js/data/settings/actions.js +++ b/modules/ppcp-settings/resources/js/data/settings/actions.js @@ -84,3 +84,16 @@ export function persist() { } ); }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/settings/hooks.js b/modules/ppcp-settings/resources/js/data/settings/hooks.js index f161ee48e..4a97afa9e 100644 --- a/modules/ppcp-settings/resources/js/data/settings/hooks.js +++ b/modules/ppcp-settings/resources/js/data/settings/hooks.js @@ -6,17 +6,38 @@ * * @file */ -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { STORE_NAME } from './constants'; import { createHooksForStore } from '../utils'; +import { useMemo } from '@wordpress/element'; + +/** + * Single source of truth for access Redux details. + * + * This hook returns a stable API to access actions, selectors and special hooks to generate + * getter- and setters for transient or persistent properties. + * + * @return {{select, dispatch, useTransient, usePersistent}} Store data API. + */ +const useStoreData = () => { + const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] ); + const dispatch = useDispatch( STORE_NAME ); + const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); + + return useMemo( + () => ( { + select, + dispatch, + useTransient, + usePersistent, + } ), + [ select, dispatch, useTransient, usePersistent ] + ); +}; const useHooks = () => { - const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); - const { persist } = useDispatch( STORE_NAME ); - - // Read-only flags and derived state. - const [ isReady ] = useTransient( 'isReady' ); + const { usePersistent } = useStoreData(); // Persistent accessors. const [ invoicePrefix, setInvoicePrefix ] = @@ -47,8 +68,6 @@ const useHooks = () => { usePersistent( 'disabledCards' ); return { - persist, - isReady, invoicePrefix, setInvoicePrefix, authorizeOnly, @@ -79,8 +98,16 @@ const useHooks = () => { }; export const useStore = () => { - const { persist, isReady } = useHooks(); - return { persist, isReady }; + const { select, dispatch, useTransient } = useStoreData(); + const { persist, refresh } = dispatch; + const [ isReady ] = useTransient( 'isReady' ); + + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.persistentData(); + } + + return { persist, refresh, isReady }; }; export const useSettings = () => { diff --git a/modules/ppcp-settings/resources/js/data/styling/actions.js b/modules/ppcp-settings/resources/js/data/styling/actions.js index 9e1639c7f..72d4d9ea7 100644 --- a/modules/ppcp-settings/resources/js/data/styling/actions.js +++ b/modules/ppcp-settings/resources/js/data/styling/actions.js @@ -82,3 +82,16 @@ export function persist() { } ); }; } + +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/styling/hooks.js b/modules/ppcp-settings/resources/js/data/styling/hooks.js index 8227d0124..ecf999eaf 100644 --- a/modules/ppcp-settings/resources/js/data/styling/hooks.js +++ b/modules/ppcp-settings/resources/js/data/styling/hooks.js @@ -7,7 +7,7 @@ * @file */ -import { useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { createHooksForStore } from '../utils'; @@ -20,13 +20,37 @@ import { STYLING_PAYMENT_METHODS, STYLING_SHAPES, } from './configuration'; +import { persistentData } from './selectors'; + +/** + * Single source of truth for access Redux details. + * + * This hook returns a stable API to access actions, selectors and special hooks to generate + * getter- and setters for transient or persistent properties. + * + * @return {{select, dispatch, useTransient, usePersistent}} Store data API. + */ +const useStoreData = () => { + const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] ); + const dispatch = useDispatch( STORE_NAME ); + const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); + + return useMemo( + () => ( { + select, + dispatch, + useTransient, + usePersistent, + } ), + [ select, dispatch, useTransient, usePersistent ] + ); +}; const useHooks = () => { - const { useTransient } = createHooksForStore( STORE_NAME ); - const { persist, setPersistent } = useDispatch( STORE_NAME ); + const { useTransient, dispatch } = useStoreData(); + const { setPersistent } = dispatch; // Transient accessors. - const [ isReady ] = useTransient( 'isReady' ); const [ location, setLocation ] = useTransient( 'location' ); // Persistent accessors. @@ -61,8 +85,6 @@ const useHooks = () => { ); return { - persist, - isReady, location, setLocation, getLocationProp, @@ -71,8 +93,16 @@ const useHooks = () => { }; export const useStore = () => { - const { persist, isReady } = useHooks(); - return { persist, isReady }; + const { select, dispatch, useTransient } = useStoreData(); + const { persist, refresh } = dispatch; + const [ isReady ] = useTransient( 'isReady' ); + + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.persistentData(); + } + + return { persist, refresh, isReady }; }; export const useStylingLocation = () => { diff --git a/modules/ppcp-settings/resources/js/data/todos/action-types.js b/modules/ppcp-settings/resources/js/data/todos/action-types.js index 66d92218a..17bbe8a0f 100644 --- a/modules/ppcp-settings/resources/js/data/todos/action-types.js +++ b/modules/ppcp-settings/resources/js/data/todos/action-types.js @@ -5,6 +5,12 @@ */ export default { + /** + * Resets the store state to its initial values. + * Used when needing to clear all store data. + */ + RESET: 'ppcp/todos/RESET', + // Transient data SET_TRANSIENT: 'ppcp/todos/SET_TRANSIENT', SET_COMPLETED_TODOS: 'ppcp/todos/SET_COMPLETED_TODOS', diff --git a/modules/ppcp-settings/resources/js/data/todos/actions.js b/modules/ppcp-settings/resources/js/data/todos/actions.js index 41ac372cd..25b6d7647 100644 --- a/modules/ppcp-settings/resources/js/data/todos/actions.js +++ b/modules/ppcp-settings/resources/js/data/todos/actions.js @@ -17,11 +17,47 @@ import { REST_RESET_DISMISSED_TODOS_PATH, } from './constants'; -export const setIsReady = ( isReady ) => ( { - type: ACTION_TYPES.SET_TRANSIENT, - payload: { isReady }, +/** + * Special. Resets all values in the store to initial defaults. + * + * @return {Object} The action. + */ +export const reset = () => ( { + type: ACTION_TYPES.RESET, } ); +/** + * Generic transient-data updater. + * + * @param {string} prop Name of the property to update. + * @param {any} value The new value of the property. + * @return {Object} The action. + */ +export const setTransient = ( prop, value ) => ( { + type: ACTION_TYPES.SET_TRANSIENT, + payload: { [ prop ]: value }, +} ); + +/** + * Generic persistent-data updater. + * + * @param {string} prop Name of the property to update. + * @param {any} value The new value of the property. + * @return {Object} The action. + */ +export const setPersistent = ( prop, value ) => ( { + type: ACTION_TYPES.SET_PERSISTENT, + payload: { [ prop ]: value }, +} ); + +/** + * Transient. Marks the store as "ready", i.e., fully initialized. + * + * @param {boolean} isReady Whether the store is ready + * @return {Object} The action. + */ +export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady ); + export const setTodos = ( todos ) => ( { type: ACTION_TYPES.SET_TODOS, payload: todos, @@ -39,6 +75,7 @@ export const setCompletedTodos = ( completedTodos ) => ( { // Thunks +// TODO: Possibly, this should be a resolver? export function fetchTodos() { return async () => { const response = await apiFetch( { path: REST_PATH } ); @@ -46,9 +83,14 @@ export function fetchTodos() { }; } +/** + * Thunk action creator. Triggers the persistence of store data to the server. + * + * @return {Function} The thunk function. + */ export function persist() { return async ( { select } ) => { - return await apiFetch( { + await apiFetch( { path: REST_PERSIST_PATH, method: 'POST', data: select.persistentData(), @@ -56,6 +98,19 @@ export function persist() { }; } +/** + * Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values. + * + * @return {Function} The thunk function. + */ +export function refresh() { + return ( { dispatch, select } ) => { + dispatch.invalidateResolutionForStore(); + + select.persistentData(); + }; +} + export function resetDismissedTodos() { return async ( { dispatch } ) => { try { diff --git a/modules/ppcp-settings/resources/js/data/todos/hooks.js b/modules/ppcp-settings/resources/js/data/todos/hooks.js index 7fc5da5c1..5ce97a636 100644 --- a/modules/ppcp-settings/resources/js/data/todos/hooks.js +++ b/modules/ppcp-settings/resources/js/data/todos/hooks.js @@ -10,31 +10,40 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { STORE_NAME } from './constants'; import { createHooksForStore } from '../utils'; +import { useMemo } from '@wordpress/element'; -const ensureArray = ( value ) => { - if ( ! value ) { - return []; - } - return Array.isArray( value ) ? value : Object.values( value ); +/** + * Single source of truth for access Redux details. + * + * This hook returns a stable API to access actions, selectors and special hooks to generate + * getter- and setters for transient or persistent properties. + * + * @return {{select, dispatch, useTransient, usePersistent}} Store data API. + */ +const useStoreData = () => { + const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] ); + const dispatch = useDispatch( STORE_NAME ); + const { useTransient, usePersistent } = createHooksForStore( STORE_NAME ); + + return useMemo( + () => ( { + select, + dispatch, + useTransient, + usePersistent, + } ), + [ select, dispatch, useTransient, usePersistent ] + ); }; const useHooks = () => { - const { useTransient } = createHooksForStore( STORE_NAME ); - const { fetchTodos, setDismissedTodos, setCompletedTodos, persist } = - useDispatch( STORE_NAME ); - - // Read-only flags and derived state. - const [ isReady ] = useTransient( 'isReady' ); + const { dispatch, select } = useStoreData(); + const { fetchTodos, setDismissedTodos, setCompletedTodos } = dispatch; // Get todos data from store - const { todos, dismissedTodos, completedTodos } = useSelect( ( select ) => { - const store = select( STORE_NAME ); - return { - todos: ensureArray( store.getTodos() ), - dismissedTodos: ensureArray( store.getDismissedTodos() ), - completedTodos: ensureArray( store.getCompletedTodos() ), - }; - }, [] ); + const todos = select.getTodos(); + const dismissedTodos = select.getDismissedTodos(); + const completedTodos = select.getCompletedTodos(); const dismissedSet = new Set( dismissedTodos ); @@ -62,8 +71,6 @@ const useHooks = () => { ); return { - persist, - isReady, todos: filteredTodos, dismissedTodos, completedTodos, @@ -74,14 +81,21 @@ const useHooks = () => { }; export const useStore = () => { - const { persist, isReady } = useHooks(); - return { persist, isReady }; + const { select, dispatch, useTransient } = useStoreData(); + const { persist, refresh } = dispatch; + const [ isReady ] = useTransient( 'isReady' ); + + // Load persistent data from REST if not done yet. + if ( ! isReady ) { + select.getTodos(); + } + + return { persist, refresh, isReady }; }; export const useTodos = () => { - const { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady } = - useHooks(); - return { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady }; + const { todos, fetchTodos, dismissTodo, setTodoCompleted } = useHooks(); + return { todos, fetchTodos, dismissTodo, setTodoCompleted }; }; export const useDismissedTodos = () => { diff --git a/modules/ppcp-settings/resources/js/data/todos/reducer.js b/modules/ppcp-settings/resources/js/data/todos/reducer.js index 6f4c74d46..f3cc412ac 100644 --- a/modules/ppcp-settings/resources/js/data/todos/reducer.js +++ b/modules/ppcp-settings/resources/js/data/todos/reducer.js @@ -52,6 +52,21 @@ const reducer = createReducer( defaultTransient, defaultPersistent, { [ ACTION_TYPES.SET_TRANSIENT ]: ( state, payload ) => changeTransient( state, payload ), + /** + * Resets state to defaults while maintaining initialization status + * + * @param {Object} state Current state + * @return {Object} Reset state + */ + [ ACTION_TYPES.RESET ]: ( state ) => { + const cleanState = changeTransient( + changePersistent( state, defaultPersistent ), + defaultTransient + ); + cleanState.isReady = true; // Keep initialization flag + return cleanState; + }, + /** * Updates todos list * @@ -99,6 +114,7 @@ const reducer = createReducer( defaultTransient, defaultPersistent, { }, /** + * TODO: This is not used anywhere. Remove "SET_TODOS" and use this resolver instead. * Initializes persistent state with data from the server * * @param {Object} state Current state diff --git a/modules/ppcp-settings/resources/js/data/todos/selectors.js b/modules/ppcp-settings/resources/js/data/todos/selectors.js index 2f42c6ffc..1066ba3a8 100644 --- a/modules/ppcp-settings/resources/js/data/todos/selectors.js +++ b/modules/ppcp-settings/resources/js/data/todos/selectors.js @@ -11,7 +11,17 @@ const EMPTY_OBJ = Object.freeze( {} ); const EMPTY_ARR = Object.freeze( [] ); const getState = ( state ) => state || EMPTY_OBJ; +const getArray = ( value ) => { + if ( Array.isArray( value ) ) { + return value; + } + if ( value ) { + return Object.values( value ); + } + return EMPTY_ARR; +}; +// TODO: Implement a persistentData resolver! export const persistentData = ( state ) => { return getState( state ).data || EMPTY_OBJ; }; @@ -23,15 +33,15 @@ export const transientData = ( state ) => { export const getTodos = ( state ) => { const todos = state?.todos || persistentData( state ).todos; - return todos || EMPTY_ARR; + return getArray( todos ); }; export const getDismissedTodos = ( state ) => { const dismissed = state?.dismissedTodos || persistentData( state ).dismissedTodos; - return dismissed || EMPTY_ARR; + return getArray( dismissed ); }; export const getCompletedTodos = ( state ) => { - return state?.completedTodos || EMPTY_ARR; // Only look at root state, not persistent data + return getArray( state?.completedTodos ); // Only look at root state, not persistent data }; diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index e605b61ef..e98345149 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useRef } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { CommonHooks, OnboardingHooks } from '../data'; +import { useStoreManager } from './useStoreManager'; const PAYPAL_PARTNER_SDK_URL = 'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js'; @@ -30,7 +31,7 @@ export const useHandleOnboardingButton = ( isSandbox ) => { const { sandboxOnboardingUrl } = CommonHooks.useSandbox(); const { productionOnboardingUrl } = CommonHooks.useProduction(); const products = OnboardingHooks.useDetermineProducts(); - const { withActivity, startActivity } = CommonHooks.useBusyState(); + const { startActivity } = CommonHooks.useBusyState(); const { authenticateWithOAuth } = CommonHooks.useAuthentication(); const [ onboardingUrl, setOnboardingUrl ] = useState( '' ); const [ scriptLoaded, setScriptLoaded ] = useState( false ); @@ -134,7 +135,7 @@ export const useHandleOnboardingButton = ( isSandbox ) => { // Ensure the onComplete handler is not removed by a PayPal init script. timerRef.current = setInterval( addHandler, 250 ); }, - [ authenticateWithOAuth, withActivity ] + [ authenticateWithOAuth, startActivity ] ); const removeCompleteHandler = useCallback( () => { @@ -161,6 +162,7 @@ const useConnectionBase = () => { useDispatch( noticesStore ); const { verifyLoginStatus } = CommonHooks.useMerchantInfo(); const { withActivity } = CommonHooks.useBusyState(); + const { refreshAll } = useStoreManager(); return { handleFailed: ( res, genericMessage ) => { @@ -178,6 +180,7 @@ const useConnectionBase = () => { if ( loginSuccessful ) { createSuccessNotice( MESSAGES.CONNECTED ); await setCompleted( true ); + refreshAll(); } else { createErrorNotice( MESSAGES.LOGIN_FAILED ); } diff --git a/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js b/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js deleted file mode 100644 index 7237aa4df..000000000 --- a/modules/ppcp-settings/resources/js/hooks/useSaveSettings.js +++ /dev/null @@ -1,74 +0,0 @@ -import { useCallback, useMemo } from '@wordpress/element'; - -import { - CommonHooks, - PayLaterMessagingHooks, - PaymentHooks, - SettingsHooks, - StylingHooks, - TodosHooks, -} from '../data'; - -export const useSaveSettings = () => { - const { withActivity } = CommonHooks.useBusyState(); - - const { persist: persistPayment } = PaymentHooks.useStore(); - const { persist: persistSettings } = SettingsHooks.useStore(); - const { persist: persistStyling } = StylingHooks.useStore(); - const { persist: persistTodos } = TodosHooks.useStore(); - const { persist: persistPayLaterMessaging } = - PayLaterMessagingHooks.useStore(); - - const persistActions = useMemo( - () => [ - { - key: 'persist-methods', - message: 'Save payment methods', - action: persistPayment, - }, - { - key: 'persist-settings', - message: 'Save the settings', - action: persistSettings, - }, - { - key: 'persist-styling', - message: 'Save styling details', - action: persistStyling, - }, - { - key: 'persist-todos', - message: 'Save todos state', - action: persistTodos, - }, - { - key: 'persist-pay-later-messaging', - message: 'Save pay later messaging details', - action: persistPayLaterMessaging, - }, - ], - [ - persistPayLaterMessaging, - persistPayment, - persistSettings, - persistStyling, - persistTodos, - ] - ); - - const persistAll = useCallback( () => { - /** - * Executes onSave on TabPayLaterMessaging component. - * - * Todo: find a better way for this, because it's highly unreliable - * (it only works when the user is still on the "Pay Later Messaging" tab) - */ - document.getElementById( 'configurator-publishButton' )?.click(); - - persistActions.forEach( ( { key, message, action } ) => { - withActivity( key, message, action ); - } ); - }, [ persistActions, withActivity ] ); - - return { persistAll }; -}; diff --git a/modules/ppcp-settings/resources/js/hooks/useStoreManager.js b/modules/ppcp-settings/resources/js/hooks/useStoreManager.js new file mode 100644 index 000000000..190b2ac88 --- /dev/null +++ b/modules/ppcp-settings/resources/js/hooks/useStoreManager.js @@ -0,0 +1,76 @@ +import { useCallback, useMemo } from '@wordpress/element'; + +import { + CommonHooks, + PayLaterMessagingHooks, + PaymentHooks, + SettingsHooks, + StylingHooks, + TodosHooks, +} from '../data'; + +export const useStoreManager = () => { + const { withActivity } = CommonHooks.useBusyState(); + + const paymentStore = PaymentHooks.useStore(); + const settingsStore = SettingsHooks.useStore(); + const stylingStore = StylingHooks.useStore(); + const todosStore = TodosHooks.useStore(); + const payLaterStore = PayLaterMessagingHooks.useStore(); + + const storeActions = useMemo( + () => [ + { + key: 'methods', + message: 'Process payment methods', + store: paymentStore, + }, + { + key: 'settings', + message: 'Process the settings', + store: settingsStore, + }, + { + key: 'styling', + message: 'Process styling details', + store: stylingStore, + }, + { + key: 'todos', + message: 'Process todos state', + store: todosStore, + }, + { + key: 'pay-later-messaging', + message: 'Process pay later messaging details', + store: payLaterStore, + }, + ], + [ payLaterStore, paymentStore, settingsStore, stylingStore, todosStore ] + ); + + const persistAll = useCallback( () => { + /** + * Executes onSave on TabPayLaterMessaging component. + * + * Todo: find a better way for this, because it's highly unreliable + * (it only works when the user is still on the "Pay Later Messaging" tab) + */ + document.getElementById( 'configurator-publishButton' )?.click(); + + storeActions.forEach( ( { key, message, store } ) => { + withActivity( `persist-${ key }`, message, store.persist ); + } ); + }, [ storeActions, withActivity ] ); + + const refreshAll = useCallback( () => { + storeActions.forEach( ( { key, message, store } ) => { + withActivity( `refresh-${ key }`, message, store.refresh ); + } ); + }, [ storeActions, withActivity ] ); + + return { + persistAll, + refreshAll, + }; +}; diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index 9bb48f438..7e7a2ea82 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -20,13 +20,11 @@ use WooCommerce\PayPalCommerce\Settings\Data\TodosModel; use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition; use WooCommerce\PayPalCommerce\Settings\Endpoint\AuthenticationRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint; -use WooCommerce\PayPalCommerce\Settings\Endpoint\CompleteOnClickEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\PayLaterMessagingEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\PaymentRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\RefreshFeatureStatusEndpoint; -use WooCommerce\PayPalCommerce\Settings\Endpoint\ResetDismissedTodosEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\WebhookSettingsEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\SettingsRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\StylingRestEndpoint; @@ -36,6 +34,7 @@ use WooCommerce\PayPalCommerce\Settings\Service\AuthenticationManager; use WooCommerce\PayPalCommerce\Settings\Service\ConnectionUrlGenerator; use WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager; use WooCommerce\PayPalCommerce\Settings\Service\TodosEligibilityService; +use WooCommerce\PayPalCommerce\Settings\Service\TodosSortingAndFilteringService; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Settings\Service\DataSanitizer; use WooCommerce\PayPalCommerce\Settings\Service\SettingsDataManager; @@ -43,6 +42,8 @@ use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDefinition use WooCommerce\PayPalCommerce\PayLaterConfigurator\Factory\ConfigFactory; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\PayLaterConfigurator\Endpoint\SaveConfig; +use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; +use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState; return array( 'settings.url' => static function ( ContainerInterface $container ) : string { @@ -116,13 +117,51 @@ return array( ); }, /** - * Checks if valid merchant connection details are stored in the DB. + * Merchant connection details, which includes the connection status + * (onboarding/connected) and connection-aware environment checks. + * This is the preferred solution to check environment and connection state. */ - 'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool { + 'settings.connection-state' => static function ( ContainerInterface $container ) : ConnectionState { $data = $container->get( 'settings.data.general' ); assert( $data instanceof GeneralSettings ); - return $data->is_merchant_connected(); + $is_connected = $data->is_merchant_connected(); + $environment = new Environment( $data->is_sandbox_merchant() ); + + return new ConnectionState( $is_connected, $environment ); + }, + 'settings.environment' => static function ( ContainerInterface $container ) : Environment { + // We should remove this service in favor of directly using `settings.connection-state`. + $state = $container->get( 'settings.connection-state' ); + assert( $state instanceof ConnectionState ); + + return $state->get_environment(); + }, + /** + * Checks if valid merchant connection details are stored in the DB. + */ + 'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool { + /* + * This service only resolves the connection status once per request. + * We should remove this service in favor of directly using `settings.connection-state`. + */ + $state = $container->get( 'settings.connection-state' ); + assert( $state instanceof ConnectionState ); + + return $state->is_connected(); + }, + /** + * Checks if the merchant is connected to a sandbox environment. + */ + 'settings.flag.is-sandbox' => static function ( ContainerInterface $container ) : bool { + /* + * This service only resolves the sandbox flag once per request. + * We should remove this service in favor of directly using `settings.connection-state`. + */ + $state = $container->get( 'settings.connection-state' ); + assert( $state instanceof ConnectionState ); + + return $state->is_sandbox(); }, 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); @@ -268,6 +307,7 @@ return array( $container->get( 'api.env.paypal-host' ), $container->get( 'api.env.endpoint.login-seller' ), $container->get( 'api.repository.partner-referrals-data' ), + $container->get( 'settings.connection-state' ), $container->get( 'woocommerce.logger.woocommerce' ), ); }, @@ -298,7 +338,8 @@ return array( return new TodosRestEndpoint( $container->get( 'settings.data.todos' ), $container->get( 'settings.data.definition.todos' ), - $container->get( 'settings.rest.settings' ) + $container->get( 'settings.rest.settings' ), + $container->get( 'settings.service.todos_sorting' ) ); }, 'settings.data.todos' => static function ( ContainerInterface $container ) : TodosModel { @@ -307,7 +348,8 @@ return array( 'settings.data.definition.todos' => static function ( ContainerInterface $container ) : TodosDefinition { return new TodosDefinition( $container->get( 'settings.service.todos_eligibilities' ), - $container->get( 'settings.data.general' ) + $container->get( 'settings.data.general' ), + $container->get( 'wc-subscriptions.helper' ) ); }, 'settings.data.definition.methods' => static function ( ContainerInterface $container ) : PaymentMethodsDefinition { @@ -315,17 +357,10 @@ return array( $container->get( 'settings.data.payment' ), ); }, - 'settings.service.todos_eligibilities' => static function ( ContainerInterface $container ) : TodosEligibilityService { - $features = apply_filters( - 'woocommerce_paypal_payments_rest_common_merchant_features', - array() - ); - - $payment_endpoint = $container->get( 'settings.rest.payment' ); - $settings = $payment_endpoint->get_details()->get_data(); - + 'settings.service.pay_later_status' => static function ( ContainerInterface $container ) : array { $pay_later_endpoint = $container->get( 'settings.rest.pay_later_messaging' ); $pay_later_settings = $pay_later_endpoint->get_details()->get_data(); + $pay_later_statuses = array( 'cart' => $pay_later_settings['data']['cart']['status'] === 'enabled', 'checkout' => $pay_later_settings['data']['checkout']['status'] === 'enabled', @@ -336,16 +371,41 @@ return array( $pay_later_settings['data']['custom_placement'][0]['status'] === 'enabled', ); - // Settings status. - $gateways = array( + $is_pay_later_messaging_enabled_for_any_location = ! array_filter( $pay_later_statuses ); + + return array( + 'statuses' => $pay_later_statuses, + 'is_enabled_for_any_location' => $is_pay_later_messaging_enabled_for_any_location, + ); + }, + 'settings.service.button_locations' => static function ( ContainerInterface $container ) : array { + $styling_endpoint = $container->get( 'settings.rest.styling' ); + $styling_data = $styling_endpoint->get_details()->get_data()['data']; + + return array( + 'cart_enabled' => $styling_data['cart']->enabled ?? false, + 'block_checkout_enabled' => $styling_data['expressCheckout']->enabled ?? false, + 'product_enabled' => $styling_data['product']->enabled ?? false, + ); + }, + 'settings.service.gateways_status' => static function ( ContainerInterface $container ) : array { + $payment_endpoint = $container->get( 'settings.rest.payment' ); + $settings = $payment_endpoint->get_details()->get_data(); + + return array( 'apple_pay' => $settings['data']['ppcp-applepay']['enabled'] ?? false, 'google_pay' => $settings['data']['ppcp-googlepay']['enabled'] ?? false, 'axo' => $settings['data']['ppcp-axo-gateway']['enabled'] ?? false, 'card-button' => $settings['data']['ppcp-card-button-gateway']['enabled'] ?? false, ); + }, + 'settings.service.merchant_capabilities' => static function ( ContainerInterface $container ) : array { + $features = apply_filters( + 'woocommerce_paypal_payments_rest_common_merchant_features', + array() + ); - // Merchant eligibility. - $capabilities = array( + return array( 'apple_pay' => $features['apple_pay']['enabled'] ?? false, 'google_pay' => $features['google_pay']['enabled'] ?? false, 'acdc' => $features['advanced_credit_and_debit_cards']['enabled'] ?? false, @@ -353,25 +413,67 @@ return array( 'apm' => $features['alternative_payment_methods']['enabled'] ?? false, 'paylater' => $features['pay_later_messaging']['enabled'] ?? false, ); + }, - $is_pay_later_messaging_enabled_for_any_location = ! array_filter( $pay_later_statuses ); + 'settings.service.todos_eligibilities' => static function ( ContainerInterface $container ) : TodosEligibilityService { + $pay_later_service = $container->get( 'settings.service.pay_later_status' ); + $pay_later_statuses = $pay_later_service['statuses']; + $is_pay_later_messaging_enabled_for_any_location = $pay_later_service['is_enabled_for_any_location']; + $button_locations = $container->get( 'settings.service.button_locations' ); + $gateways = $container->get( 'settings.service.gateways_status' ); + $capabilities = $container->get( 'settings.service.merchant_capabilities' ); + + /** + * Initializes TodosEligibilityService with eligibility conditions for various PayPal features. + * Each parameter determines whether a specific feature should be shown in the Things To Do list. + * + * Logic relies on two main factors: + * 1. $capabilities - Whether the merchant is eligible for specific features on their PayPal account. + * 2. $gateways, $pay_later_statuses, $button_locations - Plugin settings (enabled/disabled status). + * + * @param bool $is_fastlane_eligible - Show if merchant is eligible (ACDC) but hasn't enabled Fastlane gateway. + * @param bool $is_card_payment_eligible - Show if merchant is eligible (ACDC) but hasn't enabled card button gateway. + * @param bool $is_pay_later_messaging_eligible - Show if Pay Later messaging is enabled for at least one location. + * @param bool $is_pay_later_messaging_product_eligible - Show if Pay Later is not enabled anywhere and specifically not on product page. + * @param bool $is_pay_later_messaging_cart_eligible - Show if Pay Later is not enabled anywhere and specifically not on cart. + * @param bool $is_pay_later_messaging_checkout_eligible - Show if Pay Later is not enabled anywhere and specifically not on checkout. + * @param bool $is_subscription_eligible - Show if WooCommerce Subscriptions plugin is active but merchant is not eligible for PayPal Vaulting. + * @param bool $is_paypal_buttons_cart_eligible - Show if PayPal buttons are not enabled on cart page. + * @param bool $is_paypal_buttons_block_checkout_eligible - Show if PayPal buttons are not enabled on blocks checkout. + * @param bool $is_paypal_buttons_product_eligible - Show if PayPal buttons are not enabled on product page. + * @param bool $is_apple_pay_domain_eligible - Show if merchant has Apple Pay capability on PayPal account. + * @param bool $is_digital_wallet_eligible - Show if merchant is eligible (ACDC) but doesn't have both wallet types on PayPal. + * @param bool $is_apple_pay_eligible - Show if merchant is eligible (ACDC) but doesn't have Apple Pay on PayPal. + * @param bool $is_google_pay_eligible - Show if merchant is eligible (ACDC) but doesn't have Google Pay on PayPal. + * @param bool $is_enable_apple_pay_eligible - Show if merchant has Apple Pay capability but hasn't enabled the gateway. + * @param bool $is_enable_google_pay_eligible - Show if merchant has Google Pay capability but hasn't enabled the gateway. + */ return new TodosEligibilityService( - $capabilities['acdc'] && ! $gateways['axo'], // Enable Fastlane. - $capabilities['acdc'] && ! $gateways['card-button'], // Enable Credit and Debit Cards on your checkout. - $is_pay_later_messaging_enabled_for_any_location, // Enable Pay Later messaging. - ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['product'], // Add Pay Later messaging (Product page). - ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['cart'], // Add Pay Later messaging (Cart). - ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['checkout'], // Add Pay Later messaging (Checkout). - true, // Configure a PayPal Subscription. - true, // Add PayPal buttons. - true, // Register Domain for Apple Pay. - $capabilities['acdc'] && ! ( $capabilities['apple_pay'] && $capabilities['google_pay'] ), // Add digital wallets to your account. - $capabilities['acdc'] && ! $capabilities['apple_pay'], // Add Apple Pay to your account. - $capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account. - true, // Configure a PayPal Subscription. - $capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay. - $capabilities['google_pay'] && ! $gateways['google_pay'], // Enable Google Pay. + $capabilities['acdc'] && ! $gateways['axo'], // Enable Fastlane. + $capabilities['acdc'] && ! $gateways['card-button'], // Enable Credit and Debit Cards on your checkout. + $is_pay_later_messaging_enabled_for_any_location, // Enable Pay Later messaging. + ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['product'], // Add Pay Later messaging (Product page). + ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['cart'], // Add Pay Later messaging (Cart). + ! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['checkout'], // Add Pay Later messaging (Checkout). + $container->has( 'save-payment-methods.eligible' ) && + ! $container->get( 'save-payment-methods.eligible' ) && + $container->has( 'wc-subscriptions.helper' ) && + $container->get( 'wc-subscriptions.helper' )->plugin_is_active(), // Configure a PayPal Subscription. + ! $button_locations['cart_enabled'], // Add PayPal buttons to cart. + ! $button_locations['block_checkout_enabled'], // Add PayPal buttons to block checkout. + ! $button_locations['product_enabled'], // Add PayPal buttons to product. + $capabilities['apple_pay'], // Register Domain for Apple Pay. + $capabilities['acdc'] && ! ( $capabilities['apple_pay'] && $capabilities['google_pay'] ), // Add digital wallets to your account. + $capabilities['acdc'] && ! $capabilities['apple_pay'], // Add Apple Pay to your account. + $capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account. + $capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay. + $capabilities['google_pay'] && ! $gateways['google_pay'], + ); + }, + 'settings.service.todos_sorting' => static function ( ContainerInterface $container ) : TodosSortingAndFilteringService { + return new TodosSortingAndFilteringService( + $container->get( 'settings.data.todos' ) ); }, ); diff --git a/modules/ppcp-settings/src/Data/Definition/TodosDefinition.php b/modules/ppcp-settings/src/Data/Definition/TodosDefinition.php index 204b81467..4868edd62 100644 --- a/modules/ppcp-settings/src/Data/Definition/TodosDefinition.php +++ b/modules/ppcp-settings/src/Data/Definition/TodosDefinition.php @@ -1,6 +1,6 @@ eligibilities = $eligibilities; - $this->settings = $settings; + $this->eligibilities = $eligibilities; + $this->settings = $settings; + $this->subscription_helper = $subscription_helper; } /** @@ -126,15 +137,38 @@ class TodosDefinition { 'description' => __( 'Connect a subscriptions-type product from WooCommerce with PayPal', 'woocommerce-paypal-payments' ), 'isEligible' => $eligibility_checks['configure_paypal_subscription'], 'action' => array( - 'type' => 'external', - 'url' => admin_url( 'edit.php?post_type=product&product_type=subscription' ), + 'type' => 'external', + 'url' => $this->subscription_helper->has_subscription_products() + ? admin_url( 'edit.php?post_type=product&product_type=subscription' ) // If subscription products exist, go to the subscriptions products archive page. + : admin_url( 'post-new.php?post_type=product' ), // If there are no subscriptions products, go to create one. + 'completeOnClick' => true, ), 'priority' => 5, ), - 'add_paypal_buttons' => array( - 'title' => __( 'Add PayPal buttons', 'woocommerce-paypal-payments' ), - 'description' => __( 'Allow customers to check out quickly and securely from the page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ), - 'isEligible' => $eligibility_checks['add_paypal_buttons'], + 'add_paypal_buttons_cart' => array( + 'title' => __( 'Add PayPal buttons to the Cart page', 'woocommerce-paypal-payments' ), + 'description' => __( 'Allow customers to check out quickly and securely from the Cart page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ), + 'isEligible' => $eligibility_checks['add_paypal_buttons_cart'], + 'action' => array( + 'type' => 'tab', + 'tab' => 'styling', + ), + 'priority' => 6, + ), + 'add_paypal_buttons_block_checkout' => array( + 'title' => __( 'Add PayPal buttons to the Express Checkout page', 'woocommerce-paypal-payments' ), + 'description' => __( 'Allow customers to check out quickly and securely from the Express Checkout page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ), + 'isEligible' => $eligibility_checks['add_paypal_buttons_block_checkout'], + 'action' => array( + 'type' => 'tab', + 'tab' => 'styling', + ), + 'priority' => 6, + ), + 'add_paypal_buttons_product' => array( + 'title' => __( 'Add PayPal buttons to the Product page', 'woocommerce-paypal-payments' ), + 'description' => __( 'Allow customers to check out quickly and securely from the Product page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ), + 'isEligible' => $eligibility_checks['add_paypal_buttons_product'], 'action' => array( 'type' => 'tab', 'tab' => 'styling', diff --git a/modules/ppcp-settings/src/Endpoint/TodosRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/TodosRestEndpoint.php index ac23b7f45..b1cd77b49 100644 --- a/modules/ppcp-settings/src/Endpoint/TodosRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/TodosRestEndpoint.php @@ -17,6 +17,7 @@ use WP_REST_Response; use WP_REST_Request; use WooCommerce\PayPalCommerce\Settings\Data\TodosModel; use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition; +use WooCommerce\PayPalCommerce\Settings\Service\TodosSortingAndFilteringService; /** * REST controller for the "Things To Do" items in the Overview tab. @@ -33,17 +34,6 @@ class TodosRestEndpoint extends RestEndpoint { */ protected $rest_base = 'todos'; - /** - * Pay Later messaging todo IDs in priority order. - * - * @var array - */ - private const PAY_LATER_IDS = array( - 'add_pay_later_messaging_product_page', - 'add_pay_later_messaging_cart', - 'add_pay_later_messaging_checkout', - ); - /** * The todos model instance. * @@ -65,21 +55,31 @@ class TodosRestEndpoint extends RestEndpoint { */ protected SettingsRestEndpoint $settings; + /** + * The todos sorting service. + * + * @var TodosSortingAndFilteringService + */ + protected TodosSortingAndFilteringService $sorting_service; + /** * TodosRestEndpoint constructor. * - * @param TodosModel $todos The todos model instance. - * @param TodosDefinition $todos_definition The todos definition instance. - * @param SettingsRestEndpoint $settings The settings endpoint instance. + * @param TodosModel $todos The todos model instance. + * @param TodosDefinition $todos_definition The todos definition instance. + * @param SettingsRestEndpoint $settings The settings endpoint instance. + * @param TodosSortingAndFilteringService $sorting_service The todos sorting service. */ public function __construct( TodosModel $todos, TodosDefinition $todos_definition, - SettingsRestEndpoint $settings + SettingsRestEndpoint $settings, + TodosSortingAndFilteringService $sorting_service ) { $this->todos = $todos; $this->todos_definition = $todos_definition; $this->settings = $settings; + $this->sorting_service = $sorting_service; } /** @@ -157,8 +157,7 @@ class TodosRestEndpoint extends RestEndpoint { } } - $sorted_todos = $this->sort_todos_by_priority( $todos ); - $filtered_todos = $this->filter_pay_later_todos( $sorted_todos ); + $filtered_todos = $this->sorting_service->apply_all_priority_filters( $todos ); return $this->return_success( array( @@ -245,67 +244,4 @@ class TodosRestEndpoint extends RestEndpoint { ); } } - - /** - * Filters Pay Later messaging todos to show only the highest priority eligible todo. - * - * @param array $todos The array of todos to filter. - * @return array Filtered todos with only one Pay Later messaging todo. - */ - private function filter_pay_later_todos( array $todos ): array { - $pay_later_todos = array_filter( - $todos, - function( $todo ) { - return in_array( $todo['id'], self::PAY_LATER_IDS, true ); - } - ); - - $other_todos = array_filter( - $todos, - function( $todo ) { - return ! in_array( $todo['id'], self::PAY_LATER_IDS, true ); - } - ); - - // Find the highest priority Pay Later todo that's eligible. - $priority_pay_later_todo = null; - foreach ( self::PAY_LATER_IDS as $pay_later_id ) { - $matching_todo = current( - array_filter( - $pay_later_todos, - function( $todo ) use ( $pay_later_id ) { - return $todo['id'] === $pay_later_id; - } - ) - ); - - if ( $matching_todo ) { - $priority_pay_later_todo = $matching_todo; - break; - } - } - - return $priority_pay_later_todo - ? array_merge( $other_todos, array( $priority_pay_later_todo ) ) - : $other_todos; - } - - /** - * Sorts todos by their priority value. - * - * @param array $todos Array of todos to sort. - * @return array Sorted array of todos. - */ - private function sort_todos_by_priority( array $todos ): array { - usort( - $todos, - function( $a, $b ) { - $priority_a = $a['priority'] ?? 999; - $priority_b = $b['priority'] ?? 999; - return $priority_a <=> $priority_b; - } - ); - - return $todos; - } } diff --git a/modules/ppcp-settings/src/Service/AuthenticationManager.php b/modules/ppcp-settings/src/Service/AuthenticationManager.php index 2eeb8f13e..f8b04a096 100644 --- a/modules/ppcp-settings/src/Service/AuthenticationManager.php +++ b/modules/ppcp-settings/src/Service/AuthenticationManager.php @@ -24,6 +24,7 @@ use WooCommerce\WooCommerce\Logging\Logger\NullLogger; use WooCommerce\PayPalCommerce\Settings\DTO\MerchantConnectionDTO; use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar; use WooCommerce\PayPalCommerce\Settings\Enum\SellerTypeEnum; +use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState; /** * Class that manages the connection to PayPal. @@ -64,27 +65,37 @@ class AuthenticationManager { */ private PartnerReferralsData $referrals_data; + /** + * The connection state manager. + * + * @var ConnectionState + */ + private ConnectionState $connection_state; + /** * Constructor. * - * @param GeneralSettings $common_settings Data model that stores the connection details. - * @param EnvironmentConfig $connection_host API host for direct authentication. - * @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials. - * @param PartnerReferralsData $referrals_data Partner referrals data. - * @param ?LoggerInterface $logger Logging instance. + * @param GeneralSettings $common_settings Data model that stores the connection details. + * @param EnvironmentConfig $connection_host API host for direct authentication. + * @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials. + * @param PartnerReferralsData $referrals_data Partner referrals data. + * @param ConnectionState $connection_state Connection state manager. + * @param ?LoggerInterface $logger Logging instance. */ public function __construct( GeneralSettings $common_settings, EnvironmentConfig $connection_host, EnvironmentConfig $login_endpoint, PartnerReferralsData $referrals_data, + ConnectionState $connection_state, ?LoggerInterface $logger = null ) { - $this->common_settings = $common_settings; - $this->connection_host = $connection_host; - $this->login_endpoint = $login_endpoint; - $this->referrals_data = $referrals_data; - $this->logger = $logger ?: new NullLogger(); + $this->common_settings = $common_settings; + $this->connection_host = $connection_host; + $this->login_endpoint = $login_endpoint; + $this->referrals_data = $referrals_data; + $this->connection_state = $connection_state; + $this->logger = $logger ?: new NullLogger(); } /** @@ -112,6 +123,9 @@ class AuthenticationManager { $this->common_settings->reset_merchant_data(); $this->common_settings->save(); + // Update the connection status and clear the environment flags. + $this->connection_state->disconnect(); + /** * Broadcast, that the plugin disconnected from PayPal. This allows other * modules to clean up merchant-related details, such as eligibility flags. @@ -420,6 +434,9 @@ class AuthenticationManager { if ( $this->common_settings->is_merchant_connected() ) { $this->logger->info( 'Merchant successfully connected to PayPal' ); + // Update the connection status and set the environment flags. + $this->connection_state->connect( $connection->is_sandbox ); + /** * Request to flush caches before authenticating the merchant, to * ensure the new merchant does not use stale data from previous diff --git a/modules/ppcp-settings/src/Service/SettingsDataManager.php b/modules/ppcp-settings/src/Service/SettingsDataManager.php index 2feefc4ee..4af72b7bd 100644 --- a/modules/ppcp-settings/src/Service/SettingsDataManager.php +++ b/modules/ppcp-settings/src/Service/SettingsDataManager.php @@ -211,7 +211,8 @@ class SettingsDataManager { $this->payment_methods->toggle_method_state( $method['id'], false ); } - // Always enable Venmo and Pay Later. + // Always enable PayPal, Venmo and Pay Later. + $this->payment_methods->toggle_method_state( PayPalGateway::ID, true ); $this->payment_methods->toggle_method_state( 'venmo', true ); $this->payment_methods->toggle_method_state( 'pay-later', true ); diff --git a/modules/ppcp-settings/src/Service/TodosEligibilityService.php b/modules/ppcp-settings/src/Service/TodosEligibilityService.php index 1f2a385e6..68f9a5ee5 100644 --- a/modules/ppcp-settings/src/Service/TodosEligibilityService.php +++ b/modules/ppcp-settings/src/Service/TodosEligibilityService.php @@ -1,9 +1,9 @@ is_fastlane_eligible = $is_fastlane_eligible; - $this->is_card_payment_eligible = $is_card_payment_eligible; - $this->is_pay_later_messaging_eligible = $is_pay_later_messaging_eligible; - $this->is_pay_later_messaging_product_eligible = $is_pay_later_messaging_product_eligible; - $this->is_pay_later_messaging_cart_eligible = $is_pay_later_messaging_cart_eligible; - $this->is_pay_later_messaging_checkout_eligible = $is_pay_later_messaging_checkout_eligible; - $this->is_subscription_eligible = $is_subscription_eligible; - $this->is_paypal_buttons_eligible = $is_paypal_buttons_eligible; - $this->is_apple_pay_domain_eligible = $is_apple_pay_domain_eligible; - $this->is_digital_wallet_eligible = $is_digital_wallet_eligible; - $this->is_apple_pay_eligible = $is_apple_pay_eligible; - $this->is_google_pay_eligible = $is_google_pay_eligible; - $this->is_add_subscription_eligible = $is_add_subscription_eligible; - $this->is_enable_apple_pay_eligible = $is_enable_apple_pay_eligible; - $this->is_enable_google_pay_eligible = $is_enable_google_pay_eligible; + $this->is_fastlane_eligible = $is_fastlane_eligible; + $this->is_card_payment_eligible = $is_card_payment_eligible; + $this->is_pay_later_messaging_eligible = $is_pay_later_messaging_eligible; + $this->is_pay_later_messaging_product_eligible = $is_pay_later_messaging_product_eligible; + $this->is_pay_later_messaging_cart_eligible = $is_pay_later_messaging_cart_eligible; + $this->is_pay_later_messaging_checkout_eligible = $is_pay_later_messaging_checkout_eligible; + $this->is_subscription_eligible = $is_subscription_eligible; + $this->is_paypal_buttons_cart_eligible = $is_paypal_buttons_cart_eligible; + $this->is_paypal_buttons_block_checkout_eligible = $is_paypal_buttons_block_checkout_eligible; + $this->is_paypal_buttons_product_eligible = $is_paypal_buttons_product_eligible; + $this->is_apple_pay_domain_eligible = $is_apple_pay_domain_eligible; + $this->is_digital_wallet_eligible = $is_digital_wallet_eligible; + $this->is_apple_pay_eligible = $is_apple_pay_eligible; + $this->is_google_pay_eligible = $is_google_pay_eligible; + $this->is_enable_apple_pay_eligible = $is_enable_apple_pay_eligible; + $this->is_enable_google_pay_eligible = $is_enable_google_pay_eligible; } /** @@ -189,7 +199,9 @@ class TodosEligibilityService { 'add_pay_later_messaging_cart' => fn() => $this->is_pay_later_messaging_cart_eligible, 'add_pay_later_messaging_checkout' => fn() => $this->is_pay_later_messaging_checkout_eligible, 'configure_paypal_subscription' => fn() => $this->is_subscription_eligible, - 'add_paypal_buttons' => fn() => $this->is_paypal_buttons_eligible, + 'add_paypal_buttons_cart' => fn() => $this->is_paypal_buttons_cart_eligible, + 'add_paypal_buttons_block_checkout' => fn() => $this->is_paypal_buttons_block_checkout_eligible, + 'add_paypal_buttons_product' => fn() => $this->is_paypal_buttons_product_eligible, 'register_domain_apple_pay' => fn() => $this->is_apple_pay_domain_eligible, 'add_digital_wallets' => fn() => $this->is_digital_wallet_eligible, 'add_apple_pay' => fn() => $this->is_apple_pay_eligible, diff --git a/modules/ppcp-settings/src/Service/TodosSortingAndFilteringService.php b/modules/ppcp-settings/src/Service/TodosSortingAndFilteringService.php new file mode 100644 index 000000000..6a00bacf6 --- /dev/null +++ b/modules/ppcp-settings/src/Service/TodosSortingAndFilteringService.php @@ -0,0 +1,182 @@ +todos_model = $todos_model; + } + + /** + * Returns Pay Later messaging todo IDs in priority order. + * + * @return array Pay Later messaging todo IDs. + */ + public function get_pay_later_ids(): array { + return self::PAY_LATER_IDS; + } + + /** + * Returns Button Placement todo IDs in priority order. + * + * @return array Button Placement todo IDs. + */ + public function get_button_placement_ids(): array { + return self::BUTTON_PLACEMENT_IDS; + } + + /** + * Sorts todos by their priority value. + * + * @param array $todos Array of todos to sort. + * @return array Sorted array of todos. + */ + public function sort_todos_by_priority( array $todos ): array { + usort( + $todos, + function( $a, $b ) { + $priority_a = $a['priority'] ?? 999; + $priority_b = $b['priority'] ?? 999; + return $priority_a <=> $priority_b; + } + ); + + return $todos; + } + + /** + * Filters a group of todos to show only the highest priority one. + * Takes into account dismissed todos. + * + * @param array $todos The array of todos to filter. + * @param array $group_ids Array of todo IDs in priority order. + * @return array Filtered todos with only one todo from the specified group. + */ + public function filter_highest_priority_todo( array $todos, array $group_ids ): array { + $dismissed_todos = $this->todos_model->get_dismissed_todos(); + + $group_todos = array_filter( + $todos, + function( $todo ) use ( $group_ids ) { + return in_array( $todo['id'], $group_ids, true ); + } + ); + + $other_todos = array_filter( + $todos, + function( $todo ) use ( $group_ids ) { + return ! in_array( $todo['id'], $group_ids, true ); + } + ); + + // Find the highest priority todo from the group that's eligible AND not dismissed. + $priority_todo = null; + foreach ( $group_ids as $todo_id ) { + // Skip if this todo ID is dismissed. + if ( in_array( $todo_id, $dismissed_todos, true ) ) { + continue; + } + + $matching_todo = current( + array_filter( + $group_todos, + function( $todo ) use ( $todo_id ) { + return $todo['id'] === $todo_id; + } + ) + ); + + if ( $matching_todo ) { + $priority_todo = $matching_todo; + break; + } + } + + return $priority_todo + ? array_merge( $other_todos, array( $priority_todo ) ) + : $other_todos; + } + + /** + * Filter pay later todos to show only the highest priority eligible one. + * + * @param array $todos The array of todos to filter. + * @return array Filtered todos. + */ + public function filter_pay_later_todos( array $todos ): array { + return $this->filter_highest_priority_todo( $todos, self::PAY_LATER_IDS ); + } + + /** + * Filter button placement todos to show only the highest priority eligible one. + * + * @param array $todos The array of todos to filter. + * @return array Filtered todos. + */ + public function filter_button_placement_todos( array $todos ): array { + return $this->filter_highest_priority_todo( $todos, self::BUTTON_PLACEMENT_IDS ); + } + + /** + * Apply all priority filters to the todos list. + * + * This method applies sorting and all priority filtering in the correct order. + * + * @param array $todos The original todos array. + * @return array Fully filtered and sorted todos. + */ + public function apply_all_priority_filters( array $todos ): array { + $sorted_todos = $this->sort_todos_by_priority( $todos ); + $filtered_todos = $this->filter_pay_later_todos( $sorted_todos ); + $filtered_todos = $this->filter_button_placement_todos( $filtered_todos ); + + return $filtered_todos; + } +} diff --git a/modules/ppcp-status-report/src/StatusReportModule.php b/modules/ppcp-status-report/src/StatusReportModule.php index da015d586..2baa411eb 100644 --- a/modules/ppcp-status-report/src/StatusReportModule.php +++ b/modules/ppcp-status-report/src/StatusReportModule.php @@ -21,7 +21,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Compat\PPEC\PPECHelper; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Webhooks\WebhookEventStorage; /** @@ -58,8 +57,8 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo $subscriptions_mode_settings = $c->get( 'wcgateway.settings.fields.subscriptions_mode' ) ?: array(); - /* @var State $state The state. */ - $state = $c->get( 'onboarding.state' ); + /* @var bool $is_connected Whether onboarding is complete. */ + $is_connected = $c->get( 'settings.flag.is-connected' ); /* @var Bearer $bearer The bearer. */ $bearer = $c->get( 'api.bearer' ); @@ -92,7 +91,7 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo 'exported_label' => 'Onboarded', 'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ), 'value' => $this->bool_to_html( - $this->onboarded( $bearer, $state ) + $this->onboarded( $bearer, $is_connected ) ), ), array( @@ -230,19 +229,18 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo /** * It returns the current onboarding status. * - * @param Bearer $bearer The bearer. - * @param State $state The state. + * @param Bearer $bearer The bearer. + * @param bool $is_connected Whether onboarding is complete. * @return bool */ - private function onboarded( Bearer $bearer, State $state ): bool { + private function onboarded( Bearer $bearer, bool $is_connected ): bool { try { $token = $bearer->bearer(); } catch ( RuntimeException $exception ) { return false; } - $current_state = $state->current_state(); - return $token->is_valid() && $current_state === $state::STATE_ONBOARDED; + return $is_connected && $token->is_valid(); } /** diff --git a/modules/ppcp-vaulting/services.php b/modules/ppcp-vaulting/services.php index 45355225d..da0d1d209 100644 --- a/modules/ppcp-vaulting/services.php +++ b/modules/ppcp-vaulting/services.php @@ -37,7 +37,7 @@ return array( $container->get( 'api.factory.payer' ), $container->get( 'api.factory.shipping-preference' ), $container->get( 'api.endpoint.order' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'wcgateway.processor.authorized-payments' ), $container->get( 'wcgateway.settings' ) ); diff --git a/modules/ppcp-wc-gateway/extensions.php b/modules/ppcp-wc-gateway/extensions.php index 77fea0abd..ef6c85c77 100644 --- a/modules/ppcp-wc-gateway/extensions.php +++ b/modules/ppcp-wc-gateway/extensions.php @@ -26,7 +26,7 @@ return array( return $settings->has( 'merchant_id' ) ? (string) $settings->get( 'merchant_id' ) : ''; }, 'api.partner_merchant_id' => static function ( string $previous, ContainerInterface $container ): string { - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); /** * The environment. diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 77c6438e3..920be9f0a 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -87,35 +87,21 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; return array( 'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway { - $order_processor = $container->get( 'wcgateway.order-processor' ); - $settings_renderer = $container->get( 'wcgateway.settings.render' ); - $funding_source_renderer = $container->get( 'wcgateway.funding-source.renderer' ); - $settings = $container->get( 'wcgateway.settings' ); - $session_handler = $container->get( 'session.handler' ); - $refund_processor = $container->get( 'wcgateway.processor.refunds' ); - $state = $container->get( 'onboarding.state' ); - $transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' ); - $subscription_helper = $container->get( 'wc-subscriptions.helper' ); - $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' ); - $payment_token_repository = $container->get( 'vaulting.repository.payment-token' ); - $environment = $container->get( 'onboarding.environment' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); - $api_shop_country = $container->get( 'api.shop.country' ); return new PayPalGateway( - $settings_renderer, - $funding_source_renderer, - $order_processor, - $settings, - $session_handler, - $refund_processor, - $state, - $transaction_url_provider, - $subscription_helper, - $page_id, - $environment, - $payment_token_repository, - $logger, - $api_shop_country, + $container->get( 'wcgateway.settings.render' ), + $container->get( 'wcgateway.funding-source.renderer' ), + $container->get( 'wcgateway.order-processor' ), + $container->get( 'wcgateway.settings' ), + $container->get( 'session.handler' ), + $container->get( 'wcgateway.processor.refunds' ), + $container->get( 'settings.flag.is-connected' ), + $container->get( 'wcgateway.transaction-url-provider' ), + $container->get( 'wc-subscriptions.helper' ), + $container->get( 'wcgateway.current-ppcp-settings-page-id' ), + $container->get( 'settings.environment' ), + $container->get( 'vaulting.repository.payment-token' ), + $container->get( 'woocommerce.logger.woocommerce' ), + $container->get( 'api.shop.country' ), $container->get( 'api.endpoint.order' ), $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.place-order-button-text' ), @@ -127,42 +113,26 @@ return array( ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { - $order_processor = $container->get( 'wcgateway.order-processor' ); - $settings_renderer = $container->get( 'wcgateway.settings.render' ); - $settings = $container->get( 'wcgateway.settings' ); - $dcc_configuration = $container->get( 'wcgateway.configuration.dcc' ); - $module_url = $container->get( 'wcgateway.url' ); - $session_handler = $container->get( 'session.handler' ); - $refund_processor = $container->get( 'wcgateway.processor.refunds' ); - $state = $container->get( 'onboarding.state' ); - $transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' ); - $subscription_helper = $container->get( 'wc-subscriptions.helper' ); - $payments_endpoint = $container->get( 'api.endpoint.payments' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); - $vaulted_credit_card_handler = $container->get( 'vaulting.credit-card-handler' ); - $icons = $container->get( 'wcgateway.credit-card-icons' ); - return new CreditCardGateway( - $settings_renderer, - $order_processor, - $settings, - $dcc_configuration, - $icons, - $module_url, - $session_handler, - $refund_processor, - $state, - $transaction_url_provider, - $subscription_helper, - $payments_endpoint, - $vaulted_credit_card_handler, - $container->get( 'onboarding.environment' ), + $container->get( 'wcgateway.settings.render' ), + $container->get( 'wcgateway.order-processor' ), + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.configuration.dcc' ), + $container->get( 'wcgateway.credit-card-icons' ), + $container->get( 'wcgateway.url' ), + $container->get( 'session.handler' ), + $container->get( 'wcgateway.processor.refunds' ), + $container->get( 'wcgateway.transaction-url-provider' ), + $container->get( 'wc-subscriptions.helper' ), + $container->get( 'api.endpoint.payments' ), + $container->get( 'vaulting.credit-card-handler' ), + $container->get( 'settings.environment' ), $container->get( 'api.endpoint.order' ), $container->get( 'wcgateway.endpoint.capture-card-payment' ), $container->get( 'api.prefix' ), $container->get( 'api.endpoint.payment-tokens' ), $container->get( 'vaulting.wc-payment-tokens' ), - $logger + $container->get( 'woocommerce.logger.woocommerce' ) ); }, 'wcgateway.credit-card-labels' => static function ( ContainerInterface $container ) : array { @@ -234,11 +204,11 @@ return array( $container->get( 'wcgateway.settings' ), $container->get( 'session.handler' ), $container->get( 'wcgateway.processor.refunds' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.transaction-url-provider' ), $container->get( 'wc-subscriptions.helper' ), $container->get( 'wcgateway.settings.allow_card_button_gateway.default' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'vaulting.repository.payment-token' ), $container->get( 'woocommerce.logger.woocommerce' ), $container->get( 'api.factory.paypal-checkout-url' ), @@ -321,10 +291,9 @@ return array( $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : ''; $ppcp_tab = isset( $_GET[ SectionsRenderer::KEY ] ) ? sanitize_text_field( wp_unslash( $_GET[ SectionsRenderer::KEY ] ) ) : ''; - $state = $container->get( 'onboarding.state' ); - assert( $state instanceof State ); + $is_connected = $container->get( 'settings.flag.is-connected' ); - if ( ! $ppcp_tab && PayPalGateway::ID === $section && $state->current_state() !== State::STATE_ONBOARDED ) { + if ( ! $ppcp_tab && PayPalGateway::ID === $section && ! $is_connected ) { return Settings::CONNECTION_TAB_ID; } @@ -343,29 +312,25 @@ return array( } ), 'wcgateway.notice.connect' => static function ( ContainerInterface $container ): ConnectAdminNotice { - $state = $container->get( 'onboarding.state' ); - $settings = $container->get( 'wcgateway.settings' ); - $is_current_country_send_only = $container->get( 'wcgateway.is-send-only-country' ); - return new ConnectAdminNotice( $state, $settings, $is_current_country_send_only ); + return new ConnectAdminNotice( + $container->get( 'settings.flag.is-connected' ), + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.is-send-only-country' ) + ); }, 'wcgateway.notice.currency-unsupported' => static function ( ContainerInterface $container ): UnsupportedCurrencyAdminNotice { - $state = $container->get( 'onboarding.state' ); - $shop_currency = $container->get( 'api.shop.currency.getter' ); - $supported_currencies = $container->get( 'api.supported-currencies' ); - $is_wc_gateways_list_page = $container->get( 'wcgateway.is-wc-gateways-list-page' ); - $is_ppcp_settings_page = $container->get( 'wcgateway.is-ppcp-settings-page' ); return new UnsupportedCurrencyAdminNotice( - $state, - $shop_currency, - $supported_currencies, - $is_wc_gateways_list_page, - $is_ppcp_settings_page + $container->get( 'settings.flag.is-connected' ), + $container->get( 'api.shop.currency.getter' ), + $container->get( 'api.supported-currencies' ), + $container->get( 'wcgateway.is-wc-gateways-list-page' ), + $container->get( 'wcgateway.is-ppcp-settings-page' ) ); }, 'wcgateway.notice.dcc-without-paypal' => static function ( ContainerInterface $container ): GatewayWithoutPayPalAdminNotice { return new GatewayWithoutPayPalAdminNotice( CreditCardGateway::ID, - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.is-wc-payments-page' ), $container->get( 'wcgateway.is-ppcp-settings-page' ) @@ -374,7 +339,7 @@ return array( 'wcgateway.notice.card-button-without-paypal' => static function ( ContainerInterface $container ): GatewayWithoutPayPalAdminNotice { return new GatewayWithoutPayPalAdminNotice( CardButtonGateway::ID, - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.is-wc-payments-page' ), $container->get( 'wcgateway.is-ppcp-settings-page' ), @@ -477,14 +442,12 @@ return array( return in_array( $store_country, $send_only_countries, true ); }, 'wcgateway.notice.send-only-country' => static function ( ContainerInterface $container ) { - $onboarding_state = $container->get( 'onboarding.state' ); - assert( $onboarding_state instanceof State ); return new SendOnlyCountryNotice( $container->get( 'wcgateway.send-only-message' ), $container->get( 'wcgateway.is-send-only-country' ), $container->get( 'wcgateway.is-ppcp-settings-page' ), $container->get( 'wcgateway.is-wc-gateways-list-page' ), - $onboarding_state->current_state() + $container->get( 'settings.flag.is-connected' ) ); }, @@ -527,7 +490,7 @@ return array( 'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer { return new SectionsRenderer( $container->get( 'wcgateway.current-ppcp-settings-page-id' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.helper.dcc-product-status' ), $container->get( 'api.helpers.dccapplies' ), $container->get( 'button.helper.messages-apply' ), @@ -545,57 +508,36 @@ return array( return new SettingsStatus( $settings ); }, 'wcgateway.settings.render' => static function ( ContainerInterface $container ): SettingsRenderer { - $settings = $container->get( 'wcgateway.settings' ); - $state = $container->get( 'onboarding.state' ); - $fields = $container->get( 'wcgateway.settings.fields' ); - $dcc_applies = $container->get( 'api.helpers.dccapplies' ); - $messages_apply = $container->get( 'button.helper.messages-apply' ); - $dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' ); - $settings_status = $container->get( 'wcgateway.settings.status' ); - $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' ); - $api_shop_country = $container->get( 'api.shop.country' ); return new SettingsRenderer( - $settings, - $state, - $fields, - $dcc_applies, - $messages_apply, - $dcc_product_status, - $settings_status, - $page_id, - $api_shop_country + $container->get( 'wcgateway.settings' ), + $container->get( 'onboarding.state' ), // Correct. + $container->get( 'wcgateway.settings.fields' ), + $container->get( 'api.helpers.dccapplies' ), + $container->get( 'button.helper.messages-apply' ), + $container->get( 'wcgateway.helper.dcc-product-status' ), + $container->get( 'wcgateway.settings.status' ), + $container->get( 'wcgateway.current-ppcp-settings-page-id' ), + $container->get( 'api.shop.country' ) ); }, 'wcgateway.settings.listener' => static function ( ContainerInterface $container ): SettingsListener { - $settings = $container->get( 'wcgateway.settings' ); - $fields = $container->get( 'wcgateway.settings.fields' ); - $webhook_registrar = $container->get( 'webhook.registrar' ); - $state = $container->get( 'onboarding.state' ); - $cache = $container->get( 'api.paypal-bearer-cache' ); - $bearer = $container->get( 'api.bearer' ); - $page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' ); - $signup_link_cache = $container->get( 'onboarding.signup-link-cache' ); - $signup_link_ids = $container->get( 'onboarding.signup-link-ids' ); - $pui_status_cache = $container->get( 'pui.status-cache' ); - $dcc_status_cache = $container->get( 'dcc.status-cache' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new SettingsListener( - $settings, - $fields, - $webhook_registrar, - $cache, - $state, - $bearer, - $page_id, - $signup_link_cache, - $signup_link_ids, - $pui_status_cache, - $dcc_status_cache, + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.settings.fields' ), + $container->get( 'webhook.registrar' ), + $container->get( 'api.paypal-bearer-cache' ), + $container->get( 'onboarding.state' ), // Correct. + $container->get( 'api.bearer' ), + $container->get( 'wcgateway.current-ppcp-settings-page-id' ), + $container->get( 'onboarding.signup-link-cache' ), + $container->get( 'onboarding.signup-link-ids' ), + $container->get( 'pui.status-cache' ), + $container->get( 'dcc.status-cache' ), $container->get( 'http.redirector' ), $container->get( 'api.partner_merchant_id-production' ), $container->get( 'api.partner_merchant_id-sandbox' ), $container->get( 'api.endpoint.billing-agreements' ), - $logger, + $container->get( 'woocommerce.logger.woocommerce' ), new Cache( 'ppcp-client-credentials-cache' ) ); }, @@ -607,7 +549,7 @@ return array( $threed_secure = $container->get( 'button.helper.three-d-secure' ); $authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' ); $settings = $container->get( 'wcgateway.settings' ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); $logger = $container->get( 'woocommerce.logger.woocommerce' ); $subscription_helper = $container->get( 'wc-subscriptions.helper' ); $order_helper = $container->get( 'api.order-helper' ); @@ -728,6 +670,8 @@ return array( return array(); } + // Legacy settings service, correct use of `State` class. + $state = $container->get( 'onboarding.state' ); assert( $state instanceof State ); @@ -1485,12 +1429,12 @@ return array( $container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ), $container->get( 'api.factory.purchase-unit' ), $container->get( 'wcgateway.pay-upon-invoice-payment-source-factory' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'wcgateway.transaction-url-provider' ), $container->get( 'woocommerce.logger.woocommerce' ), $container->get( 'wcgateway.pay-upon-invoice-helper' ), $container->get( 'wcgateway.checkout-helper' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.processor.refunds' ), $container->get( 'wcgateway.url' ) ); @@ -1524,7 +1468,7 @@ return array( $container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ), $container->get( 'woocommerce.logger.woocommerce' ), $container->get( 'wcgateway.settings' ), - $container->get( 'onboarding.state' ), + $container->get( 'settings.flag.is-connected' ), $container->get( 'wcgateway.current-ppcp-settings-page-id' ), $container->get( 'wcgateway.pay-upon-invoice-product-status' ), $container->get( 'wcgateway.pay-upon-invoice-helper' ), @@ -1549,7 +1493,7 @@ return array( $container->get( 'api.factory.shipping-preference' ), $container->get( 'wcgateway.url' ), $container->get( 'wcgateway.transaction-url-provider' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, @@ -1717,15 +1661,15 @@ return array( return 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING'; }, 'wcgateway.settings.connection.dcc-status-text' => static function ( ContainerInterface $container ): string { - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() < State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return ''; } $dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' ); assert( $dcc_product_status instanceof DCCProductStatus ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); $dcc_enabled = $dcc_product_status->is_active(); @@ -1755,7 +1699,7 @@ return array( ); }, 'wcgateway.settings.connection.reference-transactions-status-text' => static function ( ContainerInterface $container ): string { - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); $billing_agreements_endpoint = $container->get( 'api.endpoint.billing-agreements' ); @@ -1788,15 +1732,15 @@ return array( ); }, 'wcgateway.settings.connection.pui-status-text' => static function ( ContainerInterface $container ): string { - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() < State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + if ( ! $is_connected ) { return ''; } $pui_product_status = $container->get( 'wcgateway.pay-upon-invoice-product-status' ); assert( $pui_product_status instanceof PayUponInvoiceProductStatus ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); assert( $environment instanceof Environment ); $pui_enabled = $pui_product_status->is_active(); @@ -1917,7 +1861,7 @@ return array( $container->get( 'wcgateway.url' ), $container->get( 'ppcp.asset-version' ), $container->get( 'wcgateway.fraudnet' ), - $container->get( 'onboarding.environment' ), + $container->get( 'settings.environment' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.gateway-repository' ), $container->get( 'session.handler' ), diff --git a/modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php b/modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php index f67c7c6f1..1bb186a25 100644 --- a/modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php +++ b/modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php @@ -62,6 +62,7 @@ class SettingsCommand { $value = false; } + // TODO new-ux: The setting must also be updated in the new settings. $this->settings->set( $key, $value ); $this->settings->persist(); diff --git a/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php index 846f8e7e3..783770ad2 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CardButtonGateway.php @@ -14,7 +14,6 @@ use Psr\Log\LoggerInterface; use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -70,13 +69,6 @@ class CardButtonGateway extends \WC_Payment_Gateway { */ private $refund_processor; - /** - * The state. - * - * @var State - */ - protected $state; - /** * Service able to provide transaction url for an order. * @@ -141,7 +133,7 @@ class CardButtonGateway extends \WC_Payment_Gateway { * @param ContainerInterface $config The settings. * @param SessionHandler $session_handler The Session Handler. * @param RefundProcessor $refund_processor The Refund Processor. - * @param State $state The state. + * @param bool $is_connected Whether onboarding was completed. * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. * @param SubscriptionHelper $subscription_helper The subscription helper. * @param bool $default_enabled Whether the gateway should be enabled by default. @@ -157,7 +149,7 @@ class CardButtonGateway extends \WC_Payment_Gateway { ContainerInterface $config, SessionHandler $session_handler, RefundProcessor $refund_processor, - State $state, + bool $is_connected, TransactionUrlProvider $transaction_url_provider, SubscriptionHelper $subscription_helper, bool $default_enabled, @@ -173,12 +165,11 @@ class CardButtonGateway extends \WC_Payment_Gateway { $this->config = $config; $this->session_handler = $session_handler; $this->refund_processor = $refund_processor; - $this->state = $state; $this->transaction_url_provider = $transaction_url_provider; $this->subscription_helper = $subscription_helper; $this->default_enabled = $default_enabled; $this->environment = $environment; - $this->onboarded = $state->current_state() === State::STATE_ONBOARDED; + $this->onboarded = $is_connected; $this->payment_token_repository = $payment_token_repository; $this->logger = $logger; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index 2f1e997b5..76e53b0b5 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; use WooCommerce\PayPalCommerce\Vaulting\VaultedCreditCardHandler; @@ -109,13 +108,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { */ private $refund_processor; - /** - * The state. - * - * @var State - */ - protected $state; - /** * Service to get transaction url for an order. * @@ -204,7 +196,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { * @param string $module_url The URL to the module. * @param SessionHandler $session_handler The Session Handler. * @param RefundProcessor $refund_processor The refund processor. - * @param State $state The state. * @param TransactionUrlProvider $transaction_url_provider Service able to provide view transaction url base. * @param SubscriptionHelper $subscription_helper The subscription helper. * @param PaymentsEndpoint $payments_endpoint The payments endpoint. @@ -226,7 +217,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { string $module_url, SessionHandler $session_handler, RefundProcessor $refund_processor, - State $state, TransactionUrlProvider $transaction_url_provider, SubscriptionHelper $subscription_helper, PaymentsEndpoint $payments_endpoint, @@ -247,7 +237,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { $this->module_url = $module_url; $this->session_handler = $session_handler; $this->refund_processor = $refund_processor; - $this->state = $state; $this->transaction_url_provider = $transaction_url_provider; $this->subscription_helper = $subscription_helper; $this->payments_endpoint = $payments_endpoint; diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 9dc975b82..9927f155b 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; @@ -104,13 +103,6 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $refund_processor; - /** - * The state. - * - * @var State - */ - protected $state; - /** * Service able to provide transaction url for an order. * @@ -137,7 +129,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * * @var bool */ - private $onboarded; + private bool $onboarded; /** * ID of the current PPCP gateway settings page, or empty if it is not such page. @@ -225,7 +217,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param ContainerInterface $config The settings. * @param SessionHandler $session_handler The Session Handler. * @param RefundProcessor $refund_processor The Refund Processor. - * @param State $state The state. + * @param bool $is_connected Whether onboarding was completed. * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. * @param SubscriptionHelper $subscription_helper The subscription helper. * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. @@ -249,7 +241,7 @@ class PayPalGateway extends \WC_Payment_Gateway { ContainerInterface $config, SessionHandler $session_handler, RefundProcessor $refund_processor, - State $state, + bool $is_connected, TransactionUrlProvider $transaction_url_provider, SubscriptionHelper $subscription_helper, string $page_id, @@ -273,12 +265,11 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->config = $config; $this->session_handler = $session_handler; $this->refund_processor = $refund_processor; - $this->state = $state; $this->transaction_url_provider = $transaction_url_provider; $this->subscription_helper = $subscription_helper; $this->page_id = $page_id; $this->environment = $environment; - $this->onboarded = $state->current_state() === State::STATE_ONBOARDED; + $this->onboarded = $is_connected; $this->payment_token_repository = $payment_token_repository; $this->logger = $logger; $this->api_shop_country = $api_shop_country; diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php index 5b1fc0d36..10ec5ab2c 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; @@ -62,11 +61,11 @@ class PayUponInvoice { protected $pui_helper; /** - * The onboarding state. + * Whether onboarding was completed and the merchant is connected to PayPal. * - * @var State + * @var bool */ - protected $state; + protected bool $is_connected; /** * Current PayPal settings page id. @@ -102,7 +101,7 @@ class PayUponInvoice { * @param PayUponInvoiceOrderEndpoint $pui_order_endpoint The PUI order endpoint. * @param LoggerInterface $logger The logger. * @param Settings $settings The settings. - * @param State $state The onboarding state. + * @param bool $is_connected Whether onboarding was completed. * @param string $current_ppcp_settings_page_id Current PayPal settings page id. * @param PayUponInvoiceProductStatus $pui_product_status The PUI product status. * @param PayUponInvoiceHelper $pui_helper The PUI helper. @@ -113,7 +112,7 @@ class PayUponInvoice { PayUponInvoiceOrderEndpoint $pui_order_endpoint, LoggerInterface $logger, Settings $settings, - State $state, + bool $is_connected, string $current_ppcp_settings_page_id, PayUponInvoiceProductStatus $pui_product_status, PayUponInvoiceHelper $pui_helper, @@ -123,7 +122,7 @@ class PayUponInvoice { $this->pui_order_endpoint = $pui_order_endpoint; $this->logger = $logger; $this->settings = $settings; - $this->state = $state; + $this->is_connected = $is_connected; $this->current_ppcp_settings_page_id = $current_ppcp_settings_page_id; $this->pui_product_status = $pui_product_status; $this->pui_helper = $pui_helper; @@ -138,6 +137,10 @@ class PayUponInvoice { */ public function init(): void { if ( $this->pui_helper->is_pui_gateway_enabled() ) { + /* + * TODO new-ux: Check if we still support this setting, or if it's always enabled. + * If fraudnet is not configurable in new UI, we can ignore this. + */ $this->settings->set( 'fraudnet_enabled', true ); $this->settings->persist(); } @@ -437,7 +440,7 @@ class PayUponInvoice { function ( $methods ) { if ( ! is_array( $methods ) - || State::STATE_ONBOARDED !== $this->state->current_state() + || ! $this->is_connected // phpcs:ignore WordPress.Security.NonceVerification.Recommended || ! ( is_checkout() || isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' ) ) { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php index ecd6c1df1..1622123bc 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoiceGateway.php @@ -17,7 +17,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper; @@ -89,13 +88,6 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { */ protected $checkout_helper; - /** - * The onboarding state. - * - * @var State - */ - protected $state; - /** * The refund processor. * @@ -121,7 +113,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { * @param LoggerInterface $logger The logger. * @param PayUponInvoiceHelper $pui_helper The PUI helper. * @param CheckoutHelper $checkout_helper The checkout helper. - * @param State $state The onboarding state. + * @param bool $is_connected Whether the onboarding was completed. * @param RefundProcessor $refund_processor The refund processor. * @param string $module_url The module URL. */ @@ -134,7 +126,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { LoggerInterface $logger, PayUponInvoiceHelper $pui_helper, CheckoutHelper $checkout_helper, - State $state, + bool $is_connected, RefundProcessor $refund_processor, string $module_url ) { @@ -169,8 +161,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway { $this->module_url = $module_url; $this->icon = apply_filters( 'woocommerce_paypal_payments_pay_upon_invoice_gateway_icon', esc_url( $this->module_url ) . 'assets/images/ratepay.svg' ); - $this->state = $state; - if ( $state->current_state() === State::STATE_ONBOARDED ) { + if ( $is_connected ) { $this->supports = array( 'refunds' ); } $this->refund_processor = $refund_processor; diff --git a/modules/ppcp-wc-gateway/src/Helper/ConnectionState.php b/modules/ppcp-wc-gateway/src/Helper/ConnectionState.php new file mode 100644 index 000000000..3ee969a84 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/ConnectionState.php @@ -0,0 +1,135 @@ +is_connected = $is_connected; + $this->environment = $environment; + } + + /** + * Set connection status to "connected to PayPal" (end onboarding). + * + * @param bool $is_sandbox Whether to connect to a sandbox environment. + */ + public function connect( bool $is_sandbox = false ) : void { + if ( ! $this->is_connected ) { + /** + * Action that fires before the connection status changes from + * disconnected to connected. + */ + do_action( 'woocommerce_paypal_payments_merchant_connection_change', true ); + } + + $this->is_connected = true; + $this->environment->set_environment( $is_sandbox ); + } + + /** + * Set connection status to "not connected to PayPal" (start onboarding). + */ + public function disconnect() : void { + if ( $this->is_connected ) { + /** + * Action that fires before the connection status changes from + * connected to disconnected. + */ + do_action( 'woocommerce_paypal_payments_merchant_connection_change', false ); + } + + $this->is_connected = false; + } + + /** + * Returns the managed environment instance. + * + * @return Environment The environment instance. + */ + public function get_environment() : Environment { + return $this->environment; + } + + /** + * Is the merchant connected to a PayPal account? + * + * @return bool True, if onboarding was completed and connection details are present. + */ + public function is_connected() : bool { + return $this->is_connected; + } + + /** + * Is the merchant currently in the "onboarding phase"? + * + * @return bool True, if we don't know merchant connection details. + */ + public function is_onboarding() : bool { + return ! $this->is_connected; + } + + /** + * Is the merchant connected to a sandbox environment? + * + * @return bool True, if connected to a sandbox environment. + */ + public function is_sandbox() : bool { + return $this->is_connected && $this->environment->is_sandbox(); + } + + /** + * Is the merchant connected to a production environment and can receive payments? + * + * @return bool True, if connected to a production environment. + */ + public function is_production() : bool { + return $this->is_connected && $this->environment->is_production(); + } + + /** + * Returns the current environment's name. + * + * @return string Name of the currently connected environment; empty string if not connected. + */ + public function current_environment() : string { + return $this->is_connected ? $this->environment->current_environment() : ''; + } +} diff --git a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php index d82271b60..980b4d2d0 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php @@ -103,6 +103,7 @@ class DCCProductStatus extends ProductStatus { continue; } + // Settings used as a cache; `settings->set` is compatible with new UI. if ( in_array( 'CUSTOM_CARD_PROCESSING', $product->capabilities(), true ) ) { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); $this->settings->persist(); diff --git a/modules/ppcp-wc-gateway/src/Helper/Environment.php b/modules/ppcp-wc-gateway/src/Helper/Environment.php index 143ebd843..e855b54b4 100644 --- a/modules/ppcp-wc-gateway/src/Helper/Environment.php +++ b/modules/ppcp-wc-gateway/src/Helper/Environment.php @@ -9,8 +9,6 @@ declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Helper; -use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; - /** * Class Environment */ @@ -27,36 +25,73 @@ class Environment { public const SANDBOX = 'sandbox'; /** - * The Settings. + * Name of the current environment. * - * @var ContainerInterface + * @var string */ - private ContainerInterface $settings; + private string $environment_name; /** * Environment constructor. * - * @param ContainerInterface $settings The settings. + * @param bool $is_sandbox Whether this instance represents a sandbox environment. */ - public function __construct( ContainerInterface $settings ) { - $this->settings = $settings; + public function __construct( bool $is_sandbox = false ) { + $this->environment_name = $this->prepare_environment_name( $is_sandbox ); } /** - * Returns the current environment. + * Returns a valid environment name based on the provided argument. + * + * @param bool $is_sandbox Whether this instance represents a sandbox environment. + * @return string The environment name. + */ + private function prepare_environment_name( bool $is_sandbox ) : string { + if ( $is_sandbox ) { + return self::SANDBOX; + } + + return self::PRODUCTION; + } + + /** + * Updates the current environment. + * + * @param bool $is_sandbox Whether this instance represents a sandbox environment. + */ + public function set_environment( bool $is_sandbox ) : void { + $new_environment = $this->prepare_environment_name( $is_sandbox ); + + if ( $new_environment !== $this->environment_name ) { + /** + * Action that fires before the environment status changes. + * + * @param string $new_environment The new environment name. + * @param string $old_environment The previous environment name. + */ + do_action( + 'woocommerce_paypal_payments_merchant_environment_change', + $new_environment, + $this->environment_name + ); + } + + $this->environment_name = $new_environment; + } + + /** + * Returns the current environment's name. * * @return string */ public function current_environment() : string { - return ( - $this->settings->has( 'sandbox_on' ) && $this->settings->get( 'sandbox_on' ) - ) ? self::SANDBOX : self::PRODUCTION; + return $this->environment_name; } /** * Detect whether the current environment equals $environment * - * @deprecated Use the is_sandbox() and is_production() methods instead. + * @deprecated 3.0.0 - Use the is_sandbox() and is_production() methods instead. * These methods provide better encapsulation, are less error-prone, * and improve code readability by removing the need to pass environment constants. * @param string $environment The value to check against. diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php index 250138089..64192bd87 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php @@ -96,6 +96,7 @@ class PayUponInvoiceProductStatus extends ProductStatus { continue; } + // Settings used as a cache; `settings->set` is compatible with new UI. if ( in_array( 'PAY_UPON_INVOICE', $product->capabilities(), true ) ) { $this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED ); $this->settings->persist(); diff --git a/modules/ppcp-wc-gateway/src/Notice/ConnectAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/ConnectAdminNotice.php index 60085a992..c667277c6 100644 --- a/modules/ppcp-wc-gateway/src/Notice/ConnectAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/ConnectAdminNotice.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Notice; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; @@ -20,11 +19,11 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; class ConnectAdminNotice { /** - * The state. + * Whether the merchant completed the onboarding and is connected to PayPal. * - * @var State + * @var bool */ - private $state; + private bool $is_connected; /** * The settings. @@ -43,12 +42,16 @@ class ConnectAdminNotice { /** * ConnectAdminNotice constructor. * - * @param State $state The state. + * @param bool $is_connected Whether onboarding was completed. * @param ContainerInterface $settings The settings. * @param bool $is_current_country_send_only Whether the current store's country is classified as a send-only country. */ - public function __construct( State $state, ContainerInterface $settings, bool $is_current_country_send_only ) { - $this->state = $state; + public function __construct( + bool $is_connected, + ContainerInterface $settings, + bool $is_current_country_send_only + ) { + $this->is_connected = $is_connected; $this->settings = $settings; $this->is_current_country_send_only = $is_current_country_send_only; } @@ -77,9 +80,13 @@ class ConnectAdminNotice { /** * Whether the message should display. * + * Only display the "almost ready" message for merchants that did not complete + * the onboarding wizard. Also, ensure their store country is eligible for + * collecting PayPal payments. + * * @return bool */ protected function should_display(): bool { - return $this->state->current_state() !== State::STATE_ONBOARDED && $this->is_current_country_send_only === false; + return ! $this->is_connected && ! $this->is_current_country_send_only; } } diff --git a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php index 2dfc88904..627d23e97 100644 --- a/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/GatewayWithoutPayPalAdminNotice.php @@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Notice; use WC_Payment_Gateway; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus; @@ -32,11 +31,11 @@ class GatewayWithoutPayPalAdminNotice { private $id; /** - * The state. + * Whether the merchant completed onboarding. * - * @var State + * @var bool */ - private $state; + private bool $is_connected; /** * The settings. @@ -70,7 +69,7 @@ class GatewayWithoutPayPalAdminNotice { * ConnectAdminNotice constructor. * * @param string $id The gateway ID. - * @param State $state The state. + * @param bool $is_connected Whether onboading was completed. * @param ContainerInterface $settings The settings. * @param bool $is_payments_page Whether the current page is the WC payment page. * @param bool $is_ppcp_settings_page Whether the current page is the PPCP settings page. @@ -78,14 +77,14 @@ class GatewayWithoutPayPalAdminNotice { */ public function __construct( string $id, - State $state, + bool $is_connected, ContainerInterface $settings, bool $is_payments_page, bool $is_ppcp_settings_page, ?SettingsStatus $settings_status = null ) { $this->id = $id; - $this->state = $state; + $this->is_connected = $is_connected; $this->settings = $settings; $this->is_payments_page = $is_payments_page; $this->is_ppcp_settings_page = $is_ppcp_settings_page; @@ -161,7 +160,7 @@ class GatewayWithoutPayPalAdminNotice { * @return string One of the NOTICE_* constants. */ protected function check(): string { - if ( State::STATE_ONBOARDED !== $this->state->current_state() || + if ( ! $this->is_connected || ( ! $this->is_payments_page && ! $this->is_ppcp_settings_page ) ) { return self::NOTICE_OK; } diff --git a/modules/ppcp-wc-gateway/src/Notice/SendOnlyCountryNotice.php b/modules/ppcp-wc-gateway/src/Notice/SendOnlyCountryNotice.php index f855e923c..0746c2ac2 100644 --- a/modules/ppcp-wc-gateway/src/Notice/SendOnlyCountryNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/SendOnlyCountryNotice.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Notice; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; -use WooCommerce\PayPalCommerce\Onboarding\State; /** * Creates an admin message that notifies user about send only country. @@ -47,9 +46,9 @@ class SendOnlyCountryNotice { /** * Onboarding state * - * @var int + * @var bool */ - private int $onboarding_state; + private bool $is_connected; /** * AdminNotice constructor. @@ -58,20 +57,20 @@ class SendOnlyCountryNotice { * @param bool $is_send_only_country Determines if current WC country is a send only country. * @param bool $is_ppcp_settings_page Determines if current page is ppcp settings page. * @param bool $is_wc_gateways_list_page Determines if current page is ppcp gateway list page. - * @param int $onboarding_state Determines current onboarding state. + * @param bool $is_connected Whether onboarding was completed. */ public function __construct( string $message_text, bool $is_send_only_country, bool $is_ppcp_settings_page, bool $is_wc_gateways_list_page, - int $onboarding_state + bool $is_connected ) { $this->message_text = $message_text; $this->is_send_only_country = $is_send_only_country; $this->is_ppcp_settings_page = $is_ppcp_settings_page; $this->is_wc_gateways_list_page = $is_wc_gateways_list_page; - $this->onboarding_state = $onboarding_state; + $this->is_connected = $is_connected; } /** @@ -80,10 +79,9 @@ class SendOnlyCountryNotice { * @return Message|null */ public function message(): ?Message { - if ( ! $this->is_send_only_country || - ! $this->is_ppcp_page() || - $this->onboarding_state === State::STATE_START + ! $this->is_connected || + ! $this->is_ppcp_page() ) { return null; } diff --git a/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php index dfc9db81c..5b9dae7c4 100644 --- a/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php @@ -11,9 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Notice; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter; -use WooCommerce\PayPalCommerce\Onboarding\State; -use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; -use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; /** * Class UnsupportedCurrencyAdminNotice @@ -21,11 +18,11 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; class UnsupportedCurrencyAdminNotice { /** - * The state. + * Whether the merchant completed onboarding. * - * @var State + * @var bool */ - private $state; + private bool $is_connected; /** * The supported currencies. @@ -58,20 +55,20 @@ class UnsupportedCurrencyAdminNotice { /** * UnsupportedCurrencyAdminNotice constructor. * - * @param State $state The state. + * @param bool $is_connected Whether the merchant completed onboarding. * @param CurrencyGetter $shop_currency The shop currency. * @param array $supported_currencies The supported currencies. * @param bool $is_wc_gateways_list_page Indicates if we're on the WooCommerce gateways list page. * @param bool $is_ppcp_settings_page Indicates if we're on a PPCP Settings page. */ public function __construct( - State $state, + bool $is_connected, CurrencyGetter $shop_currency, array $supported_currencies, bool $is_wc_gateways_list_page, bool $is_ppcp_settings_page ) { - $this->state = $state; + $this->is_connected = $is_connected; $this->shop_currency = $shop_currency; $this->supported_currencies = $supported_currencies; $this->is_wc_gateways_list_page = $is_wc_gateways_list_page; @@ -110,7 +107,7 @@ class UnsupportedCurrencyAdminNotice { * @return bool */ protected function should_display(): bool { - return $this->state->current_state() === State::STATE_ONBOARDED + return $this->is_connected && ! $this->currency_supported() && ( $this->is_wc_gateways_list_page || $this->is_ppcp_settings_page ); } diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 409fde543..4a3265b23 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -28,6 +28,8 @@ return function ( ContainerInterface $container, array $fields ): array { return $fields; } + // Legacy settings module, use of `State` class is correct. + $state = $container->get( 'onboarding.state' ); assert( $state instanceof State ); diff --git a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php index f8f05f4ff..5fcf0aeb2 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php @@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway; @@ -35,11 +34,11 @@ class SectionsRenderer { protected $page_id; /** - * The onboarding state. + * Whether onboarding was completed and the merchant is connected to PayPal. * - * @var State + * @var bool */ - private $state; + private bool $is_connected; /** * The DCC product status @@ -69,18 +68,11 @@ class SectionsRenderer { */ private $pui_product_status; - /** - * SectionsRenderer constructor. - * - * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param State $state The onboarding state. - */ - /** * SectionsRenderer constructor. * * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param State $state The onboarding state. + * @param bool $is_connected Whether the merchant completed onboarding. * @param DCCProductStatus $dcc_product_status The DCC product status. * @param DccApplies $dcc_applies The DCC applies. * @param MessagesApply $messages_apply The Messages apply. @@ -88,14 +80,14 @@ class SectionsRenderer { */ public function __construct( string $page_id, - State $state, + bool $is_connected, DCCProductStatus $dcc_product_status, DccApplies $dcc_applies, MessagesApply $messages_apply, PayUponInvoiceProductStatus $pui_product_status ) { $this->page_id = $page_id; - $this->state = $state; + $this->is_connected = $is_connected; $this->dcc_product_status = $dcc_product_status; $this->dcc_applies = $dcc_applies; $this->messages_apply = $messages_apply; @@ -108,9 +100,7 @@ class SectionsRenderer { * @return bool */ public function should_render() : bool { - return ! empty( $this->page_id ) && - ( $this->state->production_state() === State::STATE_ONBOARDED || - $this->state->sandbox_state() === State::STATE_ONBOARDED ); + return $this->page_id && $this->is_connected; } /** diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php index 9340357a6..0e71dd3b8 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Settings.php +++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php @@ -120,7 +120,10 @@ class Settings implements ContainerInterface { * @return bool */ public function has( string $id ) { - if ( $this->settings_map_helper->has_mapped_key( $id ) ) { + if ( + $this->settings_map_helper->has_mapped_key( $id ) + && ! is_null( $this->settings_map_helper->mapped_value( $id ) ) + ) { return true; } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 6c01830d6..b612de369 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -214,6 +214,8 @@ class SettingsListener { Cache $client_credentials_cache ) { + // This is a legacy settings class, it's correctly relying on the `Status` class. + $this->settings = $settings; $this->setting_fields = $setting_fields; $this->webhook_registrar = $webhook_registrar; @@ -261,6 +263,8 @@ class SettingsListener { // phpcs:enable WordPress.Security.NonceVerification.Missing // phpcs:enable WordPress.Security.NonceVerification.Recommended + // This method is only used for legacy UI, `settings->set` is valid here. + $this->settings->set( 'merchant_id', $merchant_id ); $this->settings->set( 'merchant_email', $merchant_email ); @@ -363,6 +367,8 @@ class SettingsListener { return; } + // This method is only used for legacy UI, `settings->set` is valid here. + try { $token = $this->bearer->bearer(); if ( ! $token->vaulting_available() ) { @@ -472,6 +478,10 @@ class SettingsListener { && 1 === absint( $_POST['woocommerce_ppcp-gateway_enabled'] ); } + // This method initializes a feature cache. This initialization is not + // required by the new UI; we can ignore the `settings->set` usage. + // TODO new-ux: Test, if this method is called or some non-settings parts must be converted. + // phpcs:enable phpcs:disable WordPress.Security.NonceVerification.Missing // phpcs:enable phpcs:disable WordPress.Security.NonceVerification.Missing if ( $credentials_change_status ) { @@ -720,6 +730,8 @@ class SettingsListener { /** * Prevent enabling tracking if it is not enabled for merchant account. * + * This method is not used anywhere. Not relevant for new-ux. + * * @throws RuntimeException When API request fails. */ public function listen_for_tracking_enabled(): void { @@ -765,6 +777,8 @@ class SettingsListener { return; } + // This method is only used for legacy UI, `settings->set` is valid here. + $existing_setting_value = $this->settings->has( $setting_slug ) ? $this->settings->get( $setting_slug ) : null; if ( $condition ) { diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php index e32cbf0c4..f9a00170f 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php @@ -114,6 +114,8 @@ class SettingsRenderer { string $api_shop_country ) { + // This is a legacy settings class, it's correctly relying on the `Status` class. + $this->settings = $settings; $this->state = $state; $this->fields = $fields; diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 6606380b8..2f82c5636 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -14,8 +14,6 @@ use Psr\Log\LoggerInterface; use Throwable; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; -use WooCommerce\PayPalCommerce\ApiClient\Endpoint\Orders; -use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; @@ -33,7 +31,6 @@ use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository; use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer; use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn; use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail; @@ -59,7 +56,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; -use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; @@ -209,7 +205,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $c->get( 'button.client_id_for_admin' ), $c->get( 'api.shop.currency.getter' ), $c->get( 'api.shop.country' ), - $c->get( 'onboarding.environment' ), + $c->get( 'settings.environment' ), $settings_status->is_pay_later_button_enabled(), $settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(), $c->get( 'wcgateway.settings.funding-sources' ), @@ -607,16 +603,14 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $methods[] = $paypal_gateway; - $onboarding_state = $container->get( 'onboarding.state' ); - assert( $onboarding_state instanceof State ); - $settings = $container->get( 'wcgateway.settings' ); assert( $settings instanceof ContainerInterface ); $is_our_page = $container->get( 'wcgateway.is-ppcp-settings-page' ); $is_gateways_list_page = $container->get( 'wcgateway.is-wc-gateways-list-page' ); + $is_connected = $container->get( 'settings.flag.is-connected' ); - if ( $onboarding_state->current_state() !== State::STATE_ONBOARDED ) { + if ( ! $is_connected ) { return $methods; } diff --git a/modules/ppcp-wc-subscriptions/services.php b/modules/ppcp-wc-subscriptions/services.php index dc23fca9f..165f673b9 100644 --- a/modules/ppcp-wc-subscriptions/services.php +++ b/modules/ppcp-wc-subscriptions/services.php @@ -28,7 +28,7 @@ return array( $endpoint = $container->get( 'api.endpoint.order' ); $purchase_unit_factory = $container->get( 'api.factory.purchase-unit' ); $payer_factory = $container->get( 'api.factory.payer' ); - $environment = $container->get( 'onboarding.environment' ); + $environment = $container->get( 'settings.environment' ); $settings = $container->get( 'wcgateway.settings' ); $authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' ); $funding_source_renderer = $container->get( 'wcgateway.funding-source.renderer' ); diff --git a/modules/ppcp-wc-subscriptions/src/Helper/SubscriptionHelper.php b/modules/ppcp-wc-subscriptions/src/Helper/SubscriptionHelper.php index 45e481728..8bc1e40f5 100644 --- a/modules/ppcp-wc-subscriptions/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-wc-subscriptions/src/Helper/SubscriptionHelper.php @@ -21,6 +21,7 @@ use WCS_Manual_Renewal_Manager; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WP_Query; /** * Class SubscriptionHelper @@ -342,4 +343,30 @@ class SubscriptionHelper { return ''; } + + /** + * Checks if any subscription products exist. + * + * @return bool + */ + public function has_subscription_products(): bool { + // Query for subscription products. + $args = array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'posts_per_page' => 1, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + 'tax_query' => array( + array( + 'taxonomy' => 'product_type', + 'field' => 'slug', + 'terms' => 'subscription', + ), + ), + ); + + $subscription_products = new WP_Query( $args ); + + return $subscription_products->have_posts(); + } } diff --git a/modules/ppcp-webhooks/factories.php b/modules/ppcp-webhooks/factories.php index 6d36ae4fa..e568b896a 100644 --- a/modules/ppcp-webhooks/factories.php +++ b/modules/ppcp-webhooks/factories.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Webhooks; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( @@ -18,8 +17,9 @@ return array( $endpoint = $container->get( 'api.endpoint.webhook' ); assert( $endpoint instanceof WebhookEndpoint ); - $state = $container->get( 'onboarding.state' ); - if ( $state->current_state() >= State::STATE_ONBOARDED ) { + $is_connected = $container->get( 'settings.flag.is-connected' ); + + if ( $is_connected ) { return $endpoint->list(); } diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index e179fd24a..e000a1690 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -11,10 +11,8 @@ namespace WooCommerce\PayPalCommerce\Webhooks; use Exception; use Psr\Log\LoggerInterface; -use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook; use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint; @@ -183,7 +181,7 @@ return array( return new WebhooksStatusPageAssets( $container->get( 'webhook.module-url' ), $container->get( 'ppcp.asset-version' ), - $container->get( 'onboarding.environment' ) + $container->get( 'settings.environment' ) ); }, diff --git a/modules/ppcp-webhooks/src/WebhookModule.php b/modules/ppcp-webhooks/src/WebhookModule.php index f1f7a03b4..1886635f2 100644 --- a/modules/ppcp-webhooks/src/WebhookModule.php +++ b/modules/ppcp-webhooks/src/WebhookModule.php @@ -9,8 +9,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Webhooks; -use WC_Order; -use WooCommerce\PayPalCommerce\Onboarding\State; use Exception; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule; @@ -18,7 +16,6 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint; use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint; @@ -142,9 +139,10 @@ class WebhookModule implements ServiceModule, FactoryModule, ExtendingModule, Ex ); try { - $webhooks = $container->get( 'webhook.status.registered-webhooks' ); - $state = $container->get( 'onboarding.state' ); - if ( empty( $webhooks ) && $state->current_state() >= State::STATE_ONBOARDED ) { + $webhooks = $container->get( 'webhook.status.registered-webhooks' ); + $is_connected = $container->get( 'settings.flag.is-connected' ); + + if ( empty( $webhooks ) && $is_connected ) { $registrar = $container->get( 'webhook.registrar' ); assert( $registrar instanceof WebhookRegistrar ); $registrar->register(); diff --git a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php index 72b90e030..7fd20cc2f 100644 --- a/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/CreditCardGatewayTest.php @@ -10,7 +10,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\Vaulting\VaultedCreditCardHandler; @@ -34,7 +33,6 @@ class CreditCardGatewayTest extends TestCase private $moduleUrl; private $sessionHandler; private $refundProcessor; - private $state; private $transactionUrlProvider; private $subscriptionHelper; private $captureCardPayment; @@ -60,7 +58,6 @@ class CreditCardGatewayTest extends TestCase $this->moduleUrl = ''; $this->sessionHandler = Mockery::mock(SessionHandler::class); $this->refundProcessor = Mockery::mock(RefundProcessor::class); - $this->state = Mockery::mock(State::class); $this->transactionUrlProvider = Mockery::mock(TransactionUrlProvider::class); $this->subscriptionHelper = Mockery::mock(SubscriptionHelper::class); $this->captureCardPayment = Mockery::mock(CaptureCardPayment::class); @@ -73,7 +70,6 @@ class CreditCardGatewayTest extends TestCase $this->environment = Mockery::mock(Environment::class); $this->orderEndpoint = Mockery::mock(OrderEndpoint::class); - $this->state->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $this->config->shouldReceive('has')->andReturn(true); $this->config->shouldReceive('get')->andReturn(''); @@ -92,7 +88,6 @@ class CreditCardGatewayTest extends TestCase $this->moduleUrl, $this->sessionHandler, $this->refundProcessor, - $this->state, $this->transactionUrlProvider, $this->subscriptionHelper, $this->paymentsEndpoint, diff --git a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php index d806037ee..ff4f0eaa6 100644 --- a/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/PayUponInvoice/PayUponInvoiceGatewayTest.php @@ -11,7 +11,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper; @@ -30,7 +29,7 @@ class PayUponInvoiceGatewayTest extends TestCase private $testee; private $pui_helper; private $checkout_helper; - private $state; + private $is_connected; private $refund_processor; public function setUp(): void @@ -45,9 +44,7 @@ class PayUponInvoiceGatewayTest extends TestCase $this->transaction_url_provider = Mockery::mock(TransactionUrlProvider::class); $this->pui_helper = Mockery::mock(PayUponInvoiceHelper::class); $this->checkout_helper = Mockery::mock(CheckoutHelper::class); - - $this->state = Mockery::mock(State::class); - $this->state->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); + $this->is_connected = true; $this->refund_processor = Mockery::mock(RefundProcessor::class); @@ -63,7 +60,7 @@ class PayUponInvoiceGatewayTest extends TestCase $this->logger, $this->pui_helper, $this->checkout_helper, - $this->state, + $this->is_connected, $this->refund_processor, '' ); diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index a9e64ebb0..5f5cf722a 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -10,7 +10,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; -use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -38,7 +37,7 @@ class WcGatewayTest extends TestCase private $orderProcessor; private $settings; private $refundProcessor; - private $onboardingState; + private $isConnected; private $transactionUrlProvider; private $subscriptionHelper; private $environment; @@ -63,7 +62,7 @@ class WcGatewayTest extends TestCase $this->settings = Mockery::mock(Settings::class); $this->sessionHandler = Mockery::mock(SessionHandler::class); $this->refundProcessor = Mockery::mock(RefundProcessor::class); - $this->onboardingState = Mockery::mock(State::class); + $this->isConnected = true; $this->transactionUrlProvider = Mockery::mock(TransactionUrlProvider::class); $this->subscriptionHelper = Mockery::mock(SubscriptionHelper::class); $this->environment = Mockery::mock(Environment::class); @@ -76,8 +75,6 @@ class WcGatewayTest extends TestCase $this->apiShopCountry = 'DE'; $this->orderEndpoint = Mockery::mock(OrderEndpoint::class); - $this->onboardingState->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); - $this->sessionHandler ->shouldReceive('funding_source') ->andReturnUsing(function () { @@ -108,7 +105,7 @@ class WcGatewayTest extends TestCase $this->settings, $this->sessionHandler, $this->refundProcessor, - $this->onboardingState, + $this->isConnected, $this->transactionUrlProvider, $this->subscriptionHelper, PayPalGateway::ID, @@ -159,7 +156,7 @@ class WcGatewayTest extends TestCase ->andReturn($wcOrder); when('wc_get_checkout_url') - ->justReturn('test'); + ->justReturn('test'); $woocommerce = Mockery::mock(\WooCommerce::class); $cart = Mockery::mock(\WC_Cart::class); @@ -267,23 +264,6 @@ class WcGatewayTest extends TestCase ); } - /** - * @dataProvider dataForTestNeedsSetup - */ - public function testNeedsSetup($currentState, $needSetup) - { - $this->isAdmin = true; - - $this->onboardingState = Mockery::mock(State::class); - $this->onboardingState - ->expects('current_state') - ->andReturn($currentState); - - $testee = $this->createGateway(); - - $this->assertSame($needSetup, $testee->needs_setup()); - } - /** * @dataProvider dataForFundingSource */ @@ -315,14 +295,6 @@ class WcGatewayTest extends TestCase ]; } - public function dataForTestNeedsSetup(): array - { - return [ - [State::STATE_START, true], - [State::STATE_ONBOARDED, false] - ]; - } - public function dataForFundingSource(): array { return [ diff --git a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php index 951c3d9af..d7b8c2ed0 100644 --- a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php @@ -37,7 +37,7 @@ class OrderProcessorTest extends TestCase public function setUp(): void { parent::setUp(); - $this->environment = new Environment(new ReadOnlyContainer([], [], [], [])); + $this->environment = new Environment( false ); } public function testAuthorize() {