mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Merge branch 'trunk' into PCP-4110-incorrect-subscription-cancellation-handling-with-pay-pal-subscriptions
This commit is contained in:
commit
65e1ca4eb0
259 changed files with 9482 additions and 3476 deletions
40
.env.integration
Normal file
40
.env.integration
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
PPCP_INTEGRATION_WP_DIR=${ROOT_DIR}/.ddev/wordpress
|
||||||
|
|
||||||
|
BASEURL="https://woocommerce-paypal-payments.ddev.site"
|
||||||
|
AUTHORIZATION="Bearer ABC123"
|
||||||
|
|
||||||
|
CHECKOUT_URL="/checkout"
|
||||||
|
CHECKOUT_PAGE_ID=7
|
||||||
|
CART_URL="/cart"
|
||||||
|
BLOCK_CHECKOUT_URL="/checkout-block"
|
||||||
|
BLOCK_CHECKOUT_PAGE_ID=22
|
||||||
|
BLOCK_CART_URL="/cart-block"
|
||||||
|
|
||||||
|
PRODUCT_URL="/product/prod"
|
||||||
|
PRODUCT_ID=123
|
||||||
|
|
||||||
|
SUBSCRIPTION_URL="/product/sub"
|
||||||
|
|
||||||
|
PAYPAL_SUBSCRIPTIONS_PRODUCT_ID=252
|
||||||
|
|
||||||
|
APM_ID="sofort"
|
||||||
|
|
||||||
|
WP_MERCHANT_USER="admin"
|
||||||
|
WP_MERCHANT_PASSWORD="admin"
|
||||||
|
|
||||||
|
WP_CUSTOMER_USER="customer"
|
||||||
|
WP_CUSTOMER_PASSWORD="password"
|
||||||
|
|
||||||
|
CUSTOMER_EMAIL="customer@example.com"
|
||||||
|
CUSTOMER_PASSWORD="password"
|
||||||
|
CUSTOMER_FIRST_NAME="John"
|
||||||
|
CUSTOMER_LAST_NAME="Doe"
|
||||||
|
CUSTOMER_COUNTRY="DE"
|
||||||
|
CUSTOMER_ADDRESS="street 1"
|
||||||
|
CUSTOMER_POSTCODE="12345"
|
||||||
|
CUSTOMER_CITY="city"
|
||||||
|
CUSTOMER_PHONE="1234567890"
|
||||||
|
|
||||||
|
CREDIT_CARD_NUMBER="1234567890"
|
||||||
|
CREDIT_CARD_EXPIRATION="01/2042"
|
||||||
|
CREDIT_CARD_CVV="123"
|
|
@ -1,4 +1,4 @@
|
||||||
PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress
|
PPCP_INTEGRATION_WP_DIR=${ROOT_DIR}/.ddev/wordpress
|
||||||
|
|
||||||
BASEURL="https://woocommerce-paypal-payments.ddev.site"
|
BASEURL="https://woocommerce-paypal-payments.ddev.site"
|
||||||
AUTHORIZATION="Bearer ABC123"
|
AUTHORIZATION="Bearer ABC123"
|
|
@ -1,4 +1,4 @@
|
||||||
name: e2e tests
|
name: Integration tests
|
||||||
|
|
||||||
on: workflow_dispatch
|
on: workflow_dispatch
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.4', '8.2']
|
php-versions: ['7.4']
|
||||||
wc-versions: ['6.9.4', '7.7.2']
|
wc-versions: ['9.7.1']
|
||||||
|
|
||||||
name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }}
|
name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -30,10 +30,10 @@ jobs:
|
||||||
run: ddev orchestrate -f
|
run: ddev orchestrate -f
|
||||||
|
|
||||||
- name: Create config
|
- name: Create config
|
||||||
run: cp -n .env.e2e.example .env.e2e
|
run: cp -n .env.integration.example .env.integration
|
||||||
|
|
||||||
- name: Setup tests
|
- name: Setup tests
|
||||||
run: ddev php tests/e2e/PHPUnit/setup.php
|
run: ddev php tests/integration/PHPUnit/setup.php
|
||||||
|
|
||||||
- name: Run PHPUnit
|
- name: Run PHPUnit
|
||||||
run: ddev exec phpunit -c tests/e2e/phpunit.xml.dist
|
run: ddev exec phpunit -c tests/integration/phpunit.xml.dist
|
|
@ -94,6 +94,45 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the number of times a filter has been applied during the current request.
|
||||||
|
*
|
||||||
|
* @since 6.1.0
|
||||||
|
*
|
||||||
|
* @global int[] $wp_filters Stores the number of times each filter was triggered.
|
||||||
|
*
|
||||||
|
* @param string $hook_name The name of the filter hook.
|
||||||
|
* @return int The number of times the filter hook has been applied.
|
||||||
|
*/
|
||||||
|
function did_filter( $hook_name ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not a filter hook is currently being processed.
|
||||||
|
*
|
||||||
|
* The function current_filter() only returns the most recent filter being executed.
|
||||||
|
* did_filter() returns the number of times a filter has been applied during
|
||||||
|
* the current request.
|
||||||
|
*
|
||||||
|
* This function allows detection for any filter currently being executed
|
||||||
|
* (regardless of whether it's the most recent filter to fire, in the case of
|
||||||
|
* hooks called from hook callbacks) to be verified.
|
||||||
|
*
|
||||||
|
* @since 3.9.0
|
||||||
|
*
|
||||||
|
* @see current_filter()
|
||||||
|
* @see did_filter()
|
||||||
|
* @global string[] $wp_current_filter Current filter.
|
||||||
|
*
|
||||||
|
* @param string|null $hook_name Optional. Filter hook to check. Defaults to null,
|
||||||
|
* which checks if any filter is currently being run.
|
||||||
|
* @return bool Whether the filter is currently in the stack.
|
||||||
|
*/
|
||||||
|
function doing_filter( $hook_name = null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML API: WP_HTML_Tag_Processor class
|
* HTML API: WP_HTML_Tag_Processor class
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
*** Changelog ***
|
*** Changelog ***
|
||||||
|
|
||||||
|
= 3.0.0 - 2025-03-17 =
|
||||||
|
* Enhancement - Redesigned settings UI for new users #2908
|
||||||
|
* Enhancement - Enable Fastlane by default on new store setups when eligible #3199
|
||||||
|
* Enhancement - Enable support for advanced card payments and features for Hong Kong & Singapore #3089
|
||||||
|
* Fix - Dependency conflict with more recent psr/log versions on PHP8+ #2993
|
||||||
|
* Fix - PayPal Checkout Gateway subscription migration layer not renewing subscriptions #2699
|
||||||
|
* Fix - Fatal error when gateway settings initialized too early by third-party plugin #2766
|
||||||
|
* Fix - Next Payment date for Subscriptions not updating when processing a PayPal Subscriptions renewal order #2959
|
||||||
|
* Fix - Changing the subscription payment method to ACDC triggers error #2891
|
||||||
|
* Fix - Standard Card button not appearing in standalone gateway for free trial subscription products #2935
|
||||||
|
* Fix - Validation error when using Trustly payment method #3031
|
||||||
|
* Fix - Error in continuation mode due to wrong gateway selection on Checkout block #2996
|
||||||
|
* Fix - Error in error in PayLaterConfigurator #2989
|
||||||
|
* Tweak - Removed currency requirement for Vault v3 #2919
|
||||||
|
* Tweak - Update plugin author from WooCommerce to PayPal
|
||||||
|
|
||||||
= 2.9.6 - 2025-01-06 =
|
= 2.9.6 - 2025-01-06 =
|
||||||
* Fix - NOT_ENABLED_TO_VAULT_PAYMENT_SOURCE on PayPal transactions when using ACDC Vaulting without PayPal Vault approval #2955
|
* Fix - NOT_ENABLED_TO_VAULT_PAYMENT_SOURCE on PayPal transactions when using ACDC Vaulting without PayPal Vault approval #2955
|
||||||
* Fix - Express buttons for Free Trial Subscription products on Block Cart/Checkout trigger CANNOT_BE_ZERO_OR_NEGATIVE error #2872
|
* Fix - Express buttons for Free Trial Subscription products on Block Cart/Checkout trigger CANNOT_BE_ZERO_OR_NEGATIVE error #2872
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"WooCommerce\\PayPalCommerce\\": "tests/PHPUnit/",
|
"WooCommerce\\PayPalCommerce\\": "tests/PHPUnit/",
|
||||||
"WooCommerce\\PayPalCommerce\\Tests\\E2e\\": "tests/e2e/PHPUnit/"
|
"WooCommerce\\PayPalCommerce\\Tests\\Integration\\": "tests/integration/PHPUnit/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
|
|
|
@ -91,9 +91,12 @@ return function ( string $root_dir ): iterable {
|
||||||
$modules[] = ( require "$modules_dir/ppcp-axo-block/module.php" )();
|
$modules[] = ( require "$modules_dir/ppcp-axo-block/module.php" )();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$show_new_ux = '1' === get_option( 'woocommerce-ppcp-is-new-merchant' );
|
||||||
|
$preview_new_ux = '1' === getenv( 'PCP_SETTINGS_ENABLED' );
|
||||||
|
|
||||||
if ( apply_filters(
|
if ( apply_filters(
|
||||||
'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled',
|
'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled',
|
||||||
getenv( 'PCP_SETTINGS_ENABLED' ) === '1'
|
$show_new_ux || $preview_new_ux
|
||||||
) ) {
|
) ) {
|
||||||
$modules[] = ( require "$modules_dir/ppcp-settings/module.php" )();
|
$modules[] = ( require "$modules_dir/ppcp-settings/module.php" )();
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,12 +80,11 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer;
|
use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'api.host' => static function( ContainerInterface $container ) : string {
|
'api.host' => static function( ContainerInterface $container ) : string {
|
||||||
$environment = $container->get( 'onboarding.environment' );
|
$environment = $container->get( 'settings.environment' );
|
||||||
assert( $environment instanceof Environment );
|
assert( $environment instanceof Environment );
|
||||||
|
|
||||||
if ( $environment->is_sandbox() ) {
|
if ( $environment->is_sandbox() ) {
|
||||||
|
@ -671,6 +670,7 @@ return array(
|
||||||
'FR' => $default_currencies,
|
'FR' => $default_currencies,
|
||||||
'DE' => $default_currencies,
|
'DE' => $default_currencies,
|
||||||
'GR' => $default_currencies,
|
'GR' => $default_currencies,
|
||||||
|
'HK' => $default_currencies,
|
||||||
'HU' => $default_currencies,
|
'HU' => $default_currencies,
|
||||||
'IE' => $default_currencies,
|
'IE' => $default_currencies,
|
||||||
'IT' => $default_currencies,
|
'IT' => $default_currencies,
|
||||||
|
@ -688,6 +688,7 @@ return array(
|
||||||
'PT' => $default_currencies,
|
'PT' => $default_currencies,
|
||||||
'RO' => $default_currencies,
|
'RO' => $default_currencies,
|
||||||
'SK' => $default_currencies,
|
'SK' => $default_currencies,
|
||||||
|
'SG' => $default_currencies,
|
||||||
'SI' => $default_currencies,
|
'SI' => $default_currencies,
|
||||||
'ES' => $default_currencies,
|
'ES' => $default_currencies,
|
||||||
'SE' => $default_currencies,
|
'SE' => $default_currencies,
|
||||||
|
@ -736,6 +737,7 @@ return array(
|
||||||
'FR' => $mastercard_visa_amex,
|
'FR' => $mastercard_visa_amex,
|
||||||
'GB' => $mastercard_visa_amex,
|
'GB' => $mastercard_visa_amex,
|
||||||
'GR' => $mastercard_visa_amex,
|
'GR' => $mastercard_visa_amex,
|
||||||
|
'HK' => $mastercard_visa_amex,
|
||||||
'HU' => $mastercard_visa_amex,
|
'HU' => $mastercard_visa_amex,
|
||||||
'IE' => $mastercard_visa_amex,
|
'IE' => $mastercard_visa_amex,
|
||||||
'IT' => $mastercard_visa_amex,
|
'IT' => $mastercard_visa_amex,
|
||||||
|
@ -765,6 +767,7 @@ return array(
|
||||||
'SE' => $mastercard_visa_amex,
|
'SE' => $mastercard_visa_amex,
|
||||||
'SI' => $mastercard_visa_amex,
|
'SI' => $mastercard_visa_amex,
|
||||||
'SK' => $mastercard_visa_amex,
|
'SK' => $mastercard_visa_amex,
|
||||||
|
'SG' => $mastercard_visa_amex,
|
||||||
'JP' => array(
|
'JP' => array(
|
||||||
'mastercard' => array(),
|
'mastercard' => array(),
|
||||||
'visa' => array(),
|
'visa' => array(),
|
||||||
|
|
|
@ -160,7 +160,7 @@ class PartnersEndpoint {
|
||||||
|
|
||||||
$this->failure_registry->clear_failures( FailureRegistry::SELLER_STATUS_KEY );
|
$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;
|
return $status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,15 +28,23 @@ class SellerStatus {
|
||||||
*/
|
*/
|
||||||
private $capabilities;
|
private $capabilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merchant country on PayPal.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $country;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SellerStatus constructor.
|
* SellerStatus constructor.
|
||||||
*
|
*
|
||||||
* @param SellerStatusProduct[] $products The products.
|
* @param SellerStatusProduct[] $products The products.
|
||||||
* @param SellerStatusCapability[] $capabilities The capabilities.
|
* @param SellerStatusCapability[] $capabilities The capabilities.
|
||||||
|
* @param string $country Merchant country on PayPal.
|
||||||
*
|
*
|
||||||
* @psalm-suppress RedundantConditionGivenDocblockType
|
* @psalm-suppress RedundantConditionGivenDocblockType
|
||||||
*/
|
*/
|
||||||
public function __construct( array $products, array $capabilities ) {
|
public function __construct( array $products, array $capabilities, string $country = '' ) {
|
||||||
foreach ( $products as $key => $product ) {
|
foreach ( $products as $key => $product ) {
|
||||||
if ( is_a( $product, SellerStatusProduct::class ) ) {
|
if ( is_a( $product, SellerStatusProduct::class ) ) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -52,6 +60,7 @@ class SellerStatus {
|
||||||
|
|
||||||
$this->products = $products;
|
$this->products = $products;
|
||||||
$this->capabilities = $capabilities;
|
$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
|
* @return array
|
||||||
*/
|
*/
|
||||||
|
@ -95,6 +113,7 @@ class SellerStatus {
|
||||||
return array(
|
return array(
|
||||||
'products' => $products,
|
'products' => $products,
|
||||||
'capabilities' => $capabilities,
|
'capabilities' => $capabilities,
|
||||||
|
'country' => $this->country,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class SellerStatusFactory {
|
||||||
*
|
*
|
||||||
* @return SellerStatus
|
* @return SellerStatus
|
||||||
*/
|
*/
|
||||||
public function from_paypal_reponse( \stdClass $json ) : SellerStatus {
|
public function from_paypal_response( \stdClass $json ) : SellerStatus {
|
||||||
$products = array_map(
|
$products = array_map(
|
||||||
function( $json ) : SellerStatusProduct {
|
function( $json ) : SellerStatusProduct {
|
||||||
$product = new SellerStatusProduct(
|
$product = new SellerStatusProduct(
|
||||||
|
@ -49,6 +49,6 @@ class SellerStatusFactory {
|
||||||
isset( $json->capabilities ) ? (array) $json->capabilities : array()
|
isset( $json->capabilities ) ? (array) $json->capabilities : array()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SellerStatus( $products, $capabilities );
|
return new SellerStatus( $products, $capabilities, $json->country ?? '' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare( strict_types = 1 );
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
||||||
|
|
||||||
|
@ -18,43 +18,21 @@ class PartnerReferralsData {
|
||||||
/**
|
/**
|
||||||
* The DCC Applies Helper object.
|
* The DCC Applies Helper object.
|
||||||
*
|
*
|
||||||
|
* @deprecated Deprecates with the new UI. In this class, the products are
|
||||||
|
* always explicit, and should not be deducted from the
|
||||||
|
* DccApplies state at this point.
|
||||||
|
* Remove this with the legacy UI code.
|
||||||
* @var DccApplies
|
* @var DccApplies
|
||||||
*/
|
*/
|
||||||
private $dcc_applies;
|
private DccApplies $dcc_applies;
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private $products;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PartnerReferralsData constructor.
|
* PartnerReferralsData constructor.
|
||||||
*
|
*
|
||||||
* @param DccApplies $dcc_applies The DCC Applies helper.
|
* @param DccApplies $dcc_applies The DCC Applies helper.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct( DccApplies $dcc_applies ) {
|
||||||
DccApplies $dcc_applies
|
|
||||||
) {
|
|
||||||
$this->dcc_applies = $dcc_applies;
|
$this->dcc_applies = $dcc_applies;
|
||||||
$this->products = array(
|
|
||||||
$this->dcc_applies->for_country_currency() ? 'PPCP' : 'EXPRESS_CHECKOUT',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new copy of this object with the given value set.
|
|
||||||
*
|
|
||||||
* @param string[] $products The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
|
||||||
* @return static
|
|
||||||
*/
|
|
||||||
public function with_products( array $products ): self {
|
|
||||||
$obj = clone $this;
|
|
||||||
|
|
||||||
$obj->products = $products;
|
|
||||||
|
|
||||||
return $obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,82 +40,120 @@ class PartnerReferralsData {
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function nonce(): string {
|
public function nonce() : string {
|
||||||
return 'a1233wtergfsdt4365tzrshgfbaewa36AGa1233wtergfsdt4365tzrshgfbaewa36AG';
|
return 'a1233wtergfsdt4365tzrshgfbaewa36AGa1233wtergfsdt4365tzrshgfbaewa36AG';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data.
|
* Returns the data.
|
||||||
*
|
*
|
||||||
|
* @param string[] $products The list of products to use ('PPCP', 'EXPRESS_CHECKOUT').
|
||||||
|
* Default is based on DCC availability.
|
||||||
|
* @param string $onboarding_token A security token to finalize the onboarding process.
|
||||||
|
* @param bool $use_subscriptions If the merchant requires subscription features.
|
||||||
|
* @param bool $use_card_payments If the merchant wants to process credit card payments.
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function data(): array {
|
public function data( array $products = array(), string $onboarding_token = '', bool $use_subscriptions = null, bool $use_card_payments = true ) : array {
|
||||||
|
if ( ! $products ) {
|
||||||
|
$products = array(
|
||||||
|
$this->dcc_applies->for_country_currency() ? 'PPCP' : 'EXPRESS_CHECKOUT',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the partners referrals data.
|
* Filter the return-URL, which is called at the end of the OAuth onboarding
|
||||||
|
* process, when the merchant clicks the "Return to your shop" button.
|
||||||
*/
|
*/
|
||||||
return apply_filters(
|
$return_url = apply_filters(
|
||||||
'ppcp_partner_referrals_data',
|
'woocommerce_paypal_payments_partner_config_override_return_url',
|
||||||
array(
|
admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' )
|
||||||
'partner_config_override' => array(
|
);
|
||||||
/**
|
|
||||||
* Returns the URL which will be opened at the end of onboarding.
|
/**
|
||||||
*/
|
* Filter the label of the "Return to your shop" button.
|
||||||
'return_url' => apply_filters(
|
* It's displayed on the very last page of the onboarding popup.
|
||||||
'woocommerce_paypal_payments_partner_config_override_return_url',
|
*/
|
||||||
admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' )
|
$return_url_label = apply_filters(
|
||||||
),
|
'woocommerce_paypal_payments_partner_config_override_return_url_description',
|
||||||
/**
|
__( 'Return to your shop.', 'woocommerce-paypal-payments' )
|
||||||
* Returns the description of the URL which will be opened at the end of onboarding.
|
);
|
||||||
*/
|
|
||||||
'return_url_description' => apply_filters(
|
$capabilities = array();
|
||||||
'woocommerce_paypal_payments_partner_config_override_return_url_description',
|
$first_party_features = array(
|
||||||
__( 'Return to your shop.', 'woocommerce-paypal-payments' )
|
'PAYMENT',
|
||||||
),
|
'REFUND',
|
||||||
'show_add_credit_card' => true,
|
'ADVANCED_TRANSACTIONS_SEARCH',
|
||||||
|
'TRACKING_SHIPMENT_READWRITE',
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( true === $use_subscriptions ) {
|
||||||
|
$capabilities[] = 'PAYPAL_WALLET_VAULTING_ADVANCED';
|
||||||
|
$first_party_features[] = 'BILLING_AGREEMENT';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backwards compatibility. Keep those features in the legacy UI (null-value).
|
||||||
|
// Move this into the previous condition, once legacy code is removed.
|
||||||
|
if ( false !== $use_subscriptions ) {
|
||||||
|
$first_party_features[] = 'FUTURE_PAYMENT';
|
||||||
|
$first_party_features[] = 'VAULT';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( false === $use_subscriptions ) {
|
||||||
|
// Only use "ADVANCED_VAULTING" product for onboarding with subscriptions.
|
||||||
|
$products = array_filter(
|
||||||
|
$products,
|
||||||
|
static fn( $product ) => $product !== 'ADVANCED_VAULTING'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = array(
|
||||||
|
'partner_config_override' => array(
|
||||||
|
'return_url' => $return_url,
|
||||||
|
'return_url_description' => $return_url_label,
|
||||||
|
'show_add_credit_card' => $use_card_payments,
|
||||||
|
),
|
||||||
|
'products' => $products,
|
||||||
|
'capabilities' => $capabilities,
|
||||||
|
'legal_consents' => array(
|
||||||
|
array(
|
||||||
|
'type' => 'SHARE_DATA_CONSENT',
|
||||||
|
'granted' => true,
|
||||||
),
|
),
|
||||||
'products' => $this->products,
|
),
|
||||||
'legal_consents' => array(
|
'operations' => array(
|
||||||
array(
|
array(
|
||||||
'type' => 'SHARE_DATA_CONSENT',
|
'operation' => 'API_INTEGRATION',
|
||||||
'granted' => true,
|
'api_integration_preference' => array(
|
||||||
),
|
'rest_api_integration' => array(
|
||||||
),
|
'integration_method' => 'PAYPAL',
|
||||||
'operations' => array(
|
'integration_type' => 'FIRST_PARTY',
|
||||||
array(
|
'first_party_details' => array(
|
||||||
'operation' => 'API_INTEGRATION',
|
'features' => $first_party_features,
|
||||||
'api_integration_preference' => array(
|
'seller_nonce' => $this->nonce(),
|
||||||
'rest_api_integration' => array(
|
|
||||||
'integration_method' => 'PAYPAL',
|
|
||||||
'integration_type' => 'FIRST_PARTY',
|
|
||||||
'first_party_details' => array(
|
|
||||||
'features' => array(
|
|
||||||
'PAYMENT',
|
|
||||||
'FUTURE_PAYMENT',
|
|
||||||
'REFUND',
|
|
||||||
'ADVANCED_TRANSACTIONS_SEARCH',
|
|
||||||
'VAULT',
|
|
||||||
'TRACKING_SHIPMENT_READWRITE',
|
|
||||||
),
|
|
||||||
'seller_nonce' => $this->nonce(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append the validation token to the return_url
|
* Filter the final partners referrals data collection.
|
||||||
*
|
*/
|
||||||
* @param array $data The referral data.
|
$payload = apply_filters( 'ppcp_partner_referrals_data', $payload );
|
||||||
* @param string $token The token to be appended.
|
|
||||||
* @return array
|
// An empty array is not permitted.
|
||||||
*/
|
if ( isset( $payload['capabilities'] ) && ! $payload['capabilities'] ) {
|
||||||
public function append_onboarding_token( array $data, string $token ): array {
|
unset( $payload['capabilities'] );
|
||||||
$data['partner_config_override']['return_url'] =
|
}
|
||||||
add_query_arg( 'ppcpToken', $token, $data['partner_config_override']['return_url'] );
|
|
||||||
return $data;
|
// Add the nonce in the end, to maintain backwards compatibility of filters.
|
||||||
|
$payload['partner_config_override']['return_url'] = add_query_arg(
|
||||||
|
array( 'ppcpToken' => $onboarding_token ),
|
||||||
|
$payload['partner_config_override']['return_url']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ return array(
|
||||||
assert( $display_manager instanceof DisplayManager );
|
assert( $display_manager instanceof DisplayManager );
|
||||||
|
|
||||||
// Domain registration.
|
// Domain registration.
|
||||||
$env = $container->get( 'onboarding.environment' );
|
$env = $container->get( 'settings.environment' );
|
||||||
assert( $env instanceof Environment );
|
assert( $env instanceof Environment );
|
||||||
|
|
||||||
$domain_registration_url = 'https://www.paypal.com/uccservicing/apm/applepay';
|
$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\Applepay\Helper\AvailabilityNotice;
|
||||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
@ -191,6 +190,7 @@ return array(
|
||||||
'FR', // France
|
'FR', // France
|
||||||
'DE', // Germany
|
'DE', // Germany
|
||||||
'GR', // Greece
|
'GR', // Greece
|
||||||
|
'HK', // Hong Kong
|
||||||
'HU', // Hungary
|
'HU', // Hungary
|
||||||
'IE', // Ireland
|
'IE', // Ireland
|
||||||
'IT', // Italy
|
'IT', // Italy
|
||||||
|
@ -204,6 +204,7 @@ return array(
|
||||||
'PL', // Poland
|
'PL', // Poland
|
||||||
'PT', // Portugal
|
'PT', // Portugal
|
||||||
'RO', // Romania
|
'RO', // Romania
|
||||||
|
'SG', // Singapore
|
||||||
'SK', // Slovakia
|
'SK', // Slovakia
|
||||||
'SI', // Slovenia
|
'SI', // Slovenia
|
||||||
'ES', // Spain
|
'ES', // Spain
|
||||||
|
@ -233,6 +234,7 @@ return array(
|
||||||
'CZK', // Czech Koruna
|
'CZK', // Czech Koruna
|
||||||
'DKK', // Danish Krone
|
'DKK', // Danish Krone
|
||||||
'EUR', // Euro
|
'EUR', // Euro
|
||||||
|
'HKD', // Hong Kong Dollar
|
||||||
'GBP', // British Pound Sterling
|
'GBP', // British Pound Sterling
|
||||||
'HUF', // Hungarian Forint
|
'HUF', // Hungarian Forint
|
||||||
'ILS', // Israeli New Shekel
|
'ILS', // Israeli New Shekel
|
||||||
|
@ -242,6 +244,7 @@ return array(
|
||||||
'NZD', // New Zealand Dollar
|
'NZD', // New Zealand Dollar
|
||||||
'PHP', // Philippine Peso
|
'PHP', // Philippine Peso
|
||||||
'PLN', // Polish Zloty
|
'PLN', // Polish Zloty
|
||||||
|
'SGD', // Singapur-Dollar
|
||||||
'SEK', // Swedish Krona
|
'SEK', // Swedish Krona
|
||||||
'THB', // Thai Baht
|
'THB', // Thai Baht
|
||||||
'TWD', // New Taiwan Dollar
|
'TWD', // New Taiwan Dollar
|
||||||
|
@ -260,15 +263,15 @@ return array(
|
||||||
},
|
},
|
||||||
|
|
||||||
'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
||||||
$state = $container->get( 'onboarding.state' );
|
$is_connected = $container->get( 'settings.flag.is-connected' );
|
||||||
if ( $state->current_state() < State::STATE_ONBOARDED ) {
|
if ( ! $is_connected ) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$product_status = $container->get( 'applepay.apple-product-status' );
|
$product_status = $container->get( 'applepay.apple-product-status' );
|
||||||
assert( $product_status instanceof AppleProductStatus );
|
assert( $product_status instanceof AppleProductStatus );
|
||||||
|
|
||||||
$environment = $container->get( 'onboarding.environment' );
|
$environment = $container->get( 'settings.environment' );
|
||||||
assert( $environment instanceof Environment );
|
assert( $environment instanceof Environment );
|
||||||
|
|
||||||
$enabled = $product_status->is_active();
|
$enabled = $product_status->is_active();
|
||||||
|
|
|
@ -368,7 +368,7 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule
|
||||||
if ( ! $button->is_enabled() ) {
|
if ( ! $button->is_enabled() ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$env = $c->get( 'onboarding.environment' );
|
$env = $c->get( 'settings.environment' );
|
||||||
assert( $env instanceof Environment );
|
assert( $env instanceof Environment );
|
||||||
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
|
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
|
||||||
$this->load_domain_association_file( $is_sandobx );
|
$this->load_domain_association_file( $is_sandobx );
|
||||||
|
|
|
@ -1018,6 +1018,10 @@ class ApplePayButton implements ButtonInterface {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function enqueue(): void {
|
public function enqueue(): void {
|
||||||
|
if ( ! $this->is_enabled() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wp_register_script(
|
wp_register_script(
|
||||||
'wc-ppcp-applepay',
|
'wc-ppcp-applepay',
|
||||||
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
|
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
|
||||||
|
|
|
@ -89,6 +89,7 @@ class AppleProductStatus extends ProductStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Settings used as a cache; `settings->set` is compatible with new UI.
|
||||||
if ( $has_capability ) {
|
if ( $has_capability ) {
|
||||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,7 +36,7 @@ return array(
|
||||||
fn(): SmartButtonInterface => $container->get( 'button.smart-button' ),
|
fn(): SmartButtonInterface => $container->get( 'button.smart-button' ),
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'wcgateway.configuration.dcc' ),
|
$container->get( 'wcgateway.configuration.dcc' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'settings.environment' ),
|
||||||
$container->get( 'wcgateway.url' ),
|
$container->get( 'wcgateway.url' ),
|
||||||
$container->get( 'axo.payment_method_selected_map' ),
|
$container->get( 'axo.payment_method_selected_map' ),
|
||||||
$container->get( 'axo.supported-country-card-type-matrix' )
|
$container->get( 'axo.supported-country-card-type-matrix' )
|
||||||
|
|
|
@ -107,11 +107,9 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
|
||||||
'woocommerce_blocks_payment_method_type_registration',
|
'woocommerce_blocks_payment_method_type_registration',
|
||||||
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
||||||
/*
|
/*
|
||||||
* Only register the method if we are not in the admin
|
* Only register the method if we are not in the admin or the customer is not logged in.
|
||||||
* (to avoid two Debit & Credit Cards gateways in the
|
|
||||||
* checkout block in the editor: one from ACDC one from Axo).
|
|
||||||
*/
|
*/
|
||||||
if ( ! is_admin() ) {
|
if ( ! is_user_logged_in() ) {
|
||||||
$payment_method_registry->register( $c->get( 'axoblock.method' ) );
|
$payment_method_registry->register( $c->get( 'axoblock.method' ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Axo;
|
namespace WooCommerce\PayPalCommerce\Axo;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Axo\Helper\NoticeRenderer;
|
use WooCommerce\PayPalCommerce\Axo\Helper\NoticeRenderer;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary;
|
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace WooCommerce\PayPalCommerce\Axo;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
||||||
use WooCommerce\PayPalCommerce\Axo\Helper\SettingsNoticeGenerator;
|
use WooCommerce\PayPalCommerce\Axo\Helper\CompatibilityChecker;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
|
@ -38,8 +38,8 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
'axo.helpers.settings-notice-generator' => static function ( ContainerInterface $container ) : SettingsNoticeGenerator {
|
'axo.helpers.compatibility-checker' => static function ( ContainerInterface $container ) : CompatibilityChecker {
|
||||||
return new SettingsNoticeGenerator( $container->get( 'axo.fastlane-incompatible-plugin-names' ) );
|
return new CompatibilityChecker( $container->get( 'axo.fastlane-incompatible-plugin-names' ) );
|
||||||
},
|
},
|
||||||
|
|
||||||
// If AXO is configured and onboarded.
|
// If AXO is configured and onboarded.
|
||||||
|
@ -66,7 +66,7 @@ return array(
|
||||||
$container->get( 'ppcp.asset-version' ),
|
$container->get( 'ppcp.asset-version' ),
|
||||||
$container->get( 'session.handler' ),
|
$container->get( 'session.handler' ),
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'settings.environment' ),
|
||||||
$container->get( 'axo.insights' ),
|
$container->get( 'axo.insights' ),
|
||||||
$container->get( 'wcgateway.settings.status' ),
|
$container->get( 'wcgateway.settings.status' ),
|
||||||
$container->get( 'api.shop.currency.getter' ),
|
$container->get( 'api.shop.currency.getter' ),
|
||||||
|
@ -89,7 +89,7 @@ return array(
|
||||||
$container->get( 'api.factory.purchase-unit' ),
|
$container->get( 'api.factory.purchase-unit' ),
|
||||||
$container->get( 'api.factory.shipping-preference' ),
|
$container->get( 'api.factory.shipping-preference' ),
|
||||||
$container->get( 'wcgateway.transaction-url-provider' ),
|
$container->get( 'wcgateway.transaction-url-provider' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'settings.environment' ),
|
||||||
$container->get( 'woocommerce.logger.woocommerce' )
|
$container->get( 'woocommerce.logger.woocommerce' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -190,29 +190,44 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
|
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
|
||||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
$compatibility_checker = $container->get( 'axo.helpers.compatibility-checker' );
|
||||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
assert( $compatibility_checker instanceof CompatibilityChecker );
|
||||||
|
|
||||||
$settings = $container->get( 'wcgateway.settings' );
|
$settings = $container->get( 'wcgateway.settings' );
|
||||||
assert( $settings instanceof Settings );
|
assert( $settings instanceof Settings );
|
||||||
|
|
||||||
return $settings_notice_generator->generate_settings_conflict_notice( $settings );
|
return $compatibility_checker->generate_settings_conflict_notice( $settings );
|
||||||
},
|
},
|
||||||
|
|
||||||
'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string {
|
'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string {
|
||||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
$compatibility_checker = $container->get( 'axo.helpers.compatibility-checker' );
|
||||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
assert( $compatibility_checker instanceof CompatibilityChecker );
|
||||||
|
|
||||||
return $settings_notice_generator->generate_checkout_notice();
|
return $compatibility_checker->generate_checkout_notice();
|
||||||
|
},
|
||||||
|
|
||||||
|
'axo.checkout-config-notice.raw' => static function ( ContainerInterface $container ) : string {
|
||||||
|
$compatibility_checker = $container->get( 'axo.helpers.compatibility-checker' );
|
||||||
|
assert( $compatibility_checker instanceof CompatibilityChecker );
|
||||||
|
|
||||||
|
return $compatibility_checker->generate_checkout_notice( true );
|
||||||
},
|
},
|
||||||
|
|
||||||
'axo.incompatible-plugins-notice' => static function ( ContainerInterface $container ) : string {
|
'axo.incompatible-plugins-notice' => static function ( ContainerInterface $container ) : string {
|
||||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
$settings_notice_generator = $container->get( 'axo.helpers.compatibility-checker' );
|
||||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
assert( $settings_notice_generator instanceof CompatibilityChecker );
|
||||||
|
|
||||||
return $settings_notice_generator->generate_incompatible_plugins_notice();
|
return $settings_notice_generator->generate_incompatible_plugins_notice();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'axo.incompatible-plugins-notice.raw' => static function ( ContainerInterface $container ) : string {
|
||||||
|
$settings_notice_generator = new CompatibilityChecker(
|
||||||
|
$container->get( 'axo.fastlane-incompatible-plugin-names' )
|
||||||
|
);
|
||||||
|
|
||||||
|
return $settings_notice_generator->generate_incompatible_plugins_notice( true );
|
||||||
|
},
|
||||||
|
|
||||||
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
|
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
|
||||||
$dcc_configuration = $container->get( 'wcgateway.configuration.dcc' );
|
$dcc_configuration = $container->get( 'wcgateway.configuration.dcc' );
|
||||||
assert( $dcc_configuration instanceof DCCGatewayConfiguration );
|
assert( $dcc_configuration instanceof DCCGatewayConfiguration );
|
||||||
|
|
|
@ -475,7 +475,7 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function add_feature_detection_tag( bool $axo_enabled ) {
|
private function add_feature_detection_tag( bool $axo_enabled ) {
|
||||||
$show_tag = is_checkout() || is_cart() || is_shop();
|
$show_tag = is_home() || is_checkout() || is_cart() || is_shop();
|
||||||
|
|
||||||
if ( ! $show_tag ) {
|
if ( ! $show_tag ) {
|
||||||
return;
|
return;
|
||||||
|
|
254
modules/ppcp-axo/src/Helper/CompatibilityChecker.php
Normal file
254
modules/ppcp-axo/src/Helper/CompatibilityChecker.php
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Fastlane compatibility checker.
|
||||||
|
* Detects compatibility issues and generates relevant notices.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\Axo\Helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Axo\Helper;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CompatibilityChecker
|
||||||
|
*/
|
||||||
|
class CompatibilityChecker {
|
||||||
|
/**
|
||||||
|
* The list of Fastlane incompatible plugin names.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected array $incompatible_plugin_names;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the result of checkout compatibility checks.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $checkout_compatibility;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores whether DCC is enabled.
|
||||||
|
*
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
protected ?bool $is_dcc_enabled = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompatibilityChecker constructor.
|
||||||
|
*
|
||||||
|
* @param string[] $incompatible_plugin_names The list of Fastlane incompatible plugin names.
|
||||||
|
*/
|
||||||
|
public function __construct( array $incompatible_plugin_names ) {
|
||||||
|
$this->incompatible_plugin_names = $incompatible_plugin_names;
|
||||||
|
$this->checkout_compatibility = array(
|
||||||
|
'has_elementor_checkout' => null,
|
||||||
|
'has_classic_checkout' => null,
|
||||||
|
'has_block_checkout' => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the checkout uses Elementor.
|
||||||
|
*
|
||||||
|
* @return bool Whether the checkout uses Elementor.
|
||||||
|
*/
|
||||||
|
protected function has_elementor_checkout(): bool {
|
||||||
|
if ( $this->checkout_compatibility['has_elementor_checkout'] === null ) {
|
||||||
|
$this->checkout_compatibility['has_elementor_checkout'] = CartCheckoutDetector::has_elementor_checkout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->checkout_compatibility['has_elementor_checkout'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the checkout uses classic checkout.
|
||||||
|
*
|
||||||
|
* @return bool Whether the checkout uses classic checkout.
|
||||||
|
*/
|
||||||
|
protected function has_classic_checkout(): bool {
|
||||||
|
if ( $this->checkout_compatibility['has_classic_checkout'] === null ) {
|
||||||
|
$this->checkout_compatibility['has_classic_checkout'] = CartCheckoutDetector::has_classic_checkout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->checkout_compatibility['has_classic_checkout'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the checkout uses block checkout.
|
||||||
|
*
|
||||||
|
* @return bool Whether the checkout uses block checkout.
|
||||||
|
*/
|
||||||
|
protected function has_block_checkout(): bool {
|
||||||
|
if ( $this->checkout_compatibility['has_block_checkout'] === null ) {
|
||||||
|
$this->checkout_compatibility['has_block_checkout'] = CartCheckoutDetector::has_block_checkout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->checkout_compatibility['has_block_checkout'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if DCC is enabled.
|
||||||
|
*
|
||||||
|
* @param Settings $settings The plugin settings container.
|
||||||
|
* @return bool Whether DCC is enabled.
|
||||||
|
*/
|
||||||
|
protected function is_dcc_enabled( Settings $settings ): bool {
|
||||||
|
if ( $this->is_dcc_enabled === null ) {
|
||||||
|
try {
|
||||||
|
$this->is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' );
|
||||||
|
} catch ( NotFoundException $ignored ) {
|
||||||
|
$this->is_dcc_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->is_dcc_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the full HTML of the notification.
|
||||||
|
*
|
||||||
|
* @param string $message HTML of the inner message contents.
|
||||||
|
* @param bool $is_error Whether the provided message is an error. Affects the notice color.
|
||||||
|
* @param bool $raw_message Whether to return raw message without HTML wrappers.
|
||||||
|
*
|
||||||
|
* @return string The full HTML code of the notification, or an empty string, or raw message.
|
||||||
|
*/
|
||||||
|
private function render_notice( string $message, bool $is_error = false, bool $raw_message = false ) : string {
|
||||||
|
if ( ! $message ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $raw_message ) {
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'<div class="ppcp-notice %1$s"><p>%2$s</p></div>',
|
||||||
|
$is_error ? 'ppcp-notice-error' : '',
|
||||||
|
$message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there aren't any incompatibilities that would prevent Fastlane from working properly.
|
||||||
|
*
|
||||||
|
* @return bool Whether the setup is compatible.
|
||||||
|
*/
|
||||||
|
public function is_fastlane_compatible(): bool {
|
||||||
|
// Check for incompatible plugins.
|
||||||
|
if ( ! empty( $this->incompatible_plugin_names ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for checkout page incompatibilities.
|
||||||
|
if ( $this->has_elementor_checkout() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $this->has_classic_checkout() && ! $this->has_block_checkout() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No incompatibilities found.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the checkout notice.
|
||||||
|
*
|
||||||
|
* @param bool $raw_message Whether to return raw message without HTML wrappers.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generate_checkout_notice( bool $raw_message = false ): string {
|
||||||
|
$notice_content = '';
|
||||||
|
|
||||||
|
// Check for checkout incompatibilities.
|
||||||
|
$has_checkout_incompatibility = $this->has_elementor_checkout() ||
|
||||||
|
( ! $this->has_classic_checkout() && ! $this->has_block_checkout() );
|
||||||
|
|
||||||
|
if ( ! $has_checkout_incompatibility ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
|
||||||
|
$block_checkout_docs_link = __(
|
||||||
|
'https://woocommerce.com/document/woocommerce-store-editing/customizing-cart-and-checkout/#using-the-cart-and-checkout-blocks',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $this->has_elementor_checkout() ) {
|
||||||
|
$notice_content = sprintf(
|
||||||
|
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||||
|
__(
|
||||||
|
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
esc_url( $checkout_page_link ),
|
||||||
|
esc_url( $block_checkout_docs_link )
|
||||||
|
);
|
||||||
|
} elseif ( ! $this->has_classic_checkout() && ! $this->has_block_checkout() ) {
|
||||||
|
$notice_content = sprintf(
|
||||||
|
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||||
|
__(
|
||||||
|
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
esc_url( $checkout_page_link ),
|
||||||
|
esc_url( $block_checkout_docs_link )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render_notice( $notice_content, true, $raw_message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the incompatible plugins notice.
|
||||||
|
*
|
||||||
|
* @param bool $raw_message Whether to return raw message without HTML wrappers.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generate_incompatible_plugins_notice( bool $raw_message = false ): string {
|
||||||
|
if ( empty( $this->incompatible_plugin_names ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$plugins_settings_link = esc_url( admin_url( 'plugins.php' ) );
|
||||||
|
$notice_content = sprintf(
|
||||||
|
/* translators: %1$s: URL to the plugins settings page. %2$s: List of incompatible plugins. */
|
||||||
|
__(
|
||||||
|
'<span class="highlight">Note:</span> The accelerated guest buyer experience provided by Fastlane may not be fully compatible with some of the following <a href="%1$s">active plugins</a>: <ul class="ppcp-notice-list">%2$s</ul>',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
$plugins_settings_link,
|
||||||
|
implode( '', $this->incompatible_plugin_names )
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render_notice( $notice_content, false, $raw_message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a warning notice with instructions on conflicting plugin-internal settings.
|
||||||
|
*
|
||||||
|
* @param Settings $settings The plugin settings container, which is checked for conflicting values.
|
||||||
|
* @param bool $raw_message Whether to return raw message without HTML wrappers.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generate_settings_conflict_notice( Settings $settings, bool $raw_message = false ) : string {
|
||||||
|
if ( $this->is_dcc_enabled( $settings ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice_content = __(
|
||||||
|
'<span class="highlight">Warning:</span> To enable Fastlane and accelerate payments, the <strong>Advanced Card Processing</strong> payment method must also be enabled.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render_notice( $notice_content, true, $raw_message );
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,147 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Settings notice generator.
|
|
||||||
* Generates the settings notices.
|
|
||||||
*
|
|
||||||
* @package WooCommerce\PayPalCommerce\Axo\Helper
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Axo\Helper;
|
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SettingsNoticeGenerator
|
|
||||||
*/
|
|
||||||
class SettingsNoticeGenerator {
|
|
||||||
/**
|
|
||||||
* The list of Fastlane incompatible plugin names.
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
protected $incompatible_plugin_names;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SettingsNoticeGenerator constructor.
|
|
||||||
*
|
|
||||||
* @param string[] $incompatible_plugin_names The list of Fastlane incompatible plugin names.
|
|
||||||
*/
|
|
||||||
public function __construct( array $incompatible_plugin_names ) {
|
|
||||||
$this->incompatible_plugin_names = $incompatible_plugin_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the full HTML of the notification.
|
|
||||||
*
|
|
||||||
* @param string $message HTML of the inner message contents.
|
|
||||||
* @param bool $is_error Whether the provided message is an error. Affects the notice color.
|
|
||||||
*
|
|
||||||
* @return string The full HTML code of the notification, or an empty string.
|
|
||||||
*/
|
|
||||||
private function render_notice( string $message, bool $is_error = false ) : string {
|
|
||||||
if ( ! $message ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf(
|
|
||||||
'<div class="ppcp-notice %1$s"><p>%2$s</p></div>',
|
|
||||||
$is_error ? 'ppcp-notice-error' : '',
|
|
||||||
$message
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the checkout notice.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function generate_checkout_notice(): string {
|
|
||||||
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
|
|
||||||
$block_checkout_docs_link = __(
|
|
||||||
'https://woocommerce.com/document/woocommerce-store-editing/customizing-cart-and-checkout/#using-the-cart-and-checkout-blocks',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
);
|
|
||||||
|
|
||||||
$notice_content = '';
|
|
||||||
|
|
||||||
if ( CartCheckoutDetector::has_elementor_checkout() ) {
|
|
||||||
$notice_content = sprintf(
|
|
||||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
|
||||||
__(
|
|
||||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
esc_url( $checkout_page_link ),
|
|
||||||
esc_url( $block_checkout_docs_link )
|
|
||||||
);
|
|
||||||
} elseif ( ! CartCheckoutDetector::has_classic_checkout() && ! CartCheckoutDetector::has_block_checkout() ) {
|
|
||||||
$notice_content = sprintf(
|
|
||||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
|
||||||
__(
|
|
||||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
esc_url( $checkout_page_link ),
|
|
||||||
esc_url( $block_checkout_docs_link )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $notice_content ? '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the incompatible plugins notice.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function generate_incompatible_plugins_notice(): string {
|
|
||||||
if ( empty( $this->incompatible_plugin_names ) ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$plugins_settings_link = esc_url( admin_url( 'plugins.php' ) );
|
|
||||||
$notice_content = sprintf(
|
|
||||||
/* translators: %1$s: URL to the plugins settings page. %2$s: List of incompatible plugins. */
|
|
||||||
__(
|
|
||||||
'<span class="highlight">Note:</span> The accelerated guest buyer experience provided by Fastlane may not be fully compatible with some of the following <a href="%1$s">active plugins</a>: <ul class="ppcp-notice-list">%2$s</ul>',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
$plugins_settings_link,
|
|
||||||
implode( '', $this->incompatible_plugin_names )
|
|
||||||
);
|
|
||||||
|
|
||||||
return '<div class="ppcp-notice"><p>' . $notice_content . '</p></div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a warning notice with instructions on conflicting plugin-internal settings.
|
|
||||||
*
|
|
||||||
* @param Settings $settings The plugin settings container, which is checked for conflicting
|
|
||||||
* values.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function generate_settings_conflict_notice( Settings $settings ) : string {
|
|
||||||
$notice_content = '';
|
|
||||||
$is_dcc_enabled = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' );
|
|
||||||
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
|
||||||
} catch ( NotFoundException $ignored ) {
|
|
||||||
// Never happens.
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $is_dcc_enabled ) {
|
|
||||||
$notice_content = __(
|
|
||||||
'<span class="highlight">Warning:</span> To enable Fastlane and accelerate payments, the <strong>Advanced Card Processing</strong> payment method must also be enabled.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->render_notice( $notice_content, true );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -67,27 +67,6 @@ export const PayPalComponent = ( {
|
||||||
? `${ config.id }-${ fundingSource }`
|
? `${ config.id }-${ fundingSource }`
|
||||||
: config.id;
|
: config.id;
|
||||||
|
|
||||||
/**
|
|
||||||
* The block cart displays express checkout buttons. Those buttons are handled by the
|
|
||||||
* PAYPAL_GATEWAY_ID method on the server ("PayPal Smart Buttons").
|
|
||||||
*
|
|
||||||
* A possible bug in WooCommerce does not use the correct payment method ID for the express
|
|
||||||
* payment buttons inside the cart, but sends the ID of the _first_ active payment method.
|
|
||||||
*
|
|
||||||
* This function uses an internal WooCommerce dispatcher method to set the correct method ID.
|
|
||||||
*/
|
|
||||||
const enforcePaymentMethodForCart = () => {
|
|
||||||
// Do nothing, unless we're handling block cart express payment buttons.
|
|
||||||
if ( 'cart-block' !== config.scriptData.context ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the active payment method to PAYPAL_GATEWAY_ID.
|
|
||||||
wp.data
|
|
||||||
.dispatch( 'wc/store/payment' )
|
|
||||||
.__internalSetActivePaymentMethod( PAYPAL_GATEWAY_ID, {} );
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
// fill the form if in continuation (for product or mini-cart buttons)
|
// fill the form if in continuation (for product or mini-cart buttons)
|
||||||
if ( continuationFilled || ! config.scriptData.continuation?.order ) {
|
if ( continuationFilled || ! config.scriptData.continuation?.order ) {
|
||||||
|
@ -339,7 +318,6 @@ export const PayPalComponent = ( {
|
||||||
shouldskipFinalConfirmation,
|
shouldskipFinalConfirmation,
|
||||||
getCheckoutRedirectUrl,
|
getCheckoutRedirectUrl,
|
||||||
setGotoContinuationOnError,
|
setGotoContinuationOnError,
|
||||||
enforcePaymentMethodForCart,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onError,
|
onError,
|
||||||
onClose
|
onClose
|
||||||
|
@ -439,7 +417,6 @@ export const PayPalComponent = ( {
|
||||||
shouldskipFinalConfirmation,
|
shouldskipFinalConfirmation,
|
||||||
getCheckoutRedirectUrl,
|
getCheckoutRedirectUrl,
|
||||||
setGotoContinuationOnError,
|
setGotoContinuationOnError,
|
||||||
enforcePaymentMethodForCart,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onError,
|
onError,
|
||||||
onClose
|
onClose
|
||||||
|
@ -476,7 +453,6 @@ export const PayPalComponent = ( {
|
||||||
shouldskipFinalConfirmation,
|
shouldskipFinalConfirmation,
|
||||||
getCheckoutRedirectUrl,
|
getCheckoutRedirectUrl,
|
||||||
setGotoContinuationOnError,
|
setGotoContinuationOnError,
|
||||||
enforcePaymentMethodForCart,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onError,
|
onError,
|
||||||
onClose
|
onClose
|
||||||
|
|
|
@ -81,12 +81,14 @@ export const paypalShippingToWc = ( shipping ) => {
|
||||||
export const paypalPayerToWc = ( payer ) => {
|
export const paypalPayerToWc = ( payer ) => {
|
||||||
const firstName = payer?.name?.given_name ?? '';
|
const firstName = payer?.name?.given_name ?? '';
|
||||||
const lastName = payer?.name?.surname ?? '';
|
const lastName = payer?.name?.surname ?? '';
|
||||||
|
const phone = payer?.phone?.phone_number?.national_number ?? '';
|
||||||
const address = payer.address ? paypalAddressToWc( payer.address ) : {};
|
const address = payer.address ? paypalAddressToWc( payer.address ) : {};
|
||||||
return {
|
return {
|
||||||
...address,
|
...address,
|
||||||
first_name: firstName,
|
first_name: firstName,
|
||||||
last_name: lastName,
|
last_name: lastName,
|
||||||
email: payer.email_address,
|
email: payer.email_address,
|
||||||
|
phone: phone
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ export const handleApprove = async (
|
||||||
shouldskipFinalConfirmation,
|
shouldskipFinalConfirmation,
|
||||||
getCheckoutRedirectUrl,
|
getCheckoutRedirectUrl,
|
||||||
setGotoContinuationOnError,
|
setGotoContinuationOnError,
|
||||||
enforcePaymentMethodForCart,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onError,
|
onError,
|
||||||
onClose
|
onClose
|
||||||
|
@ -132,7 +131,6 @@ export const handleApprove = async (
|
||||||
location.href = getCheckoutRedirectUrl();
|
location.href = getCheckoutRedirectUrl();
|
||||||
} else {
|
} else {
|
||||||
setGotoContinuationOnError( true );
|
setGotoContinuationOnError( true );
|
||||||
enforcePaymentMethodForCart();
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
}
|
}
|
||||||
} catch ( err ) {
|
} catch ( err ) {
|
||||||
|
@ -171,7 +169,6 @@ export const handleApproveSubscription = async (
|
||||||
shouldskipFinalConfirmation,
|
shouldskipFinalConfirmation,
|
||||||
getCheckoutRedirectUrl,
|
getCheckoutRedirectUrl,
|
||||||
setGotoContinuationOnError,
|
setGotoContinuationOnError,
|
||||||
enforcePaymentMethodForCart,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onError,
|
onError,
|
||||||
onClose
|
onClose
|
||||||
|
@ -242,7 +239,6 @@ export const handleApproveSubscription = async (
|
||||||
location.href = getCheckoutRedirectUrl();
|
location.href = getCheckoutRedirectUrl();
|
||||||
} else {
|
} else {
|
||||||
setGotoContinuationOnError( true );
|
setGotoContinuationOnError( true );
|
||||||
enforcePaymentMethodForCart();
|
|
||||||
onSubmit();
|
onSubmit();
|
||||||
}
|
}
|
||||||
} catch ( err ) {
|
} catch ( err ) {
|
||||||
|
|
|
@ -28,13 +28,6 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'blocks.method' => static function ( ContainerInterface $container ): PayPalPaymentMethod {
|
'blocks.method' => static function ( ContainerInterface $container ): PayPalPaymentMethod {
|
||||||
/**
|
|
||||||
* Cart instance; might be null, esp. in customizer or in Block Editor.
|
|
||||||
*
|
|
||||||
* @var null|WC_Cart $cart
|
|
||||||
*/
|
|
||||||
$cart = WC()->cart;
|
|
||||||
|
|
||||||
return new PayPalPaymentMethod(
|
return new PayPalPaymentMethod(
|
||||||
$container->get( 'blocks.url' ),
|
$container->get( 'blocks.url' ),
|
||||||
$container->get( 'ppcp.asset-version' ),
|
$container->get( 'ppcp.asset-version' ),
|
||||||
|
@ -53,7 +46,6 @@ return array(
|
||||||
$container->get( 'wcgateway.place-order-button-text' ),
|
$container->get( 'wcgateway.place-order-button-text' ),
|
||||||
$container->get( 'wcgateway.place-order-button-description' ),
|
$container->get( 'wcgateway.place-order-button-description' ),
|
||||||
$container->get( 'wcgateway.all-funding-sources' ),
|
$container->get( 'wcgateway.all-funding-sources' ),
|
||||||
$cart && $cart->needs_shipping()
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'blocks.advanced-card-method' => static function( ContainerInterface $container ): AdvancedCardPaymentMethod {
|
'blocks.advanced-card-method' => static function( ContainerInterface $container ): AdvancedCardPaymentMethod {
|
||||||
|
|
|
@ -23,7 +23,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||||
*/
|
*/
|
||||||
class UpdateShippingEndpoint implements EndpointInterface {
|
class UpdateShippingEndpoint implements EndpointInterface {
|
||||||
const ENDPOINT = 'ppc-update-shipping';
|
const ENDPOINT = 'ppc-update-shipping';
|
||||||
const WC_STORE_API_ENDPOINT = '/wp-json/wc/store/cart/';
|
const WC_STORE_API_ENDPOINT = '/wp-json/wc/store/v1/cart/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Request Data Helper.
|
* The Request Data Helper.
|
||||||
|
|
|
@ -130,13 +130,6 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
*/
|
*/
|
||||||
private $all_funding_sources;
|
private $all_funding_sources;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether shipping details must be collected during checkout; i.e. paying for physical goods?
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $need_shipping;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assets constructor.
|
* Assets constructor.
|
||||||
*
|
*
|
||||||
|
@ -155,7 +148,6 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
* @param string $place_order_button_text The text for the standard "Place order" button.
|
* @param string $place_order_button_text The text for the standard "Place order" button.
|
||||||
* @param string $place_order_button_description The text for additional "Place order" description.
|
* @param string $place_order_button_description The text for additional "Place order" description.
|
||||||
* @param array $all_funding_sources All existing funding sources for PayPal buttons.
|
* @param array $all_funding_sources All existing funding sources for PayPal buttons.
|
||||||
* @param bool $need_shipping Whether shipping details are required for the purchase.
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $module_url,
|
string $module_url,
|
||||||
|
@ -172,8 +164,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
bool $use_place_order,
|
bool $use_place_order,
|
||||||
string $place_order_button_text,
|
string $place_order_button_text,
|
||||||
string $place_order_button_description,
|
string $place_order_button_description,
|
||||||
array $all_funding_sources,
|
array $all_funding_sources
|
||||||
bool $need_shipping
|
|
||||||
) {
|
) {
|
||||||
$this->name = PayPalGateway::ID;
|
$this->name = PayPalGateway::ID;
|
||||||
$this->module_url = $module_url;
|
$this->module_url = $module_url;
|
||||||
|
@ -191,7 +182,6 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
$this->place_order_button_text = $place_order_button_text;
|
$this->place_order_button_text = $place_order_button_text;
|
||||||
$this->place_order_button_description = $place_order_button_description;
|
$this->place_order_button_description = $place_order_button_description;
|
||||||
$this->all_funding_sources = $all_funding_sources;
|
$this->all_funding_sources = $all_funding_sources;
|
||||||
$this->need_shipping = $need_shipping;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,6 +248,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
&& $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ?? 'block-checkout' );
|
&& $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ?? 'block-checkout' );
|
||||||
$place_order_enabled = ( $this->use_place_order || $this->add_place_order_method )
|
$place_order_enabled = ( $this->use_place_order || $this->add_place_order_method )
|
||||||
&& ! $this->subscription_helper->cart_contains_subscription();
|
&& ! $this->subscription_helper->cart_contains_subscription();
|
||||||
|
$cart = WC()->cart;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'id' => $this->gateway->id,
|
'id' => $this->gateway->id,
|
||||||
|
@ -284,7 +275,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'scriptData' => $script_data,
|
'scriptData' => $script_data,
|
||||||
'needShipping' => $this->need_shipping,
|
'needShipping' => $cart && $cart->needs_shipping(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ export const handleShippingOptionsChange = async ( data, actions, config ) => {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-WC-Store-API-Nonce':
|
'Nonce':
|
||||||
config.ajax.update_customer_shipping.wp_rest_nonce,
|
config.ajax.update_customer_shipping.wp_rest_nonce,
|
||||||
},
|
},
|
||||||
body: JSON.stringify( {
|
body: JSON.stringify( {
|
||||||
rate_id: shippingOptionId,
|
rate_id: shippingOptionId,
|
||||||
|
@ -106,9 +106,9 @@ export const handleShippingAddressChange = async ( data, actions, config ) => {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-WC-Store-API-Nonce':
|
'Nonce':
|
||||||
config.ajax.update_customer_shipping
|
config.ajax.update_customer_shipping
|
||||||
.wp_rest_nonce,
|
.wp_rest_nonce,
|
||||||
},
|
},
|
||||||
body: JSON.stringify( {
|
body: JSON.stringify( {
|
||||||
shipping_address: cartData.shipping_address,
|
shipping_address: cartData.shipping_address,
|
||||||
|
|
|
@ -36,7 +36,6 @@ use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||||
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
|
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ return array(
|
||||||
return $client_id;
|
return $client_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$env = $container->get( 'onboarding.environment' );
|
$env = $container->get( 'settings.environment' );
|
||||||
/**
|
/**
|
||||||
* The environment.
|
* The environment.
|
||||||
*
|
*
|
||||||
|
@ -125,8 +124,8 @@ return array(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$state = $container->get( 'onboarding.state' );
|
$is_connected = $container->get( 'settings.flag.is-connected' );
|
||||||
if ( $state->current_state() !== State::STATE_ONBOARDED ) {
|
if ( ! $is_connected ) {
|
||||||
return new DisabledSmartButton();
|
return new DisabledSmartButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +141,7 @@ return array(
|
||||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||||
$messages_apply = $container->get( 'button.helper.messages-apply' );
|
$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' );
|
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
|
||||||
return new SmartButton(
|
return new SmartButton(
|
||||||
$container->get( 'button.url' ),
|
$container->get( 'button.url' ),
|
||||||
|
@ -241,11 +240,11 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler {
|
'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler {
|
||||||
|
return new EarlyOrderHandler(
|
||||||
$state = $container->get( 'onboarding.state' );
|
$container->get( 'settings.flag.is-connected' ),
|
||||||
$order_processor = $container->get( 'wcgateway.order-processor' );
|
$container->get( 'wcgateway.order-processor' ),
|
||||||
$session_handler = $container->get( 'session.handler' );
|
$container->get( 'session.handler' )
|
||||||
return new EarlyOrderHandler( $state, $order_processor, $session_handler );
|
);
|
||||||
},
|
},
|
||||||
'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint {
|
'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint {
|
||||||
$request_data = $container->get( 'button.request-data' );
|
$request_data = $container->get( 'button.request-data' );
|
||||||
|
|
|
@ -272,8 +272,9 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function toggle_final_review_enabled_setting(): 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->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();
|
$this->settings->persist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,6 @@ class DisabledFundingSources {
|
||||||
$disable_funding = $all_sources;
|
$disable_funding = $all_sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $disable_funding;
|
return apply_filters( 'woocommerce_paypal_payments_disabled_funding_sources', $disable_funding );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ namespace WooCommerce\PayPalCommerce\Button\Helper;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
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\Session\SessionHandler;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||||
|
@ -23,11 +21,11 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||||
class EarlyOrderHandler {
|
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.
|
* The Order Processor.
|
||||||
|
@ -46,17 +44,17 @@ class EarlyOrderHandler {
|
||||||
/**
|
/**
|
||||||
* EarlyOrderHandler constructor.
|
* EarlyOrderHandler constructor.
|
||||||
*
|
*
|
||||||
* @param State $state The State.
|
* @param bool $is_connected Whether onboarding was completed.
|
||||||
* @param OrderProcessor $order_processor The Order Processor.
|
* @param OrderProcessor $order_processor The Order Processor.
|
||||||
* @param SessionHandler $session_handler The Session Handler.
|
* @param SessionHandler $session_handler The Session Handler.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
State $state,
|
bool $is_connected,
|
||||||
OrderProcessor $order_processor,
|
OrderProcessor $order_processor,
|
||||||
SessionHandler $session_handler
|
SessionHandler $session_handler
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$this->state = $state;
|
$this->is_connected = $is_connected;
|
||||||
$this->order_processor = $order_processor;
|
$this->order_processor = $order_processor;
|
||||||
$this->session_handler = $session_handler;
|
$this->session_handler = $session_handler;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +65,7 @@ class EarlyOrderHandler {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function should_create_early_order(): 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
|
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||||
|
|
|
@ -199,8 +199,9 @@ class WooCommerceOrderCreator {
|
||||||
$shipping_options = null;
|
$shipping_options = null;
|
||||||
|
|
||||||
if ( $payer ) {
|
if ( $payer ) {
|
||||||
$address = $payer->address();
|
$address = $payer->address();
|
||||||
$payer_name = $payer->name();
|
$payer_name = $payer->name();
|
||||||
|
$payer_phone = $payer->phone();
|
||||||
|
|
||||||
$wc_email = null;
|
$wc_email = null;
|
||||||
$wc_customer = WC()->customer;
|
$wc_customer = WC()->customer;
|
||||||
|
@ -220,6 +221,7 @@ class WooCommerceOrderCreator {
|
||||||
'state' => $address ? $address->admin_area_1() : '',
|
'state' => $address ? $address->admin_area_1() : '',
|
||||||
'postcode' => $address ? $address->postal_code() : '',
|
'postcode' => $address ? $address->postal_code() : '',
|
||||||
'country' => $address ? $address->country_code() : '',
|
'country' => $address ? $address->country_code() : '',
|
||||||
|
'phone' => $payer_phone ? $payer_phone->phone()->national_number() : '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ return array(
|
||||||
'FR',
|
'FR',
|
||||||
'DE',
|
'DE',
|
||||||
'GR',
|
'GR',
|
||||||
|
'HK',
|
||||||
'HU',
|
'HU',
|
||||||
'IE',
|
'IE',
|
||||||
'IT',
|
'IT',
|
||||||
|
@ -56,6 +57,7 @@ return array(
|
||||||
'PT',
|
'PT',
|
||||||
'RO',
|
'RO',
|
||||||
'SK',
|
'SK',
|
||||||
|
'SG',
|
||||||
'SI',
|
'SI',
|
||||||
'ES',
|
'ES',
|
||||||
'SE',
|
'SE',
|
||||||
|
|
|
@ -10,9 +10,13 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Compat;
|
namespace WooCommerce\PayPalCommerce\Compat;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets;
|
use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets;
|
||||||
|
use WooCommerce\PayPalCommerce\Compat\Settings\GeneralSettingsMapHelper;
|
||||||
|
use WooCommerce\PayPalCommerce\Compat\Settings\PaymentMethodSettingsMapHelper;
|
||||||
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMap;
|
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMap;
|
||||||
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
|
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
|
||||||
|
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsTabMapHelper;
|
||||||
use WooCommerce\PayPalCommerce\Compat\Settings\StylingSettingsMapHelper;
|
use WooCommerce\PayPalCommerce\Compat\Settings\StylingSettingsMapHelper;
|
||||||
|
use WooCommerce\PayPalCommerce\Compat\Settings\SubscriptionSettingsMapHelper;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
@ -133,30 +137,26 @@ return array(
|
||||||
$styling_settings_map_helper = $container->get( 'compat.settings.styling_map_helper' );
|
$styling_settings_map_helper = $container->get( 'compat.settings.styling_map_helper' );
|
||||||
assert( $styling_settings_map_helper instanceof StylingSettingsMapHelper );
|
assert( $styling_settings_map_helper instanceof StylingSettingsMapHelper );
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
$payment_methods_map_helper = $container->get( 'compat.settings.payment_methods_map_helper' );
|
||||||
|
assert( $payment_methods_map_helper instanceof PaymentMethodSettingsMapHelper );
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
new SettingsMap(
|
new SettingsMap(
|
||||||
$container->get( 'settings.data.general' ),
|
$container->get( 'settings.data.general' ),
|
||||||
/**
|
$general_map_helper->map()
|
||||||
* The new GeneralSettings class stores the current connection
|
),
|
||||||
* details, without adding an environment-suffix (no `_sandbox`
|
new SettingsMap(
|
||||||
* or `_production` in the field name)
|
$container->get( 'settings.data.settings' ),
|
||||||
* Only the `sandbox_merchant` flag indicates, which environment
|
$settings_tab_map_helper->map()
|
||||||
* 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',
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
new SettingsMap(
|
new SettingsMap(
|
||||||
$container->get( 'settings.data.styling' ),
|
$container->get( 'settings.data.styling' ),
|
||||||
|
@ -172,15 +172,50 @@ return array(
|
||||||
*/
|
*/
|
||||||
$styling_settings_map_helper->map()
|
$styling_settings_map_helper->map()
|
||||||
),
|
),
|
||||||
|
new SettingsMap(
|
||||||
|
$container->get( 'settings.data.settings' ),
|
||||||
|
$subscription_map_helper->map()
|
||||||
|
),
|
||||||
|
/**
|
||||||
|
* We need to pass the PaymentSettings model instance to use it in some helpers.
|
||||||
|
* Once the new settings module is permanently enabled,
|
||||||
|
* this model can be passed as a dependency to the appropriate helper classes.
|
||||||
|
* For now, we must pass it this way to avoid errors when the new settings module is disabled.
|
||||||
|
*/
|
||||||
|
new SettingsMap(
|
||||||
|
$container->get( 'settings.data.payment' ),
|
||||||
|
array()
|
||||||
|
),
|
||||||
|
new SettingsMap(
|
||||||
|
$container->get( 'settings.data.payment' ),
|
||||||
|
$payment_methods_map_helper->map()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'compat.settings.settings_map_helper' => static function( ContainerInterface $container ) : SettingsMapHelper {
|
'compat.settings.settings_map_helper' => static function( ContainerInterface $container ) : SettingsMapHelper {
|
||||||
return new SettingsMapHelper(
|
return new SettingsMapHelper(
|
||||||
$container->get( 'compat.setting.new-to-old-map' ),
|
$container->get( 'compat.setting.new-to-old-map' ),
|
||||||
$container->get( 'compat.settings.styling_map_helper' )
|
$container->get( 'compat.settings.styling_map_helper' ),
|
||||||
|
$container->get( 'compat.settings.settings_tab_map_helper' ),
|
||||||
|
$container->get( 'compat.settings.subscription_map_helper' ),
|
||||||
|
$container->get( 'compat.settings.general_map_helper' ),
|
||||||
|
$container->get( 'compat.settings.payment_methods_map_helper' ),
|
||||||
|
$container->get( 'wcgateway.settings.admin-settings-enabled' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'compat.settings.styling_map_helper' => static function() : StylingSettingsMapHelper {
|
'compat.settings.styling_map_helper' => static function() : StylingSettingsMapHelper {
|
||||||
return new StylingSettingsMapHelper();
|
return new StylingSettingsMapHelper();
|
||||||
},
|
},
|
||||||
|
'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();
|
||||||
|
},
|
||||||
|
'compat.settings.payment_methods_map_helper' => static function() : PaymentMethodSettingsMapHelper {
|
||||||
|
return new PaymentMethodSettingsMapHelper();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* A helper for mapping the old/new payment method settings.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\Compat\Settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of old to new payment method settings.
|
||||||
|
*
|
||||||
|
* @psalm-import-type newSettingsKey from SettingsMap
|
||||||
|
* @psalm-import-type oldSettingsKey from SettingsMap
|
||||||
|
*/
|
||||||
|
class PaymentMethodSettingsMapHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps old setting keys to new payment method settings names.
|
||||||
|
*
|
||||||
|
* @psalm-return array<oldSettingsKey, newSettingsKey>
|
||||||
|
*/
|
||||||
|
public function map(): array {
|
||||||
|
return array(
|
||||||
|
'dcc_enabled' => CreditCardGateway::ID,
|
||||||
|
'axo_enabled' => AxoGateway::ID,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of a mapped key from the new settings.
|
||||||
|
*
|
||||||
|
* @param string $old_key The key from the legacy settings.
|
||||||
|
* @return mixed The value of the mapped setting, (null if not found).
|
||||||
|
*/
|
||||||
|
public function mapped_value( string $old_key ): ?bool {
|
||||||
|
|
||||||
|
$payment_method = $this->map()[ $old_key ] ?? false;
|
||||||
|
|
||||||
|
if ( ! $payment_method ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->is_gateway_enabled( $payment_method );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the payment gateway with the given name is enabled.
|
||||||
|
*
|
||||||
|
* @param string $gateway_name The gateway name.
|
||||||
|
* @return bool True if the payment gateway with the given name is enabled, otherwise false.
|
||||||
|
*/
|
||||||
|
protected function is_gateway_enabled( string $gateway_name ): bool {
|
||||||
|
$gateway_settings = get_option( "woocommerce_{$gateway_name}_settings", array() );
|
||||||
|
$gateway_enabled = $gateway_settings['enabled'] ?? false;
|
||||||
|
|
||||||
|
return $gateway_enabled === 'yes';
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,10 @@ declare( strict_types = 1 );
|
||||||
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\AbstractDataModel;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\PaymentSettings;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\SettingsModel;
|
||||||
use WooCommerce\PayPalCommerce\Settings\Data\StylingSettings;
|
use WooCommerce\PayPalCommerce\Settings\Data\StylingSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,17 +53,70 @@ class SettingsMapHelper {
|
||||||
*/
|
*/
|
||||||
protected StylingSettingsMapHelper $styling_settings_map_helper;
|
protected StylingSettingsMapHelper $styling_settings_map_helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper for mapping the old/new settings tab settings.
|
||||||
|
*
|
||||||
|
* @var SettingsTabMapHelper
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper for mapping old and new payment method settings.
|
||||||
|
*
|
||||||
|
* @var PaymentMethodSettingsMapHelper
|
||||||
|
*/
|
||||||
|
protected PaymentMethodSettingsMapHelper $payment_method_settings_map_helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the new settings module is enabled.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected bool $new_settings_module_enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param SettingsMap[] $settings_map A list of settings maps containing key definitions.
|
* @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 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.
|
||||||
|
* @param PaymentMethodSettingsMapHelper $payment_method_settings_map_helper A helper for mapping old and new payment method settings.
|
||||||
|
* @param bool $new_settings_module_enabled Whether the new settings module is enabled.
|
||||||
* @throws RuntimeException When an old key has multiple mappings.
|
* @throws RuntimeException When an old key has multiple mappings.
|
||||||
*/
|
*/
|
||||||
public function __construct( array $settings_map, StylingSettingsMapHelper $styling_settings_map_helper ) {
|
public function __construct(
|
||||||
|
array $settings_map,
|
||||||
|
StylingSettingsMapHelper $styling_settings_map_helper,
|
||||||
|
SettingsTabMapHelper $settings_tab_map_helper,
|
||||||
|
SubscriptionSettingsMapHelper $subscription_map_helper,
|
||||||
|
GeneralSettingsMapHelper $general_settings_map_helper,
|
||||||
|
PaymentMethodSettingsMapHelper $payment_method_settings_map_helper,
|
||||||
|
bool $new_settings_module_enabled
|
||||||
|
) {
|
||||||
$this->validate_settings_map( $settings_map );
|
$this->validate_settings_map( $settings_map );
|
||||||
$this->settings_map = $settings_map;
|
$this->settings_map = $settings_map;
|
||||||
$this->styling_settings_map_helper = $styling_settings_map_helper;
|
$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;
|
||||||
|
$this->payment_method_settings_map_helper = $payment_method_settings_map_helper;
|
||||||
|
$this->new_settings_module_enabled = $new_settings_module_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,6 +146,10 @@ class SettingsMapHelper {
|
||||||
* @return mixed|null The value of the mapped setting, or null if not found.
|
* @return mixed|null The value of the mapped setting, or null if not found.
|
||||||
*/
|
*/
|
||||||
public function mapped_value( string $old_key ) {
|
public function mapped_value( string $old_key ) {
|
||||||
|
if ( ! $this->new_settings_module_enabled ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$this->ensure_map_initialized();
|
$this->ensure_map_initialized();
|
||||||
if ( ! isset( $this->key_to_model[ $old_key ] ) ) {
|
if ( ! isset( $this->key_to_model[ $old_key ] ) ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -112,6 +173,10 @@ class SettingsMapHelper {
|
||||||
* @return bool True if the key exists in the new settings, false otherwise.
|
* @return bool True if the key exists in the new settings, false otherwise.
|
||||||
*/
|
*/
|
||||||
public function has_mapped_key( string $old_key ) : bool {
|
public function has_mapped_key( string $old_key ) : bool {
|
||||||
|
if ( ! $this->new_settings_module_enabled ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->ensure_map_initialized();
|
$this->ensure_map_initialized();
|
||||||
|
|
||||||
return isset( $this->key_to_model[ $old_key ] );
|
return isset( $this->key_to_model[ $old_key ] );
|
||||||
|
@ -134,7 +199,23 @@ class SettingsMapHelper {
|
||||||
|
|
||||||
switch ( true ) {
|
switch ( true ) {
|
||||||
case $model instanceof StylingSettings:
|
case $model instanceof StylingSettings:
|
||||||
return $this->styling_settings_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
return $this->styling_settings_map_helper->mapped_value(
|
||||||
|
$old_key,
|
||||||
|
$this->model_cache[ $model_id ],
|
||||||
|
$this->get_payment_settings_model()
|
||||||
|
);
|
||||||
|
|
||||||
|
case $model instanceof GeneralSettings:
|
||||||
|
return $this->general_settings_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
|
||||||
|
|
||||||
|
case $model instanceof SettingsModel:
|
||||||
|
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 ] );
|
||||||
|
|
||||||
|
case $model instanceof PaymentSettings:
|
||||||
|
return $this->payment_method_settings_map_helper->mapped_value( $old_key );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return $this->model_cache[ $model_id ][ $new_key ] ?? null;
|
return $this->model_cache[ $model_id ][ $new_key ] ?? null;
|
||||||
}
|
}
|
||||||
|
@ -173,4 +254,23 @@ class SettingsMapHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the PaymentSettings model instance.
|
||||||
|
*
|
||||||
|
* Once the new settings module is permanently enabled,
|
||||||
|
* this model can be passed as a dependency to the appropriate helper classes.
|
||||||
|
* For now, we must pass it this way to avoid errors when the new settings module is disabled.
|
||||||
|
*
|
||||||
|
* @return AbstractDataModel|null
|
||||||
|
*/
|
||||||
|
protected function get_payment_settings_model() : ?AbstractDataModel {
|
||||||
|
foreach ( $this->settings_map as $settings_map_instance ) {
|
||||||
|
if ( $settings_map_instance->get_model() instanceof PaymentSettings ) {
|
||||||
|
return $settings_map_instance->get_model();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
146
modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php
Normal file
146
modules/ppcp-compat/src/Settings/SettingsTabMapHelper.php
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* A helper for mapping the old/new settings tab settings.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\Compat\Settings
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer;
|
||||||
|
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of old to new styling settings.
|
||||||
|
*
|
||||||
|
* @psalm-import-type newSettingsKey from SettingsMap
|
||||||
|
* @psalm-import-type oldSettingsKey from SettingsMap
|
||||||
|
*/
|
||||||
|
class SettingsTabMapHelper {
|
||||||
|
|
||||||
|
use ContextTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps old setting keys to new setting keys.
|
||||||
|
*
|
||||||
|
* @psalm-return array<oldSettingsKey, newSettingsKey>
|
||||||
|
*/
|
||||||
|
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',
|
||||||
|
'prefix' => 'invoice_prefix',
|
||||||
|
'intent' => '',
|
||||||
|
'vault_enabled_dcc' => 'save_card_details',
|
||||||
|
'blocks_final_review_enabled' => 'enable_pay_now',
|
||||||
|
'logging_enabled' => 'enable_logging',
|
||||||
|
'vault_enabled' => 'save_paypal_and_venmo',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of a mapped 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, (null if not found).
|
||||||
|
*/
|
||||||
|
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 'subtotal_mismatch_behavior':
|
||||||
|
return $this->mapped_mismatch_behavior_value( $settings_model );
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the mapped value for the 'mismatch_behavior' from the new settings.
|
||||||
|
*
|
||||||
|
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
|
||||||
|
* @return 'extra_line'|'ditch'|null The mapped 'mismatch_behavior' setting value.
|
||||||
|
*/
|
||||||
|
protected function mapped_mismatch_behavior_value( array $settings_model ): ?string {
|
||||||
|
$subtotal_adjustment = $settings_model['subtotal_adjustment'] ?? false;
|
||||||
|
|
||||||
|
if ( ! $subtotal_adjustment ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subtotal_adjustment === 'correction' ? PurchaseUnitSanitizer::MODE_EXTRA_LINE : PurchaseUnitSanitizer::MODE_DITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the mapped value for the 'landing_page' from the new settings.
|
||||||
|
*
|
||||||
|
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
|
||||||
|
* @return 'LOGIN'|'BILLING'|'NO_PREFERENCE'|null The mapped 'landing_page' setting value.
|
||||||
|
*/
|
||||||
|
protected function mapped_landing_page_value( array $settings_model ): ?string {
|
||||||
|
$landing_page = $settings_model['landing_page'] ?? false;
|
||||||
|
|
||||||
|
if ( ! $landing_page ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $landing_page === 'login'
|
||||||
|
? ApplicationContext::LANDING_PAGE_LOGIN
|
||||||
|
: ( $landing_page === 'guest_checkout'
|
||||||
|
? ApplicationContext::LANDING_PAGE_BILLING
|
||||||
|
: 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,11 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
namespace WooCommerce\PayPalCommerce\Compat\Settings;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||||
|
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\AbstractDataModel;
|
||||||
|
use WooCommerce\PayPalCommerce\Settings\Data\PaymentSettings;
|
||||||
use WooCommerce\PayPalCommerce\Settings\DTO\LocationStylingDTO;
|
use WooCommerce\PayPalCommerce\Settings\DTO\LocationStylingDTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +27,8 @@ class StylingSettingsMapHelper {
|
||||||
|
|
||||||
use ContextTrait;
|
use ContextTrait;
|
||||||
|
|
||||||
|
protected const BUTTON_NAMES = array( GooglePayGateway::ID, ApplePayGateway::ID );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps old setting keys to new setting style names.
|
* Maps old setting keys to new setting style names.
|
||||||
*
|
*
|
||||||
|
@ -40,11 +46,13 @@ class StylingSettingsMapHelper {
|
||||||
public function map(): array {
|
public function map(): array {
|
||||||
|
|
||||||
$mapped_settings = array(
|
$mapped_settings = array(
|
||||||
'smart_button_locations' => '',
|
'smart_button_locations' => '',
|
||||||
'pay_later_button_locations' => '',
|
'pay_later_button_locations' => '',
|
||||||
'disable_funding' => '',
|
'disable_funding' => '',
|
||||||
'googlepay_button_enabled' => '',
|
'googlepay_button_enabled' => '',
|
||||||
'applepay_button_enabled' => '',
|
'applepay_button_enabled' => '',
|
||||||
|
'smart_button_enable_styling_per_location' => '',
|
||||||
|
'pay_later_button_enabled' => '',
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
|
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
|
||||||
|
@ -60,27 +68,34 @@ class StylingSettingsMapHelper {
|
||||||
/**
|
/**
|
||||||
* Retrieves the value of a mapped key from the new settings.
|
* Retrieves the value of a mapped key from the new settings.
|
||||||
*
|
*
|
||||||
* @param string $old_key The key from the legacy settings.
|
* @param string $old_key The key from the legacy settings.
|
||||||
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
||||||
|
* @param AbstractDataModel|null $payment_settings The payment settings model.
|
||||||
*
|
*
|
||||||
* @return mixed The value of the mapped setting, (null if not found).
|
* @return mixed The value of the mapped setting, (null if not found).
|
||||||
*/
|
*/
|
||||||
public function mapped_value( string $old_key, array $styling_models ) {
|
public function mapped_value( string $old_key, array $styling_models, ?AbstractDataModel $payment_settings ) {
|
||||||
switch ( $old_key ) {
|
switch ( $old_key ) {
|
||||||
case 'smart_button_locations':
|
case 'smart_button_locations':
|
||||||
return $this->mapped_smart_button_locations_value( $styling_models );
|
return $this->mapped_smart_button_locations_value( $styling_models );
|
||||||
|
|
||||||
|
case 'smart_button_enable_styling_per_location':
|
||||||
|
return true;
|
||||||
|
|
||||||
case 'pay_later_button_locations':
|
case 'pay_later_button_locations':
|
||||||
return $this->mapped_pay_later_button_locations_value( $styling_models );
|
return $this->mapped_pay_later_button_locations_value( $styling_models );
|
||||||
|
|
||||||
case 'disable_funding':
|
case 'disable_funding':
|
||||||
return $this->mapped_disabled_funding_value( $styling_models );
|
return $this->mapped_disabled_funding_value( $styling_models, $payment_settings );
|
||||||
|
|
||||||
case 'googlepay_button_enabled':
|
case 'googlepay_button_enabled':
|
||||||
return $this->mapped_google_pay_or_apple_pay_enabled_value( $styling_models, 'googlepay' );
|
return $this->mapped_button_enabled_value( $styling_models, GooglePayGateway::ID );
|
||||||
|
|
||||||
case 'applepay_button_enabled':
|
case 'applepay_button_enabled':
|
||||||
return $this->mapped_google_pay_or_apple_pay_enabled_value( $styling_models, 'applepay' );
|
return $this->mapped_button_enabled_value( $styling_models, ApplePayGateway::ID );
|
||||||
|
|
||||||
|
case 'pay_later_button_enabled':
|
||||||
|
return $this->mapped_pay_later_button_enabled_value( $styling_models, $payment_settings );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
|
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
|
||||||
|
@ -198,7 +213,7 @@ class StylingSettingsMapHelper {
|
||||||
$enabled_locations = array();
|
$enabled_locations = array();
|
||||||
$locations = array_flip( $this->locations_map() );
|
$locations = array_flip( $this->locations_map() );
|
||||||
foreach ( $styling_models as $model ) {
|
foreach ( $styling_models as $model ) {
|
||||||
if ( ! $model->enabled || ! in_array( 'paylater', $model->methods, true ) ) {
|
if ( ! $model->enabled || ! in_array( 'pay-later', $model->methods, true ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,52 +229,116 @@ class StylingSettingsMapHelper {
|
||||||
/**
|
/**
|
||||||
* Retrieves the mapped disabled funding value from the new settings.
|
* Retrieves the mapped disabled funding value from the new settings.
|
||||||
*
|
*
|
||||||
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
||||||
|
* @param AbstractDataModel|null $payment_settings The payment settings model.
|
||||||
* @return array|null The list of disabled funding, or null if none are disabled.
|
* @return array|null The list of disabled funding, or null if none are disabled.
|
||||||
*/
|
*/
|
||||||
protected function mapped_disabled_funding_value( array $styling_models ): ?array {
|
protected function mapped_disabled_funding_value( array $styling_models, ?AbstractDataModel $payment_settings ): ?array {
|
||||||
$disabled_funding = array();
|
if ( is_null( $payment_settings ) ) {
|
||||||
$locations_to_context_map = $this->current_context_to_new_button_location_map();
|
return null;
|
||||||
|
|
||||||
foreach ( $styling_models as $model ) {
|
|
||||||
if ( $model->location !== $locations_to_context_map[ $this->context() ] || in_array( 'venmo', $model->methods, true ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$disabled_funding[] = 'venmo';
|
|
||||||
|
|
||||||
return $disabled_funding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
$disabled_funding = array();
|
||||||
|
$locations_to_context_map = $this->current_context_to_new_button_location_map();
|
||||||
|
$current_context = $locations_to_context_map[ $this->context() ] ?? '';
|
||||||
|
assert( $payment_settings instanceof PaymentSettings );
|
||||||
|
|
||||||
|
foreach ( $styling_models as $model ) {
|
||||||
|
if ( $model->location === $current_context ) {
|
||||||
|
if ( ! in_array( 'venmo', $model->methods, true ) || ! $payment_settings->get_venmo_enabled() ) {
|
||||||
|
$disabled_funding[] = 'venmo';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $disabled_funding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the mapped enabled/disabled Google Pay or Apple Pay value from the new settings.
|
* Retrieves the mapped enabled or disabled PayLater button value from the new settings.
|
||||||
*
|
*
|
||||||
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
||||||
* @param 'googlepay'|'applepay' $button_name The button name ('googlepay' or 'applepay').
|
* @param AbstractDataModel|null $payment_settings The payment settings model.
|
||||||
* @return int The enabled (1) or disabled (0) state.
|
* @return int|null The enabled (1) or disabled (0) state or null if it should fall back to old settings value.
|
||||||
* @throws RuntimeException If an invalid button name is provided.
|
|
||||||
*/
|
*/
|
||||||
protected function mapped_google_pay_or_apple_pay_enabled_value( array $styling_models, string $button_name ): ?int {
|
protected function mapped_pay_later_button_enabled_value( array $styling_models, ?AbstractDataModel $payment_settings ): ?int {
|
||||||
if ( $button_name !== 'googlepay' && $button_name !== 'applepay' ) {
|
|
||||||
throw new RuntimeException( 'Wrong button name is provided. Either "googlepay" or "applepay" can be used' );
|
if ( ! $payment_settings instanceof PaymentSettings ) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$locations_to_context_map = $this->current_context_to_new_button_location_map();
|
$locations_to_context_map = $this->current_context_to_new_button_location_map();
|
||||||
|
$current_context = $locations_to_context_map[ $this->context() ] ?? '';
|
||||||
|
|
||||||
foreach ( $styling_models as $model ) {
|
foreach ( $styling_models as $model ) {
|
||||||
if ( ! $model->enabled
|
if ( $model->enabled && $model->location === $current_context ) {
|
||||||
|| $model->location !== $locations_to_context_map[ $this->context() ]
|
if ( in_array( 'pay-later', $model->methods, true ) && $payment_settings->get_paylater_enabled() ) {
|
||||||
|| ! in_array( $button_name, $model->methods, true )
|
return 1;
|
||||||
) {
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the mapped enabled or disabled button value from the new settings.
|
||||||
|
*
|
||||||
|
* @param LocationStylingDTO[] $styling_models The list of location styling models.
|
||||||
|
* @param string $button_name The button name (see {@link self::BUTTON_NAMES}).
|
||||||
|
* @return int The enabled (1) or disabled (0) state.
|
||||||
|
* @throws RuntimeException If an invalid button name is provided.
|
||||||
|
*/
|
||||||
|
protected function mapped_button_enabled_value( array $styling_models, string $button_name ): ?int {
|
||||||
|
if ( ! in_array( $button_name, self::BUTTON_NAMES, true ) ) {
|
||||||
|
throw new RuntimeException( 'Wrong button name is provided.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$locations_to_context_map = $this->current_context_to_new_button_location_map();
|
||||||
|
$current_context = $locations_to_context_map[ $this->context() ] ?? '';
|
||||||
|
|
||||||
|
foreach ( $styling_models as $model ) {
|
||||||
|
if ( $model->enabled && $model->location === $current_context ) {
|
||||||
|
if ( in_array( $button_name, $model->methods, true ) && $this->is_gateway_enabled( $button_name ) ) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $current_context === 'classic_checkout' ) {
|
||||||
|
/**
|
||||||
|
* Outputs an inline CSS style that hides the Google Pay gateway (on Classic Checkout)
|
||||||
|
* In case if the button is disabled from the styling settings but the gateway itself is enabled.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
add_action(
|
||||||
|
'woocommerce_paypal_payments_checkout_button_render',
|
||||||
|
static function (): void {
|
||||||
|
?>
|
||||||
|
<style data-hide-gateway='<?php echo esc_attr( GooglePayGateway::ID ); ?>'>
|
||||||
|
.wc_payment_method.payment_method_ppcp-googlepay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the payment gateway with the given name is enabled.
|
||||||
|
*
|
||||||
|
* @param string $gateway_name The gateway name.
|
||||||
|
* @return bool True if the payment gateway with the given name is enabled, otherwise false.
|
||||||
|
*/
|
||||||
|
protected function is_gateway_enabled( string $gateway_name ): bool {
|
||||||
|
$gateway_settings = get_option( "woocommerce_{$gateway_name}_settings", array() );
|
||||||
|
$gateway_enabled = $gateway_settings['enabled'] ?? false;
|
||||||
|
|
||||||
|
return $gateway_enabled === 'yes';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,22 +81,23 @@ const GooglePayComponent = ( { isEditing, buttonAttributes } ) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const features = [ 'products' ];
|
const features = [ 'products' ];
|
||||||
|
if ( buttonConfig?.is_enabled ) {
|
||||||
registerExpressPaymentMethod( {
|
registerExpressPaymentMethod( {
|
||||||
name: buttonData.id,
|
name: buttonData.id,
|
||||||
title: `PayPal - ${ buttonData.title }`,
|
title: `PayPal - ${ buttonData.title }`,
|
||||||
description: __(
|
description: __(
|
||||||
'Eligible users will see the PayPal button.',
|
'Eligible users will see the PayPal button.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
gatewayId: 'ppcp-gateway',
|
gatewayId: 'ppcp-gateway',
|
||||||
label: <div dangerouslySetInnerHTML={ { __html: buttonData.title } } />,
|
label: <div dangerouslySetInnerHTML={ { __html: buttonData.title } } />,
|
||||||
content: <GooglePayComponent isEditing={ false } />,
|
content: <GooglePayComponent isEditing={ false } />,
|
||||||
edit: <GooglePayComponent isEditing={ true } />,
|
edit: <GooglePayComponent isEditing={ true } />,
|
||||||
ariaLabel: buttonData.title,
|
ariaLabel: buttonData.title,
|
||||||
canMakePayment: () => buttonData.enabled,
|
canMakePayment: () => buttonData.enabled,
|
||||||
supports: {
|
supports: {
|
||||||
features,
|
features,
|
||||||
style: [ 'height', 'borderRadius' ],
|
style: [ 'height', 'borderRadius' ],
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmApplies;
|
||||||
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
|
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
|
||||||
use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice;
|
use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
@ -106,6 +105,7 @@ return array(
|
||||||
'FR', // France
|
'FR', // France
|
||||||
'DE', // Germany
|
'DE', // Germany
|
||||||
'GR', // Greece
|
'GR', // Greece
|
||||||
|
'HK', // Hong Kong
|
||||||
'HU', // Hungary
|
'HU', // Hungary
|
||||||
'IE', // Ireland
|
'IE', // Ireland
|
||||||
'IT', // Italy
|
'IT', // Italy
|
||||||
|
@ -119,6 +119,7 @@ return array(
|
||||||
'PL', // Poland
|
'PL', // Poland
|
||||||
'PT', // Portugal
|
'PT', // Portugal
|
||||||
'RO', // Romania
|
'RO', // Romania
|
||||||
|
'SG', // Singapore
|
||||||
'SK', // Slovakia
|
'SK', // Slovakia
|
||||||
'SI', // Slovenia
|
'SI', // Slovenia
|
||||||
'ES', // Spain
|
'ES', // Spain
|
||||||
|
@ -148,6 +149,7 @@ return array(
|
||||||
'CZK', // Czech Koruna
|
'CZK', // Czech Koruna
|
||||||
'DKK', // Danish Krone
|
'DKK', // Danish Krone
|
||||||
'EUR', // Euro
|
'EUR', // Euro
|
||||||
|
'HKD', // Hong Kong Dollar
|
||||||
'GBP', // British Pound Sterling
|
'GBP', // British Pound Sterling
|
||||||
'HUF', // Hungarian Forint
|
'HUF', // Hungarian Forint
|
||||||
'ILS', // Israeli New Shekel
|
'ILS', // Israeli New Shekel
|
||||||
|
@ -157,6 +159,7 @@ return array(
|
||||||
'NZD', // New Zealand Dollar
|
'NZD', // New Zealand Dollar
|
||||||
'PHP', // Philippine Peso
|
'PHP', // Philippine Peso
|
||||||
'PLN', // Polish Zloty
|
'PLN', // Polish Zloty
|
||||||
|
'SGD', // Singapur-Dollar
|
||||||
'SEK', // Swedish Krona
|
'SEK', // Swedish Krona
|
||||||
'THB', // Thai Baht
|
'THB', // Thai Baht
|
||||||
'TWD', // New Taiwan Dollar
|
'TWD', // New Taiwan Dollar
|
||||||
|
@ -174,7 +177,7 @@ return array(
|
||||||
$container->get( 'session.handler' ),
|
$container->get( 'session.handler' ),
|
||||||
$container->get( 'wc-subscriptions.helper' ),
|
$container->get( 'wc-subscriptions.helper' ),
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'settings.environment' ),
|
||||||
$container->get( 'wcgateway.settings.status' ),
|
$container->get( 'wcgateway.settings.status' ),
|
||||||
$container->get( 'woocommerce.logger.woocommerce' )
|
$container->get( 'woocommerce.logger.woocommerce' )
|
||||||
);
|
);
|
||||||
|
@ -221,15 +224,15 @@ return array(
|
||||||
},
|
},
|
||||||
|
|
||||||
'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
||||||
$state = $container->get( 'onboarding.state' );
|
$is_connected = $container->get( 'settings.flag.is-connected' );
|
||||||
if ( $state->current_state() < State::STATE_ONBOARDED ) {
|
if ( ! $is_connected ) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$product_status = $container->get( 'googlepay.helpers.apm-product-status' );
|
$product_status = $container->get( 'googlepay.helpers.apm-product-status' );
|
||||||
assert( $product_status instanceof ApmProductStatus );
|
assert( $product_status instanceof ApmProductStatus );
|
||||||
|
|
||||||
$environment = $container->get( 'onboarding.environment' );
|
$environment = $container->get( 'settings.environment' );
|
||||||
assert( $environment instanceof Environment );
|
assert( $environment instanceof Environment );
|
||||||
|
|
||||||
$enabled = $product_status->is_active();
|
$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 ) {
|
if ( $has_capability ) {
|
||||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods;
|
||||||
|
|
||||||
use WC_Order;
|
use WC_Order;
|
||||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
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\ExecutableModule;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||||
|
@ -43,8 +42,31 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function run( ContainerInterface $c ): bool {
|
public function run( ContainerInterface $c ) : bool {
|
||||||
|
add_action( 'after_setup_theme', fn() => $this->run_with_translations( $c ) );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up WP hooks that depend on translation features.
|
||||||
|
* Runs after the theme setup, when translations are available, which is fired
|
||||||
|
* before the `init` hook, which usually contains most of the logic.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $c The DI container.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function run_with_translations( ContainerInterface $c ) : void {
|
||||||
|
// When Local APMs are disabled, none of the following hooks are needed.
|
||||||
|
if ( ! $this->should_add_local_apm_gateways( $c ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
add_filter(
|
||||||
'woocommerce_payment_gateways',
|
'woocommerce_payment_gateways',
|
||||||
/**
|
/**
|
||||||
|
@ -53,14 +75,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
* @psalm-suppress MissingClosureParamType
|
* @psalm-suppress MissingClosureParamType
|
||||||
*/
|
*/
|
||||||
function ( $methods ) use ( $c ) {
|
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 ) ) {
|
if ( ! is_array( $methods ) ) {
|
||||||
return $methods;
|
return $methods;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +88,10 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the "available gateways" list by REMOVING gateways that
|
||||||
|
* are not available for the current customer.
|
||||||
|
*/
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_available_payment_gateways',
|
'woocommerce_available_payment_gateways',
|
||||||
/**
|
/**
|
||||||
|
@ -82,29 +100,22 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
* @psalm-suppress MissingClosureParamType
|
* @psalm-suppress MissingClosureParamType
|
||||||
*/
|
*/
|
||||||
function ( $methods ) use ( $c ) {
|
function ( $methods ) use ( $c ) {
|
||||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
if ( ! is_array( $methods ) || is_admin() || empty( WC()->customer ) ) {
|
||||||
return $methods;
|
// Don't restrict the gateway list on wp-admin or when no customer is known.
|
||||||
}
|
|
||||||
if ( ! is_array( $methods ) ) {
|
|
||||||
return $methods;
|
return $methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! is_admin() ) {
|
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||||
if ( ! isset( WC()->customer ) ) {
|
$customer_country = WC()->customer->get_billing_country() ?: WC()->customer->get_shipping_country();
|
||||||
return $methods;
|
$site_currency = get_woocommerce_currency();
|
||||||
}
|
|
||||||
|
|
||||||
$customer_country = WC()->customer->get_billing_country() ?: WC()->customer->get_shipping_country();
|
// Remove unsupported gateways from the customer's payment options.
|
||||||
$site_currency = get_woocommerce_currency();
|
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' );
|
if ( ! $is_currency_supported || ! $is_country_supported ) {
|
||||||
foreach ( $payment_methods as $payment_method ) {
|
unset( $methods[ $payment_method['id'] ] );
|
||||||
if (
|
|
||||||
! in_array( $customer_country, $payment_method['countries'], true )
|
|
||||||
|| ! in_array( $site_currency, $payment_method['currencies'], true )
|
|
||||||
) {
|
|
||||||
unset( $methods[ $payment_method['id'] ] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,12 +123,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(
|
add_action(
|
||||||
'woocommerce_blocks_payment_method_type_registration',
|
'woocommerce_blocks_payment_method_type_registration',
|
||||||
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
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' );
|
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||||
foreach ( $payment_methods as $key => $value ) {
|
foreach ( $payment_methods as $key => $value ) {
|
||||||
$payment_method_registry->register( $c->get( 'ppcp-local-apms.' . $key . '.payment-method' ) );
|
$payment_method_registry->register( $c->get( 'ppcp-local-apms.' . $key . '.payment-method' ) );
|
||||||
|
@ -128,9 +142,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_paypal_payments_localized_script_data',
|
'woocommerce_paypal_payments_localized_script_data',
|
||||||
function ( array $data ) use ( $c ) {
|
function ( array $data ) use ( $c ) {
|
||||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||||
|
|
||||||
$default_disable_funding = $data['url_params']['disable-funding'] ?? '';
|
$default_disable_funding = $data['url_params']['disable-funding'] ?? '';
|
||||||
|
@ -149,9 +160,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
* @psalm-suppress MissingClosureParamType
|
* @psalm-suppress MissingClosureParamType
|
||||||
*/
|
*/
|
||||||
function( $order_id ) use ( $c ) {
|
function( $order_id ) use ( $c ) {
|
||||||
if ( ! self::should_add_local_apm_gateways( $c ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$order = wc_get_order( $order_id );
|
$order = wc_get_order( $order_id );
|
||||||
if ( ! $order instanceof WC_Order ) {
|
if ( ! $order instanceof WC_Order ) {
|
||||||
return;
|
return;
|
||||||
|
@ -184,9 +192,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_paypal_payments_payment_capture_completed_webhook_handler',
|
'woocommerce_paypal_payments_payment_capture_completed_webhook_handler',
|
||||||
function( WC_Order $wc_order, string $order_id ) use ( $c ) {
|
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' );
|
$payment_methods = $c->get( 'ppcp-local-apms.payment-methods' );
|
||||||
if (
|
if (
|
||||||
! $this->is_local_apm( $wc_order->get_payment_method(), $payment_methods )
|
! $this->is_local_apm( $wc_order->get_payment_method(), $payment_methods )
|
||||||
|
@ -202,8 +207,6 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
10,
|
10,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,12 +232,42 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
|
||||||
* @param ContainerInterface $container Container.
|
* @param ContainerInterface $container Container.
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function should_add_local_apm_gateways( ContainerInterface $container ): bool {
|
private function should_add_local_apm_gateways( ContainerInterface $container ) : bool {
|
||||||
|
// APMs are only available after merchant onboarding is 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.
|
||||||
|
*/
|
||||||
|
return $this->is_rest_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The general plugin functionality must be enabled.
|
||||||
$settings = $container->get( 'wcgateway.settings' );
|
$settings = $container->get( 'wcgateway.settings' );
|
||||||
assert( $settings instanceof Settings );
|
assert( $settings instanceof Settings );
|
||||||
return $settings->has( 'enabled' )
|
if ( ! $settings->has( 'enabled' ) || ! $settings->get( 'enabled' ) ) {
|
||||||
&& $settings->get( 'enabled' ) === true
|
return false;
|
||||||
&& $settings->has( 'allow_local_apm_gateways' )
|
}
|
||||||
|
|
||||||
|
// Register APM gateways, when the relevant setting is active.
|
||||||
|
return $settings->has( 'allow_local_apm_gateways' )
|
||||||
&& $settings->get( 'allow_local_apm_gateways' ) === true;
|
&& $settings->get( 'allow_local_apm_gateways' ) === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks, whether the current request is trying to access a WooCommerce REST endpoint.
|
||||||
|
*
|
||||||
|
* @return bool True, if the request path matches the WC-Rest namespace.
|
||||||
|
*/
|
||||||
|
private function is_rest_request(): bool {
|
||||||
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||||
|
$request_uri = wp_unslash( $_SERVER['REQUEST_URI'] ?? '' );
|
||||||
|
|
||||||
|
return str_contains( $request_uri, '/wp-json/wc/' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ class LocalApmProductStatus extends ProductStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Settings used as a cache; `settings->set` is compatible with new UI.
|
||||||
if ( $has_capability ) {
|
if ( $has_capability ) {
|
||||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -326,9 +326,9 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) {
|
||||||
|
|
||||||
isDisconnecting = true;
|
isDisconnecting = true;
|
||||||
|
|
||||||
const saveButton = document.querySelector( '.woocommerce-save-button' );
|
const saveButton = document.querySelector( '.woocommerce-save-button' );
|
||||||
saveButton.removeAttribute( 'disabled' );
|
saveButton.removeAttribute( 'disabled' );
|
||||||
saveButton.click();
|
saveButton.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prevent the message about unsaved checkbox/radiobutton when reloading the page.
|
// Prevent the message about unsaved checkbox/radiobutton when reloading the page.
|
||||||
|
@ -345,9 +345,11 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) {
|
||||||
|
|
||||||
const sandboxSwitchElement = document.querySelector( '#ppcp-sandbox_on' );
|
const sandboxSwitchElement = document.querySelector( '#ppcp-sandbox_on' );
|
||||||
|
|
||||||
sandboxSwitchElement?.addEventListener( 'click', () => {
|
sandboxSwitchElement?.addEventListener( 'click', () => {
|
||||||
document.querySelector( '.woocommerce-save-button' )?.removeAttribute( 'disabled' );
|
document
|
||||||
});
|
.querySelector( '.woocommerce-save-button' )
|
||||||
|
?.removeAttribute( 'disabled' );
|
||||||
|
} );
|
||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const selectors = sandboxSwitchElement.checked
|
const selectors = sandboxSwitchElement.checked
|
||||||
|
@ -389,7 +391,8 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) {
|
||||||
|
|
||||||
const isSandboxInBackend =
|
const isSandboxInBackend =
|
||||||
PayPalCommerceGatewayOnboarding.current_env === 'sandbox';
|
PayPalCommerceGatewayOnboarding.current_env === 'sandbox';
|
||||||
if ( sandboxSwitchElement.checked !== isSandboxInBackend ) {
|
|
||||||
|
if ( sandboxSwitchElement?.checked !== isSandboxInBackend ) {
|
||||||
sandboxSwitchElement.checked = isSandboxInBackend;
|
sandboxSwitchElement.checked = isSandboxInBackend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,11 @@ use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingSendOnlyNoticeRenderer;
|
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingSendOnlyNoticeRenderer;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'api.paypal-host' => function( ContainerInterface $container ) : string {
|
'api.paypal-host' => function( ContainerInterface $container ) : string {
|
||||||
$environment = $container->get( 'onboarding.environment' );
|
$environment = $container->get( 'settings.environment' );
|
||||||
/**
|
/**
|
||||||
* The current environment.
|
* The current environment.
|
||||||
*
|
*
|
||||||
|
@ -34,7 +35,7 @@ return array(
|
||||||
|
|
||||||
},
|
},
|
||||||
'api.paypal-website-url' => function( ContainerInterface $container ) : string {
|
'api.paypal-website-url' => function( ContainerInterface $container ) : string {
|
||||||
$environment = $container->get( 'onboarding.environment' );
|
$environment = $container->get( 'settings.environment' );
|
||||||
assert( $environment instanceof Environment );
|
assert( $environment instanceof Environment );
|
||||||
if ( $environment->current_environment_is( Environment::SANDBOX ) ) {
|
if ( $environment->current_environment_is( Environment::SANDBOX ) ) {
|
||||||
return $container->get( 'api.paypal-website-url-sandbox' );
|
return $container->get( 'api.paypal-website-url-sandbox' );
|
||||||
|
@ -56,9 +57,16 @@ return array(
|
||||||
|
|
||||||
return $state->current_state() >= State::STATE_ONBOARDED;
|
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' );
|
$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 {
|
'onboarding.assets' => function( ContainerInterface $container ) : OnboardingAssets {
|
||||||
|
@ -68,7 +76,7 @@ return array(
|
||||||
$container->get( 'onboarding.url' ),
|
$container->get( 'onboarding.url' ),
|
||||||
$container->get( 'ppcp.asset-version' ),
|
$container->get( 'ppcp.asset-version' ),
|
||||||
$state,
|
$state,
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'settings.environment' ),
|
||||||
$login_seller_endpoint,
|
$login_seller_endpoint,
|
||||||
$container->get( 'wcgateway.current-ppcp-settings-page-id' )
|
$container->get( 'wcgateway.current-ppcp-settings-page-id' )
|
||||||
);
|
);
|
||||||
|
|
|
@ -133,9 +133,11 @@ class OnboardingRESTController {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function get_status( $request ) {
|
public function get_status( $request ) {
|
||||||
$environment = $this->container->get( 'onboarding.environment' );
|
$environment = $this->container->get( 'settings.environment' );
|
||||||
$state = $this->container->get( 'onboarding.state' );
|
$state = $this->container->get( 'onboarding.state' );
|
||||||
|
|
||||||
|
// Legacy onboarding module; using `State::STATE_ONBOARDED` checks is valid here.
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'environment' => $environment->current_environment(),
|
'environment' => $environment->current_environment(),
|
||||||
'onboarded' => ( $state->current_state() >= State::STATE_ONBOARDED ),
|
'onboarded' => ( $state->current_state() >= State::STATE_ONBOARDED ),
|
||||||
|
|
|
@ -103,12 +103,8 @@ class OnboardingRenderer {
|
||||||
'displayMode' => 'minibrowser',
|
'displayMode' => 'minibrowser',
|
||||||
);
|
);
|
||||||
|
|
||||||
$data = $this->partner_referrals_data
|
|
||||||
->with_products( $products )
|
|
||||||
->data();
|
|
||||||
|
|
||||||
$environment = $is_production ? 'production' : 'sandbox';
|
$environment = $is_production ? 'production' : 'sandbox';
|
||||||
$product = 'PPCP' === $data['products'][0] ? 'ppcp' : 'express_checkout';
|
$product = strtolower( $products[0] ?? 'express_checkout' );
|
||||||
$cache_key = $environment . '-' . $product;
|
$cache_key = $environment . '-' . $product;
|
||||||
|
|
||||||
$onboarding_url = new OnboardingUrl( $this->cache, $cache_key, get_current_user_id() );
|
$onboarding_url = new OnboardingUrl( $this->cache, $cache_key, get_current_user_id() );
|
||||||
|
@ -122,8 +118,7 @@ class OnboardingRenderer {
|
||||||
|
|
||||||
$onboarding_url->init();
|
$onboarding_url->init();
|
||||||
|
|
||||||
$data = $this->partner_referrals_data
|
$data = $this->partner_referrals_data->data( $products, $onboarding_url->token() ?: '' );
|
||||||
->append_onboarding_token( $data, $onboarding_url->token() ?: '' );
|
|
||||||
|
|
||||||
$url = $is_production ? $this->production_partner_referrals->signup_link( $data ) : $this->sandbox_partner_referrals->signup_link( $data );
|
$url = $is_production ? $this->production_partner_referrals->signup_link( $data ) : $this->sandbox_partner_referrals->signup_link( $data );
|
||||||
$url = add_query_arg( $args, $url );
|
$url = add_query_arg( $args, $url );
|
||||||
|
|
|
@ -95,6 +95,7 @@ class SaveConfig {
|
||||||
* @param array $config The configurator config.
|
* @param array $config The configurator config.
|
||||||
*/
|
*/
|
||||||
public function save_config( array $config ): void {
|
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_enable_styling_per_messaging_location', true );
|
||||||
$this->settings->set( 'pay_later_messaging_enabled', true );
|
$this->settings->set( 'pay_later_messaging_enabled', true );
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
||||||
function( $subscription ) use ( $c ) {
|
function( $subscription ) use ( $c ) {
|
||||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||||
if ( $subscription_id ) {
|
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';
|
$host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -488,7 +488,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$environment = $c->get( 'onboarding.environment' );
|
$environment = $c->get( 'settings.environment' );
|
||||||
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
|
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
|
||||||
$this->render_paypal_subscription_fields( $product, $environment );
|
$this->render_paypal_subscription_fields( $product, $environment );
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
@ -522,7 +522,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$environment = $c->get( 'onboarding.environment' );
|
$environment = $c->get( 'settings.environment' );
|
||||||
$this->render_paypal_subscription_fields( $product, $environment );
|
$this->render_paypal_subscription_fields( $product, $environment );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Applying Default Configuration After Onboarding
|
||||||
|
|
||||||
|
The `OnboardingProfile` has a property named `setup_done`, which indicated whether the default
|
||||||
|
configuration was set up.
|
||||||
|
|
||||||
|
### `OnboardingProfile::is_setup_done()`
|
||||||
|
|
||||||
|
This flag indicated, whether the default plugin configuration was applied or not.
|
||||||
|
It's set to true after the merchant's authentication attempt was successful, and settings were
|
||||||
|
adjusted.
|
||||||
|
|
||||||
|
The only way to reset this flag, is to enable the "**Start Over**" toggle and disconnecting the
|
||||||
|
merchant:
|
||||||
|
https://example.com/wp-admin/admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&panel=settings#disconnect-merchant
|
||||||
|
|
||||||
|
### `class SettingsDataManager`
|
||||||
|
|
||||||
|
The `SettingsDataManager` service is responsible for applying all defaults options at the end of the
|
||||||
|
onboarding process.
|
||||||
|
|
||||||
|
### `SettingsDataManager::set_defaults_for_new_merchant()`
|
||||||
|
|
||||||
|
This method expects a DTO argument (`ConfigurationFlagsDTO`) that provides relevant details about
|
||||||
|
the merchant and onboarding choices.
|
||||||
|
|
||||||
|
It verifies, if default settings were already applied (by checking the
|
||||||
|
`OnboardingProfile::is_setup_done()` state). If not done yet, the DTO object is inspected to
|
||||||
|
initialize the plugin's configuration, before marking the `setup_done` flag as completed.
|
||||||
|
|
||||||
|
## Default Settings Matrix
|
||||||
|
|
||||||
|
### Decision Flags
|
||||||
|
|
||||||
|
- **Country**: The merchant country.
|
||||||
|
- According to PayPal settings, not the WooCommerce country
|
||||||
|
- Test case: Set Woo country to Germany and sign in with a US merchant account; this should
|
||||||
|
trigger the "Country: US" branches below.
|
||||||
|
- **Seller Type**: Business or Casual.
|
||||||
|
- According to PayPal, not the onboarding choice
|
||||||
|
- Test case: Choose "Personal" during onboarding but log in with a business account; this should
|
||||||
|
trigger the "Account: Business" branches below.
|
||||||
|
- **Subscriptions**: An onboarding choice on the "Products" screen.
|
||||||
|
- **Cards**: An onboarding choice, on the "Checkout Options" screen.
|
||||||
|
- Refers to the first option on the checkout options screen ("Custom Card Fields", etc.)
|
||||||
|
|
||||||
|
### Payment Methods
|
||||||
|
|
||||||
|
By default, all payment methods are turned off after onboarding, unless the conditions specified in
|
||||||
|
the following table are met.
|
||||||
|
|
||||||
|
| Payment Method | Country | Seller Type | Subscriptions | Cards | Notes |
|
||||||
|
|----------------|---------|-------------|---------------|-------|-------------------------------|
|
||||||
|
| Venmo | US | *any* | *any* | *any* | Always |
|
||||||
|
| Pay Later | US | *any* | *any* | *any* | Always |
|
||||||
|
| ACDC | US | Business | *any* | ✅ | Greyed out for Casual Sellers |
|
||||||
|
| BCDC | US | *any* | *any* | ✅ | |
|
||||||
|
| Apple Pay | US | Business | *any* | ✅ | Based on feature eligibility |
|
||||||
|
| Google Pay | US | Business | *any* | ✅ | Based on feature eligibility |
|
||||||
|
| All APMs | US | Business | *any* | ✅ | Based on feature eligibility |
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
| Feature | Country | Seller-Type | Subscriptions | Cards | Notes |
|
||||||
|
|-----------------------------|---------|-------------|---------------|-------|----------------------------|
|
||||||
|
| Pay Now Experience | US | _any_ | _any_ | _any_ | |
|
||||||
|
| Save PayPal and Venmo | US | Business | ✅ | _any_ | |
|
||||||
|
| Save Credit and Debit Cards | US | Business | ✅ | ✅ | Requires ACDC eligibility* |
|
||||||
|
|
||||||
|
- `*` If merchant has no ACDC eligibility, the setting should be disabled (not toggleable).
|
||||||
|
|
||||||
|
### Styling
|
||||||
|
|
||||||
|
All US merchants use the same settings, regardless of onboarding choices.
|
||||||
|
|
||||||
|
| Button Location | Enabled | Displayed Payment Methods |
|
||||||
|
|------------------|---------|-------------------------------------------------|
|
||||||
|
| Cart | ✅ | PayPal, Venmo, Pay Later, Google Pay, Apple Pay |
|
||||||
|
| Classic Checkout | ✅ | PayPal, Venmo, Pay Later, Google Pay, Apple Pay |
|
||||||
|
| Express Checkout | ✅ | PayPal, Venmo, Pay Later, Google Pay, Apple Pay |
|
||||||
|
| Mini Cart | ✅ | PayPal, Venmo, Pay Later, Google Pay, Apple Pay |
|
||||||
|
| Product Page | ✅ | PayPal, Venmo, Pay Later |
|
||||||
|
|
||||||
|
### Pay Later Messaging
|
||||||
|
|
||||||
|
All US merchants use the same settings, regardless of onboarding choices.
|
||||||
|
|
||||||
|
| Location | Enabled |
|
||||||
|
|-------------------|---------|
|
||||||
|
| Product | ✅ |
|
||||||
|
| Cart | ✅ |
|
||||||
|
| Checkout | ✅ |
|
||||||
|
| Home | ❌ |
|
||||||
|
| Shop | ❌ |
|
||||||
|
| WooCommerce Block | ❌ |
|
120
modules/ppcp-settings/docs/authentication-flows.md
Normal file
120
modules/ppcp-settings/docs/authentication-flows.md
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
# Authentication Flows
|
||||||
|
|
||||||
|
The settings UI offers two distinct authentication methods:
|
||||||
|
|
||||||
|
- OAuth
|
||||||
|
- Direct API
|
||||||
|
|
||||||
|
## OAuth
|
||||||
|
|
||||||
|
This is the usual authentication UI for most users. It opens a "PayPal popup" with a login mask.
|
||||||
|
The authentication flow consists of **three steps**:
|
||||||
|
|
||||||
|
- Generate a referral URL with a special token
|
||||||
|
- Translate a one-time OAuth secret into permanent API credentials
|
||||||
|
- Complete authentication by confirming the token from step 1
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
|
||||||
|
1. Available on the first onboarding page (for sandbox login), or on the last page of the onboarding wizard.
|
||||||
|
2. Authentication is initiated by clicking a "Connect" button which opens a popup with a PayPal login mask
|
||||||
|
- Sometimes the login opens in a new tab, mainly on Mac when using the browser in full-screen mode
|
||||||
|
3. After completing the login, the final page shows a "Return to your store" button; clicking that button closes the popup/tab and completes the authentication process
|
||||||
|
|
||||||
|
**More details on what happens:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant R as React API
|
||||||
|
participant S as PHP Server
|
||||||
|
|
||||||
|
R->>S: Request partner referral URL
|
||||||
|
Note over S: Generate and store a one-time token
|
||||||
|
create participant W as WooCommerce API
|
||||||
|
S->>W: Request referral URL
|
||||||
|
destroy W
|
||||||
|
W->>S: Generate the full partner referral URL
|
||||||
|
S->>R: Return referral URL
|
||||||
|
create participant P as PayPal Popup
|
||||||
|
R->>P: Open PayPal popup, which was generated by WooCommerce APi
|
||||||
|
Note over P: Complete login inside Popup
|
||||||
|
P->>R: Call JS function with OAuth ID and shared secret
|
||||||
|
R->>S: Send OAuth data to REST endpoint
|
||||||
|
create participant PP as PayPal API
|
||||||
|
S->>PP: Request permanent credentials
|
||||||
|
PP->>S: Translate one-time secret to permanent credentials
|
||||||
|
destroy P
|
||||||
|
P->>R: Redirect browser tab with unique token
|
||||||
|
Note over R: App unmounts during redirect
|
||||||
|
|
||||||
|
Note over S: During page load
|
||||||
|
Note over S: Verify token and finalize authentication
|
||||||
|
S->>PP: Request merchant details
|
||||||
|
destroy PP
|
||||||
|
PP->>S: Return merchant details (e.g. country)
|
||||||
|
Note over S: Render the settings page with React app
|
||||||
|
S->>R: Boot react app in "settings mode"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Authentication starts _before_ the "Connect" button is rendered, as we generate a one-time partner referral URL
|
||||||
|
- See `ConnectionUrlGenerator::generate()`
|
||||||
|
- This referral URL configures PayPal: Which items render inside the Popup? What is the "return
|
||||||
|
URL" for the final step? Is it a sandbox or live login?
|
||||||
|
2. _...The merchant completes the login or account creation flow inside the popup..._
|
||||||
|
3. During page-load of the final confirmation page inside the popup: PayPal directly calls a JS function on the WooCommerce settings page, i.e. the popup communicates with the open WooCommerce tab. This JS function sends an oauth ID and shared secret (OTP) to a REST endpoint
|
||||||
|
- See `AuthenticatoinRestEndpoint::connect_oauth()`
|
||||||
|
- See `AuthenticationManager::authenticate_via_oauth()` → translates the one-time shared secret
|
||||||
|
into a permanent client secret
|
||||||
|
- At this stage, the authentication is _incomplete_, as some details are only provided by the
|
||||||
|
final step
|
||||||
|
4. When clicking the "Return to store" button, the popup closes and the WooCommerce settings page "reloads"; it's actually a _redirect_ which is initiated by PayPal and receives a unique token (which was generated by the `ConnectionUrlGenerator`) that is required to complete authentication.
|
||||||
|
- See `ConnectionListener::process()`
|
||||||
|
- See `AuthenticationManager::finish_oauth_authentication()`
|
||||||
|
- This listener runs on every wp-admin page load and bails if the required token is not present
|
||||||
|
5. After the final page reload, the React app directly enters "Settings mode"
|
||||||
|
|
||||||
|
## Direct API
|
||||||
|
|
||||||
|
This method is only available for business accounts, as it requires the merchant to create a PayPal REST app that's linked to their account.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Setup the PayPal REST app</strong></summary>
|
||||||
|
|
||||||
|
1. Visit https://developer.paypal.com/
|
||||||
|
2. In section "Apps & Credentials" click "Create App"
|
||||||
|
3. After the app is ready, it displays the `Client ID` and `Secret Key` values
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
|
||||||
|
1. Available on the first onboarding screen, via the "See advanced options" form at the bottom of the page
|
||||||
|
2. Activate the "Manual Connection" toggle; then enter the `Client ID` and `Secret Key` and hit Enter
|
||||||
|
|
||||||
|
**What happens:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant R as React
|
||||||
|
participant S as Server
|
||||||
|
participant P as PayPal API
|
||||||
|
|
||||||
|
R->>S: Send credentials to REST endpoint
|
||||||
|
S->>P: Authenticate via Direct API
|
||||||
|
P->>S: Return authentication result
|
||||||
|
S->>P: Request merchant details
|
||||||
|
P->>S: Return merchant details (e.g. country)
|
||||||
|
|
||||||
|
Note over S: Process authentication result
|
||||||
|
S->>R: Return authentication status
|
||||||
|
Note over R: Update UI to authenticated state<br/>(no page reload)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Client ID and Secret are sent to a REST endpoint of the plugin. The authentication happens on server-side.
|
||||||
|
- See `AuthenticatoinRestEndpoint::connect_direct()`
|
||||||
|
- See `AuthenticationManager::authenticate_via_direct_api()`
|
||||||
|
2. After authentication is completed, the merchant account is prepared on server side and a confirmation is returned to the React app.
|
||||||
|
- See `AuthenticationManager::update_connection_details()` → condition `is_merchant_connected()`
|
||||||
|
3. The React app directly switches to the "Settings mode" without a page reload.
|
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.
|
||||||
|
|
||||||
|
---
|
|
@ -17,6 +17,7 @@ $color-text-text: #070707;
|
||||||
$color-border: #AEAEAE;
|
$color-border: #AEAEAE;
|
||||||
$color-divider: #F0F0F0;
|
$color-divider: #F0F0F0;
|
||||||
$color-error-red: #cc1818;
|
$color-error-red: #cc1818;
|
||||||
|
$color-warning: #e2a030;
|
||||||
|
|
||||||
$shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
|
$shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
|
||||||
$color-gradient-dark: #001435;
|
$color-gradient-dark: #001435;
|
||||||
|
@ -66,6 +67,7 @@ $card-vertical-gap: 48px;
|
||||||
--color-text-teriary: #{$color-text-tertiary};
|
--color-text-teriary: #{$color-text-tertiary};
|
||||||
--color-text-description: #{$color-gray-700};
|
--color-text-description: #{$color-gray-700};
|
||||||
--color-error: #{$color-error-red};
|
--color-error: #{$color-error-red};
|
||||||
|
--color-warning: #{$color-warning};
|
||||||
|
|
||||||
// Default settings-block theme.
|
// Default settings-block theme.
|
||||||
--block-item-gap: 16px;
|
--block-item-gap: 16px;
|
||||||
|
|
|
@ -63,6 +63,10 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.components-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ppcp--method-icon {
|
.ppcp--method-icon {
|
||||||
|
@ -79,6 +83,234 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp--method-toggle-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp-r-payment-methods {
|
||||||
|
.ppcp-highlight {
|
||||||
|
animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
border: 1px solid $color-blueberry;
|
||||||
|
border-radius: var(--container-border-radius);
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ppcp-highlight-fade {
|
||||||
|
0%, 20% {
|
||||||
|
background-color: rgba($color-blueberry, 0.08);
|
||||||
|
border-color: $color-blueberry;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: $color-gray-300;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: 8;
|
||||||
|
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: 9;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warning message */
|
||||||
|
.ppcp--method-warning {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
cursor: help;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
color: $color-warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add invisible bridge to prevent gap between icon and popover */
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 30px;
|
||||||
|
height: 15px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popover bubble
|
||||||
|
.ppcp--method-warning-message {
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(100% + 15px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 250px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: $color-white;
|
||||||
|
border: 1px solid $color-gray-200;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 9;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity 0.2s ease, visibility 0.2s;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -6px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: $color-white;
|
||||||
|
border-right: 1px solid $color-gray-200;
|
||||||
|
border-bottom: 1px solid $color-gray-200;
|
||||||
|
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.01);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp--method-notice-list {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
font-weight: 700;
|
||||||
|
background: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: inside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .ppcp--method-warning-message,
|
||||||
|
& .ppcp--method-warning-message:hover {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For RTL support
|
||||||
|
html[dir="rtl"] .ppcp--method-warning {
|
||||||
|
&:before {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
transform: translateX(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp--method-warning-message {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
transform: translateX(50%);
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
margin-right: -6px;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
&--save {
|
&--save {
|
||||||
margin-top: -4px;
|
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@
|
||||||
&__field-rows {
|
&__field-rows {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 24px;
|
gap: 18px;
|
||||||
|
|
||||||
&--acdc {
|
&--acdc {
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
|
@ -98,19 +97,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-radio-control {
|
.components-radio-control {
|
||||||
.components-flex {
|
|
||||||
gap: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
label {
|
||||||
@include font(14, 20, 400);
|
@include font(14, 20, 400);
|
||||||
color: $color-black;
|
color: $color-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__option {
|
|
||||||
gap: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
border-color: $color-gray-700;
|
border-color: $color-gray-700;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
padding-top: var(--block-separator-gap, 32px);
|
padding-top: var(--block-separator-gap, 32px);
|
||||||
border-top: var(--block-separator-size, 1px) solid var(--block-separator-color);
|
border-top: var(--block-separator-size, 1px) solid var(--block-separator-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ppcp--pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ppcp-r-settings-block {
|
.ppcp-r-settings-block {
|
||||||
|
|
|
@ -69,4 +69,34 @@ $width_gap: 24px;
|
||||||
color: var(--color-text-teriary);
|
color: var(--color-text-teriary);
|
||||||
margin: 0;
|
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 {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
|
||||||
|
&.ppcp--dimmed {
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Modal for disconnecting the merchant from the current PayPal account.
|
||||||
|
*/
|
||||||
|
.ppcp--modal-disconnect {
|
||||||
|
.ppcp--toggle-danger .components-form-toggle {
|
||||||
|
&.is-checked {
|
||||||
|
--wp-components-color-accent: #cc1818;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp--action-buttons {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 32px;
|
||||||
|
|
||||||
|
.components-button {
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -226,30 +226,91 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payment Methods
|
.ppcp-r-settings {
|
||||||
.ppcp-r-payment-methods {
|
.ppcp-highlight {
|
||||||
display: flex;
|
position: relative;
|
||||||
flex-direction: column;
|
z-index: 1;
|
||||||
gap: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ppcp-highlight {
|
&::before {
|
||||||
animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1);
|
content: '';
|
||||||
border: 1px solid $color-blueberry;
|
position: absolute;
|
||||||
border-radius: var(--container-border-radius);
|
top: -8px;
|
||||||
position: relative;
|
left: -12px;
|
||||||
z-index: 1;
|
right: -12px;
|
||||||
}
|
bottom: -8px;
|
||||||
|
border: 1px solid $color-blueberry;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: ppcp-setting-highlight-bg 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes ppcp-highlight-fade {
|
&::after {
|
||||||
0%, 20% {
|
content: '';
|
||||||
background-color: rgba($color-blueberry, 0.08);
|
position: absolute;
|
||||||
border-color: $color-blueberry;
|
top: -8px;
|
||||||
border-width: 1px;
|
left: -12px;
|
||||||
|
width: 4px;
|
||||||
|
bottom: -8px;
|
||||||
|
background-color: $color-blueberry;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: ppcp-setting-highlight-accent 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
100% {
|
|
||||||
background-color: transparent;
|
@keyframes ppcp-setting-highlight-bg {
|
||||||
border-color: $color-gray-300;
|
0%, 15% {
|
||||||
border-width: 1px;
|
background-color: rgba($color-blueberry, 0.08);
|
||||||
|
border-color: $color-blueberry;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ppcp-setting-highlight-accent {
|
||||||
|
0%, 15% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ppcp-r-settings-section {
|
||||||
|
.ppcp--setting-row {
|
||||||
|
position: relative;
|
||||||
|
padding: 12px;
|
||||||
|
margin: 0 -12px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($color-gray-100, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTL support
|
||||||
|
html[dir="rtl"] {
|
||||||
|
.ppcp-highlight {
|
||||||
|
&::after {
|
||||||
|
left: auto;
|
||||||
|
right: -12px;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#configurator-eligibleContainer.css-4nclxm.e1vy3g880 {
|
#configurator-eligibleContainer.css-4nclxm.e1vy3g880 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
padding: 48px 0px 48px 48px;
|
padding: 16px 0px 16px 16px;
|
||||||
|
|
||||||
#configurator-controlPanelContainer.css-5urmrq.e1vy3g880 {
|
#configurator-controlPanelContainer.css-5urmrq.e1vy3g880 {
|
||||||
width: 374px;
|
width: 374px;
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
.css-7xkxom, .css-8tvj6u {
|
.css-7xkxom, .css-8tvj6u {
|
||||||
height: auto;
|
height: auto;
|
||||||
|
width: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.css-10nkerk.ej6n7t60 {
|
.css-10nkerk.ej6n7t60 {
|
||||||
|
@ -37,14 +38,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.css-1vc34jy-handler {
|
.css-1vc34jy-handler {
|
||||||
height: 1.6rem;
|
height: 1.7em;
|
||||||
width: 1.6rem;
|
width: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.css-8vwtr6-state {
|
.css-8vwtr6-state {
|
||||||
height: 1.6rem;
|
height: 1.4rem;
|
||||||
|
width: 3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.css-1s8clkf.etu8a6w2 {
|
||||||
|
width: 374px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__subheader, #configurator-controlPanelSubHeader {
|
&__subheader, #configurator-controlPanelSubHeader {
|
||||||
|
@ -68,6 +74,7 @@
|
||||||
|
|
||||||
.css-rok10q, .css-dfgbdq-text_body_strong {
|
.css-rok10q, .css-dfgbdq-text_body_strong {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__publish-button {
|
&__publish-button {
|
||||||
|
@ -110,4 +117,30 @@
|
||||||
width: 100%;
|
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-separator-gap: 24px;
|
||||||
--block-header-gap: 18px;
|
--block-header-gap: 18px;
|
||||||
--panel-width: 422px;
|
--panel-width: 422px;
|
||||||
--sticky-offset-top: 92px; // 32px admin-bar + 60px TopNavigation height
|
--sticky-offset-top: 132px; // 32px admin-bar + 100px TopNavigation height
|
||||||
--preview-height-reduction: 236px; // 32px admin-bar + 60px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin
|
--preview-height-reduction: 276px; // 32px admin-bar + 100px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 1px solid var(--color-separators);
|
border: 1px solid var(--color-separators);
|
||||||
|
|
|
@ -11,3 +11,4 @@
|
||||||
|
|
||||||
@import './components/reusable-components/payment-method-modal';
|
@import './components/reusable-components/payment-method-modal';
|
||||||
@import './components/screens/fullscreen';
|
@import './components/screens/fullscreen';
|
||||||
|
@import './components/screens/modals';
|
||||||
|
|
|
@ -6,13 +6,13 @@ import SpinnerOverlay from './ReusableComponents/SpinnerOverlay';
|
||||||
import SendOnlyMessage from './Screens/SendOnlyMessage';
|
import SendOnlyMessage from './Screens/SendOnlyMessage';
|
||||||
import OnboardingScreen from './Screens/Onboarding';
|
import OnboardingScreen from './Screens/Onboarding';
|
||||||
import SettingsScreen from './Screens/Settings';
|
import SettingsScreen from './Screens/Settings';
|
||||||
import { getQuery } from '../utils/navigation';
|
import { getQuery, cleanUrlQueryParams } from '../utils/navigation';
|
||||||
|
|
||||||
const SettingsApp = () => {
|
const SettingsApp = () => {
|
||||||
const { isReady: onboardingIsReady, completed: onboardingCompleted } =
|
const { isReady: onboardingIsReady, completed: onboardingCompleted } =
|
||||||
OnboardingHooks.useSteps();
|
OnboardingHooks.useSteps();
|
||||||
|
const { isReady: merchantIsReady } = CommonHooks.useStore();
|
||||||
const {
|
const {
|
||||||
isReady: merchantIsReady,
|
|
||||||
merchant: { isSendOnlyCountry },
|
merchant: { isSendOnlyCountry },
|
||||||
} = CommonHooks.useMerchantInfo();
|
} = CommonHooks.useMerchantInfo();
|
||||||
|
|
||||||
|
@ -32,9 +32,19 @@ const SettingsApp = () => {
|
||||||
loading: ! onboardingIsReady,
|
loading: ! onboardingIsReady,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const [ activePanel, setActivePanel ] = useState(
|
const [ activePanel, setActivePanel ] = useState( getQuery().panel );
|
||||||
getQuery().panel || 'overview'
|
|
||||||
);
|
const removeUnsupportedArgs = () => {
|
||||||
|
const urlWasCleaned = cleanUrlQueryParams( [
|
||||||
|
'page',
|
||||||
|
'tab',
|
||||||
|
'section',
|
||||||
|
] );
|
||||||
|
|
||||||
|
if ( urlWasCleaned ) {
|
||||||
|
setActivePanel( '' );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Content = useMemo( () => {
|
const Content = useMemo( () => {
|
||||||
if ( ! onboardingIsReady || ! merchantIsReady ) {
|
if ( ! onboardingIsReady || ! merchantIsReady ) {
|
||||||
|
@ -42,16 +52,18 @@ const SettingsApp = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isSendOnlyCountry ) {
|
if ( isSendOnlyCountry ) {
|
||||||
|
removeUnsupportedArgs();
|
||||||
return <SendOnlyMessage />;
|
return <SendOnlyMessage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! onboardingCompleted ) {
|
if ( ! onboardingCompleted ) {
|
||||||
|
removeUnsupportedArgs();
|
||||||
return <OnboardingScreen />;
|
return <OnboardingScreen />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsScreen
|
<SettingsScreen
|
||||||
activePanel={ activePanel }
|
activePanel={ activePanel || 'overview' }
|
||||||
setActivePanel={ setActivePanel }
|
setActivePanel={ setActivePanel }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Icon } from '@wordpress/components';
|
||||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { useAccordionState } from '../../hooks/useAccordionState';
|
import { useToggleState } from '../../hooks/useToggleState';
|
||||||
import {
|
import {
|
||||||
Content,
|
Content,
|
||||||
Description,
|
Description,
|
||||||
|
@ -21,7 +21,7 @@ const Accordion = ( {
|
||||||
children = null,
|
children = null,
|
||||||
className = '',
|
className = '',
|
||||||
} ) => {
|
} ) => {
|
||||||
const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } );
|
const { isOpen, toggleOpen } = useToggleState( id, initiallyOpen );
|
||||||
const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
|
const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
|
||||||
'ppcp--is-open': isOpen,
|
'ppcp--is-open': isOpen,
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import { ToggleControl } from '@wordpress/components';
|
import { ToggleControl } from '@wordpress/components';
|
||||||
import { Action, Description } from '../Elements';
|
import { Action, Description } from '../Elements';
|
||||||
|
|
||||||
const ControlToggleButton = ( { label, description, value, onChange } ) => (
|
const ControlToggleButton = ( {
|
||||||
<Action>
|
id = '',
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
disabled = false,
|
||||||
|
} ) => (
|
||||||
|
<Action id={ id }>
|
||||||
<ToggleControl
|
<ToggleControl
|
||||||
className="ppcp--control-toggle"
|
className="ppcp--control-toggle"
|
||||||
__nextHasNoMarginBottom
|
__nextHasNoMarginBottom
|
||||||
|
@ -12,6 +19,7 @@ const ControlToggleButton = ( { label, description, value, onChange } ) => (
|
||||||
help={
|
help={
|
||||||
description ? <Description>{ description }</Description> : null
|
description ? <Description>{ description }</Description> : null
|
||||||
}
|
}
|
||||||
|
disabled={ disabled }
|
||||||
/>
|
/>
|
||||||
</Action>
|
</Action>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,6 +18,7 @@ const DataStoreControl = React.forwardRef(
|
||||||
control: ControlComponent,
|
control: ControlComponent,
|
||||||
value: externalValue,
|
value: externalValue,
|
||||||
onChange,
|
onChange,
|
||||||
|
onConfirm = null,
|
||||||
delay = 300,
|
delay = 300,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
|
@ -25,7 +26,9 @@ const DataStoreControl = React.forwardRef(
|
||||||
) => {
|
) => {
|
||||||
const [ internalValue, setInternalValue ] = useState( externalValue );
|
const [ internalValue, setInternalValue ] = useState( externalValue );
|
||||||
const onChangeRef = useRef( onChange );
|
const onChangeRef = useRef( onChange );
|
||||||
|
const onConfirmRef = useRef( onConfirm );
|
||||||
onChangeRef.current = onChange;
|
onChangeRef.current = onChange;
|
||||||
|
onConfirmRef.current = onConfirm;
|
||||||
|
|
||||||
const debouncedUpdate = useRef(
|
const debouncedUpdate = useRef(
|
||||||
debounce( ( value ) => {
|
debounce( ( value ) => {
|
||||||
|
@ -36,7 +39,7 @@ const DataStoreControl = React.forwardRef(
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
setInternalValue( externalValue );
|
setInternalValue( externalValue );
|
||||||
debouncedUpdate?.cancel();
|
debouncedUpdate?.cancel();
|
||||||
}, [ externalValue ] );
|
}, [ debouncedUpdate, externalValue ] );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
return () => debouncedUpdate?.cancel();
|
return () => debouncedUpdate?.cancel();
|
||||||
|
@ -50,12 +53,25 @@ const DataStoreControl = React.forwardRef(
|
||||||
[ debouncedUpdate ]
|
[ debouncedUpdate ]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback(
|
||||||
|
( event ) => {
|
||||||
|
if ( onConfirmRef.current && event.key === 'Enter' ) {
|
||||||
|
event.preventDefault();
|
||||||
|
debouncedUpdate.flush();
|
||||||
|
onConfirmRef.current();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ debouncedUpdate ]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlComponent
|
<ControlComponent
|
||||||
ref={ ref }
|
ref={ ref }
|
||||||
{ ...props }
|
{ ...props }
|
||||||
value={ internalValue }
|
value={ internalValue }
|
||||||
onChange={ handleChange }
|
onChange={ handleChange }
|
||||||
|
onKeyDown={ handleKeyDown }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
const Action = ( { children } ) => (
|
const Action = ( { id, children } ) => (
|
||||||
<div className="ppcp--action">{ children }</div>
|
<div className="ppcp--action" { ...( id ? { id } : {} ) }>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Action;
|
export default 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 Action } from './Action';
|
||||||
|
export { default as CardActions } from './CardActions';
|
||||||
export { default as Content } from './Content';
|
export { default as Content } from './Content';
|
||||||
export { default as ContentWrapper } from './ContentWrapper';
|
export { default as ContentWrapper } from './ContentWrapper';
|
||||||
export { default as Description } from './Description';
|
export { default as Description } from './Description';
|
||||||
|
|
|
@ -9,7 +9,13 @@ const OptionSelector = ( {
|
||||||
} ) => (
|
} ) => (
|
||||||
<div className="ppcp-r-select-box-wrapper">
|
<div className="ppcp-r-select-box-wrapper">
|
||||||
{ options.map(
|
{ options.map(
|
||||||
( { value: itemValue, title, description, contents } ) => {
|
( {
|
||||||
|
value: itemValue,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
contents,
|
||||||
|
isDisabled = false,
|
||||||
|
} ) => {
|
||||||
let isSelected;
|
let isSelected;
|
||||||
|
|
||||||
if ( Array.isArray( value ) ) {
|
if ( Array.isArray( value ) ) {
|
||||||
|
@ -27,6 +33,7 @@ const OptionSelector = ( {
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
isMulti={ multiSelect }
|
isMulti={ multiSelect }
|
||||||
isSelected={ isSelected }
|
isSelected={ isSelected }
|
||||||
|
isDisabled={ isDisabled }
|
||||||
>
|
>
|
||||||
{ contents }
|
{ contents }
|
||||||
</OptionItem>
|
</OptionItem>
|
||||||
|
@ -46,13 +53,13 @@ const OptionItem = ( {
|
||||||
isMulti,
|
isMulti,
|
||||||
isSelected,
|
isSelected,
|
||||||
children,
|
children,
|
||||||
|
isDisabled = false,
|
||||||
} ) => {
|
} ) => {
|
||||||
const boxClassName = classNames( 'ppcp-r-select-box', {
|
const boxClassName = classNames( 'ppcp-r-select-box', {
|
||||||
'ppcp--selected': isSelected,
|
'ppcp--selected': isSelected,
|
||||||
'ppcp--multiselect': isMulti,
|
'ppcp--multiselect': isMulti,
|
||||||
'ppcp--no-title': ! itemTitle,
|
'ppcp--no-title': ! itemTitle,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line jsx-a11y/label-has-associated-control -- label has a nested input control.
|
// eslint-disable-next-line jsx-a11y/label-has-associated-control -- label has a nested input control.
|
||||||
<label className={ boxClassName }>
|
<label className={ boxClassName }>
|
||||||
|
@ -61,6 +68,7 @@ const OptionItem = ( {
|
||||||
isRadio={ ! isMulti }
|
isRadio={ ! isMulti }
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
isSelected={ isSelected }
|
isSelected={ isSelected }
|
||||||
|
isDisabled={ isDisabled }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="ppcp--box-content">
|
<div className="ppcp--box-content">
|
||||||
|
@ -80,7 +88,7 @@ const OptionItem = ( {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const InputField = ( { value, onChange, isRadio, isSelected } ) => {
|
const InputField = ( { value, onChange, isRadio, isSelected, isDisabled } ) => {
|
||||||
if ( isRadio ) {
|
if ( isRadio ) {
|
||||||
return (
|
return (
|
||||||
<PayPalRdb
|
<PayPalRdb
|
||||||
|
@ -96,6 +104,7 @@ const InputField = ( { value, onChange, isRadio, isSelected } ) => {
|
||||||
value={ value }
|
value={ value }
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
checked={ isSelected }
|
checked={ isSelected }
|
||||||
|
disabled={ isDisabled }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
import { Icon } from '@wordpress/components';
|
import { Icon } from '@wordpress/components';
|
||||||
|
|
||||||
import data from '../../utils/data';
|
import data from '../../utils/data';
|
||||||
|
|
||||||
const PaymentMethodIcon = ( { icons, type } ) => {
|
const PaymentMethodIcon = ( { type } ) => (
|
||||||
const validIcon = Array.isArray( icons ) && icons.includes( type );
|
<Icon
|
||||||
|
icon={ data().getImage( `icon-button-${ type }.svg` ) }
|
||||||
if ( validIcon || icons === 'all' ) {
|
className="ppcp--method-icon"
|
||||||
return (
|
/>
|
||||||
<Icon
|
);
|
||||||
icon={ data().getImage( 'icon-button-' + type + '.svg' ) }
|
|
||||||
className="ppcp--method-icon"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PaymentMethodIcon;
|
export default PaymentMethodIcon;
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
import PaymentMethodIcon from './PaymentMethodIcon';
|
import PaymentMethodIcon from './PaymentMethodIcon';
|
||||||
|
|
||||||
const PaymentMethodIcons = ( props ) => {
|
const PaymentMethodIcons = ( { icons = [] } ) => (
|
||||||
return (
|
<div className="ppcp-r-payment-method-icons">
|
||||||
<div className="ppcp-r-payment-method-icons">
|
{ icons.map( ( type ) => (
|
||||||
<PaymentMethodIcon type="paypal" icons={ props.icons } />
|
<PaymentMethodIcon key={ type } type={ type } />
|
||||||
<PaymentMethodIcon type="venmo" icons={ props.icons } />
|
) ) }
|
||||||
<PaymentMethodIcon type="visa" icons={ props.icons } />
|
</div>
|
||||||
<PaymentMethodIcon type="mastercard" icons={ props.icons } />
|
);
|
||||||
<PaymentMethodIcon type="amex" icons={ props.icons } />
|
|
||||||
<PaymentMethodIcon type="discover" icons={ props.icons } />
|
|
||||||
<PaymentMethodIcon type="apple-pay" icons={ props.icons } />
|
|
||||||
<PaymentMethodIcon type="google-pay" icons={ props.icons } />
|
|
||||||
<PaymentMethodIcon type="ideal" icons={ props.icons } />
|
|
||||||
<PaymentMethodIcon type="bancontact" icons={ props.icons } />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PaymentMethodIcons;
|
export default PaymentMethodIcons;
|
||||||
|
|
|
@ -1,39 +1,44 @@
|
||||||
import { ToggleControl, Icon, Button } from '@wordpress/components';
|
import { ToggleControl, Icon, Button } from '@wordpress/components';
|
||||||
import { cog } from '@wordpress/icons';
|
import { cog } from '@wordpress/icons';
|
||||||
import { useEffect } from '@wordpress/element';
|
|
||||||
import { useActiveHighlight } from '../../../data/common/hooks';
|
|
||||||
|
|
||||||
import SettingsBlock from '../SettingsBlock';
|
import SettingsBlock from '../SettingsBlock';
|
||||||
import PaymentMethodIcon from '../PaymentMethodIcon';
|
import PaymentMethodIcon from '../PaymentMethodIcon';
|
||||||
|
import WarningMessages from '../../../Components/Screens/Settings/Components/Payment/WarningMessages';
|
||||||
|
|
||||||
const PaymentMethodItemBlock = ( {
|
const PaymentMethodItemBlock = ( {
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
onTriggerModal,
|
onTriggerModal,
|
||||||
onSelect,
|
onSelect,
|
||||||
isSelected,
|
isSelected,
|
||||||
|
isDisabled,
|
||||||
|
disabledMessage,
|
||||||
|
warningMessages,
|
||||||
} ) => {
|
} ) => {
|
||||||
const { activeHighlight, setActiveHighlight } = useActiveHighlight();
|
const hasWarning =
|
||||||
const isHighlighted = activeHighlight === paymentMethod.id;
|
warningMessages && Object.keys( warningMessages ).length > 0;
|
||||||
|
|
||||||
// Reset the active highlight after 2 seconds
|
// Determine class names based on states
|
||||||
useEffect( () => {
|
const methodItemClasses = [
|
||||||
if ( isHighlighted ) {
|
'ppcp--method-item',
|
||||||
const timer = setTimeout( () => {
|
isDisabled ? 'ppcp--method-item--disabled' : '',
|
||||||
setActiveHighlight( null );
|
hasWarning && ! isDisabled ? 'ppcp--method-item--warning' : '',
|
||||||
}, 2000 );
|
]
|
||||||
|
.filter( Boolean )
|
||||||
return () => clearTimeout( timer );
|
.join( ' ' );
|
||||||
}
|
|
||||||
}, [ isHighlighted, setActiveHighlight ] );
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsBlock
|
<SettingsBlock
|
||||||
id={ paymentMethod.id }
|
id={ paymentMethod.id }
|
||||||
className={ `ppcp--method-item ${
|
className={ methodItemClasses }
|
||||||
isHighlighted ? 'ppcp-highlight' : ''
|
|
||||||
}` }
|
|
||||||
separatorAndGap={ false }
|
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-inner">
|
||||||
<div className="ppcp--method-title-wrapper">
|
<div className="ppcp--method-title-wrapper">
|
||||||
{ paymentMethod?.icon && (
|
{ paymentMethod?.icon && (
|
||||||
|
@ -50,11 +55,18 @@ const PaymentMethodItemBlock = ( {
|
||||||
{ paymentMethod.itemDescription }
|
{ paymentMethod.itemDescription }
|
||||||
</p>
|
</p>
|
||||||
<div className="ppcp--method-footer">
|
<div className="ppcp--method-footer">
|
||||||
<ToggleControl
|
<div className="ppcp--method-toggle-wrapper">
|
||||||
__nextHasNoMarginBottom
|
<ToggleControl
|
||||||
checked={ isSelected }
|
__nextHasNoMarginBottom
|
||||||
onChange={ onSelect }
|
checked={ isSelected }
|
||||||
/>
|
onChange={ onSelect }
|
||||||
|
/>
|
||||||
|
{ hasWarning && ! isDisabled && isSelected && (
|
||||||
|
<WarningMessages
|
||||||
|
warningMessages={ warningMessages }
|
||||||
|
/>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
{ paymentMethod?.fields && onTriggerModal && (
|
{ paymentMethod?.fields && onTriggerModal && (
|
||||||
<Button
|
<Button
|
||||||
className="ppcp--method-settings"
|
className="ppcp--method-settings"
|
||||||
|
|
|
@ -19,20 +19,25 @@ const PaymentMethodsBlock = ( { paymentMethods = [], onTriggerModal } ) => {
|
||||||
<SettingsBlock className="ppcp--grid ppcp-r-settings-block__payment-methods">
|
<SettingsBlock className="ppcp--grid ppcp-r-settings-block__payment-methods">
|
||||||
{ paymentMethods
|
{ paymentMethods
|
||||||
// Remove empty/invalid payment method entries.
|
// Remove empty/invalid payment method entries.
|
||||||
.filter( ( m ) => m.id )
|
.filter( ( m ) => m && m.id )
|
||||||
.map( ( paymentMethod ) => (
|
.map( ( paymentMethod ) => {
|
||||||
<PaymentMethodItemBlock
|
return (
|
||||||
key={ paymentMethod.id }
|
<PaymentMethodItemBlock
|
||||||
paymentMethod={ paymentMethod }
|
key={ paymentMethod.id }
|
||||||
isSelected={ paymentMethod.enabled }
|
paymentMethod={ paymentMethod }
|
||||||
onSelect={ ( checked ) =>
|
isSelected={ paymentMethod.enabled }
|
||||||
handleSelect( paymentMethod.id, checked )
|
isDisabled={ paymentMethod.isDisabled }
|
||||||
}
|
disabledMessage={ paymentMethod.disabledMessage }
|
||||||
onTriggerModal={ () =>
|
onSelect={ ( checked ) =>
|
||||||
onTriggerModal?.( paymentMethod.id )
|
handleSelect( paymentMethod.id, checked )
|
||||||
}
|
}
|
||||||
/>
|
onTriggerModal={ () =>
|
||||||
) ) }
|
onTriggerModal?.( paymentMethod.id )
|
||||||
|
}
|
||||||
|
warningMessages={ paymentMethod.warningMessages }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} ) }
|
||||||
</SettingsBlock>
|
</SettingsBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@ const TodoSettingsBlock = ( {
|
||||||
todosData,
|
todosData,
|
||||||
className = '',
|
className = '',
|
||||||
setActiveModal,
|
setActiveModal,
|
||||||
setActiveHighlight,
|
|
||||||
onDismissTodo,
|
onDismissTodo,
|
||||||
} ) => {
|
} ) => {
|
||||||
const [ dismissingIds, setDismissingIds ] = useState( new Set() );
|
const [ dismissingIds, setDismissingIds ] = useState( new Set() );
|
||||||
|
@ -44,29 +43,30 @@ const TodoSettingsBlock = ( {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = async ( todo ) => {
|
const handleClick = async ( todo ) => {
|
||||||
if ( todo.action.type === 'tab' ) {
|
const { action } = todo;
|
||||||
const tabId = TAB_IDS[ todo.action.tab.toUpperCase() ];
|
const highlight = Boolean( action.highlight );
|
||||||
await selectTab( tabId, todo.action.section );
|
|
||||||
} else if ( todo.action.type === 'external' ) {
|
// Handle different action types.
|
||||||
window.open( todo.action.url, '_blank' );
|
if ( action.type === 'tab' ) {
|
||||||
// If it has completeOnClick flag, trigger the action
|
const tabId = TAB_IDS[ action.tab.toUpperCase() ];
|
||||||
if ( todo.action.completeOnClick === true ) {
|
await selectTab( tabId, action.section, highlight );
|
||||||
await completeOnClick( todo.id );
|
} else if ( action.type === 'external' ) {
|
||||||
}
|
window.open( action.url, '_blank' );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( todo.action.modal ) {
|
if ( action.completeOnClick ) {
|
||||||
setActiveModal( todo.action.modal );
|
await completeOnClick( todo.id );
|
||||||
}
|
}
|
||||||
if ( todo.action.highlight ) {
|
|
||||||
setActiveHighlight( todo.action.highlight );
|
if ( action.modal ) {
|
||||||
|
setActiveModal( action.modal );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter out dismissed todos for display
|
// Filter out dismissed todos for display and limit to 5.
|
||||||
const visibleTodos = todosData.filter(
|
const visibleTodos = todosData
|
||||||
( todo ) => ! dismissedTodos.includes( todo.id )
|
.filter( ( todo ) => ! dismissedTodos.includes( todo.id ) )
|
||||||
);
|
.slice( 0, 5 );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -32,7 +32,6 @@ const ButtonOrPlaceholder = ( {
|
||||||
|
|
||||||
if ( href ) {
|
if ( href ) {
|
||||||
buttonProps.href = href;
|
buttonProps.href = href;
|
||||||
buttonProps.target = 'PPFrame';
|
|
||||||
buttonProps[ 'data-paypal-button' ] = 'true';
|
buttonProps[ 'data-paypal-button' ] = 'true';
|
||||||
buttonProps[ 'data-paypal-onboard-button' ] = 'true';
|
buttonProps[ 'data-paypal-onboard-button' ] = 'true';
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ const ManualConnectionForm = () => {
|
||||||
label={ clientIdLabel }
|
label={ clientIdLabel }
|
||||||
value={ manualClientId }
|
value={ manualClientId }
|
||||||
onChange={ setManualClientId }
|
onChange={ setManualClientId }
|
||||||
|
onConfirm={ handleManualConnect }
|
||||||
className={ classNames( {
|
className={ classNames( {
|
||||||
'ppcp--has-error': ! clientValid,
|
'ppcp--has-error': ! clientValid,
|
||||||
} ) }
|
} ) }
|
||||||
|
@ -173,6 +174,7 @@ const ManualConnectionForm = () => {
|
||||||
label={ secretKeyLabel }
|
label={ secretKeyLabel }
|
||||||
value={ manualClientSecret }
|
value={ manualClientSecret }
|
||||||
onChange={ setManualClientSecret }
|
onChange={ setManualClientSecret }
|
||||||
|
onConfirm={ handleManualConnect }
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { usePaymentConfig } from '../hooks/usePaymentConfig';
|
||||||
const PaymentFlow = ( {
|
const PaymentFlow = ( {
|
||||||
useAcdc,
|
useAcdc,
|
||||||
isFastlane,
|
isFastlane,
|
||||||
isPayLater,
|
|
||||||
storeCountry,
|
storeCountry,
|
||||||
onlyOptional = false,
|
onlyOptional = false,
|
||||||
} ) => {
|
} ) => {
|
||||||
|
@ -18,7 +17,8 @@ const PaymentFlow = ( {
|
||||||
optionalTitle,
|
optionalTitle,
|
||||||
optionalDescription,
|
optionalDescription,
|
||||||
learnMoreConfig,
|
learnMoreConfig,
|
||||||
} = usePaymentConfig( storeCountry, isPayLater, useAcdc, isFastlane );
|
paypalCheckoutDescription,
|
||||||
|
} = usePaymentConfig( storeCountry, useAcdc, isFastlane );
|
||||||
|
|
||||||
if ( onlyOptional ) {
|
if ( onlyOptional ) {
|
||||||
return (
|
return (
|
||||||
|
@ -34,6 +34,7 @@ const PaymentFlow = ( {
|
||||||
<DefaultMethodsSection
|
<DefaultMethodsSection
|
||||||
methods={ includedMethods }
|
methods={ includedMethods }
|
||||||
learnMoreConfig={ learnMoreConfig }
|
learnMoreConfig={ learnMoreConfig }
|
||||||
|
paypalCheckoutDescription={ paypalCheckoutDescription }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<OptionalMethodsSection
|
<OptionalMethodsSection
|
||||||
|
@ -48,10 +49,17 @@ const PaymentFlow = ( {
|
||||||
|
|
||||||
export default PaymentFlow;
|
export default PaymentFlow;
|
||||||
|
|
||||||
const DefaultMethodsSection = ( { methods, learnMoreConfig } ) => {
|
const DefaultMethodsSection = ( {
|
||||||
|
methods,
|
||||||
|
learnMoreConfig,
|
||||||
|
paypalCheckoutDescription,
|
||||||
|
} ) => {
|
||||||
return (
|
return (
|
||||||
<div className="ppcp-r-welcome-docs__col">
|
<div className="ppcp-r-welcome-docs__col">
|
||||||
<PayPalCheckout learnMore={ learnMoreConfig.PayPalCheckout } />
|
<PayPalCheckout
|
||||||
|
learnMore={ learnMoreConfig.PayPalCheckout }
|
||||||
|
description={ paypalCheckoutDescription }
|
||||||
|
/>
|
||||||
<BadgeBox
|
<BadgeBox
|
||||||
title={ __(
|
title={ __(
|
||||||
'Included in PayPal Checkout',
|
'Included in PayPal Checkout',
|
||||||
|
|
|
@ -5,15 +5,15 @@ import BadgeBox from '../../../../ReusableComponents/BadgeBox';
|
||||||
|
|
||||||
const PayPalCheckout = ( {
|
const PayPalCheckout = ( {
|
||||||
learnMore = 'https://www.paypal.com/us/business/accept-payments/checkout',
|
learnMore = 'https://www.paypal.com/us/business/accept-payments/checkout',
|
||||||
|
description,
|
||||||
} ) => {
|
} ) => {
|
||||||
|
const title = __( 'PayPal Checkout', 'woocommerce-paypal-payments' );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BadgeBox
|
<BadgeBox
|
||||||
title={ __( 'PayPal Checkout', 'woocommerce-paypal-payments' ) }
|
title={ title }
|
||||||
textBadge={ <PricingTitleBadge item="checkout" /> }
|
textBadge={ <PricingTitleBadge item="checkout" /> }
|
||||||
description={ __(
|
description={ description }
|
||||||
'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
) }
|
|
||||||
learnMoreLink={ learnMore }
|
learnMoreLink={ learnMore }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ const PricingDescription = () => {
|
||||||
return null;
|
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 countryLinks = learnMoreLinks[ storeCountry ] || learnMoreLinks.US;
|
||||||
|
|
||||||
const label = sprintf(
|
const label = sprintf(
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { __ } from '@wordpress/i18n';
|
||||||
import PricingDescription from './PricingDescription';
|
import PricingDescription from './PricingDescription';
|
||||||
import PaymentFlow from './PaymentFlow';
|
import PaymentFlow from './PaymentFlow';
|
||||||
|
|
||||||
const WelcomeDocs = ( { useAcdc, isFastlane, isPayLater, storeCountry } ) => {
|
const WelcomeDocs = ( { useAcdc, isFastlane, storeCountry } ) => {
|
||||||
return (
|
return (
|
||||||
<div className="ppcp-r-welcome-docs">
|
<div className="ppcp-r-welcome-docs">
|
||||||
<h2 className="ppcp-r-welcome-docs__title">
|
<h2 className="ppcp-r-welcome-docs__title">
|
||||||
|
@ -15,7 +15,6 @@ const WelcomeDocs = ( { useAcdc, isFastlane, isPayLater, storeCountry } ) => {
|
||||||
<PaymentFlow
|
<PaymentFlow
|
||||||
useAcdc={ useAcdc }
|
useAcdc={ useAcdc }
|
||||||
isFastlane={ isFastlane }
|
isFastlane={ isFastlane }
|
||||||
isPayLater={ isPayLater }
|
|
||||||
storeCountry={ storeCountry }
|
storeCountry={ storeCountry }
|
||||||
/>
|
/>
|
||||||
<PricingDescription />
|
<PricingDescription />
|
||||||
|
|
|
@ -22,9 +22,34 @@ const StepBusiness = ( {} ) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
|
if ( ! businessChoice ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === businessChoice );
|
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === businessChoice );
|
||||||
}, [ businessChoice, setIsCasualSeller ] );
|
}, [ businessChoice, setIsCasualSeller ] );
|
||||||
|
|
||||||
|
const { canUseSubscriptions } = OnboardingHooks.useFlags();
|
||||||
|
const businessChoices = [
|
||||||
|
{
|
||||||
|
value: BUSINESS_TYPES.BUSINESS,
|
||||||
|
title: __( 'Business', 'woocommerce-paypal-payments' ),
|
||||||
|
description: __(
|
||||||
|
'Recommended for individuals and organizations that primarily use PayPal to sell goods or services or receive donations, even if your business is not incorporated.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: BUSINESS_TYPES.CASUAL_SELLER,
|
||||||
|
title: __( 'Personal Account', 'woocommerce-paypal-payments' ),
|
||||||
|
description: __(
|
||||||
|
'Ideal for those who primarily make purchases or send personal transactions to family and friends.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
contents: canUseSubscriptions ? <DetailsAccountType /> : null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ppcp-r-page-business">
|
<div className="ppcp-r-page-business">
|
||||||
<OnboardingHeader
|
<OnboardingHeader
|
||||||
|
@ -45,23 +70,13 @@ const StepBusiness = ( {} ) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const businessChoices = [
|
const DetailsAccountType = () => (
|
||||||
{
|
<p>
|
||||||
value: BUSINESS_TYPES.BUSINESS,
|
{ __(
|
||||||
title: __( 'Business', 'woocommerce-paypal-payments' ),
|
'* Business account is required for subscriptions.',
|
||||||
description: __(
|
|
||||||
'Recommended for individuals and organizations that primarily use PayPal to sell goods or services or receive donations, even if your business is not incorporated.',
|
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
) }
|
||||||
},
|
</p>
|
||||||
{
|
);
|
||||||
value: BUSINESS_TYPES.CASUAL_SELLER,
|
|
||||||
title: __( 'Personal Account', 'woocommerce-paypal-payments' ),
|
|
||||||
description: __(
|
|
||||||
'Ideal for those who primarily make purchases or send personal transactions to family and friends.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default StepBusiness;
|
export default StepBusiness;
|
||||||
|
|
|
@ -59,33 +59,18 @@ const StepPaymentMethods = () => {
|
||||||
export default StepPaymentMethods;
|
export default StepPaymentMethods;
|
||||||
|
|
||||||
const PaymentStepTitle = () => {
|
const PaymentStepTitle = () => {
|
||||||
const { storeCountry } = CommonHooks.useWooSettings();
|
return __( 'Add Credit and Debit Cards', 'woocommerce-paypal-payments' );
|
||||||
|
|
||||||
if ( 'US' === storeCountry ) {
|
|
||||||
return __(
|
|
||||||
'Add Expanded Checkout for More Ways to Pay',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __(
|
|
||||||
'Add optional payment methods to your Checkout',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const OptionalMethodDescription = () => {
|
const OptionalMethodDescription = () => {
|
||||||
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
|
|
||||||
const { isCasualSeller } = OnboardingHooks.useBusiness();
|
const { isCasualSeller } = OnboardingHooks.useBusiness();
|
||||||
|
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
|
||||||
|
const { canUseCardPayments } = OnboardingHooks.useFlags();
|
||||||
|
|
||||||
/**
|
|
||||||
* Casual sellers = Personal accounts. Those accounts have no ACDC-capabilities, but should get
|
|
||||||
* the choice for BCDC-payments.
|
|
||||||
*/
|
|
||||||
return (
|
return (
|
||||||
<PaymentFlow
|
<PaymentFlow
|
||||||
onlyOptional={ true }
|
onlyOptional={ true }
|
||||||
useAcdc={ ! isCasualSeller }
|
useAcdc={ ! isCasualSeller && canUseCardPayments }
|
||||||
isFastlane={ true }
|
isFastlane={ true }
|
||||||
isPayLater={ true }
|
isPayLater={ true }
|
||||||
storeCountry={ storeCountry }
|
storeCountry={ storeCountry }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { useEffect, useState } from '@wordpress/element';
|
import { useEffect, useState } from '@wordpress/element';
|
||||||
|
|
||||||
import { OptionSelector } from '../../../ReusableComponents/Fields';
|
import { OptionSelector } from '../../../ReusableComponents/Fields';
|
||||||
|
@ -10,26 +10,28 @@ const StepProducts = () => {
|
||||||
const { canUseSubscriptions } = OnboardingHooks.useFlags();
|
const { canUseSubscriptions } = OnboardingHooks.useFlags();
|
||||||
const [ optionState, setOptionState ] = useState( null );
|
const [ optionState, setOptionState ] = useState( null );
|
||||||
const [ productChoices, setProductChoices ] = useState( [] );
|
const [ productChoices, setProductChoices ] = useState( [] );
|
||||||
|
const { isCasualSeller } = OnboardingHooks.useBusiness();
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
const initChoices = () => {
|
const initChoices = () => {
|
||||||
if ( optionState === canUseSubscriptions ) {
|
const choices = productChoicesFull.map( ( choice ) => {
|
||||||
return;
|
if (
|
||||||
}
|
choice.value === PRODUCT_TYPES.SUBSCRIPTIONS &&
|
||||||
|
! canUseSubscriptions
|
||||||
let choices = productChoicesFull;
|
) {
|
||||||
|
return {
|
||||||
// Remove subscription details, if not available.
|
...choice,
|
||||||
if ( ! canUseSubscriptions ) {
|
isDisabled: true,
|
||||||
choices = choices.filter(
|
contents: (
|
||||||
( { value } ) => value !== PRODUCT_TYPES.SUBSCRIPTIONS
|
<DetailsSubscriptions
|
||||||
);
|
showLink={ true }
|
||||||
setProducts(
|
showNotice={ isCasualSeller }
|
||||||
products.filter(
|
/>
|
||||||
( value ) => value !== PRODUCT_TYPES.SUBSCRIPTIONS
|
),
|
||||||
)
|
};
|
||||||
);
|
}
|
||||||
}
|
return choice;
|
||||||
|
} );
|
||||||
|
|
||||||
setProductChoices( choices );
|
setProductChoices( choices );
|
||||||
setOptionState( canUseSubscriptions );
|
setOptionState( canUseSubscriptions );
|
||||||
|
@ -48,7 +50,46 @@ const StepProducts = () => {
|
||||||
|
|
||||||
setProducts( getNewValue() );
|
setProducts( getNewValue() );
|
||||||
};
|
};
|
||||||
|
const productChoicesFull = [
|
||||||
|
{
|
||||||
|
value: PRODUCT_TYPES.VIRTUAL,
|
||||||
|
title: __( 'Virtual', 'woocommerce-paypal-payments' ),
|
||||||
|
description: __(
|
||||||
|
'Items do not require shipping.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
contents: <DetailsVirtual />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PRODUCT_TYPES.PHYSICAL,
|
||||||
|
title: __( 'Physical Goods', 'woocommerce-paypal-payments' ),
|
||||||
|
description: __(
|
||||||
|
'Items require shipping.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
contents: <DetailsPhysical />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PRODUCT_TYPES.SUBSCRIPTIONS,
|
||||||
|
title: __( 'Subscriptions', 'woocommerce-paypal-payments' ),
|
||||||
|
description: __(
|
||||||
|
'Recurring payments for either physical goods or services.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
isDisabled: 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 (
|
return (
|
||||||
<div className="ppcp-r-page-products">
|
<div className="ppcp-r-page-products">
|
||||||
<OnboardingHeader
|
<OnboardingHeader
|
||||||
|
@ -87,41 +128,29 @@ const DetailsPhysical = () => (
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
||||||
const DetailsSubscriptions = () => (
|
const DetailsSubscriptions = ( { showLink, showNotice } ) => (
|
||||||
<a
|
<>
|
||||||
target="__blank"
|
{ showLink && (
|
||||||
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
|
<p
|
||||||
>
|
dangerouslySetInnerHTML={ {
|
||||||
{ __( 'WooCommerce Subscriptions', 'woocommerce-paypal-payments' ) }
|
__html: sprintf(
|
||||||
</a>
|
/* translators: %s is the URL to the WooCommerce Subscriptions product page */
|
||||||
|
__(
|
||||||
|
'* To use subscriptions, you must have <a target="_blank" href="%s">WooCommerce Subscriptions</a> enabled.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
'https://woocommerce.com/products/woocommerce-subscriptions/'
|
||||||
|
),
|
||||||
|
} }
|
||||||
|
/>
|
||||||
|
) }
|
||||||
|
{ showNotice && (
|
||||||
|
<p>
|
||||||
|
{ __(
|
||||||
|
'* Business account is required for subscriptions.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
</p>
|
||||||
|
) }
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const productChoicesFull = [
|
|
||||||
{
|
|
||||||
value: PRODUCT_TYPES.VIRTUAL,
|
|
||||||
title: __( 'Virtual', 'woocommerce-paypal-payments' ),
|
|
||||||
description: __(
|
|
||||||
'Items do not require shipping.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
contents: <DetailsVirtual />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PRODUCT_TYPES.PHYSICAL,
|
|
||||||
title: __( 'Physical Goods', 'woocommerce-paypal-payments' ),
|
|
||||||
description: __(
|
|
||||||
'Items require shipping.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
contents: <DetailsPhysical />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: PRODUCT_TYPES.SUBSCRIPTIONS,
|
|
||||||
title: __( 'Subscriptions', 'woocommerce-paypal-payments' ),
|
|
||||||
description: __(
|
|
||||||
'Recurring payments for either physical goods or services.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
),
|
|
||||||
contents: <DetailsSubscriptions />,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
|
@ -4,14 +4,32 @@ import { Button } from '@wordpress/components';
|
||||||
import PaymentMethodIcons from '../../../ReusableComponents/PaymentMethodIcons';
|
import PaymentMethodIcons from '../../../ReusableComponents/PaymentMethodIcons';
|
||||||
import { Separator } from '../../../ReusableComponents/Elements';
|
import { Separator } from '../../../ReusableComponents/Elements';
|
||||||
import Accordion from '../../../ReusableComponents/AccordionSection';
|
import Accordion from '../../../ReusableComponents/AccordionSection';
|
||||||
import { CommonHooks } from '../../../../data';
|
import { CommonHooks, OnboardingHooks } from '../../../../data';
|
||||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||||
import OnboardingHeader from '../Components/OnboardingHeader';
|
import OnboardingHeader from '../Components/OnboardingHeader';
|
||||||
import WelcomeDocs from '../Components/WelcomeDocs';
|
import WelcomeDocs from '../Components/WelcomeDocs';
|
||||||
import AdvancedOptionsForm from '../Components/AdvancedOptionsForm';
|
import AdvancedOptionsForm from '../Components/AdvancedOptionsForm';
|
||||||
|
import { usePaymentConfig } from '../hooks/usePaymentConfig';
|
||||||
|
|
||||||
const StepWelcome = ( { setStep, currentStep } ) => {
|
const StepWelcome = ( { setStep, currentStep } ) => {
|
||||||
const { storeCountry } = CommonHooks.useWooSettings();
|
const { storeCountry } = CommonHooks.useWooSettings();
|
||||||
|
const { canUseCardPayments, canUseFastlane } = OnboardingHooks.useFlags();
|
||||||
|
|
||||||
|
const { acdcIcons, bcdcIcons } = usePaymentConfig(
|
||||||
|
storeCountry,
|
||||||
|
canUseCardPayments,
|
||||||
|
canUseFastlane
|
||||||
|
);
|
||||||
|
|
||||||
|
const onboardingHeaderDescription = canUseCardPayments
|
||||||
|
? __(
|
||||||
|
'Your all-in-one integration for PayPal checkout solutions that enable buyers to pay via PayPal, Pay Later, all major credit/debit cards, Apple Pay, Google Pay, and more.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
)
|
||||||
|
: __(
|
||||||
|
'Your all-in-one integration for PayPal checkout solutions that enable buyers to pay via PayPal, Pay Later, all major credit/debit cards, and more.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ppcp-r-page-welcome">
|
<div className="ppcp-r-page-welcome">
|
||||||
|
@ -20,17 +38,16 @@ const StepWelcome = ( { setStep, currentStep } ) => {
|
||||||
'Welcome to PayPal Payments',
|
'Welcome to PayPal Payments',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
description={ __(
|
description={ onboardingHeaderDescription }
|
||||||
'Your all-in-one integration for PayPal checkout solutions that enable buyers to pay via PayPal, Pay Later, all major credit/debit cards, Apple Pay, Google Pay, and more.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
) }
|
|
||||||
/>
|
/>
|
||||||
<div className="ppcp-r-inner-container">
|
<div className="ppcp-r-inner-container">
|
||||||
<WelcomeFeatures />
|
<WelcomeFeatures />
|
||||||
<PaymentMethodIcons icons="all" />
|
<PaymentMethodIcons
|
||||||
|
icons={ canUseCardPayments ? acdcIcons : bcdcIcons }
|
||||||
|
/>
|
||||||
<p className="ppcp-r-button__description">
|
<p className="ppcp-r-button__description">
|
||||||
{ __(
|
{ __(
|
||||||
`Click the button below to be guided through connecting your existing PayPal account or creating a new one.You will be able to choose the payment options that are right for your store.`,
|
'Click the button below to be guided through connecting your existing PayPal account or creating a new one. You will be able to choose the payment options that are right for your store.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
</p>
|
</p>
|
||||||
|
@ -49,9 +66,8 @@ const StepWelcome = ( { setStep, currentStep } ) => {
|
||||||
</div>
|
</div>
|
||||||
<Separator className="ppcp-r-page-welcome-mode-separator" />
|
<Separator className="ppcp-r-page-welcome-mode-separator" />
|
||||||
<WelcomeDocs
|
<WelcomeDocs
|
||||||
useAcdc={ true }
|
useAcdc={ canUseCardPayments }
|
||||||
isFastlane={ true }
|
isFastlane={ canUseFastlane }
|
||||||
isPayLater={ true }
|
|
||||||
storeCountry={ storeCountry }
|
storeCountry={ storeCountry }
|
||||||
/>
|
/>
|
||||||
<Separator text={ __( 'or', 'woocommerce-paypal-payments' ) } />
|
<Separator text={ __( 'or', 'woocommerce-paypal-payments' ) } />
|
||||||
|
|
|
@ -36,8 +36,7 @@ const ALL_STEPS = [
|
||||||
id: 'methods',
|
id: 'methods',
|
||||||
title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ),
|
title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ),
|
||||||
StepComponent: StepPaymentMethods,
|
StepComponent: StepPaymentMethods,
|
||||||
canProceed: ( { methods } ) =>
|
canProceed: ( { methods } ) => methods.optionalMethods !== null,
|
||||||
methods.areOptionalPaymentMethodsEnabled !== null,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'complete',
|
id: 'complete',
|
||||||
|
@ -60,8 +59,8 @@ export const getSteps = ( flags ) => {
|
||||||
const steps = filterSteps( ALL_STEPS, [
|
const steps = filterSteps( ALL_STEPS, [
|
||||||
// Casual selling: Unlock the "Personal Account" choice.
|
// Casual selling: Unlock the "Personal Account" choice.
|
||||||
( step ) => flags.canUseCasualSelling || step.id !== 'business',
|
( step ) => flags.canUseCasualSelling || step.id !== 'business',
|
||||||
// Card payments: Unlocks the "Extended Checkout" choice.
|
// Skip payment methods screen.
|
||||||
( step ) => flags.canUseCardPayments || step.id !== 'methods',
|
( step ) => ! flags.shouldSkipPaymentMethods || step.id !== 'methods',
|
||||||
] );
|
] );
|
||||||
|
|
||||||
const totalStepsCount = steps.length;
|
const totalStepsCount = steps.length;
|
||||||
|
|
|
@ -28,6 +28,7 @@ const defaultConfig = {
|
||||||
|
|
||||||
// Extended: Items on right side for ACDC-flow.
|
// Extended: Items on right side for ACDC-flow.
|
||||||
extendedMethods: [
|
extendedMethods: [
|
||||||
|
{ name: 'CardFields', Component: CardFields },
|
||||||
{ name: 'DigitalWallets', Component: DigitalWallets },
|
{ name: 'DigitalWallets', Component: DigitalWallets },
|
||||||
{ name: 'APMs', Component: AlternativePaymentMethods },
|
{ name: 'APMs', Component: AlternativePaymentMethods },
|
||||||
],
|
],
|
||||||
|
@ -41,6 +42,26 @@ const defaultConfig = {
|
||||||
'with additional application',
|
'with additional application',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// PayPal Checkout description.
|
||||||
|
paypalCheckoutDescription: __(
|
||||||
|
'Our all-in-one checkout solution lets you offer PayPal, Pay Later options, and more to help maximise conversion',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
|
||||||
|
// Icon groups.
|
||||||
|
bcdcIcons: [ 'paypal', 'visa', 'mastercard', 'amex', 'discover' ],
|
||||||
|
acdcIcons: [
|
||||||
|
'paypal',
|
||||||
|
'visa',
|
||||||
|
'mastercard',
|
||||||
|
'amex',
|
||||||
|
'discover',
|
||||||
|
'apple-pay',
|
||||||
|
'google-pay',
|
||||||
|
'ideal',
|
||||||
|
'bancontact',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const countrySpecificConfigs = {
|
const countrySpecificConfigs = {
|
||||||
|
@ -60,11 +81,35 @@ const countrySpecificConfigs = {
|
||||||
{ name: 'APMs', Component: AlternativePaymentMethods },
|
{ name: 'APMs', Component: AlternativePaymentMethods },
|
||||||
{ name: 'Fastlane', Component: Fastlane },
|
{ name: 'Fastlane', Component: Fastlane },
|
||||||
],
|
],
|
||||||
|
paypalCheckoutDescription: __(
|
||||||
|
'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
optionalTitle: __( 'Expanded Checkout', 'woocommerce-paypal-payments' ),
|
optionalTitle: __( 'Expanded Checkout', 'woocommerce-paypal-payments' ),
|
||||||
optionalDescription: __(
|
optionalDescription: __(
|
||||||
'Accept debit/credit cards, PayPal, Apple Pay, Google Pay, and more. Note: Additional application required for more methods',
|
'Accept debit/credit cards, PayPal, Apple Pay, Google Pay, and more. Note: Additional application required for more methods',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
),
|
),
|
||||||
|
bcdcIcons: [
|
||||||
|
'paypal',
|
||||||
|
'venmo',
|
||||||
|
'visa',
|
||||||
|
'mastercard',
|
||||||
|
'amex',
|
||||||
|
'discover',
|
||||||
|
],
|
||||||
|
acdcIcons: [
|
||||||
|
'paypal',
|
||||||
|
'venmo',
|
||||||
|
'visa',
|
||||||
|
'mastercard',
|
||||||
|
'amex',
|
||||||
|
'discover',
|
||||||
|
'apple-pay',
|
||||||
|
'google-pay',
|
||||||
|
'ideal',
|
||||||
|
'bancontact',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
GB: {
|
GB: {
|
||||||
includedMethods: [
|
includedMethods: [
|
||||||
|
@ -72,6 +117,12 @@ const countrySpecificConfigs = {
|
||||||
{ name: 'PayInThree', Component: PayInThree },
|
{ name: 'PayInThree', Component: PayInThree },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
MX: {
|
||||||
|
extendedMethods: [
|
||||||
|
{ name: 'CardFields', Component: CardFields },
|
||||||
|
{ name: 'APMs', Component: AlternativePaymentMethods },
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterMethods = ( methods, conditions ) => {
|
const filterMethods = ( methods, conditions ) => {
|
||||||
|
@ -80,22 +131,12 @@ const filterMethods = ( methods, conditions ) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePaymentConfig = (
|
export const usePaymentConfig = ( country, useAcdc, isFastlane ) => {
|
||||||
country,
|
|
||||||
isPayLater,
|
|
||||||
useAcdc,
|
|
||||||
isFastlane
|
|
||||||
) => {
|
|
||||||
return useMemo( () => {
|
return useMemo( () => {
|
||||||
const countryConfig = countrySpecificConfigs[ country ] || {};
|
const countryConfig = countrySpecificConfigs[ country ] || {};
|
||||||
const config = { ...defaultConfig, ...countryConfig };
|
const config = { ...defaultConfig, ...countryConfig };
|
||||||
const learnMoreConfig = learnMoreLinks[ country ] || {};
|
const learnMoreConfig = learnMoreLinks[ country ] || {};
|
||||||
|
|
||||||
// Filter the "left side" list. PayLater is conditional.
|
|
||||||
const includedMethods = filterMethods( config.includedMethods, [
|
|
||||||
( method ) => isPayLater || method.name !== 'PayLater',
|
|
||||||
] );
|
|
||||||
|
|
||||||
// Determine the "right side" items: Either BCDC or ACDC items.
|
// Determine the "right side" items: Either BCDC or ACDC items.
|
||||||
const optionalMethods = useAcdc
|
const optionalMethods = useAcdc
|
||||||
? config.extendedMethods
|
? config.extendedMethods
|
||||||
|
@ -108,9 +149,10 @@ export const usePaymentConfig = (
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
includedMethods,
|
|
||||||
optionalMethods: availableOptionalMethods,
|
optionalMethods: availableOptionalMethods,
|
||||||
learnMoreConfig,
|
learnMoreConfig,
|
||||||
|
acdcIcons: config.acdcIcons,
|
||||||
|
bcdcIcons: config.bcdcIcons,
|
||||||
};
|
};
|
||||||
}, [ country, isPayLater, useAcdc, isFastlane ] );
|
}, [ country, useAcdc, isFastlane ] );
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { PayLaterMessagingHooks } from '../../../data';
|
import { PayLaterMessagingHooks } from '../../../data';
|
||||||
|
import { useEffect } from '@wordpress/element';
|
||||||
|
|
||||||
const TabPayLaterMessaging = () => {
|
const TabPayLaterMessaging = () => {
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { __ } from '@wordpress/i18n';
|
||||||
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
|
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
|
||||||
|
|
||||||
import TopNavigation from '../../../ReusableComponents/TopNavigation';
|
import TopNavigation from '../../../ReusableComponents/TopNavigation';
|
||||||
import { useSaveSettings } from '../../../../hooks/useSaveSettings';
|
import { useStoreManager } from '../../../../hooks/useStoreManager';
|
||||||
import { CommonHooks } from '../../../../data';
|
import { CommonHooks } from '../../../../data';
|
||||||
import TabBar from '../../../ReusableComponents/TabBar';
|
import TabBar from '../../../ReusableComponents/TabBar';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -20,7 +20,7 @@ const SettingsNavigation = ( {
|
||||||
activePanel,
|
activePanel,
|
||||||
setActivePanel,
|
setActivePanel,
|
||||||
} ) => {
|
} ) => {
|
||||||
const { persistAll } = useSaveSettings();
|
const { persistAll } = useStoreManager();
|
||||||
|
|
||||||
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );
|
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Button, Icon } from '@wordpress/components';
|
||||||
|
import { reusableBlock } from '@wordpress/icons';
|
||||||
|
|
||||||
|
const FeatureDescription = ( { refreshHandler, isRefreshing } ) => {
|
||||||
|
const buttonLabel = isRefreshing
|
||||||
|
? __( 'Refreshing…', 'woocommerce-paypal-payments' )
|
||||||
|
: __( 'Refresh', 'woocommerce-paypal-payments' );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{ __(
|
||||||
|
'Enable additional features and capabilities on your WooCommerce store.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{ __(
|
||||||
|
'Click Refresh to update your current features after making changes.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={ refreshHandler }
|
||||||
|
disabled={ isRefreshing }
|
||||||
|
>
|
||||||
|
<Icon icon={ reusableBlock } size={ 18 } />
|
||||||
|
{ buttonLabel }
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeatureDescription;
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { FeatureSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
|
||||||
|
import { Content } from '../../../../../ReusableComponents/Elements';
|
||||||
|
import { TITLE_BADGE_POSITIVE } from '../../../../../ReusableComponents/TitleBadge';
|
||||||
|
import { selectTab, TAB_IDS } from '../../../../../../utils/tabSelector';
|
||||||
|
import { useDispatch } from '@wordpress/data';
|
||||||
|
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../../../data/common';
|
||||||
|
|
||||||
|
const FeatureItem = ( {
|
||||||
|
isBusy,
|
||||||
|
isSandbox,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
buttons,
|
||||||
|
enabled,
|
||||||
|
notes,
|
||||||
|
} ) => {
|
||||||
|
const { setActiveModal } = useDispatch( COMMON_STORE_NAME );
|
||||||
|
const getButtonUrl = ( button ) => {
|
||||||
|
if ( button.urls ) {
|
||||||
|
return isSandbox ? button.urls.sandbox : button.urls.live;
|
||||||
|
}
|
||||||
|
|
||||||
|
return button.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const visibleButtons = buttons.filter(
|
||||||
|
( button ) =>
|
||||||
|
! button.showWhen || // Learn more buttons
|
||||||
|
( enabled && button.showWhen === 'enabled' ) ||
|
||||||
|
( ! enabled && button.showWhen === 'disabled' )
|
||||||
|
);
|
||||||
|
const handleClick = async ( feature ) => {
|
||||||
|
if ( feature.action?.type === 'tab' ) {
|
||||||
|
const highlight = Boolean( feature.action?.highlight );
|
||||||
|
const tabId = TAB_IDS[ feature.action.tab.toUpperCase() ];
|
||||||
|
await selectTab( tabId, feature.action.section, highlight );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( feature.action?.modal ) {
|
||||||
|
setActiveModal( feature.action.modal );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionProps = {
|
||||||
|
isBusy,
|
||||||
|
enabled,
|
||||||
|
notes,
|
||||||
|
buttons: visibleButtons.map( ( button ) => ( {
|
||||||
|
...button,
|
||||||
|
url: getButtonUrl( button ),
|
||||||
|
onClick: () => handleClick( button ),
|
||||||
|
} ) ),
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( enabled ) {
|
||||||
|
actionProps.badge = {
|
||||||
|
text: __( 'Active', 'woocommerce-paypal-payments' ),
|
||||||
|
type: TITLE_BADGE_POSITIVE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Content>
|
||||||
|
<FeatureSettingsBlock
|
||||||
|
title={ title }
|
||||||
|
description={ description }
|
||||||
|
actionProps={ actionProps }
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeatureItem;
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
|
import { useState } from '@wordpress/element';
|
||||||
|
import { useDispatch } from '@wordpress/data';
|
||||||
|
import { store as noticesStore } from '@wordpress/notices';
|
||||||
|
import FeatureItem from './FeatureItem';
|
||||||
|
import FeatureDescription from './FeatureDescription';
|
||||||
|
import { ContentWrapper } from '../../../../../ReusableComponents/Elements';
|
||||||
|
import SettingsCard from '../../../../../ReusableComponents/SettingsCard';
|
||||||
|
import { useMerchantInfo } from '../../../../../../data/common/hooks';
|
||||||
|
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../../../data/common';
|
||||||
|
import {
|
||||||
|
NOTIFICATION_ERROR,
|
||||||
|
NOTIFICATION_SUCCESS,
|
||||||
|
} from '../../../../../ReusableComponents/Icons';
|
||||||
|
import { useFeatures } from '../../../../../../data/features/hooks';
|
||||||
|
|
||||||
|
const Features = () => {
|
||||||
|
const [ isRefreshing, setIsRefreshing ] = useState( false );
|
||||||
|
const { merchant } = useMerchantInfo();
|
||||||
|
const { features, fetchFeatures } = useFeatures();
|
||||||
|
const { refreshFeatureStatuses } = useDispatch( COMMON_STORE_NAME );
|
||||||
|
const { createSuccessNotice, createErrorNotice } =
|
||||||
|
useDispatch( noticesStore );
|
||||||
|
|
||||||
|
if ( ! features || features.length === 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshHandler = async () => {
|
||||||
|
setIsRefreshing( true );
|
||||||
|
try {
|
||||||
|
const statusResult = await refreshFeatureStatuses();
|
||||||
|
if ( ! statusResult?.success ) {
|
||||||
|
throw new Error(
|
||||||
|
statusResult?.message || 'Failed to refresh status'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const featuresResult = await fetchFeatures();
|
||||||
|
if ( featuresResult.success ) {
|
||||||
|
createSuccessNotice(
|
||||||
|
__(
|
||||||
|
'Features refreshed successfully.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
{ icon: NOTIFICATION_SUCCESS }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
featuresResult?.message || 'Failed to fetch features'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch ( error ) {
|
||||||
|
createErrorNotice(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: error message */
|
||||||
|
__( 'Operation failed: %s', 'woocommerce-paypal-payments' ),
|
||||||
|
error.message ||
|
||||||
|
__( 'Unknown error', 'woocommerce-paypal-payments' )
|
||||||
|
),
|
||||||
|
{ icon: NOTIFICATION_ERROR }
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setIsRefreshing( false );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsCard
|
||||||
|
className="ppcp-r-tab-overview-features"
|
||||||
|
title={ __( 'Features', 'woocommerce-paypal-payments' ) }
|
||||||
|
description={
|
||||||
|
<FeatureDescription
|
||||||
|
refreshHandler={ refreshHandler }
|
||||||
|
isRefreshing={ isRefreshing }
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
contentContainer={ false }
|
||||||
|
>
|
||||||
|
<ContentWrapper>
|
||||||
|
{ features.map( ( { id, enabled, ...feature } ) => (
|
||||||
|
<FeatureItem
|
||||||
|
key={ id }
|
||||||
|
isBusy={ isRefreshing }
|
||||||
|
isSandbox={ merchant.isSandbox }
|
||||||
|
enabled={ enabled }
|
||||||
|
{ ...feature }
|
||||||
|
/>
|
||||||
|
) ) }
|
||||||
|
</ContentWrapper>
|
||||||
|
</SettingsCard>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Features;
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { FeatureSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
|
||||||
|
import {
|
||||||
|
Content,
|
||||||
|
ContentWrapper,
|
||||||
|
} from '../../../../../ReusableComponents/Elements';
|
||||||
|
import SettingsCard from '../../../../../ReusableComponents/SettingsCard';
|
||||||
|
|
||||||
|
const Help = () => {
|
||||||
|
return (
|
||||||
|
<SettingsCard
|
||||||
|
className="ppcp-r-tab-overview-help"
|
||||||
|
title={ __( 'Help Center', 'woocommerce-paypal-payments' ) }
|
||||||
|
description={ __(
|
||||||
|
'Access detailed guides and responsive support to streamline setup and enhance your experience.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
contentContainer={ false }
|
||||||
|
>
|
||||||
|
<ContentWrapper>
|
||||||
|
<Content>
|
||||||
|
<FeatureSettingsBlock
|
||||||
|
title={ __(
|
||||||
|
'Documentation',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
description={ __(
|
||||||
|
'Find detailed guides and resources to help you set up, manage, and optimize your PayPal integration.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
actionProps={ {
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
type: 'tertiary',
|
||||||
|
text: __(
|
||||||
|
'View full documentation',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} }
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Content>
|
||||||
|
<FeatureSettingsBlock
|
||||||
|
title={ __( 'Support', 'woocommerce-paypal-payments' ) }
|
||||||
|
description={ __(
|
||||||
|
'Need help? Access troubleshooting tips or contact our support team for personalized assistance.',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
) }
|
||||||
|
actionProps={ {
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
type: 'tertiary',
|
||||||
|
text: __(
|
||||||
|
'View support options',
|
||||||
|
'woocommerce-paypal-payments'
|
||||||
|
),
|
||||||
|
url: 'https://woocommerce.com/document/woocommerce-paypal-payments/#get-help ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} }
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
</ContentWrapper>
|
||||||
|
</SettingsCard>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Help;
|
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