mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge branch 'PCP-4210-features-refactor-to-use-rest-endpoints' of github.com:woocommerce/woocommerce-paypal-payments into PCP-4210-features-refactor-to-use-rest-endpoints
This commit is contained in:
commit
78d14f4858
132 changed files with 2957 additions and 1152 deletions
|
@ -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() ) {
|
||||
|
@ -671,6 +670,7 @@ return array(
|
|||
'FR' => $default_currencies,
|
||||
'DE' => $default_currencies,
|
||||
'GR' => $default_currencies,
|
||||
'HK' => $default_currencies,
|
||||
'HU' => $default_currencies,
|
||||
'IE' => $default_currencies,
|
||||
'IT' => $default_currencies,
|
||||
|
@ -688,6 +688,7 @@ return array(
|
|||
'PT' => $default_currencies,
|
||||
'RO' => $default_currencies,
|
||||
'SK' => $default_currencies,
|
||||
'SG' => $default_currencies,
|
||||
'SI' => $default_currencies,
|
||||
'ES' => $default_currencies,
|
||||
'SE' => $default_currencies,
|
||||
|
@ -736,6 +737,7 @@ return array(
|
|||
'FR' => $mastercard_visa_amex,
|
||||
'GB' => $mastercard_visa_amex,
|
||||
'GR' => $mastercard_visa_amex,
|
||||
'HK' => $mastercard_visa_amex,
|
||||
'HU' => $mastercard_visa_amex,
|
||||
'IE' => $mastercard_visa_amex,
|
||||
'IT' => $mastercard_visa_amex,
|
||||
|
@ -765,6 +767,7 @@ return array(
|
|||
'SE' => $mastercard_visa_amex,
|
||||
'SI' => $mastercard_visa_amex,
|
||||
'SK' => $mastercard_visa_amex,
|
||||
'SG' => $mastercard_visa_amex,
|
||||
'JP' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
|
|
|
@ -160,7 +160,7 @@ class PartnersEndpoint {
|
|||
|
||||
$this->failure_registry->clear_failures( FailureRegistry::SELLER_STATUS_KEY );
|
||||
|
||||
$status = $this->seller_status_factory->from_paypal_reponse( $json );
|
||||
$status = $this->seller_status_factory->from_paypal_response( $json );
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,23 @@ class SellerStatus {
|
|||
*/
|
||||
private $capabilities;
|
||||
|
||||
/**
|
||||
* Merchant country on PayPal.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $country;
|
||||
|
||||
/**
|
||||
* SellerStatus constructor.
|
||||
*
|
||||
* @param SellerStatusProduct[] $products The products.
|
||||
* @param SellerStatusCapability[] $capabilities The capabilities.
|
||||
* @param string $country Merchant country on PayPal.
|
||||
*
|
||||
* @psalm-suppress RedundantConditionGivenDocblockType
|
||||
*/
|
||||
public function __construct( array $products, array $capabilities ) {
|
||||
public function __construct( array $products, array $capabilities, string $country = '' ) {
|
||||
foreach ( $products as $key => $product ) {
|
||||
if ( is_a( $product, SellerStatusProduct::class ) ) {
|
||||
continue;
|
||||
|
@ -52,6 +60,7 @@ class SellerStatus {
|
|||
|
||||
$this->products = $products;
|
||||
$this->capabilities = $capabilities;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +82,16 @@ class SellerStatus {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the enitity as array.
|
||||
* Returns merchant's country on PayPal.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function country() : string {
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -95,6 +113,7 @@ class SellerStatus {
|
|||
return array(
|
||||
'products' => $products,
|
||||
'capabilities' => $capabilities,
|
||||
'country' => $this->country,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class SellerStatusFactory {
|
|||
*
|
||||
* @return SellerStatus
|
||||
*/
|
||||
public function from_paypal_reponse( \stdClass $json ) : SellerStatus {
|
||||
public function from_paypal_response( \stdClass $json ) : SellerStatus {
|
||||
$products = array_map(
|
||||
function( $json ) : SellerStatusProduct {
|
||||
$product = new SellerStatusProduct(
|
||||
|
@ -49,6 +49,6 @@ class SellerStatusFactory {
|
|||
isset( $json->capabilities ) ? (array) $json->capabilities : array()
|
||||
);
|
||||
|
||||
return new SellerStatus( $products, $capabilities );
|
||||
return new SellerStatus( $products, $capabilities, $json->country ?? '' );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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(
|
||||
|
@ -191,6 +190,7 @@ return array(
|
|||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HK', // Hong Kong
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
|
@ -204,6 +204,7 @@ return array(
|
|||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SG', // Singapore
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
|
@ -233,6 +234,7 @@ return array(
|
|||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'HKD', // Hong Kong Dollar
|
||||
'GBP', // British Pound Sterling
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
|
@ -242,6 +244,7 @@ return array(
|
|||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SGD', // Singapur-Dollar
|
||||
'SEK', // Swedish Krona
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
|
@ -260,15 +263,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();
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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' )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
},
|
||||
|
|
|
@ -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' );
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -43,6 +43,7 @@ return array(
|
|||
'FR',
|
||||
'DE',
|
||||
'GR',
|
||||
'HK',
|
||||
'HU',
|
||||
'IE',
|
||||
'IT',
|
||||
|
@ -56,6 +57,7 @@ return array(
|
|||
'PT',
|
||||
'RO',
|
||||
'SK',
|
||||
'SG',
|
||||
'SI',
|
||||
'ES',
|
||||
'SE',
|
||||
|
|
|
@ -10,10 +10,12 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Compat;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\GeneralSettingsMapHelper;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMap;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsTabMapHelper;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\StylingSettingsMapHelper;
|
||||
use WooCommerce\PayPalCommerce\Compat\Settings\SubscriptionSettingsMapHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
|
@ -137,30 +139,16 @@ return array(
|
|||
$settings_tab_map_helper = $container->get( 'compat.settings.settings_tab_map_helper' );
|
||||
assert( $settings_tab_map_helper instanceof SettingsTabMapHelper );
|
||||
|
||||
$subscription_map_helper = $container->get( 'compat.settings.subscription_map_helper' );
|
||||
assert( $subscription_map_helper instanceof SubscriptionSettingsMapHelper );
|
||||
|
||||
$general_map_helper = $container->get( 'compat.settings.general_map_helper' );
|
||||
assert( $general_map_helper instanceof GeneralSettingsMapHelper );
|
||||
|
||||
return array(
|
||||
new SettingsMap(
|
||||
$container->get( 'settings.data.general' ),
|
||||
/**
|
||||
* The new GeneralSettings class stores the current connection
|
||||
* details, without adding an environment-suffix (no `_sandbox`
|
||||
* or `_production` in the field name)
|
||||
* Only the `sandbox_merchant` flag indicates, which environment
|
||||
* the credentials are used for.
|
||||
*/
|
||||
array(
|
||||
'merchant_id' => 'merchant_id',
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'sandbox_on' => 'sandbox_merchant',
|
||||
'live_client_id' => 'client_id',
|
||||
'live_client_secret' => 'client_secret',
|
||||
'live_merchant_id' => 'merchant_id',
|
||||
'live_merchant_email' => 'merchant_email',
|
||||
'sandbox_client_id' => 'client_id',
|
||||
'sandbox_client_secret' => 'client_secret',
|
||||
'sandbox_merchant_id' => 'merchant_id',
|
||||
'sandbox_merchant_email' => 'merchant_email',
|
||||
)
|
||||
$general_map_helper->map()
|
||||
),
|
||||
new SettingsMap(
|
||||
$container->get( 'settings.data.settings' ),
|
||||
|
@ -180,13 +168,19 @@ return array(
|
|||
*/
|
||||
$styling_settings_map_helper->map()
|
||||
),
|
||||
new SettingsMap(
|
||||
$container->get( 'settings.data.settings' ),
|
||||
$subscription_map_helper->map()
|
||||
),
|
||||
);
|
||||
},
|
||||
'compat.settings.settings_map_helper' => static function( ContainerInterface $container ) : SettingsMapHelper {
|
||||
return new SettingsMapHelper(
|
||||
$container->get( 'compat.setting.new-to-old-map' ),
|
||||
$container->get( 'compat.settings.styling_map_helper' ),
|
||||
$container->get( 'compat.settings.settings_tab_map_helper' )
|
||||
$container->get( 'compat.settings.settings_tab_map_helper' ),
|
||||
$container->get( 'compat.settings.subscription_map_helper' ),
|
||||
$container->get( 'compat.settings.general_map_helper' )
|
||||
);
|
||||
},
|
||||
'compat.settings.styling_map_helper' => static function() : StylingSettingsMapHelper {
|
||||
|
@ -195,4 +189,10 @@ return array(
|
|||
'compat.settings.settings_tab_map_helper' => static function() : SettingsTabMapHelper {
|
||||
return new SettingsTabMapHelper();
|
||||
},
|
||||
'compat.settings.subscription_map_helper' => static function( ContainerInterface $container ) : SubscriptionSettingsMapHelper {
|
||||
return new SubscriptionSettingsMapHelper( $container->get( 'wc-subscriptions.helper' ) );
|
||||
},
|
||||
'compat.settings.general_map_helper' => static function() : GeneralSettingsMapHelper {
|
||||
return new GeneralSettingsMapHelper();
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/**
|
||||
* A helper for mapping old and new general settings.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Compat\Settings
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||
|
||||
/**
|
||||
* Handles mapping between old and new general settings.
|
||||
*
|
||||
* @psalm-import-type newSettingsKey from SettingsMap
|
||||
* @psalm-import-type oldSettingsKey from SettingsMap
|
||||
*/
|
||||
class GeneralSettingsMapHelper {
|
||||
|
||||
/**
|
||||
* Maps old setting keys to new setting keys.
|
||||
*
|
||||
* The new GeneralSettings class stores the current connection
|
||||
* details, without adding an environment-suffix (no `_sandbox`
|
||||
* or `_production` in the field name)
|
||||
* Only the `sandbox_merchant` flag indicates, which environment
|
||||
* the credentials are used for.
|
||||
*
|
||||
* @psalm-return array<oldSettingsKey, newSettingsKey>
|
||||
*/
|
||||
public function map(): array {
|
||||
return array(
|
||||
'merchant_id' => 'merchant_id',
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'sandbox_on' => 'sandbox_merchant',
|
||||
'live_client_id' => 'client_id',
|
||||
'live_client_secret' => 'client_secret',
|
||||
'live_merchant_id' => 'merchant_id',
|
||||
'live_merchant_email' => 'merchant_email',
|
||||
'merchant_email' => 'merchant_email',
|
||||
'sandbox_client_id' => 'client_id',
|
||||
'sandbox_client_secret' => 'client_secret',
|
||||
'sandbox_merchant_id' => 'merchant_id',
|
||||
'sandbox_merchant_email' => 'merchant_email',
|
||||
'enabled' => '',
|
||||
'allow_local_apm_gateways' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mapped value for the given key from the new settings.
|
||||
*
|
||||
* @param string $old_key The key from the legacy settings.
|
||||
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
|
||||
* @return mixed The value of the mapped setting, or null if not applicable.
|
||||
*/
|
||||
public function mapped_value( string $old_key, array $settings_model ) {
|
||||
$settings_map = $this->map();
|
||||
$new_key = $settings_map[ $old_key ] ?? false;
|
||||
switch ( $old_key ) {
|
||||
case 'enabled':
|
||||
case 'allow_local_apm_gateways':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return $settings_model[ $new_key ] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ declare( strict_types = 1 );
|
|||
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||
|
||||
use RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\SettingsModel;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\StylingSettings;
|
||||
|
||||
|
@ -57,23 +58,43 @@ class SettingsMapHelper {
|
|||
*/
|
||||
protected SettingsTabMapHelper $settings_tab_map_helper;
|
||||
|
||||
/**
|
||||
* A helper for mapping old and new subscription settings.
|
||||
*
|
||||
* @var SubscriptionSettingsMapHelper
|
||||
*/
|
||||
protected SubscriptionSettingsMapHelper $subscription_map_helper;
|
||||
|
||||
/**
|
||||
* A helper for mapping old and new general settings.
|
||||
*
|
||||
* @var GeneralSettingsMapHelper
|
||||
*/
|
||||
protected GeneralSettingsMapHelper $general_settings_map_helper;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SettingsMap[] $settings_map A list of settings maps containing key definitions.
|
||||
* @param StylingSettingsMapHelper $styling_settings_map_helper A helper for mapping the old/new styling settings.
|
||||
* @param SettingsTabMapHelper $settings_tab_map_helper A helper for mapping the old/new settings tab settings.
|
||||
* @param SettingsMap[] $settings_map A list of settings maps containing key definitions.
|
||||
* @param StylingSettingsMapHelper $styling_settings_map_helper A helper for mapping the old/new styling settings.
|
||||
* @param SettingsTabMapHelper $settings_tab_map_helper A helper for mapping the old/new settings tab settings.
|
||||
* @param SubscriptionSettingsMapHelper $subscription_map_helper A helper for mapping old and new subscription settings.
|
||||
* @param GeneralSettingsMapHelper $general_settings_map_helper A helper for mapping old and new general settings.
|
||||
* @throws RuntimeException When an old key has multiple mappings.
|
||||
*/
|
||||
public function __construct(
|
||||
array $settings_map,
|
||||
StylingSettingsMapHelper $styling_settings_map_helper,
|
||||
SettingsTabMapHelper $settings_tab_map_helper
|
||||
SettingsTabMapHelper $settings_tab_map_helper,
|
||||
SubscriptionSettingsMapHelper $subscription_map_helper,
|
||||
GeneralSettingsMapHelper $general_settings_map_helper
|
||||
) {
|
||||
$this->validate_settings_map( $settings_map );
|
||||
$this->settings_map = $settings_map;
|
||||
$this->styling_settings_map_helper = $styling_settings_map_helper;
|
||||
$this->settings_tab_map_helper = $settings_tab_map_helper;
|
||||
$this->subscription_map_helper = $subscription_map_helper;
|
||||
$this->general_settings_map_helper = $general_settings_map_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,8 +171,13 @@ class SettingsMapHelper {
|
|||
case $model instanceof StylingSettings:
|
||||
return $this->styling_settings_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
||||
|
||||
case $model instanceof GeneralSettings:
|
||||
return $this->general_settings_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
||||
|
||||
case $model instanceof SettingsModel:
|
||||
return $this->settings_tab_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
||||
return $old_key === 'subscriptions_mode'
|
||||
? $this->subscription_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] )
|
||||
: $this->settings_tab_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
||||
|
||||
default:
|
||||
return $this->model_cache[ $model_id ][ $new_key ] ?? null;
|
||||
|
|
|
@ -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<string, scalar|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<string, scalar|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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/**
|
||||
* A helper for mapping old and new subscription settings.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Compat\Settings
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
|
||||
/**
|
||||
* Handles mapping between old and new subscription settings.
|
||||
*
|
||||
* In the new settings UI, the Subscriptions mode value is set automatically based on the merchant type.
|
||||
* This class fakes the mapping and injects the appropriate value based on the merchant:
|
||||
* - Non-vaulting merchants will use PayPal Subscriptions.
|
||||
* - Merchants with vaulting will use PayPal Vaulting.
|
||||
* - Disabled subscriptions can be controlled using a filter.
|
||||
*
|
||||
* @psalm-import-type newSettingsKey from SettingsMap
|
||||
* @psalm-import-type oldSettingsKey from SettingsMap
|
||||
*/
|
||||
class SubscriptionSettingsMapHelper {
|
||||
|
||||
public const OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_VAULTING = 'vaulting_api';
|
||||
public const OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_SUBSCRIPTIONS = 'subscriptions_api';
|
||||
public const OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_DISABLED = 'disable_paypal_subscriptions';
|
||||
|
||||
/**
|
||||
* The subscription helper.
|
||||
*
|
||||
* @var SubscriptionHelper $subscription_helper
|
||||
*/
|
||||
protected SubscriptionHelper $subscription_helper;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
*/
|
||||
public function __construct( SubscriptionHelper $subscription_helper ) {
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the old subscription setting key.
|
||||
*
|
||||
* This method creates a placeholder mapping as this setting doesn't exist in the new settings.
|
||||
* The Subscriptions mode value is set automatically based on the merchant type.
|
||||
*
|
||||
* @psalm-return array<oldSettingsKey, newSettingsKey>
|
||||
*/
|
||||
public function map(): array {
|
||||
return array( 'subscriptions_mode' => '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mapped value for the subscriptions_mode key from the new settings.
|
||||
*
|
||||
* @param string $old_key The key from the legacy settings.
|
||||
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
|
||||
*
|
||||
* @return 'vaulting_api'|'subscriptions_api'|'disable_paypal_subscriptions'|null The mapped subscriptions_mode value, or null if not applicable.
|
||||
*/
|
||||
public function mapped_value( string $old_key, array $settings_model ): ?string {
|
||||
if ( $old_key !== 'subscriptions_mode' || ! $this->subscription_helper->plugin_is_active() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$vaulting = $settings_model['save_paypal_and_venmo'] ?? false;
|
||||
$subscription_mode_value = $vaulting ? self::OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_VAULTING : self::OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_SUBSCRIPTIONS;
|
||||
|
||||
/**
|
||||
* Allows disabling the subscription mode when using the new settings UI.
|
||||
*
|
||||
* @returns bool true if the subscription mode should be disabled, false otherwise (default is false).
|
||||
*/
|
||||
$subscription_mode_disabled = (bool) apply_filters( 'woocommerce_paypal_payments_subscription_mode_disabled', false );
|
||||
|
||||
return $subscription_mode_disabled ? self::OLD_SETTINGS_SUBSCRIPTION_MODE_VALUE_DISABLED : $subscription_mode_value;
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
@ -106,6 +105,7 @@ return array(
|
|||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HK', // Hong Kong
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
|
@ -119,6 +119,7 @@ return array(
|
|||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SG', // Singapore
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
|
@ -148,6 +149,7 @@ return array(
|
|||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'HKD', // Hong Kong Dollar
|
||||
'GBP', // British Pound Sterling
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
|
@ -157,6 +159,7 @@ return array(
|
|||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SGD', // Singapur-Dollar
|
||||
'SEK', // Swedish Krona
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
|
@ -174,7 +177,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 +224,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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -44,7 +43,16 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
// When Local APMs are disabled, none of the following hooks are needed.
|
||||
if ( ! $this->should_add_local_apm_gateways( $c ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "woocommerce_payment_gateways" filter is responsible for ADDING
|
||||
* custom payment gateways to WooCommerce. Here, we add all the local
|
||||
* APM gateways to the filtered list, so they become available later on.
|
||||
*/
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
/**
|
||||
|
@ -53,14 +61,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $methods ) use ( $c ) {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return $methods;
|
||||
}
|
||||
$onboarding_state = $c->get( 'onboarding.state' );
|
||||
if ( $onboarding_state->current_state() === State::STATE_START ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
if ( ! is_array( $methods ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
@ -74,6 +74,10 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the "available gateways" list by REMOVING gateways that
|
||||
* are not available for the current customer.
|
||||
*/
|
||||
add_filter(
|
||||
'woocommerce_available_payment_gateways',
|
||||
/**
|
||||
|
@ -82,29 +86,22 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $methods ) use ( $c ) {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return $methods;
|
||||
}
|
||||
if ( ! is_array( $methods ) ) {
|
||||
if ( ! is_array( $methods ) || is_admin() || empty( WC()->customer ) ) {
|
||||
// Don't restrict the gateway list on wp-admin or when no customer is known.
|
||||
return $methods;
|
||||
}
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
if ( ! isset( WC()->customer ) ) {
|
||||
return $methods;
|
||||
}
|
||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||
$customer_country = WC()->customer->get_billing_country() ?: WC()->customer->get_shipping_country();
|
||||
$site_currency = get_woocommerce_currency();
|
||||
|
||||
$customer_country = WC()->customer->get_billing_country() ?: WC()->customer->get_shipping_country();
|
||||
$site_currency = get_woocommerce_currency();
|
||||
// Remove unsupported gateways from the customer's payment options.
|
||||
foreach ( $payment_methods as $payment_method ) {
|
||||
$is_currency_supported = in_array( $site_currency, $payment_method['currencies'], true );
|
||||
$is_country_supported = in_array( $customer_country, $payment_method['countries'], true );
|
||||
|
||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||
foreach ( $payment_methods as $payment_method ) {
|
||||
if (
|
||||
! in_array( $customer_country, $payment_method['countries'], true )
|
||||
|| ! in_array( $site_currency, $payment_method['currencies'], true )
|
||||
) {
|
||||
unset( $methods[ $payment_method['id'] ] );
|
||||
}
|
||||
if ( ! $is_currency_supported || ! $is_country_supported ) {
|
||||
unset( $methods[ $payment_method['id'] ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,12 +109,15 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds all local APM gateways in the "payment_method_type" block registry
|
||||
* to make the payment methods available in the Block Checkout.
|
||||
*
|
||||
* @see IntegrationRegistry::initialize
|
||||
*/
|
||||
add_action(
|
||||
'woocommerce_blocks_payment_method_type_registration',
|
||||
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return;
|
||||
}
|
||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||
foreach ( $payment_methods as $key => $value ) {
|
||||
$payment_method_registry->register( $c->get( 'ppcp-local-apms.' . $key . '.payment-method' ) );
|
||||
|
@ -128,9 +128,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
add_filter(
|
||||
'woocommerce_paypal_payments_localized_script_data',
|
||||
function ( array $data ) use ( $c ) {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return $data;
|
||||
}
|
||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||
|
||||
$default_disable_funding = $data['url_params']['disable-funding'] ?? '';
|
||||
|
@ -149,9 +146,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $order_id ) use ( $c ) {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return;
|
||||
}
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( ! $order instanceof WC_Order ) {
|
||||
return;
|
||||
|
@ -184,9 +178,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
add_action(
|
||||
'woocommerce_paypal_payments_payment_capture_completed_webhook_handler',
|
||||
function( WC_Order $wc_order, string $order_id ) use ( $c ) {
|
||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
||||
return;
|
||||
}
|
||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||
if (
|
||||
! $this->is_local_apm( $wc_order->get_payment_method(), $payment_methods )
|
||||
|
@ -229,12 +220,35 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
|||
* @param ContainerInterface $container Container.
|
||||
* @return bool
|
||||
*/
|
||||
private function should_add_local_apm_gateways( ContainerInterface $container ): bool {
|
||||
private function should_add_local_apm_gateways( ContainerInterface $container ) : bool {
|
||||
// Merchant onboarding must be completed.
|
||||
$is_connected = $container->get( 'settings.flag.is-connected' );
|
||||
if ( ! $is_connected ) {
|
||||
/**
|
||||
* When the merchant is _not_ connected yet, we still need to
|
||||
* register the APM gateways in one case:
|
||||
*
|
||||
* During the authentication process (which happens via a REST call)
|
||||
* the gateways need to be present, so they can be correctly
|
||||
* pre-configured for new merchants.
|
||||
*
|
||||
* TODO is there a cleaner solution for this?
|
||||
*/
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$request_uri = wp_unslash( $_SERVER['REQUEST_URI'] ?? '' );
|
||||
|
||||
return str_contains( $request_uri, '/wp-json/wc/' );
|
||||
}
|
||||
|
||||
// The general plugin functionality must be enabled.
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
return $settings->has( 'enabled' )
|
||||
&& $settings->get( 'enabled' ) === true
|
||||
&& $settings->has( 'allow_local_apm_gateways' )
|
||||
if ( ! $settings->has( 'enabled' ) || ! $settings->get( 'enabled' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register APM gateways, when the relevant setting is active.
|
||||
return $settings->has( 'allow_local_apm_gateways' )
|
||||
&& $settings->get( 'allow_local_apm_gateways' ) === true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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';
|
||||
?>
|
||||
<tr>
|
||||
|
@ -476,7 +476,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
return;
|
||||
}
|
||||
|
||||
$environment = $c->get( 'onboarding.environment' );
|
||||
$environment = $c->get( 'settings.environment' );
|
||||
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
|
||||
$this->render_paypal_subscription_fields( $product, $environment );
|
||||
echo '</div>';
|
||||
|
@ -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 );
|
||||
|
||||
}
|
||||
|
|
35
modules/ppcp-settings/docs/glossary.md
Normal file
35
modules/ppcp-settings/docs/glossary.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Glossary
|
||||
|
||||
This document provides definitions and explanations of key terms used in the plugin.
|
||||
|
||||
---
|
||||
|
||||
## Eligibility
|
||||
|
||||
**Eligibility** determines whether a merchant can access a specific feature within the plugin. It is a boolean value (`true` or `false`) that depends on certain criteria, such as:
|
||||
|
||||
- **Country**: The merchant's location or the country where their business operates.
|
||||
- **Other Factors**: Additional conditions, such as subscription plans, business type, or compliance requirements.
|
||||
|
||||
If a merchant is **eligible** (`true`) for a feature, the feature will be visible and accessible in the plugin. If they are **not eligible** (`false`), the feature will be hidden or unavailable.
|
||||
|
||||
---
|
||||
|
||||
## Capability
|
||||
|
||||
**Capability** refers to the activation status of a feature for an eligible merchant. Even if a merchant is eligible for a feature, they may need to activate it in their PayPal dashboard to use it. Capability has two states:
|
||||
|
||||
- **Active**: The feature is enabled, and the merchant can configure and use it.
|
||||
- **Inactive**: The feature is not enabled, and the merchant will be guided on how to activate it (e.g., through instructions or prompts).
|
||||
|
||||
Capability ensures that eligible merchants have control over which features they want to use and configure within the plugin.
|
||||
|
||||
---
|
||||
|
||||
### Example Workflow
|
||||
|
||||
1. A merchant is **eligible** for a feature based on their country and other factors.
|
||||
2. If the feature is **active** (capability is enabled), the merchant can configure and use it.
|
||||
3. If the feature is **inactive**, the plugin will provide instructions on how to activate it.
|
||||
|
||||
---
|
|
@ -82,3 +82,85 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled state styling.
|
||||
.ppcp--method-item--disabled {
|
||||
position: relative;
|
||||
|
||||
// Apply grayscale and disable interactions.
|
||||
.ppcp--method-inner {
|
||||
opacity: 0.7;
|
||||
filter: grayscale(1);
|
||||
pointer-events: none;
|
||||
transition: filter 0.2s ease;
|
||||
}
|
||||
|
||||
// Override text colors.
|
||||
.ppcp--method-title {
|
||||
color: $color-gray-700 !important;
|
||||
}
|
||||
|
||||
.ppcp--method-description p {
|
||||
color: $color-gray-500 !important;
|
||||
}
|
||||
|
||||
.ppcp--method-disabled-message {
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
// Style all buttons and toggle controls.
|
||||
.components-button,
|
||||
.components-form-toggle {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
// Hover state - only blur the inner content.
|
||||
&:hover {
|
||||
.ppcp--method-inner {
|
||||
filter: blur(2px) grayscale(1);
|
||||
}
|
||||
|
||||
.ppcp--method-disabled-message {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled overlay.
|
||||
.ppcp--method-disabled-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba($color-white, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 50;
|
||||
border-radius: var(--container-border-radius);
|
||||
pointer-events: auto;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.ppcp--method-item--disabled:hover .ppcp--method-disabled-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ppcp--method-disabled-message {
|
||||
padding: 14px 18px;
|
||||
text-align: center;
|
||||
@include font(13, 20, 500);
|
||||
color: $color-text-tertiary;
|
||||
position: relative;
|
||||
z-index: 51;
|
||||
border: none;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
} ) => (
|
||||
<Action>
|
||||
<ToggleControl
|
||||
className="ppcp--control-toggle"
|
||||
|
@ -12,6 +18,7 @@ const ControlToggleButton = ( { label, description, value, onChange } ) => (
|
|||
help={
|
||||
description ? <Description>{ description }</Description> : null
|
||||
}
|
||||
disabled={ disabled }
|
||||
/>
|
||||
</Action>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const CardActions = ( { isDimmed = false, children } ) => {
|
||||
const className = classNames( 'ppcp--card-actions', {
|
||||
'ppcp--dimmed': isDimmed,
|
||||
} );
|
||||
|
||||
return <div className={ className }>{ children }</div>;
|
||||
};
|
||||
|
||||
export default CardActions;
|
|
@ -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';
|
||||
|
|
|
@ -11,6 +11,8 @@ const PaymentMethodItemBlock = ( {
|
|||
onTriggerModal,
|
||||
onSelect,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
disabledMessage,
|
||||
} ) => {
|
||||
const { activeHighlight, setActiveHighlight } = useActiveHighlight();
|
||||
const isHighlighted = activeHighlight === paymentMethod.id;
|
||||
|
@ -31,9 +33,16 @@ const PaymentMethodItemBlock = ( {
|
|||
id={ paymentMethod.id }
|
||||
className={ `ppcp--method-item ${
|
||||
isHighlighted ? 'ppcp-highlight' : ''
|
||||
}` }
|
||||
} ${ isDisabled ? 'ppcp--method-item--disabled' : '' }` }
|
||||
separatorAndGap={ false }
|
||||
>
|
||||
{ isDisabled && (
|
||||
<div className="ppcp--method-disabled-overlay">
|
||||
<p className="ppcp--method-disabled-message">
|
||||
{ disabledMessage }
|
||||
</p>
|
||||
</div>
|
||||
) }
|
||||
<div className="ppcp--method-inner">
|
||||
<div className="ppcp--method-title-wrapper">
|
||||
{ paymentMethod?.icon && (
|
||||
|
|
|
@ -19,12 +19,14 @@ const PaymentMethodsBlock = ( { paymentMethods = [], onTriggerModal } ) => {
|
|||
<SettingsBlock className="ppcp--grid ppcp-r-settings-block__payment-methods">
|
||||
{ paymentMethods
|
||||
// Remove empty/invalid payment method entries.
|
||||
.filter( ( m ) => m.id )
|
||||
.filter( ( m ) => m && m.id )
|
||||
.map( ( paymentMethod ) => (
|
||||
<PaymentMethodItemBlock
|
||||
key={ paymentMethod.id }
|
||||
paymentMethod={ paymentMethod }
|
||||
isSelected={ paymentMethod.enabled }
|
||||
isDisabled={ paymentMethod.isDisabled }
|
||||
disabledMessage={ paymentMethod.disabledMessage }
|
||||
onSelect={ ( checked ) =>
|
||||
handleSelect( paymentMethod.id, checked )
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -22,8 +22,13 @@ const StepBusiness = ( {} ) => {
|
|||
);
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! businessChoice ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === businessChoice );
|
||||
}, [ businessChoice, setIsCasualSeller ] );
|
||||
|
||||
const { canUseSubscriptions } = OnboardingHooks.useFlags();
|
||||
const businessChoices = [
|
||||
{
|
||||
|
|
|
@ -76,7 +76,17 @@ const StepProducts = () => {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
isDisabled: isCasualSeller,
|
||||
contents: <DetailsSubscriptions showNotice={ isCasualSeller } />,
|
||||
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.
|
||||
*/
|
||||
<DetailsSubscriptions
|
||||
showLink={ false }
|
||||
showNotice={ isCasualSeller }
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
|
@ -117,14 +127,19 @@ const DetailsPhysical = () => (
|
|||
</ul>
|
||||
);
|
||||
|
||||
const DetailsSubscriptions = ( { showNotice } ) => (
|
||||
const DetailsSubscriptions = ( { showLink, showNotice } ) => (
|
||||
<>
|
||||
<a
|
||||
target="__blank"
|
||||
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
|
||||
>
|
||||
{ __( 'WooCommerce Subscriptions', 'woocommerce-paypal-payments' ) }
|
||||
</a>
|
||||
{ showLink && (
|
||||
<a
|
||||
target="__blank"
|
||||
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
|
||||
>
|
||||
{ __(
|
||||
'WooCommerce Subscriptions',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</a>
|
||||
) }
|
||||
{ showNotice && (
|
||||
<p>
|
||||
{ __(
|
||||
|
|
|
@ -36,8 +36,7 @@ const ALL_STEPS = [
|
|||
id: 'methods',
|
||||
title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ),
|
||||
StepComponent: StepPaymentMethods,
|
||||
canProceed: ( { methods } ) =>
|
||||
methods.areOptionalPaymentMethodsEnabled !== null,
|
||||
canProceed: ( { methods } ) => methods.optionalMethods !== null,
|
||||
},
|
||||
{
|
||||
id: 'complete',
|
||||
|
|
|
@ -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' );
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { createInterpolateElement } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { scrollAndHighlight } from '../../../../../utils/scrollAndHighlight';
|
||||
|
||||
/**
|
||||
* Component to display a payment method dependency message
|
||||
*
|
||||
* @param {Object} props - Component props
|
||||
* @param {string} props.parentId - ID of the parent payment method
|
||||
* @param {string} props.parentName - Display name of the parent payment method
|
||||
* @return {JSX.Element} The formatted message with link
|
||||
*/
|
||||
const DependencyMessage = ( { parentId, parentName } ) => {
|
||||
// Using WordPress createInterpolateElement with proper React elements
|
||||
return createInterpolateElement(
|
||||
/* translators: %s: payment method name */
|
||||
__(
|
||||
'This payment method requires <methodLink /> to be enabled.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
{
|
||||
methodLink: (
|
||||
<strong>
|
||||
<a
|
||||
href="#"
|
||||
onClick={ ( e ) => {
|
||||
e.preventDefault();
|
||||
scrollAndHighlight( parentId );
|
||||
} }
|
||||
>
|
||||
{ parentName }
|
||||
</a>
|
||||
</strong>
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default DependencyMessage;
|
|
@ -0,0 +1,71 @@
|
|||
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
||||
import { PaymentMethodsBlock } from '../../../../ReusableComponents/SettingsBlocks';
|
||||
import usePaymentDependencyState from '../../../../../hooks/usePaymentDependencyState';
|
||||
import DependencyMessage from './DependencyMessage';
|
||||
|
||||
/**
|
||||
* Renders a payment method card with dependency handling
|
||||
*
|
||||
* @param {Object} props - Component props
|
||||
* @param {string} props.id - Unique identifier for the card
|
||||
* @param {string} props.title - Title of the payment method card
|
||||
* @param {string} props.description - Description of the payment method
|
||||
* @param {string} props.icon - Icon path for the payment method
|
||||
* @param {Array} props.methods - List of payment methods to display
|
||||
* @param {Object} props.methodsMap - Map of all payment methods by ID
|
||||
* @param {Function} props.onTriggerModal - Callback when a method is clicked
|
||||
* @param {boolean} props.isDisabled - Whether the entire card is disabled
|
||||
* @param {(string|JSX.Element)} props.disabledMessage - Message to show when disabled
|
||||
* @return {JSX.Element} The rendered component
|
||||
*/
|
||||
const PaymentMethodCard = ( {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
methods,
|
||||
methodsMap = {},
|
||||
onTriggerModal,
|
||||
isDisabled = false,
|
||||
disabledMessage,
|
||||
} ) => {
|
||||
const dependencyState = usePaymentDependencyState( methods, methodsMap );
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
id={ id }
|
||||
title={ title }
|
||||
description={ description }
|
||||
icon={ icon }
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ methods.map( ( method ) => {
|
||||
const dependency = dependencyState[ method.id ];
|
||||
|
||||
const dependencyMessage = dependency ? (
|
||||
<DependencyMessage
|
||||
parentId={ dependency.parentId }
|
||||
parentName={ dependency.parentName }
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return {
|
||||
...method,
|
||||
isDisabled:
|
||||
method.isDisabled ||
|
||||
isDisabled ||
|
||||
Boolean( dependency?.isDisabled ),
|
||||
disabledMessage:
|
||||
method.disabledMessage ||
|
||||
dependencyMessage ||
|
||||
disabledMessage,
|
||||
};
|
||||
} ) }
|
||||
onTriggerModal={ onTriggerModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentMethodCard;
|
|
@ -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 (
|
||||
<SettingsBlock
|
||||
title={ __( 'Order Intent', 'woocommerce-paypal-payments' ) }
|
||||
|
@ -34,6 +41,7 @@ const OrderIntent = () => {
|
|||
) }
|
||||
onChange={ setCaptureVirtualOnlyOrders }
|
||||
value={ captureVirtualOnlyOrders }
|
||||
disabled={ ! authorizeOnly }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
|
|
@ -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={ <ConnectionDescription /> }
|
||||
>
|
||||
<SettingsBlock>
|
||||
<SettingsBlock className="ppcp--pull-right">
|
||||
<ControlStaticValue
|
||||
value={
|
||||
<ConnectionStatusBadge
|
||||
isActive={ merchant.isConnected }
|
||||
isSandbox={ merchant.isSandbox }
|
||||
isBusinessSeller={ merchant.isBusinessSeller }
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SettingsBlock>
|
||||
<SettingsBlock
|
||||
title={ __( 'Merchant ID', 'woocommerce-paypal-payments' ) }
|
||||
className="ppcp--no-gap"
|
||||
>
|
||||
<ControlStaticValue value={ merchant.id } />
|
||||
</SettingsBlock>
|
||||
|
@ -59,9 +62,9 @@ const ConnectionDescription = () => {
|
|||
'Your PayPal account connection details.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
<div className="ppcp--card-actions">
|
||||
<CardActions isDimmed={ true }>
|
||||
<DisconnectButton />
|
||||
</div>
|
||||
</CardActions>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
>
|
||||
<ContentWrapper>
|
||||
<Content>
|
||||
{ /*<Content>
|
||||
<ConnectionDetails
|
||||
updateFormValue={ updateFormValue }
|
||||
settings={ settings }
|
||||
/>
|
||||
</Content>
|
||||
</Content>*/ }
|
||||
|
||||
<Content>
|
||||
<Troubleshooting
|
||||
|
|
|
@ -5,11 +5,19 @@ import TitleBadge, {
|
|||
TITLE_BADGE_POSITIVE,
|
||||
} from '../../../../../ReusableComponents/TitleBadge';
|
||||
|
||||
const ConnectionStatusBadge = ( { isActive, isSandbox } ) => {
|
||||
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 <TitleBadge type={ TITLE_BADGE_POSITIVE } text={ label } />;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { useCallback } from '@wordpress/element';
|
||||
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import { PaymentMethodsBlock } from '../../../ReusableComponents/SettingsBlocks';
|
||||
import { CommonHooks, OnboardingHooks, PaymentHooks } from '../../../../data';
|
||||
import { useActiveModal } from '../../../../data/common/hooks';
|
||||
import Modal from '../Components/Payment/Modal';
|
||||
import PaymentMethodCard from '../Components/Payment/PaymentMethodCard';
|
||||
|
||||
const TabPaymentMethods = () => {
|
||||
const methods = PaymentHooks.usePaymentMethods();
|
||||
const { setPersistent, changePaymentSettings } = PaymentHooks.useStore();
|
||||
const store = PaymentHooks.useStore();
|
||||
const { setPersistent, changePaymentSettings } = store;
|
||||
const { activeModal, setActiveModal } = useActiveModal();
|
||||
|
||||
// Get all methods as a map for dependency checking
|
||||
const methodsMap = {};
|
||||
methods.all.forEach( ( method ) => {
|
||||
methodsMap[ method.id ] = method;
|
||||
} );
|
||||
|
||||
const getActiveMethod = () => {
|
||||
if ( ! activeModal ) {
|
||||
return null;
|
||||
|
@ -60,6 +66,7 @@ const TabPaymentMethods = () => {
|
|||
icon="icon-checkout-standard.svg"
|
||||
methods={ methods.paypal }
|
||||
onTriggerModal={ setActiveModal }
|
||||
methodsMap={ methodsMap }
|
||||
/>
|
||||
{ merchant.isBusinessSeller && canUseCardPayments && (
|
||||
<PaymentMethodCard
|
||||
|
@ -75,6 +82,7 @@ const TabPaymentMethods = () => {
|
|||
icon="icon-checkout-online-methods.svg"
|
||||
methods={ methods.cardPayment }
|
||||
onTriggerModal={ setActiveModal }
|
||||
methodsMap={ methodsMap }
|
||||
/>
|
||||
) }
|
||||
<PaymentMethodCard
|
||||
|
@ -90,6 +98,7 @@ const TabPaymentMethods = () => {
|
|||
icon="icon-checkout-alternative-methods.svg"
|
||||
methods={ methods.apm }
|
||||
onTriggerModal={ setActiveModal }
|
||||
methodsMap={ methodsMap }
|
||||
/>
|
||||
|
||||
{ activeModal && (
|
||||
|
@ -104,25 +113,3 @@ const TabPaymentMethods = () => {
|
|||
};
|
||||
|
||||
export default TabPaymentMethods;
|
||||
|
||||
const PaymentMethodCard = ( {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
methods,
|
||||
onTriggerModal,
|
||||
} ) => (
|
||||
<SettingsCard
|
||||
id={ id }
|
||||
title={ title }
|
||||
description={ description }
|
||||
icon={ icon }
|
||||
contentContainer={ false }
|
||||
>
|
||||
<PaymentMethodsBlock
|
||||
paymentMethods={ methods }
|
||||
onTriggerModal={ onTriggerModal }
|
||||
/>
|
||||
</SettingsCard>
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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,46 +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 );
|
||||
stores.push( FeaturesStoreName );
|
||||
} 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
|
||||
|
@ -103,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();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
export default {
|
||||
// Transient data.
|
||||
SET_TRANSIENT: 'PAYMENT:SET_TRANSIENT',
|
||||
SET_DISABLED_BY_DEPENDENCY: 'PAYMENT:SET_DISABLED_BY_DEPENDENCY',
|
||||
RESTORE_DEPENDENCY_STATE: 'PAYMENT:RESTORE_DEPENDENCY_STATE',
|
||||
|
||||
// Persistent data.
|
||||
SET_PERSISTENT: 'PAYMENT:SET_PERSISTENT',
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as actions from './actions';
|
|||
import * as hooks from './hooks';
|
||||
import * as resolvers from './resolvers';
|
||||
import { initTodoSync } from '../sync/todo-state-sync';
|
||||
import { initPaymentDependencySync } from '../sync/payment-methods-sync';
|
||||
|
||||
/**
|
||||
* Initializes and registers the settings store with WordPress data layer.
|
||||
|
@ -24,9 +25,12 @@ export const initStore = () => {
|
|||
|
||||
register( store );
|
||||
|
||||
// Initialize todo sync after store registration. Potentially should be moved elsewhere.
|
||||
// Initialize todo sync after store registration.
|
||||
initTodoSync();
|
||||
|
||||
// Initialize payment method dependency sync.
|
||||
initPaymentDependencySync();
|
||||
|
||||
return Boolean( wp.data.select( STORE_NAME ) );
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -85,6 +88,56 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
|
|||
|
||||
[ ACTION_TYPES.HYDRATE ]: ( state, payload ) =>
|
||||
changePersistent( state, payload.data ),
|
||||
|
||||
[ ACTION_TYPES.SET_DISABLED_BY_DEPENDENCY ]: ( state, payload ) => {
|
||||
const { methodId } = payload;
|
||||
const method = state.data[ methodId ];
|
||||
|
||||
if ( ! method ) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Create a new state with the method disabled due to dependency
|
||||
const updatedData = {
|
||||
...state.data,
|
||||
[ methodId ]: {
|
||||
...method,
|
||||
enabled: false,
|
||||
_disabledByDependency: true,
|
||||
_originalState: method.enabled,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
data: updatedData,
|
||||
};
|
||||
},
|
||||
|
||||
[ ACTION_TYPES.RESTORE_DEPENDENCY_STATE ]: ( state, payload ) => {
|
||||
const { methodId } = payload;
|
||||
const method = state.data[ methodId ];
|
||||
|
||||
if ( ! method || ! method._disabledByDependency ) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Restore the method to its original state
|
||||
const updatedData = {
|
||||
...state.data,
|
||||
[ methodId ]: {
|
||||
...method,
|
||||
enabled: method._originalState === true,
|
||||
_disabledByDependency: false,
|
||||
_originalState: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
data: updatedData,
|
||||
};
|
||||
},
|
||||
} );
|
||||
|
||||
export default reducer;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import { subscribe, select } from '@wordpress/data';
|
||||
|
||||
// Store name
|
||||
const PAYMENT_STORE = 'wc/paypal/payment';
|
||||
|
||||
// Track original states of dependent methods
|
||||
const originalStates = {};
|
||||
|
||||
/**
|
||||
* Initialize payment method dependency synchronization
|
||||
*/
|
||||
export const initPaymentDependencySync = () => {
|
||||
let previousPaymentState = null;
|
||||
let isProcessing = false;
|
||||
|
||||
const unsubscribe = subscribe( () => {
|
||||
if ( isProcessing ) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProcessing = true;
|
||||
|
||||
try {
|
||||
const paymentHooks = select( PAYMENT_STORE );
|
||||
if ( ! paymentHooks ) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const methods = paymentHooks.persistentData();
|
||||
if ( ! methods ) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! previousPaymentState ) {
|
||||
previousPaymentState = { ...methods };
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const changedMethods = Object.keys( methods )
|
||||
.filter(
|
||||
( key ) =>
|
||||
key !== '__meta' &&
|
||||
methods[ key ] &&
|
||||
previousPaymentState[ key ]
|
||||
)
|
||||
.filter(
|
||||
( methodId ) =>
|
||||
methods[ methodId ].enabled !==
|
||||
previousPaymentState[ methodId ].enabled
|
||||
);
|
||||
|
||||
if ( changedMethods.length > 0 ) {
|
||||
changedMethods.forEach( ( changedId ) => {
|
||||
const isNowEnabled = methods[ changedId ].enabled;
|
||||
|
||||
const dependents = Object.entries( methods )
|
||||
.filter(
|
||||
( [ key, method ] ) =>
|
||||
key !== '__meta' &&
|
||||
method &&
|
||||
method.depends_on &&
|
||||
method.depends_on.includes( changedId )
|
||||
)
|
||||
.map( ( [ key ] ) => key );
|
||||
|
||||
if ( dependents.length > 0 ) {
|
||||
if ( ! isNowEnabled ) {
|
||||
handleDisableDependents( dependents, methods );
|
||||
} else {
|
||||
handleRestoreDependents( dependents, methods );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
previousPaymentState = { ...methods };
|
||||
} catch ( error ) {
|
||||
// Keep error handling without the console.error
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
}
|
||||
} );
|
||||
|
||||
return unsubscribe;
|
||||
};
|
||||
|
||||
const handleDisableDependents = ( dependentIds, methods ) => {
|
||||
dependentIds.forEach( ( methodId ) => {
|
||||
if ( methods[ methodId ] ) {
|
||||
if ( ! ( methodId in originalStates ) ) {
|
||||
originalStates[ methodId ] = methods[ methodId ].enabled;
|
||||
}
|
||||
methods[ methodId ].enabled = false;
|
||||
methods[ methodId ].isDisabled = true;
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
const handleRestoreDependents = ( dependentIds, methods ) => {
|
||||
dependentIds.forEach( ( methodId ) => {
|
||||
if (
|
||||
methods[ methodId ] &&
|
||||
methodId in originalStates &&
|
||||
checkAllDependenciesSatisfied( methodId, methods )
|
||||
) {
|
||||
methods[ methodId ].enabled = originalStates[ methodId ];
|
||||
methods[ methodId ].isDisabled = false;
|
||||
delete originalStates[ methodId ];
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
const checkAllDependenciesSatisfied = ( methodId, methods ) => {
|
||||
const method = methods[ methodId ];
|
||||
if ( ! method || ! method.depends_on ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! method.depends_on.some( ( parentId ) => {
|
||||
const parent = methods[ parentId ];
|
||||
return ! parent || parent.enabled === false;
|
||||
} );
|
||||
};
|
|
@ -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',
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Gets the display name for a parent payment method
|
||||
*
|
||||
* @param {string} parentId - ID of the parent payment method
|
||||
* @param {Object} methodsMap - Map of all payment methods by ID
|
||||
* @return {string} The display name to use for the parent method
|
||||
*/
|
||||
const getParentMethodName = ( parentId, methodsMap ) => {
|
||||
const parentMethod = methodsMap[ parentId ];
|
||||
return parentMethod
|
||||
? parentMethod.itemTitle || parentMethod.title || ''
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds disabled parent dependencies for a method
|
||||
*
|
||||
* @param {Object} method - The payment method to check
|
||||
* @param {Object} methodsMap - Map of all payment methods by ID
|
||||
* @return {Array} List of disabled parent IDs, empty if none
|
||||
*/
|
||||
const findDisabledParents = ( method, methodsMap ) => {
|
||||
if ( ! method.depends_on?.length && ! method._disabledByDependency ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parents = method.depends_on || [];
|
||||
|
||||
return parents.filter( ( parentId ) => {
|
||||
const parent = methodsMap[ parentId ];
|
||||
return parent && ! parent.enabled;
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook to handle payment method dependencies
|
||||
*
|
||||
* @param {Array} methods - List of payment methods
|
||||
* @param {Object} methodsMap - Map of payment methods by ID
|
||||
* @return {Object} Dependency state object with methods that should be disabled
|
||||
*/
|
||||
const usePaymentDependencyState = ( methods, methodsMap ) => {
|
||||
return useSelect(
|
||||
( select ) => {
|
||||
const paymentStore = select( 'wc/paypal/payment' );
|
||||
if ( ! paymentStore ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const result = {};
|
||||
|
||||
methods.forEach( ( method ) => {
|
||||
const disabledParents = findDisabledParents(
|
||||
method,
|
||||
methodsMap
|
||||
);
|
||||
|
||||
if ( disabledParents.length > 0 ) {
|
||||
const parentId = disabledParents[ 0 ];
|
||||
const parentName = getParentMethodName(
|
||||
parentId,
|
||||
methodsMap
|
||||
);
|
||||
|
||||
result[ method.id ] = {
|
||||
isDisabled: true,
|
||||
parentId,
|
||||
parentName,
|
||||
};
|
||||
}
|
||||
} );
|
||||
|
||||
return result;
|
||||
},
|
||||
[ methods, methodsMap ]
|
||||
);
|
||||
};
|
||||
|
||||
export default usePaymentDependencyState;
|
|
@ -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 };
|
||||
};
|
76
modules/ppcp-settings/resources/js/hooks/useStoreManager.js
Normal file
76
modules/ppcp-settings/resources/js/hooks/useStoreManager.js
Normal file
|
@ -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,
|
||||
};
|
||||
};
|
|
@ -24,7 +24,7 @@ export const learnMoreLinks = {
|
|||
'https://www.paypal.com/uk/business/paypal-business-fees',
|
||||
PayPalCheckout:
|
||||
'https://www.paypal.com/uk/business/accept-payments/checkout',
|
||||
PayLater:
|
||||
PayInThree:
|
||||
'https://www.paypal.com/uk/business/accept-payments/checkout/installments',
|
||||
},
|
||||
FR: {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Scroll to a specific element and highlight it
|
||||
*
|
||||
* @param {string} elementId - ID of the element to scroll to
|
||||
* @param {boolean} [highlight=true] - Whether to highlight the element
|
||||
* @return {Promise} - Resolves when scroll and highlight are complete
|
||||
*/
|
||||
export const scrollAndHighlight = ( elementId, highlight = true ) => {
|
||||
return new Promise( ( resolve ) => {
|
||||
const scrollTarget = document.getElementById( elementId );
|
||||
|
||||
if ( scrollTarget ) {
|
||||
const navContainer = document.querySelector(
|
||||
'.ppcp-r-navigation-container'
|
||||
);
|
||||
const navHeight = navContainer ? navContainer.offsetHeight : 0;
|
||||
|
||||
// Get the current scroll position and element's position relative to viewport
|
||||
const rect = scrollTarget.getBoundingClientRect();
|
||||
|
||||
// Calculate the final position with offset
|
||||
const scrollPosition =
|
||||
rect.top + window.scrollY - ( navHeight + 55 );
|
||||
|
||||
window.scrollTo( {
|
||||
top: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
} );
|
||||
|
||||
// Add highlight if requested
|
||||
if ( highlight ) {
|
||||
scrollTarget.classList.add( 'ppcp-highlight' );
|
||||
|
||||
// Remove highlight after animation
|
||||
setTimeout( () => {
|
||||
scrollTarget.classList.remove( 'ppcp-highlight' );
|
||||
}, 2000 );
|
||||
}
|
||||
|
||||
// Resolve after scroll animation
|
||||
setTimeout( resolve, 300 );
|
||||
} else {
|
||||
console.error(
|
||||
`Failed to scroll: Element with ID "${ elementId }" not found`
|
||||
);
|
||||
resolve();
|
||||
}
|
||||
} );
|
||||
};
|
|
@ -7,6 +7,8 @@ export const TAB_IDS = {
|
|||
PAY_LATER_MESSAGING: 'tab-panel-0-pay-later-messaging',
|
||||
};
|
||||
|
||||
import { scrollAndHighlight } from './scrollAndHighlight';
|
||||
|
||||
/**
|
||||
* Select a tab by simulating a click event and scroll to specified element,
|
||||
* accounting for navigation container height
|
||||
|
@ -23,40 +25,8 @@ export const selectTab = ( tabId, scrollToId ) => {
|
|||
if ( tab ) {
|
||||
tab.click();
|
||||
setTimeout( () => {
|
||||
const scrollTarget = scrollToId
|
||||
? document.getElementById( scrollToId )
|
||||
: document.getElementById( 'ppcp-settings-container' );
|
||||
|
||||
if ( scrollTarget ) {
|
||||
const navContainer = document.querySelector(
|
||||
'.ppcp-r-navigation-container'
|
||||
);
|
||||
const navHeight = navContainer
|
||||
? navContainer.offsetHeight
|
||||
: 0;
|
||||
|
||||
// Get the current scroll position and element's position relative to viewport
|
||||
const rect = scrollTarget.getBoundingClientRect();
|
||||
|
||||
// Calculate the final position with offset
|
||||
const scrollPosition =
|
||||
rect.top + window.scrollY - ( navHeight + 55 );
|
||||
|
||||
window.scrollTo( {
|
||||
top: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
} );
|
||||
|
||||
// Resolve after scroll animation
|
||||
setTimeout( resolve, 300 );
|
||||
} else {
|
||||
console.error(
|
||||
`Failed to scroll: Element with ID "${
|
||||
scrollToId || 'ppcp-settings-container'
|
||||
}" not found`
|
||||
);
|
||||
resolve();
|
||||
}
|
||||
const targetId = scrollToId || 'ppcp-settings-container';
|
||||
scrollAndHighlight( targetId, false ).then( resolve );
|
||||
}, 100 );
|
||||
} else {
|
||||
console.error(
|
||||
|
|
|
@ -10,9 +10,10 @@ declare( strict_types = 1 );
|
|||
namespace WooCommerce\PayPalCommerce\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Settings\Ajax\SwitchSettingsUiEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\Definition\FeaturesDefinition;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\FeaturesSettings;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDependenciesDefinition;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\OnboardingProfile;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\PaymentSettings;
|
||||
|
@ -22,14 +23,12 @@ 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\FeaturesRestEndpoint;
|
||||
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;
|
||||
|
@ -40,6 +39,7 @@ use WooCommerce\PayPalCommerce\Settings\Service\ConnectionUrlGenerator;
|
|||
use WooCommerce\PayPalCommerce\Settings\Service\FeaturesEligibilityService;
|
||||
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;
|
||||
|
@ -47,9 +47,11 @@ 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 {
|
||||
'settings.url' => static function ( ContainerInterface $container ) : string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
|
@ -60,7 +62,7 @@ return array(
|
|||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'settings.data.onboarding' => static function ( ContainerInterface $container ) : OnboardingProfile {
|
||||
'settings.data.onboarding' => static function ( ContainerInterface $container ) : OnboardingProfile {
|
||||
$can_use_casual_selling = $container->get( 'settings.casual-selling.eligible' );
|
||||
$can_use_vaulting = $container->has( 'save-payment-methods.eligible' ) && $container->get( 'save-payment-methods.eligible' );
|
||||
$can_use_card_payments = $container->has( 'card-fields.eligible' ) && $container->get( 'card-fields.eligible' );
|
||||
|
@ -80,27 +82,27 @@ return array(
|
|||
$can_use_subscriptions
|
||||
);
|
||||
},
|
||||
'settings.data.general' => static function ( ContainerInterface $container ) : GeneralSettings {
|
||||
'settings.data.general' => static function ( ContainerInterface $container ) : GeneralSettings {
|
||||
return new GeneralSettings(
|
||||
$container->get( 'api.shop.country' ),
|
||||
$container->get( 'api.shop.currency.getter' )->get(),
|
||||
$container->get( 'wcgateway.is-send-only-country' )
|
||||
);
|
||||
},
|
||||
'settings.data.styling' => static function ( ContainerInterface $container ) : StylingSettings {
|
||||
'settings.data.styling' => static function ( ContainerInterface $container ) : StylingSettings {
|
||||
return new StylingSettings(
|
||||
$container->get( 'settings.service.sanitizer' )
|
||||
);
|
||||
},
|
||||
'settings.data.payment' => static function ( ContainerInterface $container ) : PaymentSettings {
|
||||
'settings.data.payment' => static function ( ContainerInterface $container ) : PaymentSettings {
|
||||
return new PaymentSettings();
|
||||
},
|
||||
'settings.data.settings' => static function ( ContainerInterface $container ) : SettingsModel {
|
||||
'settings.data.settings' => static function ( ContainerInterface $container ) : SettingsModel {
|
||||
return new SettingsModel(
|
||||
$container->get( 'settings.service.sanitizer' )
|
||||
);
|
||||
},
|
||||
'settings.data.paylater-messaging' => static function ( ContainerInterface $container ) : array {
|
||||
'settings.data.paylater-messaging' => static function ( ContainerInterface $container ) : array {
|
||||
// TODO: Create an AbstractDataModel wrapper for this configuration!
|
||||
|
||||
$config_factors = $container->get( 'paylater-configurator.factory.config' );
|
||||
|
@ -120,69 +122,107 @@ 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.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint {
|
||||
'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' ) );
|
||||
},
|
||||
'settings.rest.common' => static function ( ContainerInterface $container ) : CommonRestEndpoint {
|
||||
'settings.rest.common' => static function ( ContainerInterface $container ) : CommonRestEndpoint {
|
||||
return new CommonRestEndpoint( $container->get( 'settings.data.general' ) );
|
||||
},
|
||||
'settings.rest.payment' => static function ( ContainerInterface $container ) : PaymentRestEndpoint {
|
||||
'settings.rest.payment' => static function ( ContainerInterface $container ) : PaymentRestEndpoint {
|
||||
return new PaymentRestEndpoint(
|
||||
$container->get( 'settings.data.payment' ),
|
||||
$container->get( 'settings.data.definition.methods' )
|
||||
);
|
||||
},
|
||||
'settings.rest.styling' => static function ( ContainerInterface $container ) : StylingRestEndpoint {
|
||||
'settings.rest.styling' => static function ( ContainerInterface $container ) : StylingRestEndpoint {
|
||||
return new StylingRestEndpoint(
|
||||
$container->get( 'settings.data.styling' ),
|
||||
$container->get( 'settings.service.sanitizer' )
|
||||
);
|
||||
},
|
||||
'settings.rest.refresh_feature_status' => static function ( ContainerInterface $container ) : RefreshFeatureStatusEndpoint {
|
||||
'settings.rest.refresh_feature_status' => static function ( ContainerInterface $container ) : RefreshFeatureStatusEndpoint {
|
||||
return new RefreshFeatureStatusEndpoint(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
new Cache( 'ppcp-timeout' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'settings.rest.authentication' => static function ( ContainerInterface $container ) : AuthenticationRestEndpoint {
|
||||
'settings.rest.authentication' => static function ( ContainerInterface $container ) : AuthenticationRestEndpoint {
|
||||
return new AuthenticationRestEndpoint(
|
||||
$container->get( 'settings.service.authentication_manager' ),
|
||||
$container->get( 'settings.service.data-manager' )
|
||||
);
|
||||
},
|
||||
'settings.rest.login_link' => static function ( ContainerInterface $container ) : LoginLinkRestEndpoint {
|
||||
'settings.rest.login_link' => static function ( ContainerInterface $container ) : LoginLinkRestEndpoint {
|
||||
return new LoginLinkRestEndpoint(
|
||||
$container->get( 'settings.service.connection-url-generator' ),
|
||||
);
|
||||
},
|
||||
'settings.rest.webhooks' => static function ( ContainerInterface $container ) : WebhookSettingsEndpoint {
|
||||
'settings.rest.webhooks' => static function ( ContainerInterface $container ) : WebhookSettingsEndpoint {
|
||||
return new WebhookSettingsEndpoint(
|
||||
$container->get( 'api.endpoint.webhook' ),
|
||||
$container->get( 'webhook.registrar' ),
|
||||
$container->get( 'webhook.status.simulation' )
|
||||
);
|
||||
},
|
||||
'settings.rest.pay_later_messaging' => static function ( ContainerInterface $container ) : PayLaterMessagingEndpoint {
|
||||
'settings.rest.pay_later_messaging' => static function ( ContainerInterface $container ) : PayLaterMessagingEndpoint {
|
||||
return new PayLaterMessagingEndpoint(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'paylater-configurator.endpoint.save-config' )
|
||||
);
|
||||
},
|
||||
'settings.rest.settings' => static function ( ContainerInterface $container ) : SettingsRestEndpoint {
|
||||
'settings.rest.settings' => static function ( ContainerInterface $container ) : SettingsRestEndpoint {
|
||||
return new SettingsRestEndpoint(
|
||||
$container->get( 'settings.data.settings' )
|
||||
);
|
||||
},
|
||||
'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'AR',
|
||||
'AU',
|
||||
|
@ -232,13 +272,13 @@ return array(
|
|||
'VN',
|
||||
);
|
||||
},
|
||||
'settings.casual-selling.eligible' => static function ( ContainerInterface $container ) : bool {
|
||||
'settings.casual-selling.eligible' => static function ( ContainerInterface $container ) : bool {
|
||||
$country = $container->get( 'api.shop.country' );
|
||||
$eligible_countries = $container->get( 'settings.casual-selling.supported-countries' );
|
||||
|
||||
return in_array( $country, $eligible_countries, true );
|
||||
},
|
||||
'settings.handler.connection-listener' => static function ( ContainerInterface $container ) : ConnectionListener {
|
||||
'settings.handler.connection-listener' => static function ( ContainerInterface $container ) : ConnectionListener {
|
||||
$page_id = $container->has( 'wcgateway.current-ppcp-settings-page-id' ) ? $container->get( 'wcgateway.current-ppcp-settings-page-id' ) : '';
|
||||
|
||||
return new ConnectionListener(
|
||||
|
@ -249,16 +289,16 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'settings.service.signup-link-cache' => static function ( ContainerInterface $container ) : Cache {
|
||||
'settings.service.signup-link-cache' => static function ( ContainerInterface $container ) : Cache {
|
||||
return new Cache( 'ppcp-paypal-signup-link' );
|
||||
},
|
||||
'settings.service.onboarding-url-manager' => static function ( ContainerInterface $container ) : OnboardingUrlManager {
|
||||
'settings.service.onboarding-url-manager' => static function ( ContainerInterface $container ) : OnboardingUrlManager {
|
||||
return new OnboardingUrlManager(
|
||||
$container->get( 'settings.service.signup-link-cache' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'settings.service.connection-url-generator' => static function ( ContainerInterface $container ) : ConnectionUrlGenerator {
|
||||
'settings.service.connection-url-generator' => static function ( ContainerInterface $container ) : ConnectionUrlGenerator {
|
||||
return new ConnectionUrlGenerator(
|
||||
$container->get( 'api.env.endpoint.partner-referrals' ),
|
||||
$container->get( 'api.repository.partner-referrals-data' ),
|
||||
|
@ -266,19 +306,21 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'settings.service.authentication_manager' => static function ( ContainerInterface $container ) : AuthenticationManager {
|
||||
'settings.service.authentication_manager' => static function ( ContainerInterface $container ) : AuthenticationManager {
|
||||
return new AuthenticationManager(
|
||||
$container->get( 'settings.data.general' ),
|
||||
$container->get( 'api.env.paypal-host' ),
|
||||
$container->get( 'api.env.endpoint.login-seller' ),
|
||||
$container->get( 'api.repository.partner-referrals-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'settings.connection-state' ),
|
||||
$container->get( 'api.endpoint.partners' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'settings.service.sanitizer' => static function ( ContainerInterface $container ) : DataSanitizer {
|
||||
'settings.service.sanitizer' => static function ( ContainerInterface $container ) : DataSanitizer {
|
||||
return new DataSanitizer();
|
||||
},
|
||||
'settings.service.data-manager' => static function ( ContainerInterface $container ) : SettingsDataManager {
|
||||
'settings.service.data-manager' => static function ( ContainerInterface $container ) : SettingsDataManager {
|
||||
return new SettingsDataManager(
|
||||
$container->get( 'settings.data.definition.methods' ),
|
||||
$container->get( 'settings.data.onboarding' ),
|
||||
|
@ -290,7 +332,7 @@ return array(
|
|||
$container->get( 'settings.data.todos' ),
|
||||
);
|
||||
},
|
||||
'settings.ajax.switch_ui' => static function ( ContainerInterface $container ) : SwitchSettingsUiEndpoint {
|
||||
'settings.ajax.switch_ui' => static function ( ContainerInterface $container ) : SwitchSettingsUiEndpoint {
|
||||
return new SwitchSettingsUiEndpoint(
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'button.request-data' ),
|
||||
|
@ -298,38 +340,36 @@ return array(
|
|||
$container->get( 'api.merchant_id' ) !== ''
|
||||
);
|
||||
},
|
||||
'settings.rest.todos' => static function ( ContainerInterface $container ) : TodosRestEndpoint {
|
||||
'settings.rest.todos' => static function ( ContainerInterface $container ) : TodosRestEndpoint {
|
||||
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 {
|
||||
'settings.data.todos' => static function ( ContainerInterface $container ) : TodosModel {
|
||||
return new TodosModel();
|
||||
},
|
||||
'settings.data.definition.todos' => static function ( ContainerInterface $container ) : TodosDefinition {
|
||||
'settings.data.definition.todos' => static function ( ContainerInterface $container ) : TodosDefinition {
|
||||
return new TodosDefinition(
|
||||
$container->get( 'settings.service.todos_eligibilities' ),
|
||||
$container->get( 'settings.data.general' )
|
||||
);
|
||||
},
|
||||
'settings.data.definition.methods' => static function ( ContainerInterface $container ) : PaymentMethodsDefinition {
|
||||
'settings.data.definition.methods' => static function ( ContainerInterface $container ) : PaymentMethodsDefinition {
|
||||
return new PaymentMethodsDefinition(
|
||||
$container->get( 'settings.data.payment' ),
|
||||
$container->get( 'settings.data.definition.method_dependencies' )
|
||||
);
|
||||
},
|
||||
'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.data.definition.method_dependencies' => static function ( ContainerInterface $container ) : PaymentMethodsDependenciesDefinition {
|
||||
return new PaymentMethodsDependenciesDefinition();
|
||||
},
|
||||
'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',
|
||||
|
@ -340,16 +380,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,
|
||||
|
@ -357,25 +422,61 @@ 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 three main factors:
|
||||
* 1. $container->get( 'x.eligible' ) - Module based eligibility check, usually whether the WooCommerce store is using a supported country/currency matrix.
|
||||
* 2. $capabilities - Whether the merchant is eligible for specific features on their PayPal account.
|
||||
* 3. $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_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.
|
||||
$container->get( 'axo.eligible' ) && $capabilities['acdc'] && ! $gateways['axo'], // Enable Fastlane.
|
||||
$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.
|
||||
$container->get( 'applepay.eligible' ) && $capabilities['acdc'] && ! $capabilities['apple_pay'], // Add Apple Pay to your account.
|
||||
$container->get( 'googlepay.eligible' ) && $capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account.
|
||||
$container->get( 'applepay.eligible' ) && $capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay.
|
||||
$container->get( 'googlepay.eligible' ) && $capabilities['google_pay'] && ! $gateways['google_pay'],
|
||||
);
|
||||
},
|
||||
'settings.rest.features' => static function ( ContainerInterface $container ) : FeaturesRestEndpoint {
|
||||
|
@ -385,12 +486,6 @@ return array(
|
|||
);
|
||||
},
|
||||
'settings.data.definition.features' => static function ( ContainerInterface $container ) : FeaturesDefinition {
|
||||
return new FeaturesDefinition(
|
||||
$container->get( 'settings.service.features_eligibilities' ),
|
||||
$container->get( 'settings.data.general' )
|
||||
);
|
||||
},
|
||||
'settings.service.features_eligibilities' => static function( ContainerInterface $container ): FeaturesEligibilityService {
|
||||
$features = apply_filters(
|
||||
'woocommerce_paypal_payments_rest_common_merchant_features',
|
||||
array()
|
||||
|
@ -403,8 +498,7 @@ return array(
|
|||
$gateways = array(
|
||||
'card-button' => $settings['data']['ppcp-card-button-gateway']['enabled'] ?? false,
|
||||
);
|
||||
|
||||
// Merchant eligibility.
|
||||
// Merchant capabilities, serve to show active or inactive badge and buttons.
|
||||
$capabilities = array(
|
||||
'apple_pay' => $features['apple_pay']['enabled'] ?? false,
|
||||
'google_pay' => $features['google_pay']['enabled'] ?? false,
|
||||
|
@ -413,14 +507,37 @@ return array(
|
|||
'apm' => $features['alternative_payment_methods']['enabled'] ?? false,
|
||||
'paylater' => $features['pay_later_messaging']['enabled'] ?? false,
|
||||
);
|
||||
$merchant_capabilities = array(
|
||||
'save_paypal' => $capabilities['save_paypal'], // Save PayPal and Venmo eligibility.
|
||||
'acdc' => $capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility.
|
||||
'apm' => $capabilities['apm'], // Alternative payment methods eligibility.
|
||||
'google_pay' => $capabilities['acdc'] && ! $capabilities['google_pay'], // Google Pay eligibility.
|
||||
'apple_pay' => $capabilities['acdc'] && ! $capabilities['apple_pay'], // Apple Pay eligibility.
|
||||
'pay_later' => $capabilities['paylater'],
|
||||
);
|
||||
return new FeaturesDefinition(
|
||||
$container->get( 'settings.service.features_eligibilities' ),
|
||||
$container->get( 'settings.data.general' ),
|
||||
$merchant_capabilities
|
||||
);
|
||||
},
|
||||
'settings.service.features_eligibilities' => static function( ContainerInterface $container ): FeaturesEligibilityService {
|
||||
|
||||
$messages_apply = $container->get( 'button.helper.messages-apply' );
|
||||
assert( $messages_apply instanceof MessagesApply );
|
||||
$pay_later_eligible = $messages_apply->for_country();
|
||||
|
||||
$merchant_country = $container->get( 'api.shop.country' );
|
||||
$ineligible_countries = array( 'RU', 'BR', 'JP' );
|
||||
$apm_eligible = ! in_array( $merchant_country, $ineligible_countries, true );
|
||||
|
||||
return new FeaturesEligibilityService(
|
||||
$capabilities['save_paypal'], // Save PayPal and Venmo eligibility.
|
||||
$capabilities['acdc'] && ! $gateways['card-button'], // Advanced credit and debit cards eligibility.
|
||||
$capabilities['apm'], // Alternative payment methods eligibility.
|
||||
$capabilities['acdc'] && $capabilities['google_pay'], // Google Pay eligibility.
|
||||
$capabilities['acdc'] && $capabilities['apple_pay'], // Apple Pay eligibility.
|
||||
$capabilities['paylater'], // Pay Later eligibility.
|
||||
$container->get( 'save-payment-methods.eligible' ), // Save PayPal and Venmo eligibility.
|
||||
$container->get( 'card-fields.eligible' ), // Advanced credit and debit cards eligibility.
|
||||
$apm_eligible, // Alternative payment methods eligibility.
|
||||
$container->get( 'googlepay.eligible' ), // Google Pay eligibility.
|
||||
$container->get( 'applepay.eligible' ), // Apple Pay eligibility.
|
||||
$pay_later_eligible, // Pay Later eligibility.
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -52,6 +52,13 @@ class MerchantConnectionDTO {
|
|||
*/
|
||||
public string $merchant_email = '';
|
||||
|
||||
/**
|
||||
* Merchant's country.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $merchant_country = '';
|
||||
|
||||
/**
|
||||
* Whether the merchant is a business or personal account.
|
||||
* Possible values: ['business'|'personal'|'unknown']
|
||||
|
@ -68,6 +75,7 @@ class MerchantConnectionDTO {
|
|||
* @param string $client_secret API client secret.
|
||||
* @param string $merchant_id PayPal's 13-character merchant ID.
|
||||
* @param string $merchant_email Email address of the merchant account.
|
||||
* @param string $merchant_country Merchant's country.
|
||||
* @param string $seller_type Whether the merchant is a business or personal account.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -76,13 +84,15 @@ class MerchantConnectionDTO {
|
|||
string $client_secret,
|
||||
string $merchant_id,
|
||||
string $merchant_email,
|
||||
string $merchant_country,
|
||||
string $seller_type = SellerTypeEnum::UNKNOWN
|
||||
) {
|
||||
$this->is_sandbox = $is_sandbox;
|
||||
$this->client_id = $client_id;
|
||||
$this->client_secret = $client_secret;
|
||||
$this->merchant_id = $merchant_id;
|
||||
$this->merchant_email = $merchant_email;
|
||||
$this->seller_type = $seller_type;
|
||||
$this->is_sandbox = $is_sandbox;
|
||||
$this->client_id = $client_id;
|
||||
$this->client_secret = $client_secret;
|
||||
$this->merchant_id = $merchant_id;
|
||||
$this->merchant_email = $merchant_email;
|
||||
$this->merchant_country = $merchant_country;
|
||||
$this->seller_type = $seller_type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ abstract class AbstractDataModel {
|
|||
*/
|
||||
public function load() : void {
|
||||
$saved_data = get_option( static::OPTION_KEY, array() );
|
||||
$filtered_data = array_intersect_key( $saved_data, $this->data );
|
||||
$filtered_data = array_intersect_key( (array) $saved_data, $this->data );
|
||||
$this->data = array_merge( $this->data, $filtered_data );
|
||||
}
|
||||
|
||||
|
|
|
@ -35,18 +35,28 @@ class FeaturesDefinition {
|
|||
*/
|
||||
protected GeneralSettings $settings;
|
||||
|
||||
/**
|
||||
* The merchant capabilities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $merchant_capabilities;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FeaturesEligibilityService $eligibilities The features eligibility service.
|
||||
* @param GeneralSettings $settings The general settings service.
|
||||
* @param array $merchant_capabilities The merchant capabilities.
|
||||
*/
|
||||
public function __construct(
|
||||
FeaturesEligibilityService $eligibilities,
|
||||
GeneralSettings $settings
|
||||
GeneralSettings $settings,
|
||||
array $merchant_capabilities
|
||||
) {
|
||||
$this->eligibilities = $eligibilities;
|
||||
$this->settings = $settings;
|
||||
$this->eligibilities = $eligibilities;
|
||||
$this->settings = $settings;
|
||||
$this->merchant_capabilities = $merchant_capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +65,23 @@ class FeaturesDefinition {
|
|||
* @return array The array of feature definitions.
|
||||
*/
|
||||
public function get(): array {
|
||||
$all_features = $this->all_available_features();
|
||||
$eligible_features = array();
|
||||
$eligibility_checks = $this->eligibilities->get_eligibility_checks();
|
||||
foreach ( $all_features as $feature_key => $feature ) {
|
||||
if ( $eligibility_checks[ $feature_key ]() ) {
|
||||
$eligible_features[ $feature_key ] = $feature;
|
||||
}
|
||||
}
|
||||
return $eligible_features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available features.
|
||||
*
|
||||
* @return array[] The array of all available features.
|
||||
*/
|
||||
public function all_available_features(): array {
|
||||
$paylater_countries = array(
|
||||
'UK',
|
||||
'ES',
|
||||
|
@ -72,15 +98,14 @@ class FeaturesDefinition {
|
|||
'save_paypal_and_venmo' => array(
|
||||
'title' => __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Securely save PayPal and Venmo payment methods for subscriptions or return buyers.', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['save_paypal_and_venmo'],
|
||||
'enabled' => $this->merchant_capabilities['save_paypal'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'settings',
|
||||
'section' => 'ppcp--save-payment-methods',
|
||||
'type' => 'tab',
|
||||
'tab' => 'settings',
|
||||
),
|
||||
'showWhen' => 'enabled',
|
||||
'class' => 'small-button',
|
||||
|
@ -106,16 +131,17 @@ class FeaturesDefinition {
|
|||
'advanced_credit_and_debit_cards' => array(
|
||||
'title' => __( 'Advanced Credit and Debit Cards', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Process major credit and debit cards including Visa, Mastercard, American Express and Discover.', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['advanced_credit_and_debit_cards'],
|
||||
'enabled' => $this->merchant_capabilities['acdc'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-payments-card',
|
||||
'modal' => 'ppcp-credit-card-gateway',
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-credit-card-gateway',
|
||||
'highlight' => 'ppcp-credit-card-gateway',
|
||||
'modal' => 'ppcp-credit-card-gateway',
|
||||
),
|
||||
'showWhen' => 'enabled',
|
||||
'class' => 'small-button',
|
||||
|
@ -141,15 +167,16 @@ class FeaturesDefinition {
|
|||
'alternative_payment_methods' => array(
|
||||
'title' => __( 'Alternative Payment Methods', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Offer global, country-specific payment options for your customers.', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['alternative_payment_methods'],
|
||||
'enabled' => $this->merchant_capabilities['apm'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-alternative-payments-card',
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-alternative-payments-card',
|
||||
'highlight' => 'ppcp-alternative-payments-card',
|
||||
),
|
||||
'showWhen' => 'enabled',
|
||||
'class' => 'small-button',
|
||||
|
@ -172,16 +199,17 @@ class FeaturesDefinition {
|
|||
'google_pay' => array(
|
||||
'title' => __( 'Google Pay', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['google_pay'],
|
||||
'enabled' => $this->merchant_capabilities['google_pay'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-payments-card',
|
||||
'modal' => 'ppcp-googlepay',
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-payments-card',
|
||||
'highlight' => 'ppcp-googlepay',
|
||||
'modal' => 'ppcp-googlepay',
|
||||
),
|
||||
'showWhen' => 'enabled',
|
||||
'class' => 'small-button',
|
||||
|
@ -210,16 +238,17 @@ class FeaturesDefinition {
|
|||
'apple_pay' => array(
|
||||
'title' => __( 'Apple Pay', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Let customers pay using their Apple Pay wallet.', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['apple_pay'],
|
||||
'enabled' => $this->merchant_capabilities['apple_pay'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
'text' => __( 'Configure', 'woocommerce-paypal-payments' ),
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-payments-card',
|
||||
'modal' => 'ppcp-applepay',
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-payments-card',
|
||||
'highlight' => 'ppcp-applepay',
|
||||
'modal' => 'ppcp-applepay',
|
||||
),
|
||||
'showWhen' => 'enabled',
|
||||
'class' => 'small-button',
|
||||
|
@ -258,7 +287,7 @@ class FeaturesDefinition {
|
|||
'Let customers know they can buy now and pay later with PayPal. Adding this messaging can boost conversion rates and increase cart sizes by 39%¹, with no extra cost to you—plus, you get paid up front.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'isEligible' => $eligibility_checks['pay_later'],
|
||||
'enabled' => $this->merchant_capabilities['pay_later'],
|
||||
'buttons' => array(
|
||||
array(
|
||||
'type' => 'secondary',
|
||||
|
@ -273,7 +302,7 @@ class FeaturesDefinition {
|
|||
array(
|
||||
'type' => 'tertiary',
|
||||
'text' => __( 'Learn more', 'woocommerce-paypal-payments' ),
|
||||
'url' => "https://www.paypal.com/{$country_location}/business/accept-payments/checkout/installments",
|
||||
'url' => "https://www.paypal.com/$country_location/business/accept-payments/checkout/installments",
|
||||
'class' => 'small-button',
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* PayPal Commerce Todos Definitions
|
||||
* Payment Methods Definitions
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Data\Definition
|
||||
*/
|
||||
|
@ -40,6 +40,13 @@ class PaymentMethodsDefinition {
|
|||
*/
|
||||
private PaymentSettings $settings;
|
||||
|
||||
/**
|
||||
* Payment method dependencies definition.
|
||||
*
|
||||
* @var PaymentMethodsDependenciesDefinition
|
||||
*/
|
||||
private PaymentMethodsDependenciesDefinition $dependencies_definition;
|
||||
|
||||
/**
|
||||
* List of WooCommerce payment gateways.
|
||||
*
|
||||
|
@ -50,10 +57,15 @@ class PaymentMethodsDefinition {
|
|||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param PaymentSettings $settings Payment methods data model.
|
||||
* @param PaymentSettings $settings Payment methods data model.
|
||||
* @param PaymentMethodsDependenciesDefinition $dependencies_definition Payment dependencies definition.
|
||||
*/
|
||||
public function __construct( PaymentSettings $settings ) {
|
||||
$this->settings = $settings;
|
||||
public function __construct(
|
||||
PaymentSettings $settings,
|
||||
PaymentMethodsDependenciesDefinition $dependencies_definition
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->dependencies_definition = $dependencies_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,15 +85,30 @@ class PaymentMethodsDefinition {
|
|||
|
||||
$result = array();
|
||||
foreach ( $all_methods as $method ) {
|
||||
$result[ $method['id'] ] = $this->build_method_definition(
|
||||
$method['id'],
|
||||
$method_id = $method['id'];
|
||||
|
||||
// Add dependency info if applicable.
|
||||
$depends_on = $this->dependencies_definition->get_parent_methods( $method_id );
|
||||
if ( ! empty( $depends_on ) ) {
|
||||
$method['depends_on'] = $depends_on;
|
||||
}
|
||||
|
||||
$result[ $method_id ] = $this->build_method_definition(
|
||||
$method_id,
|
||||
$method['title'],
|
||||
$method['description'],
|
||||
$method['icon'],
|
||||
$method['fields'] ?? array()
|
||||
$method['fields'] ?? array(),
|
||||
$depends_on
|
||||
);
|
||||
}
|
||||
|
||||
// Add dependency maps to metadata.
|
||||
$result['__meta'] = array(
|
||||
'dependencies' => $this->dependencies_definition->get_dependencies(),
|
||||
'dependents' => $this->dependencies_definition->get_dependents_map(),
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -95,14 +122,15 @@ class PaymentMethodsDefinition {
|
|||
* @param string $icon Admin-side icon of the payment method.
|
||||
* @param array|false $fields Optional. Additional fields to display in the edit modal.
|
||||
* Setting this to false omits all fields.
|
||||
* @param array $depends_on Optional. IDs of payment methods that this depends on.
|
||||
* @return array Payment method definition.
|
||||
*/
|
||||
private function build_method_definition(
|
||||
string $gateway_id,
|
||||
string $title,
|
||||
string $description,
|
||||
string $icon,
|
||||
$fields = array()
|
||||
string $icon, $fields = array(),
|
||||
array $depends_on = array()
|
||||
) : array {
|
||||
$gateway = $this->wc_gateways[ $gateway_id ] ?? null;
|
||||
|
||||
|
@ -119,6 +147,11 @@ class PaymentMethodsDefinition {
|
|||
'itemDescription' => $description,
|
||||
);
|
||||
|
||||
// Add dependency information if provided - ensure it's included directly in the config.
|
||||
if ( ! empty( $depends_on ) ) {
|
||||
$config['depends_on'] = $depends_on;
|
||||
}
|
||||
|
||||
if ( is_array( $fields ) ) {
|
||||
$config['fields'] = array_merge(
|
||||
array(
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/**
|
||||
* Payment Methods Dependencies Definition
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Data\Definition
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Settings\Data\Definition;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\BancontactGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\BlikGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\EPSGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\IDealGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\MultibancoGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\MyBankGateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\P24Gateway;
|
||||
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\TrustlyGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodsDependenciesDefinition
|
||||
*
|
||||
* Defines dependency relationships between payment methods.
|
||||
*/
|
||||
class PaymentMethodsDependenciesDefinition {
|
||||
|
||||
/**
|
||||
* Get all payment method dependencies
|
||||
*
|
||||
* Maps dependent method ID => array of parent method IDs.
|
||||
* A dependent method is disabled if ANY of its required parents is disabled.
|
||||
*
|
||||
* @return array The dependency relationships between payment methods
|
||||
*/
|
||||
public function get_dependencies(): array {
|
||||
$dependencies = array(
|
||||
CardButtonGateway::ID => array( PayPalGateway::ID ),
|
||||
CreditCardGateway::ID => array( PayPalGateway::ID ),
|
||||
AxoGateway::ID => array( PayPalGateway::ID, CreditCardGateway::ID ),
|
||||
ApplePayGateway::ID => array( PayPalGateway::ID, CreditCardGateway::ID ),
|
||||
GooglePayGateway::ID => array( PayPalGateway::ID, CreditCardGateway::ID ),
|
||||
BancontactGateway::ID => array( PayPalGateway::ID ),
|
||||
BlikGateway::ID => array( PayPalGateway::ID ),
|
||||
EPSGateway::ID => array( PayPalGateway::ID ),
|
||||
IDealGateway::ID => array( PayPalGateway::ID ),
|
||||
MultibancoGateway::ID => array( PayPalGateway::ID ),
|
||||
MyBankGateway::ID => array( PayPalGateway::ID ),
|
||||
P24Gateway::ID => array( PayPalGateway::ID ),
|
||||
TrustlyGateway::ID => array( PayPalGateway::ID ),
|
||||
PayUponInvoiceGateway::ID => array( PayPalGateway::ID ),
|
||||
OXXO::ID => array( PayPalGateway::ID ),
|
||||
'venmo' => array( PayPalGateway::ID ),
|
||||
'pay-later' => array( PayPalGateway::ID ),
|
||||
);
|
||||
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_payment_method_dependencies',
|
||||
$dependencies
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mapping from parent methods to their dependent methods
|
||||
*
|
||||
* @return array Parent-to-child dependency map
|
||||
*/
|
||||
public function get_dependents_map(): array {
|
||||
$result = array();
|
||||
$dependencies = $this->get_dependencies();
|
||||
|
||||
foreach ( $dependencies as $child_id => $parent_ids ) {
|
||||
foreach ( $parent_ids as $parent_id ) {
|
||||
if ( ! isset( $result[ $parent_id ] ) ) {
|
||||
$result[ $parent_id ] = array();
|
||||
}
|
||||
$result[ $parent_id ][] = $child_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all parent methods that a method depends on
|
||||
*
|
||||
* @param string $method_id Method ID to check.
|
||||
* @return array Array of parent method IDs
|
||||
*/
|
||||
public function get_parent_methods( string $method_id ): array {
|
||||
return $this->get_dependencies()[ $method_id ] ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get methods that depend on a parent method
|
||||
*
|
||||
* @param string $parent_id Parent method ID.
|
||||
* @return array Array of dependent method IDs
|
||||
*/
|
||||
public function get_dependent_methods( string $parent_id ): array {
|
||||
return $this->get_dependents_map()[ $parent_id ] ?? array();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* PayPal Commerce Todos Definitions
|
||||
* Todos Definitions
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Data\Definition
|
||||
*/
|
||||
|
@ -69,18 +69,6 @@ class TodosDefinition {
|
|||
),
|
||||
'priority' => 1,
|
||||
),
|
||||
'enable_credit_debit_cards' => array(
|
||||
'title' => __( 'Enable Credit and Debit Cards on your checkout', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Credit and Debit Cards is now available for Blocks checkout pages', 'woocommerce-paypal-payments' ),
|
||||
'isEligible' => $eligibility_checks['enable_credit_debit_cards'],
|
||||
'action' => array(
|
||||
'type' => 'tab',
|
||||
'tab' => 'payment_methods',
|
||||
'section' => 'ppcp-card-button-gateway',
|
||||
'highlight' => 'ppcp-card-button-gateway',
|
||||
),
|
||||
'priority' => 2,
|
||||
),
|
||||
'enable_pay_later_messaging' => array(
|
||||
'title' => __( 'Enable Pay Later messaging', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Show Pay Later messaging to boost conversion rate and increase cart size', 'woocommerce-paypal-payments' ),
|
||||
|
@ -126,15 +114,36 @@ 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' => 'https://woocommerce.com/document/woocommerce-paypal-payments/#paypal-subscriptions',
|
||||
'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 <x> 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',
|
||||
|
|
|
@ -74,6 +74,7 @@ class GeneralSettings extends AbstractDataModel {
|
|||
'sandbox_merchant' => false,
|
||||
'merchant_id' => '',
|
||||
'merchant_email' => '',
|
||||
'merchant_country' => '',
|
||||
'client_id' => '',
|
||||
'client_secret' => '',
|
||||
'seller_type' => 'unknown',
|
||||
|
@ -138,6 +139,7 @@ class GeneralSettings extends AbstractDataModel {
|
|||
$this->data['sandbox_merchant'] = $connection->is_sandbox;
|
||||
$this->data['merchant_id'] = sanitize_text_field( $connection->merchant_id );
|
||||
$this->data['merchant_email'] = sanitize_email( $connection->merchant_email );
|
||||
$this->data['merchant_country'] = sanitize_text_field( $connection->merchant_country );
|
||||
$this->data['client_id'] = sanitize_text_field( $connection->client_id );
|
||||
$this->data['client_secret'] = sanitize_text_field( $connection->client_secret );
|
||||
$this->data['seller_type'] = sanitize_text_field( $connection->seller_type );
|
||||
|
@ -156,6 +158,7 @@ class GeneralSettings extends AbstractDataModel {
|
|||
$this->data['client_secret'],
|
||||
$this->data['merchant_id'],
|
||||
$this->data['merchant_email'],
|
||||
$this->data['merchant_country'],
|
||||
$this->data['seller_type']
|
||||
);
|
||||
}
|
||||
|
@ -171,6 +174,7 @@ class GeneralSettings extends AbstractDataModel {
|
|||
$this->data['sandbox_merchant'] = $defaults['sandbox_merchant'];
|
||||
$this->data['merchant_id'] = $defaults['merchant_id'];
|
||||
$this->data['merchant_email'] = $defaults['merchant_email'];
|
||||
$this->data['merchant_country'] = $defaults['merchant_country'];
|
||||
$this->data['client_id'] = $defaults['client_id'];
|
||||
$this->data['client_secret'] = $defaults['client_secret'];
|
||||
$this->data['seller_type'] = $defaults['seller_type'];
|
||||
|
@ -239,4 +243,13 @@ class GeneralSettings extends AbstractDataModel {
|
|||
public function get_merchant_email() : string {
|
||||
return $this->data['merchant_email'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently connected merchant's country.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_merchant_country() : string {
|
||||
return $this->data['merchant_country'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,18 +84,9 @@ class FeaturesRestEndpoint extends RestEndpoint {
|
|||
public function get_features(): WP_REST_Response {
|
||||
$features = array();
|
||||
foreach ( $this->features_definition->get() as $id => $feature ) {
|
||||
// Evaluate eligibility check.
|
||||
if ( is_callable( $feature['isEligible'] ) ) {
|
||||
$is_eligible = $feature['isEligible']();
|
||||
} else {
|
||||
$is_eligible = (bool) $feature['isEligible'];
|
||||
}
|
||||
|
||||
// Include all features with their eligibility state.
|
||||
$features[] = array_merge(
|
||||
array( 'id' => $id ),
|
||||
array_diff_key( $feature, array( 'isEligible' => true ) ),
|
||||
array( 'isEligible' => $is_eligible )
|
||||
$feature
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,17 @@ class PaymentRestEndpoint extends RestEndpoint {
|
|||
$gateway_settings = array();
|
||||
$all_methods = $this->gateways();
|
||||
|
||||
// First extract __meta if present.
|
||||
if ( isset( $all_methods['__meta'] ) ) {
|
||||
$gateway_settings['__meta'] = $all_methods['__meta'];
|
||||
}
|
||||
|
||||
foreach ( $all_methods as $key => $method ) {
|
||||
// Skip the __meta key as we've already handled it.
|
||||
if ( $key === '__meta' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$gateway_settings[ $key ] = array(
|
||||
'id' => $method['id'],
|
||||
'title' => $method['title'],
|
||||
|
@ -164,6 +174,11 @@ class PaymentRestEndpoint extends RestEndpoint {
|
|||
if ( isset( $method['fields'] ) ) {
|
||||
$gateway_settings[ $key ]['fields'] = $method['fields'];
|
||||
}
|
||||
|
||||
// Preserve dependency information.
|
||||
if ( isset( $method['depends_on'] ) ) {
|
||||
$gateway_settings[ $key ]['depends_on'] = $method['depends_on'];
|
||||
}
|
||||
}
|
||||
|
||||
$gateway_settings['paypalShowLogo'] = $this->settings->get_paypal_show_logo();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\Settings\Service;
|
|||
use JsonException;
|
||||
use Throwable;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller;
|
||||
|
@ -24,6 +26,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 +67,47 @@ class AuthenticationManager {
|
|||
*/
|
||||
private PartnerReferralsData $referrals_data;
|
||||
|
||||
/**
|
||||
* The connection state manager.
|
||||
*
|
||||
* @var ConnectionState
|
||||
*/
|
||||
private ConnectionState $connection_state;
|
||||
|
||||
/**
|
||||
* Partners endpoint.
|
||||
*
|
||||
* @var PartnersEndpoint
|
||||
*/
|
||||
private PartnersEndpoint $partners_endpoint;
|
||||
|
||||
/**
|
||||
* 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 PartnersEndpoint $partners_endpoint Partners endpoint.
|
||||
* @param ?LoggerInterface $logger Logging instance.
|
||||
*/
|
||||
public function __construct(
|
||||
GeneralSettings $common_settings,
|
||||
EnvironmentConfig $connection_host,
|
||||
EnvironmentConfig $login_endpoint,
|
||||
PartnerReferralsData $referrals_data,
|
||||
ConnectionState $connection_state,
|
||||
PartnersEndpoint $partners_endpoint,
|
||||
?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->partners_endpoint = $partners_endpoint;
|
||||
$this->logger = $logger ?: new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,6 +135,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.
|
||||
|
@ -182,12 +208,19 @@ class AuthenticationManager {
|
|||
|
||||
$payee = $this->request_payee( $client_id, $client_secret, $use_sandbox );
|
||||
|
||||
try {
|
||||
$seller_status = $this->partners_endpoint->seller_status();
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
$seller_status = null;
|
||||
}
|
||||
|
||||
$connection = new MerchantConnectionDTO(
|
||||
$use_sandbox,
|
||||
$client_id,
|
||||
$client_secret,
|
||||
$payee['merchant_id'],
|
||||
$payee['email_address'],
|
||||
! is_null( $seller_status ) ? $seller_status->country() : '',
|
||||
SellerTypeEnum::BUSINESS
|
||||
);
|
||||
|
||||
|
@ -253,10 +286,17 @@ class AuthenticationManager {
|
|||
*/
|
||||
$connection = $this->common_settings->get_merchant_data();
|
||||
|
||||
$connection->is_sandbox = $use_sandbox;
|
||||
$connection->client_id = $credentials['client_id'];
|
||||
$connection->client_secret = $credentials['client_secret'];
|
||||
$connection->merchant_id = $credentials['merchant_id'];
|
||||
try {
|
||||
$seller_status = $this->partners_endpoint->seller_status();
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
$seller_status = null;
|
||||
}
|
||||
|
||||
$connection->is_sandbox = $use_sandbox;
|
||||
$connection->client_id = $credentials['client_id'];
|
||||
$connection->client_secret = $credentials['client_secret'];
|
||||
$connection->merchant_id = $credentials['merchant_id'];
|
||||
$connection->merchant_country = ! is_null( $seller_status ) ? $seller_status->country() : '';
|
||||
|
||||
$this->update_connection_details( $connection );
|
||||
}
|
||||
|
@ -292,6 +332,13 @@ class AuthenticationManager {
|
|||
$connection->seller_type = $seller_type;
|
||||
}
|
||||
|
||||
try {
|
||||
$seller_status = $this->partners_endpoint->seller_status();
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
$seller_status = null;
|
||||
}
|
||||
$connection->merchant_country = ! is_null( $seller_status ) ? $seller_status->country() : '';
|
||||
|
||||
$this->update_connection_details( $connection );
|
||||
}
|
||||
|
||||
|
@ -420,6 +467,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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
@ -224,10 +225,11 @@ class SettingsDataManager {
|
|||
if ( $flags->use_card_payments ) {
|
||||
// Enable ACDC for business sellers.
|
||||
$this->payment_methods->toggle_method_state( CreditCardGateway::ID, true );
|
||||
}
|
||||
|
||||
$this->payment_methods->toggle_method_state( ApplePayGateway::ID, true );
|
||||
$this->payment_methods->toggle_method_state( GooglePayGateway::ID, true );
|
||||
// Apple Pay and Google Pay depend on the ACDC gateway.
|
||||
$this->payment_methods->toggle_method_state( ApplePayGateway::ID, true );
|
||||
$this->payment_methods->toggle_method_state( GooglePayGateway::ID, true );
|
||||
}
|
||||
|
||||
// Enable all APM methods.
|
||||
foreach ( $methods_apm as $method ) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* PayPal Commerce eligibility service for WooCommerce.
|
||||
* Eligibility service for Todos.
|
||||
*
|
||||
* This file contains the TodosEligibilityService class which manages eligibility checks
|
||||
* for various PayPal Commerce features including Fastlane, card payments, Pay Later messaging,
|
||||
* for various features including Fastlane, card payments, Pay Later messaging,
|
||||
* subscriptions, Apple Pay, Google Pay, and other digital wallet features.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Service
|
||||
|
@ -24,13 +24,6 @@ class TodosEligibilityService {
|
|||
*/
|
||||
private bool $is_fastlane_eligible;
|
||||
|
||||
/**
|
||||
* Whether card payments are eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_card_payment_eligible;
|
||||
|
||||
/**
|
||||
* Whether Pay Later messaging is eligible.
|
||||
*
|
||||
|
@ -66,6 +59,27 @@ class TodosEligibilityService {
|
|||
*/
|
||||
private bool $is_subscription_eligible;
|
||||
|
||||
/**
|
||||
* Whether PayPal buttons for cart are eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_paypal_buttons_cart_eligible;
|
||||
|
||||
/**
|
||||
* Whether PayPal buttons for block checkout are eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_paypal_buttons_block_checkout_eligible;
|
||||
|
||||
/**
|
||||
* Whether PayPal buttons for product page are eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_paypal_buttons_product_eligible;
|
||||
|
||||
/**
|
||||
* Whether Apple Pay domain registration is eligible.
|
||||
*
|
||||
|
@ -94,20 +108,6 @@ class TodosEligibilityService {
|
|||
*/
|
||||
private bool $is_google_pay_eligible;
|
||||
|
||||
/**
|
||||
* Whether PayPal buttons are eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_paypal_buttons_eligible;
|
||||
|
||||
/**
|
||||
* Whether PayPal subscription configuration is eligible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $is_add_subscription_eligible;
|
||||
|
||||
/**
|
||||
* Whether enabling Apple Pay is eligible.
|
||||
*
|
||||
|
@ -126,53 +126,53 @@ class TodosEligibilityService {
|
|||
* Constructor.
|
||||
*
|
||||
* @param bool $is_fastlane_eligible Whether Fastlane is eligible.
|
||||
* @param bool $is_card_payment_eligible Whether card payments are eligible.
|
||||
* @param bool $is_pay_later_messaging_eligible Whether Pay Later messaging is eligible.
|
||||
* @param bool $is_pay_later_messaging_product_eligible Whether Pay Later messaging for product page is eligible.
|
||||
* @param bool $is_pay_later_messaging_cart_eligible Whether Pay Later messaging for cart is eligible.
|
||||
* @param bool $is_pay_later_messaging_checkout_eligible Whether Pay Later messaging for checkout is eligible.
|
||||
* @param bool $is_subscription_eligible Whether subscriptions are eligible.
|
||||
* @param bool $is_paypal_buttons_eligible Whether PayPal buttons are eligible.
|
||||
* @param bool $is_paypal_buttons_cart_eligible Whether PayPal buttons for cart are eligible.
|
||||
* @param bool $is_paypal_buttons_block_checkout_eligible Whether PayPal buttons for block checkout are eligible.
|
||||
* @param bool $is_paypal_buttons_product_eligible Whether PayPal buttons for product page are eligible.
|
||||
* @param bool $is_apple_pay_domain_eligible Whether Apple Pay domain registration is eligible.
|
||||
* @param bool $is_digital_wallet_eligible Whether digital wallet features are eligible.
|
||||
* @param bool $is_apple_pay_eligible Whether Apple Pay is eligible.
|
||||
* @param bool $is_google_pay_eligible Whether Google Pay is eligible.
|
||||
* @param bool $is_add_subscription_eligible Whether PayPal subscription configuration is eligible.
|
||||
* @param bool $is_enable_apple_pay_eligible Whether enabling Apple Pay is eligible.
|
||||
* @param bool $is_enable_google_pay_eligible Whether enabling Google Pay is eligible.
|
||||
*/
|
||||
public function __construct(
|
||||
bool $is_fastlane_eligible,
|
||||
bool $is_card_payment_eligible,
|
||||
bool $is_pay_later_messaging_eligible,
|
||||
bool $is_pay_later_messaging_product_eligible,
|
||||
bool $is_pay_later_messaging_cart_eligible,
|
||||
bool $is_pay_later_messaging_checkout_eligible,
|
||||
bool $is_subscription_eligible,
|
||||
bool $is_paypal_buttons_eligible,
|
||||
bool $is_paypal_buttons_cart_eligible,
|
||||
bool $is_paypal_buttons_block_checkout_eligible,
|
||||
bool $is_paypal_buttons_product_eligible,
|
||||
bool $is_apple_pay_domain_eligible,
|
||||
bool $is_digital_wallet_eligible,
|
||||
bool $is_apple_pay_eligible,
|
||||
bool $is_google_pay_eligible,
|
||||
bool $is_add_subscription_eligible,
|
||||
bool $is_enable_apple_pay_eligible,
|
||||
bool $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_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_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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,13 +183,14 @@ class TodosEligibilityService {
|
|||
public function get_eligibility_checks(): array {
|
||||
return array(
|
||||
'enable_fastlane' => fn() => $this->is_fastlane_eligible,
|
||||
'enable_credit_debit_cards' => fn() => $this->is_card_payment_eligible,
|
||||
'enable_pay_later_messaging' => fn() => $this->is_pay_later_messaging_eligible,
|
||||
'add_pay_later_messaging_product_page' => fn() => $this->is_pay_later_messaging_product_eligible,
|
||||
'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,
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
/**
|
||||
* Service for sorting and filtering todo items.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Service
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Settings\Service;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\TodosModel;
|
||||
|
||||
/**
|
||||
* Service class that provides todo sorting and filtering functionality.
|
||||
*/
|
||||
class TodosSortingAndFilteringService {
|
||||
|
||||
/**
|
||||
* 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',
|
||||
);
|
||||
|
||||
/**
|
||||
* Button placement todo IDs in priority order.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private const BUTTON_PLACEMENT_IDS = array(
|
||||
'add_paypal_buttons_cart',
|
||||
'add_paypal_buttons_block_checkout',
|
||||
'add_paypal_buttons_product',
|
||||
);
|
||||
|
||||
/**
|
||||
* The TodosModel instance.
|
||||
*
|
||||
* @var TodosModel
|
||||
*/
|
||||
private TodosModel $todos_model;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param TodosModel $todos_model The TodosModel instance.
|
||||
*/
|
||||
public function __construct( TodosModel $todos_model ) {
|
||||
$this->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;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ declare( strict_types = 1 );
|
|||
namespace WooCommerce\PayPalCommerce\Settings;
|
||||
|
||||
use WC_Payment_Gateway;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
|
||||
|
@ -106,7 +108,11 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
)
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'ppcp-switch-settings-ui' );
|
||||
wp_enqueue_script( 'ppcp-switch-settings-ui', '', array( 'wp-i18n' ), $script_asset_file['version'] );
|
||||
wp_set_script_translations(
|
||||
'ppcp-switch-settings-ui',
|
||||
'woocommerce-paypal-payments',
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -159,7 +165,11 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'ppcp-admin-settings' );
|
||||
wp_enqueue_script( 'ppcp-admin-settings', '', array( 'wp-i18n' ), $script_asset_file['version'] );
|
||||
wp_set_script_translations(
|
||||
'ppcp-admin-settings',
|
||||
'woocommerce-paypal-payments',
|
||||
);
|
||||
|
||||
/**
|
||||
* Require resolves.
|
||||
|
@ -198,11 +208,14 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
wp_enqueue_script(
|
||||
'ppcp-paylater-configurator-lib',
|
||||
'https://www.paypalobjects.com/merchant-library/merchant-configurator.js',
|
||||
array(),
|
||||
array( 'wp-i18n' ),
|
||||
$script_asset_file['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations(
|
||||
'ppcp-paylater-configurator-lib',
|
||||
'woocommerce-paypal-payments',
|
||||
);
|
||||
$script_data['PcpPayLaterConfigurator'] = array(
|
||||
'config' => array(),
|
||||
'merchantClientId' => $settings->get( 'client_id' ),
|
||||
|
@ -303,8 +316,7 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
|
||||
$flags = new ConfigurationFlagsDTO();
|
||||
|
||||
// TODO: Get the merchant country from PayPal here!
|
||||
$flags->country_code = 'US';
|
||||
$flags->country_code = $general_settings->get_merchant_country();
|
||||
$flags->is_business_seller = $general_settings->is_business_seller();
|
||||
$flags->use_card_payments = $onboarding_profile->get_accept_card_payments();
|
||||
$flags->use_subscriptions = in_array( ProductChoicesEnum::SUBSCRIPTIONS, $onboarding_profile->get_products(), true );
|
||||
|
@ -330,6 +342,12 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
assert( $dcc_applies instanceof DCCApplies );
|
||||
|
||||
$general_settings = $container->get( 'settings.data.general' );
|
||||
assert( $general_settings instanceof GeneralSettings );
|
||||
|
||||
$merchant_data = $general_settings->get_merchant_data();
|
||||
$merchant_country = $merchant_data->merchant_country;
|
||||
|
||||
// Unset BCDC if merchant is eligible for ACDC and country is eligible for card fields.
|
||||
$card_fields_eligible = $container->get( 'card-fields.eligible' );
|
||||
if ( $dcc_product_status->is_active() && $card_fields_eligible ) {
|
||||
|
@ -341,21 +359,31 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
unset( $payment_methods['venmo'] );
|
||||
}
|
||||
|
||||
// Unset if not eligible for Google Pay.
|
||||
if ( ! $googlepay_product_status->is_active() ) {
|
||||
// Unset if country/currency is not supported or merchant not eligible for Google Pay.
|
||||
if ( ! $container->get( 'googlepay.eligible' ) || ! $googlepay_product_status->is_active() ) {
|
||||
unset( $payment_methods['ppcp-googlepay'] );
|
||||
}
|
||||
|
||||
// Unset if not eligible for Apple Pay.
|
||||
if ( ! $applepay_product_status->is_active() ) {
|
||||
// Unset if country/currency is not supported or merchant not eligible for Apple Pay.
|
||||
if ( ! $container->get( 'applepay.eligible' ) || ! $applepay_product_status->is_active() ) {
|
||||
unset( $payment_methods['ppcp-applepay'] );
|
||||
}
|
||||
|
||||
// Unset Fastlane if store location is not United States or merchant is not eligible for ACDC.
|
||||
if ( $container->get( 'api.shop.country' ) !== 'US' || ! $dcc_product_status->is_active() ) {
|
||||
// Unset Fastlane if country/currency is not supported or merchant is not eligible for BCDC.
|
||||
if ( ! $container->get( 'axo.eligible' ) || ! $dcc_product_status->is_active() ) {
|
||||
unset( $payment_methods['ppcp-axo-gateway'] );
|
||||
}
|
||||
|
||||
// Unset OXXO if merchant country is not Mexico.
|
||||
if ( 'MX' !== $merchant_country ) {
|
||||
unset( $payment_methods[ OXXO::ID ] );
|
||||
}
|
||||
|
||||
// Unset Pay Unon Invoice if merchant country is not Germany.
|
||||
if ( 'DE' !== $merchant_country ) {
|
||||
unset( $payment_methods[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
// For non-ACDC regions unset ACDC, local APMs and set BCDC.
|
||||
if ( ! $dcc_applies ) {
|
||||
unset( $payment_methods[ CreditCardGateway::ID ] );
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue