mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 09:08:09 +08:00
🔀 Merge branch 'trunk'
# Conflicts: # modules/ppcp-applepay/resources/js/ApplepayButton.js # modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js
This commit is contained in:
commit
6c54377a6e
53 changed files with 2415 additions and 2100 deletions
1
.megaignore
Normal file
1
.megaignore
Normal file
|
@ -0,0 +1 @@
|
|||
-s:*
|
|
@ -1,5 +1,22 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.8.3 - 2024-08-12 =
|
||||
* Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474
|
||||
* Fix - Do not add tax info into order meta during order creation #2471
|
||||
* Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425
|
||||
* Fix - PayPal js files loaded on non PayPal pages #2411
|
||||
* Fix - Google Pay: Fix the incorrect popup triggering #2414
|
||||
* Fix - Add tax configurator when programmatically creating WC orders #2431
|
||||
* Fix - Shipping callback compatibility with WC Name Your Price plugin #2402
|
||||
* Fix - Uncaught Error: Cannot use object of type ...\Settings as array in .../AbstractPaymentMethodType.php (3253) #2334
|
||||
* Fix - Prevent displaying smart button multiple times on variable product page #2420
|
||||
* Fix - Prevent enabling Standard Card Button when ACDC is enabled #2404
|
||||
* Fix - Use client credentials for user tokens #2491
|
||||
* Fix - Apple Pay: Fix the shipping callback #2492
|
||||
* Enhancement - Separate Google Pay button for Classic Checkout #2430
|
||||
* Enhancement - Add Apple Pay and Google Pay support for China, simplify country-currency matrix #2468
|
||||
* Enhancement - Add AMEX support for Advanced Card Processing in China #2469
|
||||
|
||||
= 2.8.2 - 2024-07-22 =
|
||||
* Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415
|
||||
* Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\ClientCredentials;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
|
@ -1655,18 +1656,27 @@ return array(
|
|||
return new PurchaseUnitSanitizer( $behavior, $line_name );
|
||||
}
|
||||
),
|
||||
'api.client-credentials' => static function( ContainerInterface $container ): ClientCredentials {
|
||||
return new ClientCredentials(
|
||||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
'api.client-credentials-cache' => static function( ContainerInterface $container ): Cache {
|
||||
return new Cache( 'ppcp-client-credentials-cache' );
|
||||
},
|
||||
'api.user-id-token' => static function( ContainerInterface $container ): UserIdToken {
|
||||
return new UserIdToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.client-credentials' )
|
||||
);
|
||||
},
|
||||
'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken {
|
||||
return new SdkClientToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.client-credentials' ),
|
||||
$container->get( 'api.client-credentials-cache' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Authentication
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Authentication;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class ClientCredentials
|
||||
*/
|
||||
class ClientCredentials {
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* ClientCredentials constructor.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
*/
|
||||
public function __construct( Settings $settings ) {
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encoded client credentials.
|
||||
*
|
||||
* @return string
|
||||
* @throws NotFoundException If setting does not found.
|
||||
*/
|
||||
public function credentials(): string {
|
||||
$client_id = $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : '';
|
||||
$client_secret = $this->settings->has( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : '';
|
||||
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
||||
return 'Basic ' . base64_encode( $client_id . ':' . $client_secret );
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
|
@ -20,6 +21,8 @@ class SdkClientToken {
|
|||
|
||||
use RequestTrait;
|
||||
|
||||
const CACHE_KEY = 'sdk-client-token-key';
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
|
@ -27,13 +30,6 @@ class SdkClientToken {
|
|||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -41,35 +37,52 @@ class SdkClientToken {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @var ClientCredentials
|
||||
*/
|
||||
private $client_credentials;
|
||||
|
||||
/**
|
||||
* The cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* SdkClientToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param ClientCredentials $client_credentials The client credentials.
|
||||
* @param Cache $cache The cache.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
ClientCredentials $client_credentials,
|
||||
Cache $cache
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->client_credentials = $client_credentials;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `sdk_client_token` which uniquely identifies the payer.
|
||||
*
|
||||
* @param string $target_customer_id Vaulted customer id.
|
||||
* Returns the client token for SDK `data-sdk-client-token`.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws PayPalApiException If the request fails.
|
||||
* @throws RuntimeException If something unexpected happens.
|
||||
*/
|
||||
public function sdk_client_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
public function sdk_client_token(): string {
|
||||
if ( $this->cache->has( self::CACHE_KEY ) ) {
|
||||
return $this->cache->get( self::CACHE_KEY );
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' );
|
||||
|
@ -77,19 +90,10 @@ class SdkClientToken {
|
|||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=' . $domain;
|
||||
|
||||
if ( $target_customer_id ) {
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'target_customer_id' => $target_customer_id,
|
||||
),
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Authorization' => $this->client_credentials->credentials(),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
),
|
||||
);
|
||||
|
@ -105,6 +109,11 @@ class SdkClientToken {
|
|||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
return $json->access_token;
|
||||
$access_token = $json->access_token;
|
||||
$expires_in = (int) $json->expires_in;
|
||||
|
||||
$this->cache->set( self::CACHE_KEY, $access_token, $expires_in );
|
||||
|
||||
return $access_token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,6 @@ class UserIdToken {
|
|||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -41,21 +34,28 @@ class UserIdToken {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @var ClientCredentials
|
||||
*/
|
||||
private $client_credentials;
|
||||
|
||||
/**
|
||||
* UserIdToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param ClientCredentials $client_credentials The client credentials.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
ClientCredentials $client_credentials
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->client_credentials = $client_credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,8 +69,6 @@ class UserIdToken {
|
|||
* @throws RuntimeException If something unexpected happens.
|
||||
*/
|
||||
public function id_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token';
|
||||
if ( $target_customer_id ) {
|
||||
$url = add_query_arg(
|
||||
|
@ -84,7 +82,7 @@ class UserIdToken {
|
|||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Authorization' => $this->client_credentials->credentials(),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
),
|
||||
);
|
||||
|
|
|
@ -930,10 +930,12 @@ class ApplePayButton {
|
|||
return {
|
||||
action: 'ppcp_update_shipping_method',
|
||||
shipping_method: event.shippingMethod,
|
||||
simplified_contact:
|
||||
this.updatedContactInfo ||
|
||||
this.initialPaymentRequest.shippingContact ||
|
||||
this.initialPaymentRequest.billingContact,
|
||||
simplified_contact: this.hasValidContactInfo(
|
||||
this.updatedContactInfo
|
||||
)
|
||||
? this.updatedContactInfo
|
||||
: this.initialPaymentRequest?.shippingContact ??
|
||||
this.initialPaymentRequest?.billingContact,
|
||||
product_id: productId,
|
||||
products: JSON.stringify( this.products ),
|
||||
caller_page: 'productDetail',
|
||||
|
@ -949,10 +951,12 @@ class ApplePayButton {
|
|||
return {
|
||||
action: 'ppcp_update_shipping_method',
|
||||
shipping_method: event.shippingMethod,
|
||||
simplified_contact:
|
||||
this.updatedContactInfo ||
|
||||
this.initialPaymentRequest.shippingContact ||
|
||||
this.initialPaymentRequest.billingContact,
|
||||
simplified_contact: this.hasValidContactInfo(
|
||||
this.updatedContactInfo
|
||||
)
|
||||
? this.updatedContactInfo
|
||||
: this.initialPaymentRequest?.shippingContact ??
|
||||
this.initialPaymentRequest?.billingContact,
|
||||
caller_page: 'cart',
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
};
|
||||
|
@ -1233,6 +1237,12 @@ class ApplePayButton {
|
|||
|
||||
return btoa( utf8Str );
|
||||
}
|
||||
|
||||
hasValidContactInfo( value ) {
|
||||
return Array.isArray( value )
|
||||
? value.length > 0
|
||||
: Object.keys( value || {} ).length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApplePayButton;
|
||||
|
|
|
@ -23,7 +23,7 @@ class BaseHandler {
|
|||
}
|
||||
|
||||
shippingAllowed() {
|
||||
return this.buttonConfig.product.needsShipping;
|
||||
return this.buttonConfig.product.needShipping;
|
||||
}
|
||||
|
||||
transactionInfo() {
|
||||
|
|
|
@ -28,11 +28,12 @@ return array(
|
|||
$apm_applies = $container->get( 'applepay.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency();
|
||||
return $apm_applies->for_country() && $apm_applies->for_currency();
|
||||
},
|
||||
'applepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
return new ApmApplies(
|
||||
$container->get( 'applepay.supported-country-currency-matrix' ),
|
||||
$container->get( 'applepay.supported-countries' ),
|
||||
$container->get( 'applepay.supported-currencies' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
|
@ -164,769 +165,91 @@ return array(
|
|||
$container->get( 'blocks.method' )
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* The list of which countries can be used for ApplePay.
|
||||
*/
|
||||
'applepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
'applepay.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for ApplePay.
|
||||
* Returns which countries can be used for ApplePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_applepay_supported_country_currency_matrix',
|
||||
'woocommerce_paypal_payments_applepay_supported_countries',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'AU', // Australia
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CA', // Canada
|
||||
'CN', // China
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LV', // Latvia
|
||||
'LI', // Liechtenstein
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'NO', // Norway
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
'SE', // Sweden
|
||||
'US', // United States
|
||||
'GB', // United Kingdom
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for ApplePay.
|
||||
*/
|
||||
'applepay.supported-currencies' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which currencies can be used for ApplePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_applepay_supported_currencies',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AUD', // Australian Dollar
|
||||
'BRL', // Brazilian Real
|
||||
'CAD', // Canadian Dollar
|
||||
'CHF', // Swiss Franc
|
||||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'GBP', // British Pound Sterling
|
||||
'HKD', // Hong Kong Dollar
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
'JPY', // Japanese Yen
|
||||
'MXN', // Mexican Peso
|
||||
'NOK', // Norwegian Krone
|
||||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SEK', // Swedish Krona
|
||||
'SGD', // Singapore Dollar
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
'USD', // United States Dollar
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Applepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* The list of which countries can be used for ApplePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
private $allowed_countries;
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for ApplePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_currencies;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
|
@ -37,32 +44,41 @@ class ApmApplies {
|
|||
private $country;
|
||||
|
||||
/**
|
||||
* ApmApplies constructor.
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* @param array $allowed_countries The list of which countries can be used for ApplePay.
|
||||
* @param array $allowed_currencies The list of which currencies can be used for ApplePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
array $allowed_countries,
|
||||
array $allowed_currencies,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->allowed_countries = $allowed_countries;
|
||||
$this->allowed_currencies = $allowed_currencies;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether ApplePay can be used in the current country and the current currency used.
|
||||
* Returns whether ApplePay can be used in the current country used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
public function for_country(): bool {
|
||||
return in_array( $this->country, $this->allowed_countries, true );
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
|
||||
/**
|
||||
* Returns whether ApplePay can be used in the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_currency(): bool {
|
||||
return in_array( $this->currency, $this->allowed_currencies, true );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ return array(
|
|||
->rule()
|
||||
->condition_element( 'axo_enabled', '1' )
|
||||
->action_visible( 'axo_gateway_title' )
|
||||
->action_visible( 'axo_checkout_config_notice' )
|
||||
->action_visible( 'axo_main_notice' )
|
||||
->action_visible( 'axo_privacy' )
|
||||
->action_visible( 'axo_name_on_card' )
|
||||
->action_visible( 'axo_style_heading' )
|
||||
|
@ -114,9 +114,17 @@ return array(
|
|||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
'axo_checkout_config_notice' => array(
|
||||
'axo_main_notice' => array(
|
||||
'heading' => '',
|
||||
'html' => $container->get( 'axo.checkout-config-notice' ),
|
||||
'html' => implode(
|
||||
'',
|
||||
array(
|
||||
$container->get( 'axo.settings-conflict-notice' ),
|
||||
$container->get( 'axo.shipping-config-notice' ),
|
||||
$container->get( 'axo.checkout-config-notice' ),
|
||||
$container->get( 'axo.incompatible-plugins-notice' ),
|
||||
)
|
||||
),
|
||||
'type' => 'ppcp-html',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
|
|
|
@ -709,6 +709,8 @@ class AxoManager {
|
|||
}`
|
||||
);
|
||||
|
||||
this.emailInput.value = this.stripSpaces( this.emailInput.value );
|
||||
|
||||
this.$( this.el.paymentContainer.selector + '-detail' ).html( '' );
|
||||
this.$( this.el.paymentContainer.selector + '-form' ).html( '' );
|
||||
|
||||
|
@ -1134,6 +1136,10 @@ class AxoManager {
|
|||
return emailPattern.test( value );
|
||||
}
|
||||
|
||||
stripSpaces( str ) {
|
||||
return str.replace( /\s+/g, '' );
|
||||
}
|
||||
|
||||
validateEmail( billingEmail ) {
|
||||
const billingEmailSelector = document.querySelector( billingEmail );
|
||||
const value = document.querySelector( billingEmail + ' input' ).value;
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
export function log( message, level = 'info' ) {
|
||||
const wpDebug = window.wc_ppcp_axo?.wp_debug;
|
||||
const endpoint = window.wc_ppcp_axo?.ajax?.frontend_logger?.endpoint;
|
||||
if ( ! endpoint ) {
|
||||
const loggingEnabled = window.wc_ppcp_axo?.logging_enabled;
|
||||
|
||||
if ( wpDebug ) {
|
||||
switch ( level ) {
|
||||
case 'error':
|
||||
console.error( `[AXO] ${ message }` );
|
||||
break;
|
||||
default:
|
||||
console.log( `[AXO] ${ message }` );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! endpoint || ! loggingEnabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -15,15 +27,5 @@ export function log( message, level = 'info' ) {
|
|||
level,
|
||||
},
|
||||
} ),
|
||||
} ).then( () => {
|
||||
if ( wpDebug ) {
|
||||
switch ( level ) {
|
||||
case 'error':
|
||||
console.error( `[AXO] ${ message }` );
|
||||
break;
|
||||
default:
|
||||
console.log( `[AXO] ${ message }` );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ namespace WooCommerce\PayPalCommerce\Axo;
|
|||
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\SettingsNoticeGenerator;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
return array(
|
||||
|
@ -25,7 +25,7 @@ return array(
|
|||
$apm_applies = $container->get( 'axo.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency() && $apm_applies->for_settings();
|
||||
return $apm_applies->for_country_currency();
|
||||
},
|
||||
|
||||
'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
|
@ -36,6 +36,10 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'axo.helpers.settings-notice-generator' => static function ( ContainerInterface $container ) : SettingsNoticeGenerator {
|
||||
return new SettingsNoticeGenerator();
|
||||
},
|
||||
|
||||
// If AXO is configured and onboarded.
|
||||
'axo.available' => static function ( ContainerInterface $container ): bool {
|
||||
return true;
|
||||
|
@ -159,48 +163,35 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
return $settings_notice_generator->generate_settings_conflict_notice( $settings );
|
||||
},
|
||||
|
||||
'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
|
||||
$block_checkout_docs_link = __(
|
||||
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
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>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 classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( 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 currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <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 classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( ! CartCheckoutDetector::has_classic_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>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 classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
return $settings_notice_generator->generate_checkout_notice();
|
||||
},
|
||||
|
||||
return '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>';
|
||||
'axo.shipping-config-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
return $settings_notice_generator->generate_shipping_notice();
|
||||
},
|
||||
|
||||
'axo.incompatible-plugins-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
return $settings_notice_generator->generate_incompatible_plugins_notice();
|
||||
},
|
||||
|
||||
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
|
||||
|
@ -230,6 +221,7 @@ return array(
|
|||
|
||||
return '<div class="ppcp-notice ppcp-notice-warning"><p>' . $notice_content . '</p></div>';
|
||||
},
|
||||
|
||||
'axo.endpoint.frontend-logger' => static function ( ContainerInterface $container ): FrontendLoggerEndpoint {
|
||||
return new FrontendLoggerEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
|
|
|
@ -216,6 +216,7 @@ class AxoManager {
|
|||
'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '',
|
||||
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
|
|
|
@ -66,7 +66,7 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
// Add the gateway in admin area.
|
||||
if ( is_admin() ) {
|
||||
$methods[] = $gateway;
|
||||
// $methods[] = $gateway; - Temporarily remove Fastlane from the payment gateway list in admin area.
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
@ -77,9 +77,10 @@ class AxoModule implements ModuleInterface {
|
|||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
|
||||
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ) ?? false;
|
||||
|
||||
if ( ! $is_dcc_enabled ) {
|
||||
if ( ! $is_paypal_enabled || ! $is_dcc_enabled ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
@ -87,6 +88,10 @@ class AxoModule implements ModuleInterface {
|
|||
return $methods;
|
||||
}
|
||||
|
||||
if ( ! $this->is_compatible_shipping_config() ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$methods[] = $gateway;
|
||||
return $methods;
|
||||
},
|
||||
|
@ -144,13 +149,20 @@ class AxoModule implements ModuleInterface {
|
|||
function () use ( $c ) {
|
||||
$module = $this;
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
|
||||
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
|
||||
// Check if the module is applicable, correct country, currency, ... etc.
|
||||
if ( ! $c->get( 'axo.eligible' )
|
||||
if ( ! $is_paypal_enabled
|
||||
|| ! $c->get( 'axo.eligible' )
|
||||
|| 'continuation' === $c->get( 'button.context' )
|
||||
|| $subscription_helper->cart_contains_subscription() ) {
|
||||
|| $subscription_helper->cart_contains_subscription()
|
||||
|| ! $this->is_compatible_shipping_config() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -194,9 +206,17 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
add_action(
|
||||
'wp_head',
|
||||
function () {
|
||||
function () use ( $c ) {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
|
||||
echo '<script async src="https://www.paypalobjects.com/insights/v1/paypal-insights.sandbox.min.js"></script>';
|
||||
|
||||
// Add meta tag to allow feature-detection of the site's AXO payment state.
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$this->add_feature_detection_tag(
|
||||
$settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' )
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -280,15 +300,7 @@ class AxoModule implements ModuleInterface {
|
|||
array $localized_script_data
|
||||
): array {
|
||||
try {
|
||||
$target_customer_id = '';
|
||||
if ( is_user_logged_in() ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
||||
if ( ! $target_customer_id ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
||||
}
|
||||
}
|
||||
|
||||
$sdk_client_token = $api->sdk_client_token( $target_customer_id );
|
||||
$sdk_client_token = $api->sdk_client_token();
|
||||
$localized_script_data['axo'] = array(
|
||||
'sdk_client_token' => $sdk_client_token,
|
||||
);
|
||||
|
@ -341,6 +353,7 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
return ! is_user_logged_in()
|
||||
&& CartCheckoutDetector::has_classic_checkout()
|
||||
&& $this->is_compatible_shipping_config()
|
||||
&& $is_axo_enabled
|
||||
&& $is_dcc_enabled
|
||||
&& ! $this->is_excluded_endpoint();
|
||||
|
@ -396,4 +409,32 @@ class AxoModule implements ModuleInterface {
|
|||
// Exclude the Order Pay endpoint.
|
||||
return is_wc_endpoint_url( 'order-pay' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to evaluate if the shipping configuration is compatible.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_compatible_shipping_config(): bool {
|
||||
return ! wc_shipping_enabled() || ( wc_shipping_enabled() && ! wc_ship_to_billing_address_only() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a meta tag to allow feature detection on certain pages.
|
||||
*
|
||||
* @param bool $axo_enabled Whether the gateway is enabled.
|
||||
* @return void
|
||||
*/
|
||||
private function add_feature_detection_tag( bool $axo_enabled ) {
|
||||
$show_tag = is_checkout() || is_cart() || is_shop();
|
||||
|
||||
if ( ! $show_tag ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<meta name="ppcp.axo" content="ppcp.axo.%s" />',
|
||||
$axo_enabled ? 'enabled' : 'disabled'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ class AxoGateway extends WC_Payment_Gateway {
|
|||
? $this->ppcp_settings->get( 'axo_gateway_title' )
|
||||
: $this->get_option( 'title', $this->method_title );
|
||||
|
||||
$this->description = __( 'Enter your email address to continue.', 'woocommerce-paypal-payments' );
|
||||
$this->description = __( 'Enter your email address above to continue.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
|
|
@ -64,19 +64,4 @@ class ApmApplies {
|
|||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the settings are compatible with AXO.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_settings(): bool {
|
||||
if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
179
modules/ppcp-axo/src/Helper/SettingsNoticeGenerator.php
Normal file
179
modules/ppcp-axo/src/Helper/SettingsNoticeGenerator.php
Normal file
|
@ -0,0 +1,179 @@
|
|||
<?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 {
|
||||
/**
|
||||
* 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/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
|
||||
'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>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 classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( 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 currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <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 classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( ! CartCheckoutDetector::has_classic_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>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 classic layout.',
|
||||
'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 shipping notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate_shipping_notice(): string {
|
||||
$shipping_settings_link = admin_url( 'admin.php?page=wc-settings&tab=shipping§ion=options' );
|
||||
|
||||
$notice_content = '';
|
||||
|
||||
if ( wc_shipping_enabled() && wc_ship_to_billing_address_only() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Shipping destination settings page. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Shipping destination</a> of your store is currently configured to <code>Force shipping to the customer billing address</code>. To enable Fastlane and accelerate payments, the shipping destination must be configured either to <code>Default to customer shipping address</code> or <code>Default to customer billing address</code> so buyers can set separate billing and shipping details.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $shipping_settings_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 {
|
||||
$incompatible_plugins = array(
|
||||
'Elementor' => did_action( 'elementor/loaded' ),
|
||||
'CheckoutWC' => defined( 'CFW_NAME' ),
|
||||
);
|
||||
|
||||
$active_plugins_list = array_filter( $incompatible_plugins );
|
||||
|
||||
if ( empty( $active_plugins_list ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$incompatible_plugin_items = array_map(
|
||||
function ( $plugin ) {
|
||||
return "<li>{$plugin}</li>";
|
||||
},
|
||||
array_keys( $active_plugins_list )
|
||||
);
|
||||
|
||||
$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( '', $incompatible_plugin_items )
|
||||
);
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
|
@ -227,7 +227,7 @@ const PayPalComponent = ( {
|
|||
throw new Error( config.scriptData.labels.error.generic );
|
||||
}
|
||||
|
||||
if ( ! shouldHandleShippingInPayPal() ) {
|
||||
if ( ! shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError( true );
|
||||
|
@ -318,7 +318,7 @@ const PayPalComponent = ( {
|
|||
throw new Error( config.scriptData.labels.error.generic );
|
||||
}
|
||||
|
||||
if ( ! shouldHandleShippingInPayPal() ) {
|
||||
if ( ! shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError( true );
|
||||
|
@ -364,6 +364,10 @@ const PayPalComponent = ( {
|
|||
};
|
||||
|
||||
const shouldHandleShippingInPayPal = () => {
|
||||
return shouldskipFinalConfirmation() && config.needShipping
|
||||
};
|
||||
|
||||
const shouldskipFinalConfirmation = () => {
|
||||
if ( config.finalReviewEnabled ) {
|
||||
return false;
|
||||
}
|
||||
|
@ -544,7 +548,7 @@ const PayPalComponent = ( {
|
|||
if ( config.scriptData.continuation ) {
|
||||
return true;
|
||||
}
|
||||
if ( shouldHandleShippingInPayPal() ) {
|
||||
if ( shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -254,6 +254,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
),
|
||||
),
|
||||
'scriptData' => $script_data,
|
||||
'needShipping' => WC()->cart->needs_shipping(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ import {
|
|||
PaymentMethods,
|
||||
} from '../Helper/CheckoutMethodState';
|
||||
import BootstrapHelper from '../Helper/BootstrapHelper';
|
||||
import {
|
||||
ButtonEvents,
|
||||
dispatchButtonEvent,
|
||||
} from '../Helper/PaymentButtonHelpers';
|
||||
|
||||
class CheckoutBootstap {
|
||||
constructor( gateway, renderer, spinner, errorHandler ) {
|
||||
|
@ -70,6 +74,7 @@ class CheckoutBootstap {
|
|||
jQuery( document.body ).on(
|
||||
'updated_checkout payment_method_selected',
|
||||
() => {
|
||||
this.invalidatePaymentMethods();
|
||||
this.updateUi();
|
||||
}
|
||||
);
|
||||
|
@ -176,6 +181,14 @@ class CheckoutBootstap {
|
|||
);
|
||||
}
|
||||
|
||||
invalidatePaymentMethods() {
|
||||
/**
|
||||
* Custom JS event to notify other modules that the payment button on the checkout page
|
||||
* has become irrelevant or invalid.
|
||||
*/
|
||||
dispatchButtonEvent( { event: ButtonEvents.INVALIDATE } );
|
||||
}
|
||||
|
||||
updateUi() {
|
||||
const currentPaymentMethod = getCurrentPaymentMethod();
|
||||
const isPaypal = currentPaymentMethod === PaymentMethods.PAYPAL;
|
||||
|
@ -237,10 +250,20 @@ class CheckoutBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
setVisible( '#ppc-button-ppcp-googlepay', isGooglePayMethod );
|
||||
/**
|
||||
* Custom JS event that is observed by the relevant payment gateway.
|
||||
*
|
||||
* Dynamic part of the event name is the payment method ID, for example
|
||||
* "ppcp-credit-card-gateway" or "ppcp-googlepay"
|
||||
*/
|
||||
dispatchButtonEvent( {
|
||||
event: ButtonEvents.RENDER,
|
||||
paymentMethod: currentPaymentMethod,
|
||||
} );
|
||||
|
||||
setVisible( '#ppc-button-ppcp-applepay', isApplePayMethod );
|
||||
|
||||
jQuery( document.body ).trigger( 'ppcp_checkout_rendered' );
|
||||
document.body.dispatchEvent( new Event( 'ppcp_checkout_rendered' ) );
|
||||
}
|
||||
|
||||
shouldShowMessages() {
|
||||
|
|
|
@ -26,8 +26,11 @@ export function setupButtonEvents( refresh ) {
|
|||
document.addEventListener( REFRESH_BUTTON_EVENT, debouncedRefresh );
|
||||
|
||||
// Listen for cart and checkout update events.
|
||||
document.body.addEventListener( 'updated_cart_totals', debouncedRefresh );
|
||||
document.body.addEventListener( 'updated_checkout', debouncedRefresh );
|
||||
// Note: we need jQuery here, because WooCommerce uses jQuery.trigger() to dispatch the events.
|
||||
window
|
||||
.jQuery( 'body' )
|
||||
.on( 'updated_cart_totals', debouncedRefresh )
|
||||
.on( 'updated_checkout', debouncedRefresh );
|
||||
|
||||
// Use setTimeout for fragment events to avoid unnecessary refresh on initial render.
|
||||
setTimeout( () => {
|
||||
|
|
|
@ -7,6 +7,30 @@ export const PaymentMethods = {
|
|||
APPLEPAY: 'ppcp-applepay',
|
||||
};
|
||||
|
||||
/**
|
||||
* List of valid context values that the button can have.
|
||||
*
|
||||
* The "context" describes the placement or page where a payment button might be displayed.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const PaymentContext = {
|
||||
Cart: 'cart', // Classic cart.
|
||||
Checkout: 'checkout', // Classic checkout.
|
||||
BlockCart: 'cart-block', // Block cart.
|
||||
BlockCheckout: 'checkout-block', // Block checkout.
|
||||
Product: 'product', // Single product page.
|
||||
MiniCart: 'mini-cart', // Mini cart available on all pages except checkout & cart.
|
||||
PayNow: 'pay-now', // Pay for order, via admin generated link.
|
||||
Preview: 'preview', // Layout preview on settings page.
|
||||
|
||||
// Contexts that use blocks to render payment methods.
|
||||
Blocks: [ 'cart-block', 'checkout-block' ],
|
||||
|
||||
// Contexts that display "classic" payment gateways.
|
||||
Gateways: [ 'checkout', 'pay-now' ],
|
||||
};
|
||||
|
||||
export const ORDER_BUTTON_SELECTOR = '#place_order';
|
||||
|
||||
export const getCurrentPaymentMethod = () => {
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* Helper function used by PaymentButton instances.
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collection of recognized event names for payment button events.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const ButtonEvents = Object.freeze( {
|
||||
INVALIDATE: 'ppcp_invalidate_methods',
|
||||
RENDER: 'ppcp_render_method',
|
||||
REDRAW: 'ppcp_redraw_method',
|
||||
} );
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} defaultId - Default wrapper ID.
|
||||
* @param {string} miniCartId - Wrapper inside the mini-cart.
|
||||
* @param {string} smartButtonId - ID of the smart button wrapper.
|
||||
* @param {string} blockId - Block wrapper ID (express checkout, block cart).
|
||||
* @param {string} gatewayId - Gateway wrapper ID (classic checkout).
|
||||
* @return {{MiniCart, Gateway, Block, SmartButton, Default}} List of all wrapper IDs, by context.
|
||||
*/
|
||||
export function combineWrapperIds(
|
||||
defaultId = '',
|
||||
miniCartId = '',
|
||||
smartButtonId = '',
|
||||
blockId = '',
|
||||
gatewayId = ''
|
||||
) {
|
||||
const sanitize = ( id ) => id.replace( /^#/, '' );
|
||||
|
||||
return {
|
||||
Default: sanitize( defaultId ),
|
||||
SmartButton: sanitize( smartButtonId ),
|
||||
Block: sanitize( blockId ),
|
||||
Gateway: sanitize( gatewayId ),
|
||||
MiniCart: sanitize( miniCartId ),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns full payment button styles by combining the global ppcpConfig with
|
||||
* payment-method-specific styling provided via buttonConfig.
|
||||
*
|
||||
* @param {Object} ppcpConfig - Global plugin configuration.
|
||||
* @param {Object} buttonConfig - Payment method specific configuration.
|
||||
* @return {{MiniCart: (*), Default: (*)}} Combined styles, separated by context.
|
||||
*/
|
||||
export function combineStyles( ppcpConfig, buttonConfig ) {
|
||||
return {
|
||||
Default: {
|
||||
...ppcpConfig.style,
|
||||
...buttonConfig.style,
|
||||
},
|
||||
MiniCart: {
|
||||
...ppcpConfig.mini_cart_style,
|
||||
...buttonConfig.mini_cart_style,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the given event name is a valid Payment Button event.
|
||||
*
|
||||
* @param {string} event - The event name to verify.
|
||||
* @return {boolean} True, if the event name is valid.
|
||||
*/
|
||||
export function isValidButtonEvent( event ) {
|
||||
const buttonEventValues = Object.values( ButtonEvents );
|
||||
|
||||
return buttonEventValues.includes( event );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a payment button event.
|
||||
*
|
||||
* @param {Object} options - The options for dispatching the event.
|
||||
* @param {string} options.event - Event to dispatch.
|
||||
* @param {string} [options.paymentMethod] - Optional. Name of payment method, to target a specific button only.
|
||||
* @throws {Error} Throws an error if the event is invalid.
|
||||
*/
|
||||
export function dispatchButtonEvent( { event, paymentMethod = '' } ) {
|
||||
if ( ! isValidButtonEvent( event ) ) {
|
||||
throw new Error( `Invalid event: ${ event }` );
|
||||
}
|
||||
|
||||
const fullEventName = paymentMethod
|
||||
? `${ event }-${ paymentMethod }`
|
||||
: event;
|
||||
|
||||
document.body.dispatchEvent( new Event( fullEventName ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event listener for the provided button event.
|
||||
*
|
||||
* @param {Object} options - The options for the event listener.
|
||||
* @param {string} options.event - Event to observe.
|
||||
* @param {string} [options.paymentMethod] - The payment method name (optional).
|
||||
* @param {Function} options.callback - The callback function to execute when the event is triggered.
|
||||
* @throws {Error} Throws an error if the event is invalid.
|
||||
*/
|
||||
export function observeButtonEvent( { event, paymentMethod = '', callback } ) {
|
||||
if ( ! isValidButtonEvent( event ) ) {
|
||||
throw new Error( `Invalid event: ${ event }` );
|
||||
}
|
||||
|
||||
const fullEventName = paymentMethod
|
||||
? `${ event }-${ paymentMethod }`
|
||||
: event;
|
||||
|
||||
document.body.addEventListener( fullEventName, callback );
|
||||
}
|
|
@ -71,7 +71,10 @@ export const loadPaypalScript = ( config, onLoaded, onError = null ) => {
|
|||
}
|
||||
|
||||
// Load PayPal script for special case with data-client-token
|
||||
if ( config.data_client_id?.set_attribute ) {
|
||||
if (
|
||||
config.data_client_id?.set_attribute &&
|
||||
config.vault_v3_enabled !== '1'
|
||||
) {
|
||||
dataClientIdAttributeHandler(
|
||||
scriptOptions,
|
||||
config.data_client_id,
|
||||
|
|
|
@ -0,0 +1,822 @@
|
|||
import ConsoleLogger from '../../../../../ppcp-wc-gateway/resources/js/helper/ConsoleLogger';
|
||||
import { apmButtonsInit } from '../Helper/ApmButtons';
|
||||
import {
|
||||
getCurrentPaymentMethod,
|
||||
PaymentContext,
|
||||
PaymentMethods,
|
||||
} from '../Helper/CheckoutMethodState';
|
||||
import {
|
||||
ButtonEvents,
|
||||
dispatchButtonEvent,
|
||||
observeButtonEvent,
|
||||
} from '../Helper/PaymentButtonHelpers';
|
||||
|
||||
/**
|
||||
* Collection of all available styling options for this button.
|
||||
*
|
||||
* @typedef {Object} StylesCollection
|
||||
* @property {string} Default - Default button styling.
|
||||
* @property {string} MiniCart - Styles for mini-cart button.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collection of all available wrapper IDs that are possible for the button.
|
||||
*
|
||||
* @typedef {Object} WrapperCollection
|
||||
* @property {string} Default - Default button wrapper.
|
||||
* @property {string} Gateway - Wrapper for separate gateway.
|
||||
* @property {string} Block - Wrapper for block checkout button.
|
||||
* @property {string} MiniCart - Wrapper for mini-cart button.
|
||||
* @property {string} SmartButton - Wrapper for smart button container.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds the provided PaymentButton instance to a global payment-button collection.
|
||||
*
|
||||
* This is debugging logic that should not be used on a production site.
|
||||
*
|
||||
* @param {string} methodName - Used to group the buttons.
|
||||
* @param {PaymentButton} button - Appended to the button collection.
|
||||
*/
|
||||
const addToDebuggingCollection = ( methodName, button ) => {
|
||||
window.ppcpPaymentButtonList = window.ppcpPaymentButtonList || {};
|
||||
|
||||
const collection = window.ppcpPaymentButtonList;
|
||||
|
||||
collection[ methodName ] = collection[ methodName ] || [];
|
||||
collection[ methodName ].push( button );
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a context-independent instance Map for `PaymentButton` components.
|
||||
*
|
||||
* This function addresses a potential issue in multi-context environments, such as pages using
|
||||
* Block-components. In these scenarios, multiple React execution contexts can lead to duplicate
|
||||
* `PaymentButton` instances. To prevent this, we store instances in a `Map` that is bound to the
|
||||
* document's `body` (the rendering context) rather than to individual React components
|
||||
* (execution contexts).
|
||||
*
|
||||
* The `Map` is created as a non-enumerable, non-writable, and non-configurable property of
|
||||
* `document.body` to ensure its integrity and prevent accidental modifications.
|
||||
*
|
||||
* @return {Map<any, any>} A Map containing all `PaymentButton` instances for the current page.
|
||||
*/
|
||||
const getInstances = () => {
|
||||
const collectionKey = '__ppcpPBInstances';
|
||||
|
||||
if ( ! document.body[ collectionKey ] ) {
|
||||
Object.defineProperty( document.body, collectionKey, {
|
||||
value: new Map(),
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: false,
|
||||
} );
|
||||
}
|
||||
|
||||
return document.body[ collectionKey ];
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for APM payment buttons, like GooglePay and ApplePay.
|
||||
*
|
||||
* This class is not intended for the PayPal button.
|
||||
*/
|
||||
export default class PaymentButton {
|
||||
/**
|
||||
* Defines the implemented payment method.
|
||||
*
|
||||
* Used to identify and address the button internally.
|
||||
* Overwrite this in the derived class.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
static methodId = 'generic';
|
||||
|
||||
/**
|
||||
* CSS class that is added to the payment button wrapper.
|
||||
*
|
||||
* Overwrite this in the derived class.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
static cssClass = '';
|
||||
|
||||
/**
|
||||
* @type {ConsoleLogger}
|
||||
*/
|
||||
#logger;
|
||||
|
||||
/**
|
||||
* Whether the payment button is initialized.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#isInitialized = false;
|
||||
|
||||
/**
|
||||
* The button's context.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
#context;
|
||||
|
||||
/**
|
||||
* Object containing the IDs of all possible wrapper elements that might contain this
|
||||
* button; only one wrapper is relevant, depending on the value of the context.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
#wrappers;
|
||||
|
||||
/**
|
||||
* @type {StylesCollection}
|
||||
*/
|
||||
#styles;
|
||||
|
||||
/**
|
||||
* Keeps track of CSS classes that were added to the wrapper element.
|
||||
* We use this list to remove CSS classes that we've added, e.g. to change shape from
|
||||
* pill to rect in the preview.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
#appliedClasses = [];
|
||||
|
||||
/**
|
||||
* APM relevant configuration; e.g., configuration of the GooglePay button.
|
||||
*/
|
||||
#buttonConfig;
|
||||
|
||||
/**
|
||||
* Plugin-wide configuration; i.e., PayPal client ID, shop currency, etc.
|
||||
*/
|
||||
#ppcpConfig;
|
||||
|
||||
/**
|
||||
* A variation of a context bootstrap handler.
|
||||
*/
|
||||
#externalHandler;
|
||||
|
||||
/**
|
||||
* A variation of a context handler object, like CheckoutHandler.
|
||||
* This handler provides a standardized interface for certain standardized checks and actions.
|
||||
*/
|
||||
#contextHandler;
|
||||
|
||||
/**
|
||||
* Whether the current browser/website support the payment method.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#isEligible = false;
|
||||
|
||||
/**
|
||||
* Whether this button is visible. Modified by `show()` and `hide()`
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#isVisible = true;
|
||||
|
||||
/**
|
||||
* The currently visible payment button.
|
||||
*
|
||||
* @see {PaymentButton.insertButton}
|
||||
* @type {HTMLElement|null}
|
||||
*/
|
||||
#button = null;
|
||||
|
||||
/**
|
||||
* Factory method to create a new PaymentButton while limiting a single instance per context.
|
||||
*
|
||||
* @param {string} context - Button context name.
|
||||
* @param {unknown} externalHandler - Handler object.
|
||||
* @param {Object} buttonConfig - Payment button specific configuration.
|
||||
* @param {Object} ppcpConfig - Plugin wide configuration object.
|
||||
* @param {unknown} contextHandler - Handler object.
|
||||
* @return {PaymentButton} The button instance.
|
||||
*/
|
||||
static createButton(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
) {
|
||||
const buttonInstances = getInstances();
|
||||
const instanceKey = `${ this.methodId }.${ context }`;
|
||||
|
||||
if ( ! buttonInstances.has( instanceKey ) ) {
|
||||
const button = new this(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
buttonInstances.set( instanceKey, button );
|
||||
}
|
||||
|
||||
return buttonInstances.get( instanceKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list with all wrapper IDs for the implemented payment method, categorized by
|
||||
* context.
|
||||
*
|
||||
* @abstract
|
||||
* @param {Object} buttonConfig - Payment method specific configuration.
|
||||
* @param {Object} ppcpConfig - Global plugin configuration.
|
||||
* @return {{MiniCart, Gateway, Block, SmartButton, Default}} The wrapper ID collection.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||
throw new Error( 'Must be implemented in the child class' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all button styles for the implemented payment method, categorized by
|
||||
* context.
|
||||
*
|
||||
* @abstract
|
||||
* @param {Object} buttonConfig - Payment method specific configuration.
|
||||
* @param {Object} ppcpConfig - Global plugin configuration.
|
||||
* @return {{MiniCart: (*), Default: (*)}} Combined styles, separated by context.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
static getStyles( buttonConfig, ppcpConfig ) {
|
||||
throw new Error( 'Must be implemented in the child class' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the payment button instance.
|
||||
*
|
||||
* Do not create new button instances directly; use the `createButton` method instead
|
||||
* to avoid multiple button instances handling the same context.
|
||||
*
|
||||
* @private
|
||||
* @param {string} context - Button context name.
|
||||
* @param {Object} externalHandler - Handler object.
|
||||
* @param {Object} buttonConfig - Payment button specific configuration.
|
||||
* @param {Object} ppcpConfig - Plugin wide configuration object.
|
||||
* @param {Object} contextHandler - Handler object.
|
||||
*/
|
||||
constructor(
|
||||
context,
|
||||
externalHandler = null,
|
||||
buttonConfig = {},
|
||||
ppcpConfig = {},
|
||||
contextHandler = null
|
||||
) {
|
||||
if ( this.methodId === PaymentButton.methodId ) {
|
||||
throw new Error( 'Cannot initialize the PaymentButton base class' );
|
||||
}
|
||||
|
||||
if ( ! buttonConfig ) {
|
||||
buttonConfig = {};
|
||||
}
|
||||
|
||||
const isDebugging = !! buttonConfig?.is_debug;
|
||||
const methodName = this.methodId.replace( /^ppcp?-/, '' );
|
||||
|
||||
this.#context = context;
|
||||
this.#buttonConfig = buttonConfig;
|
||||
this.#ppcpConfig = ppcpConfig;
|
||||
this.#externalHandler = externalHandler;
|
||||
this.#contextHandler = contextHandler;
|
||||
|
||||
this.#logger = new ConsoleLogger( methodName, context );
|
||||
|
||||
if ( isDebugging ) {
|
||||
this.#logger.enabled = true;
|
||||
addToDebuggingCollection( methodName, this );
|
||||
}
|
||||
|
||||
this.#wrappers = this.constructor.getWrappers(
|
||||
this.#buttonConfig,
|
||||
this.#ppcpConfig
|
||||
);
|
||||
this.applyButtonStyles( this.#buttonConfig );
|
||||
|
||||
apmButtonsInit( this.#ppcpConfig );
|
||||
this.initEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal ID of the payment gateway.
|
||||
*
|
||||
* @readonly
|
||||
* @return {string} The internal gateway ID, defined in the derived class.
|
||||
*/
|
||||
get methodId() {
|
||||
return this.constructor.methodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class that is added to the button wrapper.
|
||||
*
|
||||
* @readonly
|
||||
* @return {string} CSS class, defined in the derived class.
|
||||
*/
|
||||
get cssClass() {
|
||||
return this.constructor.cssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the payment button was fully initialized.
|
||||
*
|
||||
* @readonly
|
||||
* @return {boolean} True indicates, that the button was fully initialized.
|
||||
*/
|
||||
get isInitialized() {
|
||||
return this.#isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* The button's context.
|
||||
*
|
||||
* TODO: Convert the string to a context-object (primitive obsession smell)
|
||||
*
|
||||
* @readonly
|
||||
* @return {string} The button context.
|
||||
*/
|
||||
get context() {
|
||||
return this.#context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration, specific for the implemented payment button.
|
||||
*
|
||||
* @return {Object} Configuration object.
|
||||
*/
|
||||
get buttonConfig() {
|
||||
return this.#buttonConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin-wide configuration; i.e., PayPal client ID, shop currency, etc.
|
||||
*
|
||||
* @return {Object} Configuration object.
|
||||
*/
|
||||
get ppcpConfig() {
|
||||
return this.#ppcpConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The bootstrap handler instance, or an empty object.
|
||||
*/
|
||||
get externalHandler() {
|
||||
return this.#externalHandler || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the button's context handler.
|
||||
* When no context handler was provided (like for a preview button), an empty object is
|
||||
* returned.
|
||||
*
|
||||
* @return {Object} The context handler instance, or an empty object.
|
||||
*/
|
||||
get contextHandler() {
|
||||
return this.#contextHandler || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether customers need to provide shipping details during payment.
|
||||
*
|
||||
* Can be extended by child classes to take method specific configuration into account.
|
||||
*
|
||||
* @return {boolean} True means, shipping fields are displayed and must be filled.
|
||||
*/
|
||||
get requiresShipping() {
|
||||
// Default check: Is shipping enabled in WooCommerce?
|
||||
return (
|
||||
'function' === typeof this.contextHandler.shippingAllowed &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Button wrapper details.
|
||||
*
|
||||
* @readonly
|
||||
* @return {WrapperCollection} Wrapper IDs.
|
||||
*/
|
||||
get wrappers() {
|
||||
return this.#wrappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context-relevant button style object.
|
||||
*
|
||||
* @readonly
|
||||
* @return {string} Styling options.
|
||||
*/
|
||||
get style() {
|
||||
if ( PaymentContext.MiniCart === this.context ) {
|
||||
return this.#styles.MiniCart;
|
||||
}
|
||||
|
||||
return this.#styles.Default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context-relevant wrapper ID.
|
||||
*
|
||||
* @readonly
|
||||
* @return {string} The wrapper-element's ID (without the `#` prefix).
|
||||
*/
|
||||
get wrapperId() {
|
||||
if ( PaymentContext.MiniCart === this.context ) {
|
||||
return this.wrappers.MiniCart;
|
||||
} else if ( this.isSeparateGateway ) {
|
||||
return this.wrappers.Gateway;
|
||||
} else if ( PaymentContext.Blocks.includes( this.context ) ) {
|
||||
return this.wrappers.Block;
|
||||
}
|
||||
|
||||
return this.wrappers.Default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current payment button should be rendered as a stand-alone gateway.
|
||||
* The return value `false` usually means, that the payment button is bundled with all available
|
||||
* payment buttons.
|
||||
*
|
||||
* The decision depends on the button context (placement) and the plugin settings.
|
||||
*
|
||||
* @return {boolean} True, if the current button represents a stand-alone gateway.
|
||||
*/
|
||||
get isSeparateGateway() {
|
||||
return (
|
||||
this.#buttonConfig.is_wc_gateway_enabled &&
|
||||
PaymentContext.Gateways.includes( this.context )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the currently selected payment gateway is set to the payment method.
|
||||
*
|
||||
* Only relevant on checkout pages, when `this.isSeparateGateway` is true.
|
||||
*
|
||||
* @return {boolean} True means that this payment method is selected as current gateway.
|
||||
*/
|
||||
get isCurrentGateway() {
|
||||
if ( ! this.isSeparateGateway ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to rely on `getCurrentPaymentMethod()` here, as the `CheckoutBootstrap.js`
|
||||
* module fires the "ButtonEvents.RENDER" event before any PaymentButton instances are
|
||||
* created. I.e. we cannot observe the initial gateway selection event.
|
||||
*/
|
||||
return this.methodId === getCurrentPaymentMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags a preview button without actual payment logic.
|
||||
*
|
||||
* @return {boolean} True indicates a preview instance that has no payment logic.
|
||||
*/
|
||||
get isPreview() {
|
||||
return PaymentContext.Preview === this.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the browser can accept this payment method.
|
||||
*
|
||||
* @return {boolean} True, if payments are technically possible.
|
||||
*/
|
||||
get isEligible() {
|
||||
return this.#isEligible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the eligibility state of this button component.
|
||||
*
|
||||
* @param {boolean} newState Whether the browser can accept payments.
|
||||
*/
|
||||
set isEligible( newState ) {
|
||||
if ( newState === this.#isEligible ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#isEligible = newState;
|
||||
this.triggerRedraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* The visibility state of the button.
|
||||
* This flag does not reflect actual visibility on the page, but rather, if the button
|
||||
* is intended/allowed to be displayed, in case all other checks pass.
|
||||
*
|
||||
* @return {boolean} True indicates, that the button can be displayed.
|
||||
*/
|
||||
get isVisible() {
|
||||
return this.#isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the visibility of the button.
|
||||
*
|
||||
* A visible button does not always force the button to render on the page. It only means, that
|
||||
* the button is allowed or not allowed to render, if certain other conditions are met.
|
||||
*
|
||||
* @param {boolean} newState Whether rendering the button is allowed.
|
||||
*/
|
||||
set isVisible( newState ) {
|
||||
if ( this.#isVisible === newState ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#isVisible = newState;
|
||||
this.triggerRedraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML element that wraps the current button
|
||||
*
|
||||
* @readonly
|
||||
* @return {HTMLElement|null} The wrapper element, or null.
|
||||
*/
|
||||
get wrapperElement() {
|
||||
return document.getElementById( this.wrapperId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the main button-wrapper is present in the current DOM.
|
||||
*
|
||||
* @readonly
|
||||
* @return {boolean} True, if the button context (wrapper element) is found.
|
||||
*/
|
||||
get isPresent() {
|
||||
return this.wrapperElement instanceof HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if the payment button is still attached to the DOM.
|
||||
*
|
||||
* WooCommerce performs some partial reloads in many cases, which can lead to our payment
|
||||
* button
|
||||
* to move into the browser's memory. In that case, we need to recreate the button in the
|
||||
* updated DOM.
|
||||
*
|
||||
* @return {boolean} True means, the button is still present (and typically visible) on the
|
||||
* page.
|
||||
*/
|
||||
get isButtonAttached() {
|
||||
if ( ! this.#button ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent = this.#button.parentElement;
|
||||
while ( parent?.parentElement ) {
|
||||
if ( 'BODY' === parent.tagName ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a debug detail to the browser console.
|
||||
*
|
||||
* @param {any} args
|
||||
*/
|
||||
log( ...args ) {
|
||||
this.#logger.log( ...args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error message to the browser console.
|
||||
*
|
||||
* @param {any} args
|
||||
*/
|
||||
error( ...args ) {
|
||||
this.#logger.error( ...args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current button instance has valid and complete configuration details.
|
||||
* Used during initialization to decide if the button can be initialized or should be skipped.
|
||||
*
|
||||
* Can be implemented by the derived class.
|
||||
*
|
||||
* @param {boolean} [silent=false] - Set to true to suppress console errors.
|
||||
* @return {boolean} True indicates the config is valid and initialization can continue.
|
||||
*/
|
||||
validateConfiguration( silent = false ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyButtonStyles( buttonConfig, ppcpConfig = null ) {
|
||||
if ( ! ppcpConfig ) {
|
||||
ppcpConfig = this.ppcpConfig;
|
||||
}
|
||||
|
||||
this.#styles = this.constructor.getStyles( buttonConfig, ppcpConfig );
|
||||
|
||||
if ( this.isInitialized ) {
|
||||
this.triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the button instance. Must be called before the initial `init()`.
|
||||
*
|
||||
* Parameters are defined by the derived class.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
configure() {}
|
||||
|
||||
/**
|
||||
* Must be named `init()` to simulate "protected" visibility:
|
||||
* Since the derived class also implements a method with the same name, this method can only
|
||||
* be called by the derived class, but not from any other code.
|
||||
*/
|
||||
init() {
|
||||
this.#isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be named `reinit()` to simulate "protected" visibility:
|
||||
* Since the derived class also implements a method with the same name, this method can only
|
||||
* be called by the derived class, but not from any other code.
|
||||
*/
|
||||
reinit() {
|
||||
this.#isInitialized = false;
|
||||
this.#isEligible = false;
|
||||
}
|
||||
|
||||
triggerRedraw() {
|
||||
this.showPaymentGateway();
|
||||
|
||||
dispatchButtonEvent( {
|
||||
event: ButtonEvents.REDRAW,
|
||||
paymentMethod: this.methodId,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches event listeners to show or hide the payment button when needed.
|
||||
*/
|
||||
initEventListeners() {
|
||||
// Refresh the button - this might show, hide or re-create the payment button.
|
||||
observeButtonEvent( {
|
||||
event: ButtonEvents.REDRAW,
|
||||
paymentMethod: this.methodId,
|
||||
callback: () => this.refresh(),
|
||||
} );
|
||||
|
||||
// Events relevant for buttons inside a payment gateway.
|
||||
if ( PaymentContext.Gateways.includes( this.context ) ) {
|
||||
const parentMethod = this.isSeparateGateway
|
||||
? this.methodId
|
||||
: PaymentMethods.PAYPAL;
|
||||
|
||||
// Hide the button right after the user selected _any_ gateway.
|
||||
observeButtonEvent( {
|
||||
event: ButtonEvents.INVALIDATE,
|
||||
callback: () => ( this.isVisible = false ),
|
||||
} );
|
||||
|
||||
// Show the button (again) when the user selected the current gateway.
|
||||
observeButtonEvent( {
|
||||
event: ButtonEvents.RENDER,
|
||||
paymentMethod: parentMethod,
|
||||
callback: () => ( this.isVisible = true ),
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the payment button on the page.
|
||||
*/
|
||||
refresh() {
|
||||
if ( ! this.isPresent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyWrapperStyles();
|
||||
|
||||
if ( this.isEligible && this.isPresent && this.isVisible ) {
|
||||
if ( ! this.isButtonAttached ) {
|
||||
this.log( 'refresh.addButton' );
|
||||
this.addButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the custom payment gateway visible by removing initial inline styles from the DOM.
|
||||
*
|
||||
* Only relevant on the checkout page, i.e., when `this.isSeparateGateway` is `true`
|
||||
*/
|
||||
showPaymentGateway() {
|
||||
if ( ! this.isSeparateGateway || ! this.isEligible ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const styleSelectors = `style[data-hide-gateway="${ this.methodId }"]`;
|
||||
|
||||
const styles = document.querySelectorAll( styleSelectors );
|
||||
|
||||
if ( ! styles.length ) {
|
||||
return;
|
||||
}
|
||||
this.log( 'Show gateway' );
|
||||
|
||||
styles.forEach( ( el ) => el.remove() );
|
||||
|
||||
// This code runs only once, during button initialization, and fixes the initial visibility.
|
||||
this.isVisible = this.isCurrentGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies CSS classes and inline styling to the payment button wrapper.
|
||||
*/
|
||||
applyWrapperStyles() {
|
||||
const wrapper = this.wrapperElement;
|
||||
const { shape, height } = this.style;
|
||||
|
||||
for ( const classItem of this.#appliedClasses ) {
|
||||
wrapper.classList.remove( classItem );
|
||||
}
|
||||
|
||||
this.#appliedClasses = [];
|
||||
|
||||
const newClasses = [
|
||||
`ppcp-button-${ shape }`,
|
||||
'ppcp-button-apm',
|
||||
this.cssClass,
|
||||
];
|
||||
|
||||
wrapper.classList.add( ...newClasses );
|
||||
this.#appliedClasses.push( ...newClasses );
|
||||
|
||||
if ( height ) {
|
||||
wrapper.style.height = `${ height }px`;
|
||||
}
|
||||
|
||||
// Apply the wrapper visibility.
|
||||
wrapper.style.display = this.isVisible ? 'block' : 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new payment button (HTMLElement) and must call `this.insertButton()` to display
|
||||
* that button in the correct wrapper.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
addButton() {
|
||||
throw new Error( 'Must be implemented by the child class' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the button wrapper element and inserts the provided payment button into the DOM.
|
||||
*
|
||||
* If a payment button was previously inserted to the wrapper, calling this method again will
|
||||
* first remove the previous button.
|
||||
*
|
||||
* @param {HTMLElement} button - The button element to inject.
|
||||
*/
|
||||
insertButton( button ) {
|
||||
if ( ! this.isPresent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = this.wrapperElement;
|
||||
|
||||
if ( this.#button ) {
|
||||
this.removeButton();
|
||||
}
|
||||
|
||||
this.log( 'addButton', button );
|
||||
|
||||
this.#button = button;
|
||||
wrapper.appendChild( this.#button );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the payment button from the DOM.
|
||||
*/
|
||||
removeButton() {
|
||||
if ( ! this.isPresent || ! this.#button ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log( 'removeButton' );
|
||||
|
||||
try {
|
||||
this.wrapperElement.removeChild( this.#button );
|
||||
} catch ( Exception ) {
|
||||
// Ignore this.
|
||||
}
|
||||
|
||||
this.#button = null;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,17 @@
|
|||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import widgetBuilder from './WidgetBuilder';
|
||||
import { debounce } from '../../../../../ppcp-blocks/resources/js/Helper/debounce';
|
||||
import ConsoleLogger from '../../../../../ppcp-wc-gateway/resources/js/helper/ConsoleLogger';
|
||||
|
||||
/**
|
||||
* Manages all PreviewButton instances of a certain payment method on the page.
|
||||
*/
|
||||
class PreviewButtonManager {
|
||||
/**
|
||||
* @type {ConsoleLogger}
|
||||
*/
|
||||
#logger;
|
||||
|
||||
/**
|
||||
* Resolves the promise.
|
||||
* Used by `this.boostrap()` to process enqueued initialization logic.
|
||||
|
@ -32,6 +38,9 @@ class PreviewButtonManager {
|
|||
this.apiConfig = null;
|
||||
this.apiError = '';
|
||||
|
||||
this.#logger = new ConsoleLogger( this.methodName, 'preview-manager' );
|
||||
this.#logger.enabled = true; // Manually set this to true for development.
|
||||
|
||||
this.#onInit = new Promise( ( resolve ) => {
|
||||
this.#onInitResolver = resolve;
|
||||
} );
|
||||
|
@ -61,9 +70,11 @@ class PreviewButtonManager {
|
|||
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||
* method.
|
||||
*
|
||||
* @abstract
|
||||
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||
* @return {Promise<{}>}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async fetchConfig( payPal ) {
|
||||
throw new Error(
|
||||
'The "fetchConfig" method must be implemented by the derived class'
|
||||
|
@ -74,9 +85,11 @@ class PreviewButtonManager {
|
|||
* Protected method that needs to be implemented by the derived class.
|
||||
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||
*
|
||||
* @abstract
|
||||
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||
* @return {PreviewButton}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
createButtonInstance( wrapperId ) {
|
||||
throw new Error(
|
||||
'The "createButtonInstance" method must be implemented by the derived class'
|
||||
|
@ -90,7 +103,7 @@ class PreviewButtonManager {
|
|||
* This dummy is only visible on the admin side, and not rendered on the front-end.
|
||||
*
|
||||
* @todo Consider refactoring this into a new class that extends the PreviewButton class.
|
||||
* @param wrapperId
|
||||
* @param {string} wrapperId
|
||||
* @return {any}
|
||||
*/
|
||||
createDummy( wrapperId ) {
|
||||
|
@ -128,13 +141,24 @@ class PreviewButtonManager {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a debug message to the console, with a module-specific prefix.
|
||||
*
|
||||
* @param {string} message - Log message.
|
||||
* @param {...any} args - Optional. Additional args to output.
|
||||
*/
|
||||
log( message, ...args ) {
|
||||
this.#logger.log( message, ...args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an error message to the console, with a module-specific prefix.
|
||||
* @param message
|
||||
* @param {...any} args
|
||||
*
|
||||
* @param {string} message - Log message.
|
||||
* @param {...any} args - Optional. Additional args to output.
|
||||
*/
|
||||
error( message, ...args ) {
|
||||
console.error( `${ this.methodName } ${ message }`, ...args );
|
||||
this.#logger.error( message, ...args );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,21 +266,21 @@ class PreviewButtonManager {
|
|||
}
|
||||
|
||||
if ( ! this.shouldInsertPreviewButton( id ) ) {
|
||||
this.log( 'Skip preview rendering for this preview-box', id );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.buttons[ id ] ) {
|
||||
this._addButton( id, ppcpConfig );
|
||||
} else {
|
||||
// This is a debounced method, that fires after 100ms.
|
||||
this._configureAllButtons( ppcpConfig );
|
||||
this._configureButton( id, ppcpConfig );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the preview box supports the current button.
|
||||
*
|
||||
* When this function returns false, this manager instance does not create a new preview button.
|
||||
* E.g. "Should the current preview-box display Google Pay buttons?"
|
||||
*
|
||||
* @param {string} previewId - ID of the inner preview box container.
|
||||
* @return {boolean} True if the box is eligible for the preview button, false otherwise.
|
||||
|
@ -271,10 +295,14 @@ class PreviewButtonManager {
|
|||
|
||||
/**
|
||||
* Applies a new configuration to an existing preview button.
|
||||
*
|
||||
* @private
|
||||
* @param id
|
||||
* @param ppcpConfig
|
||||
*/
|
||||
_configureButton( id, ppcpConfig ) {
|
||||
this.log( 'configureButton', id, ppcpConfig );
|
||||
|
||||
this.buttons[ id ]
|
||||
.setDynamic( this.isDynamic() )
|
||||
.setPpcpConfig( ppcpConfig )
|
||||
|
@ -283,9 +311,13 @@ class PreviewButtonManager {
|
|||
|
||||
/**
|
||||
* Apples the provided configuration to all existing preview buttons.
|
||||
* @param ppcpConfig
|
||||
*
|
||||
* @private
|
||||
* @param ppcpConfig - The new styling to use for the preview buttons.
|
||||
*/
|
||||
_configureAllButtons( ppcpConfig ) {
|
||||
this.log( 'configureAllButtons', ppcpConfig );
|
||||
|
||||
Object.entries( this.buttons ).forEach( ( [ id, button ] ) => {
|
||||
this._configureButton( id, {
|
||||
...ppcpConfig,
|
||||
|
@ -302,13 +334,20 @@ class PreviewButtonManager {
|
|||
|
||||
/**
|
||||
* Creates a new preview button, that is rendered once the bootstrapping Promise resolves.
|
||||
* @param id
|
||||
* @param ppcpConfig
|
||||
*
|
||||
* @private
|
||||
* @param id - The button to add.
|
||||
* @param ppcpConfig - The styling to apply to the preview button.
|
||||
*/
|
||||
_addButton( id, ppcpConfig ) {
|
||||
this.log( 'addButton', id, ppcpConfig );
|
||||
|
||||
const createButton = () => {
|
||||
if ( ! this.buttons[ id ] ) {
|
||||
this.log( 'createButton.new', id );
|
||||
|
||||
let newInst;
|
||||
|
||||
if ( this.apiConfig && 'object' === typeof this.apiConfig ) {
|
||||
newInst = this.createButtonInstance( id ).setButtonConfig(
|
||||
this.buttonConfig
|
||||
|
|
|
@ -139,7 +139,7 @@ class Renderer {
|
|||
};
|
||||
|
||||
// Check the condition and add the handler if needed
|
||||
if ( this.defaultSettings.should_handle_shipping_in_paypal ) {
|
||||
if ( this.defaultSettings.should_handle_shipping_in_paypal && this.defaultSettings.needShipping ) {
|
||||
options.onShippingOptionsChange = ( data, actions ) => {
|
||||
let shippingOptionsChange =
|
||||
! this.isVenmoButtonClickedWhenVaultingIsEnabled(
|
||||
|
|
|
@ -1295,6 +1295,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
'is_logged' => is_user_logged_in(),
|
||||
),
|
||||
'should_handle_shipping_in_paypal' => $this->should_handle_shipping_in_paypal && ! $this->is_checkout(),
|
||||
'needShipping' => WC()->cart->needs_shipping(),
|
||||
'vaultingEnabled' => $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ),
|
||||
);
|
||||
|
||||
|
|
|
@ -303,13 +303,6 @@ class WooCommerceOrderCreator {
|
|||
|
||||
$item->set_tax_class( $product->get_tax_class() );
|
||||
$item->set_total_tax( (float) array_sum( $taxes ) );
|
||||
|
||||
foreach ( $taxes as $tax_rate_id => $tax_amount ) {
|
||||
if ( $tax_amount > 0 ) {
|
||||
$item->add_meta_data( 'tax_rate_id', $tax_rate_id, true );
|
||||
$item->add_meta_data( 'tax_amount', $tax_amount, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/* Front end display */
|
||||
.ppcp-button-apm .gpay-card-info-container-fill .gpay-card-info-container {
|
||||
outline-offset: -1px;
|
||||
border-radius: var(--apm-button-border-radius);
|
||||
}
|
||||
|
||||
/* Admin preview */
|
||||
.ppcp-button-googlepay {
|
||||
min-height: 40px;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import CheckoutActionHandler from '../../../../ppcp-button/resources/js/modules/
|
|||
import FormValidator from '../../../../ppcp-button/resources/js/modules/Helper/FormValidator';
|
||||
|
||||
class CheckoutHandler extends BaseHandler {
|
||||
transactionInfo() {
|
||||
validateForm() {
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const spinner = new Spinner();
|
||||
|
@ -23,7 +23,7 @@ class CheckoutHandler extends BaseHandler {
|
|||
: null;
|
||||
|
||||
if ( ! formValidator ) {
|
||||
resolve( super.transactionInfo() );
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ class CheckoutHandler extends BaseHandler {
|
|||
|
||||
reject();
|
||||
} else {
|
||||
resolve( super.transactionInfo() );
|
||||
resolve();
|
||||
}
|
||||
} );
|
||||
} catch ( error ) {
|
||||
|
|
|
@ -1,10 +1,120 @@
|
|||
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
import {
|
||||
combineStyles,
|
||||
combineWrapperIds,
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
|
||||
import PaymentButton from '../../../ppcp-button/resources/js/modules/Renderer/PaymentButton';
|
||||
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
|
||||
import UpdatePaymentData from './Helper/UpdatePaymentData';
|
||||
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
||||
import { PaymentMethods } from '../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState';
|
||||
|
||||
/**
|
||||
* Plugin-specific styling.
|
||||
*
|
||||
* Note that most properties of this object do not apply to the Google Pay button.
|
||||
*
|
||||
* @typedef {Object} PPCPStyle
|
||||
* @property {string} shape - Outline shape.
|
||||
* @property {?number} height - Button height in pixel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Style options that are defined by the Google Pay SDK and are required to render the button.
|
||||
*
|
||||
* @typedef {Object} GooglePayStyle
|
||||
* @property {string} type - Defines the button label.
|
||||
* @property {string} color - Button color
|
||||
* @property {string} language - The locale; an empty string will apply the user-agent's language.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Google Pay JS SDK
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects
|
||||
* @typedef {Object} GooglePaySDK
|
||||
* @property {typeof PaymentsClient} PaymentsClient - Main API client for payment actions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Payments Client class, generated by the Google Pay SDK.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
* @typedef {Object} PaymentsClient
|
||||
* @property {Function} createButton - The convenience method is used to generate a Google Pay payment button styled with the latest Google Pay branding for insertion into a webpage.
|
||||
* @property {Function} isReadyToPay - Use the isReadyToPay(isReadyToPayRequest) method to determine a user's ability to return a form of payment from the Google Pay API.
|
||||
* @property {Function} loadPaymentData - This method presents a Google Pay payment sheet that allows selection of a payment method and optionally configured parameters
|
||||
* @property {Function} onPaymentAuthorized - This method is called when a payment is authorized in the payment sheet.
|
||||
* @property {Function} onPaymentDataChanged - This method handles payment data changes in the payment sheet such as shipping address and shipping options.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This object describes the transaction details.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo
|
||||
* @typedef {Object} TransactionInfo
|
||||
* @property {string} currencyCode - Required. The ISO 4217 alphabetic currency code.
|
||||
* @property {string} countryCode - Optional. required for EEA countries,
|
||||
* @property {string} transactionId - Optional. A unique ID that identifies a facilitation attempt. Highly encouraged for troubleshooting.
|
||||
* @property {string} totalPriceStatus - Required. [ESTIMATED|FINAL] The status of the total price used:
|
||||
* @property {string} totalPrice - Required. Total monetary value of the transaction with an optional decimal precision of two decimal places.
|
||||
* @property {Array} displayItems - Optional. A list of cart items shown in the payment sheet (e.g. subtotals, sales taxes, shipping charges, discounts etc.).
|
||||
* @property {string} totalPriceLabel - Optional. Custom label for the total price within the display items.
|
||||
* @property {string} checkoutOption - Optional. Affects the submit button text displayed in the Google Pay payment sheet.
|
||||
*/
|
||||
|
||||
class GooglepayButton extends PaymentButton {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static methodId = PaymentMethods.GOOGLEPAY;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static cssClass = 'google-pay';
|
||||
|
||||
/**
|
||||
* Client reference, provided by the Google Pay JS SDK.
|
||||
*/
|
||||
#paymentsClient = null;
|
||||
|
||||
/**
|
||||
* Details about the processed transaction.
|
||||
*
|
||||
* @type {?TransactionInfo}
|
||||
*/
|
||||
#transactionInfo = null;
|
||||
|
||||
googlePayConfig = null;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||
return combineWrapperIds(
|
||||
buttonConfig?.button?.wrapper || '',
|
||||
buttonConfig?.button?.mini_cart_wrapper || '',
|
||||
ppcpConfig?.button?.wrapper || '',
|
||||
'ppc-button-googlepay-container',
|
||||
'ppc-button-ppcp-googlepay'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getStyles( buttonConfig, ppcpConfig ) {
|
||||
const styles = combineStyles(
|
||||
ppcpConfig?.button || {},
|
||||
buttonConfig?.button || {}
|
||||
);
|
||||
|
||||
if ( 'buy' === styles.MiniCart.type ) {
|
||||
styles.MiniCart.type = 'pay';
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
class GooglepayButton {
|
||||
constructor(
|
||||
context,
|
||||
externalHandler,
|
||||
|
@ -12,274 +122,257 @@ class GooglepayButton {
|
|||
ppcpConfig,
|
||||
contextHandler
|
||||
) {
|
||||
apmButtonsInit( ppcpConfig );
|
||||
// Disable debug output in the browser console:
|
||||
// buttonConfig.is_debug = false;
|
||||
|
||||
this.isInitialized = false;
|
||||
super(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.contextHandler = contextHandler;
|
||||
this.init = this.init.bind( this );
|
||||
this.onPaymentAuthorized = this.onPaymentAuthorized.bind( this );
|
||||
this.onPaymentDataChanged = this.onPaymentDataChanged.bind( this );
|
||||
this.onButtonClick = this.onButtonClick.bind( this );
|
||||
|
||||
this.paymentsClient = null;
|
||||
|
||||
this.log = function () {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
this.log( 'Create instance' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
get requiresShipping() {
|
||||
return super.requiresShipping && this.buttonConfig.shipping?.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay API.
|
||||
*
|
||||
* @return {?GooglePaySDK} API for the Google Pay JS SDK, or null when SDK is not ready yet.
|
||||
*/
|
||||
get googlePayApi() {
|
||||
return window.google?.payments?.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay PaymentsClient instance created by this button.
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
*
|
||||
* @return {?PaymentsClient} The SDK object, or null when SDK is not ready yet.
|
||||
*/
|
||||
get paymentsClient() {
|
||||
return this.#paymentsClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Details about the processed transaction.
|
||||
*
|
||||
* This object defines the price that is charged, and text that is displayed inside the
|
||||
* payment sheet.
|
||||
*
|
||||
* @return {?TransactionInfo} The TransactionInfo object.
|
||||
*/
|
||||
get transactionInfo() {
|
||||
return this.#transactionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the new transaction details to the payment button.
|
||||
*
|
||||
* @param {TransactionInfo} newTransactionInfo - Transaction details.
|
||||
*/
|
||||
set transactionInfo( newTransactionInfo ) {
|
||||
this.#transactionInfo = newTransactionInfo;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
validateConfiguration( silent = false ) {
|
||||
const validEnvs = [ 'PRODUCTION', 'TEST' ];
|
||||
|
||||
const isInvalid = ( ...args ) => {
|
||||
if ( ! silent ) {
|
||||
this.error( ...args );
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
init( config, transactionInfo ) {
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
|
||||
if ( ! this.validateConfig() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler.validateContext() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.googlePayConfig = config;
|
||||
this.transactionInfo = transactionInfo;
|
||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
|
||||
this.initClient();
|
||||
this.initEventHandlers();
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
config
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
if ( response.result ) {
|
||||
if (
|
||||
( this.context === 'checkout' ||
|
||||
this.context === 'pay-now' ) &&
|
||||
this.buttonConfig.is_wc_gateway_enabled === '1'
|
||||
) {
|
||||
const wrapper = document.getElementById(
|
||||
'ppc-button-ppcp-googlepay'
|
||||
if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
|
||||
return isInvalid(
|
||||
'Invalid environment:',
|
||||
this.buttonConfig.environment
|
||||
);
|
||||
|
||||
if ( wrapper ) {
|
||||
const { ppcpStyle, buttonStyle } =
|
||||
this.contextConfig();
|
||||
|
||||
wrapper.classList.add(
|
||||
`ppcp-button-${ ppcpStyle.shape }`,
|
||||
'ppcp-button-apm',
|
||||
'ppcp-button-googlepay'
|
||||
);
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
wrapper.style.height = `${ ppcpStyle.height }px`;
|
||||
}
|
||||
|
||||
this.addButtonCheckout(
|
||||
this.baseCardPaymentMethod,
|
||||
wrapper,
|
||||
buttonStyle
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
// Preview buttons only need a valid environment.
|
||||
if ( this.isPreview ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.addButton( this.baseCardPaymentMethod );
|
||||
}
|
||||
} )
|
||||
.catch( function ( err ) {
|
||||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.init( this.googlePayConfig, this.transactionInfo );
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
if (
|
||||
[ 'PRODUCTION', 'TEST' ].indexOf(
|
||||
this.buttonConfig.environment
|
||||
) === -1
|
||||
) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid environment.',
|
||||
this.buttonConfig.environment
|
||||
return isInvalid(
|
||||
'No API configuration - missing configure() call?'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler ) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid context handler.',
|
||||
this.contextHandler
|
||||
if ( ! this.transactionInfo ) {
|
||||
return isInvalid(
|
||||
'No transactionInfo - missing configure() call?'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! typeof this.contextHandler?.validateContext() ) {
|
||||
return isInvalid( 'Invalid context handler.', this.contextHandler );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configurations relative to this button context.
|
||||
* Configures the button instance. Must be called before the initial `init()`.
|
||||
*
|
||||
* @param {Object} apiConfig - API configuration.
|
||||
* @param {Object} transactionInfo - Transaction details; required before "init" call.
|
||||
*/
|
||||
contextConfig() {
|
||||
const config = {
|
||||
wrapper: this.buttonConfig.button.wrapper,
|
||||
ppcpStyle: this.ppcpConfig.button.style,
|
||||
buttonStyle: this.buttonConfig.button.style,
|
||||
ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
|
||||
};
|
||||
configure( apiConfig, transactionInfo ) {
|
||||
this.googlePayConfig = apiConfig;
|
||||
this.#transactionInfo = transactionInfo;
|
||||
|
||||
if ( this.context === 'mini-cart' ) {
|
||||
config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
|
||||
config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
|
||||
config.buttonStyle = this.buttonConfig.button.mini_cart_style;
|
||||
config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
|
||||
|
||||
// Handle incompatible types.
|
||||
if ( config.buttonStyle.type === 'buy' ) {
|
||||
config.buttonStyle.type = 'pay';
|
||||
}
|
||||
this.allowedPaymentMethods = this.googlePayConfig.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
}
|
||||
|
||||
if (
|
||||
[ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
|
||||
) {
|
||||
config.ppcpButtonWrapper =
|
||||
'#express-payment-method-ppcp-gateway-paypal';
|
||||
init() {
|
||||
// Use `reinit()` to force a full refresh of an initialized button.
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return config;
|
||||
// Stop, if configuration is invalid.
|
||||
if ( ! this.validateConfiguration() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
initClient() {
|
||||
const callbacks = {
|
||||
onPaymentAuthorized: this.onPaymentAuthorized.bind( this ),
|
||||
};
|
||||
super.init();
|
||||
this.#paymentsClient = this.createPaymentsClient();
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
callbacks.onPaymentDataChanged =
|
||||
this.onPaymentDataChanged.bind( this );
|
||||
if ( ! this.isPresent ) {
|
||||
this.log( 'Payment wrapper not found', this.wrapperId );
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient = new google.payments.api.PaymentsClient( {
|
||||
if ( ! this.paymentsClient ) {
|
||||
this.log( 'Could not initialize the payments client' );
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
this.googlePayConfig
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
this.log( 'PaymentsClient.isReadyToPay response:', response );
|
||||
this.isEligible = !! response.result;
|
||||
} )
|
||||
.catch( ( err ) => {
|
||||
this.error( err );
|
||||
this.isEligible = false;
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
// Missing (invalid) configuration indicates, that the first `init()` call did not happen yet.
|
||||
if ( ! this.validateConfiguration( true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.reinit();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an object with relevant paymentDataCallbacks for the current button instance.
|
||||
*
|
||||
* @return {Object} An object containing callbacks for the current scope & configuration.
|
||||
*/
|
||||
preparePaymentDataCallbacks() {
|
||||
const callbacks = {};
|
||||
|
||||
// We do not attach any callbacks to preview buttons.
|
||||
if ( this.isPreview ) {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
callbacks.onPaymentAuthorized = this.onPaymentAuthorized;
|
||||
|
||||
if ( this.requiresShipping ) {
|
||||
callbacks.onPaymentDataChanged = this.onPaymentDataChanged;
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
createPaymentsClient() {
|
||||
if ( ! this.googlePayApi ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const callbacks = this.preparePaymentDataCallbacks();
|
||||
|
||||
/**
|
||||
* Consider providing merchant info here:
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#PaymentOptions
|
||||
*/
|
||||
return new this.googlePayApi.PaymentsClient( {
|
||||
environment: this.buttonConfig.environment,
|
||||
// add merchant info maybe
|
||||
paymentDataCallbacks: callbacks,
|
||||
} );
|
||||
}
|
||||
|
||||
initEventHandlers() {
|
||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||
|
||||
if ( wrapper === ppcpButtonWrapper ) {
|
||||
throw new Error(
|
||||
`[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper }"`
|
||||
);
|
||||
}
|
||||
|
||||
const syncButtonVisibility = () => {
|
||||
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
||||
setVisible( wrapper, $ppcpButtonWrapper.is( ':visible' ) );
|
||||
setEnabled(
|
||||
wrapper,
|
||||
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
||||
);
|
||||
};
|
||||
|
||||
jQuery( document ).on(
|
||||
'ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled',
|
||||
( ev, data ) => {
|
||||
if ( jQuery( data.selector ).is( ppcpButtonWrapper ) ) {
|
||||
syncButtonVisibility();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
syncButtonVisibility();
|
||||
}
|
||||
|
||||
buildReadyToPayRequest( allowedPaymentMethods, baseRequest ) {
|
||||
this.log( 'Ready To Pay request', baseRequest, allowedPaymentMethods );
|
||||
|
||||
return Object.assign( {}, baseRequest, {
|
||||
allowedPaymentMethods,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Google Pay purchase button
|
||||
* @param baseCardPaymentMethod
|
||||
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
|
||||
* correct wrapper.
|
||||
*/
|
||||
addButton( baseCardPaymentMethod ) {
|
||||
this.log( 'addButton', this.context );
|
||||
|
||||
const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig();
|
||||
|
||||
this.waitForWrapper( wrapper, () => {
|
||||
jQuery( wrapper ).addClass( 'ppcp-button-' + ppcpStyle.shape );
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
jQuery( wrapper ).css( 'height', `${ ppcpStyle.height }px` );
|
||||
addButton() {
|
||||
if ( ! this.paymentsClient ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const baseCardPaymentMethod = this.baseCardPaymentMethod;
|
||||
const { color, type, language } = this.style;
|
||||
|
||||
/**
|
||||
* @see https://developers.google.com/pay/api/web/reference/client#createButton
|
||||
*/
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick.bind( this ),
|
||||
onClick: this.onButtonClick,
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: buttonStyle.color || 'black',
|
||||
buttonType: buttonStyle.type || 'pay',
|
||||
buttonLocale: buttonStyle.language || 'en',
|
||||
buttonColor: color || 'black',
|
||||
buttonType: type || 'pay',
|
||||
buttonLocale: language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
|
||||
jQuery( wrapper ).append( button );
|
||||
} );
|
||||
}
|
||||
|
||||
addButtonCheckout( baseCardPaymentMethod, wrapper, buttonStyle ) {
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick.bind( this ),
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: buttonStyle.color || 'black',
|
||||
buttonType: buttonStyle.type || 'pay',
|
||||
buttonLocale: buttonStyle.language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
|
||||
wrapper.appendChild( button );
|
||||
}
|
||||
|
||||
waitForWrapper( selector, callback, delay = 100, timeout = 2000 ) {
|
||||
const startTime = Date.now();
|
||||
const interval = setInterval( () => {
|
||||
const el = document.querySelector( selector );
|
||||
const timeElapsed = Date.now() - startTime;
|
||||
|
||||
if ( el ) {
|
||||
clearInterval( interval );
|
||||
callback( el );
|
||||
} else if ( timeElapsed > timeout ) {
|
||||
clearInterval( interval );
|
||||
}
|
||||
}, delay );
|
||||
this.insertButton( button );
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -290,19 +383,49 @@ class GooglepayButton {
|
|||
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
||||
*/
|
||||
onButtonClick() {
|
||||
this.log( 'onButtonClick', this.context );
|
||||
this.log( 'onButtonClick' );
|
||||
|
||||
const initiatePaymentRequest = () => {
|
||||
window.ppcpFundingSource = 'googlepay';
|
||||
const paymentDataRequest = this.paymentDataRequest();
|
||||
|
||||
this.log(
|
||||
'onButtonClick: paymentDataRequest',
|
||||
paymentDataRequest,
|
||||
this.context
|
||||
);
|
||||
|
||||
window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler.
|
||||
|
||||
this.paymentsClient.loadPaymentData( paymentDataRequest );
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.validateForm ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler.validateForm().catch( ( error ) => {
|
||||
this.error( 'Form validation failed:', error );
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
const getTransactionInfo = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.transactionInfo ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler
|
||||
.transactionInfo()
|
||||
.then( ( transactionInfo ) => {
|
||||
this.transactionInfo = transactionInfo;
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
this.error( 'Failed to get transaction info:', error );
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
validateForm()
|
||||
.then( getTransactionInfo )
|
||||
.then( initiatePaymentRequest );
|
||||
}
|
||||
|
||||
paymentDataRequest() {
|
||||
|
@ -318,10 +441,7 @@ class GooglepayButton {
|
|||
paymentDataRequest.transactionInfo = this.transactionInfo;
|
||||
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
if ( this.requiresShipping ) {
|
||||
paymentDataRequest.callbackIntents = [
|
||||
'SHIPPING_ADDRESS',
|
||||
'SHIPPING_OPTION',
|
||||
|
@ -350,8 +470,7 @@ class GooglepayButton {
|
|||
}
|
||||
|
||||
onPaymentDataChanged( paymentData ) {
|
||||
this.log( 'onPaymentDataChanged', this.context );
|
||||
this.log( 'paymentData', paymentData );
|
||||
this.log( 'onPaymentDataChanged', paymentData );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
|
@ -396,7 +515,7 @@ class GooglepayButton {
|
|||
|
||||
resolve( paymentDataRequestUpdate );
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during onPaymentDataChanged:', error );
|
||||
this.error( 'Error during onPaymentDataChanged:', error );
|
||||
reject( error );
|
||||
}
|
||||
} );
|
||||
|
@ -424,18 +543,18 @@ class GooglepayButton {
|
|||
//------------------------
|
||||
|
||||
onPaymentAuthorized( paymentData ) {
|
||||
this.log( 'onPaymentAuthorized', this.context );
|
||||
this.log( 'onPaymentAuthorized' );
|
||||
return this.processPayment( paymentData );
|
||||
}
|
||||
|
||||
async processPayment( paymentData ) {
|
||||
this.log( 'processPayment', this.context );
|
||||
this.log( 'processPayment' );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const id = await this.contextHandler.createOrder();
|
||||
|
||||
this.log( 'processPayment: createOrder', id, this.context );
|
||||
this.log( 'processPayment: createOrder', id );
|
||||
|
||||
const confirmOrderResponse = await widgetBuilder.paypal
|
||||
.Googlepay()
|
||||
|
@ -446,8 +565,7 @@ class GooglepayButton {
|
|||
|
||||
this.log(
|
||||
'processPayment: confirmOrder',
|
||||
confirmOrderResponse,
|
||||
this.context
|
||||
confirmOrderResponse
|
||||
);
|
||||
|
||||
/** Capture the Order on the Server */
|
||||
|
@ -517,7 +635,7 @@ class GooglepayButton {
|
|||
};
|
||||
}
|
||||
|
||||
this.log( 'processPaymentResponse', response, this.context );
|
||||
this.log( 'processPaymentResponse', response );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class GooglepayManager {
|
|||
bootstrap.handler
|
||||
);
|
||||
|
||||
const button = new GooglepayButton(
|
||||
const button = GooglepayButton.createButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
|
@ -30,13 +30,19 @@ class GooglepayManager {
|
|||
|
||||
this.buttons.push( button );
|
||||
|
||||
const initButton = () => {
|
||||
button.configure( this.googlePayConfig, this.transactionInfo );
|
||||
button.init();
|
||||
};
|
||||
|
||||
// Initialize button only if googlePayConfig and transactionInfo are already fetched.
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
initButton();
|
||||
} else {
|
||||
await this.init();
|
||||
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
initButton();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
@ -53,8 +59,18 @@ class GooglepayManager {
|
|||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
}
|
||||
|
||||
if ( ! this.googlePayConfig ) {
|
||||
console.error( 'No GooglePayConfig received during init' );
|
||||
} else if ( ! this.transactionInfo ) {
|
||||
console.error( 'No transactionInfo found during init' );
|
||||
} else {
|
||||
for ( const button of this.buttons ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
button.configure(
|
||||
this.googlePayConfig,
|
||||
this.transactionInfo
|
||||
);
|
||||
button.init();
|
||||
}
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during initialization:', error );
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import GooglepayButton from './GooglepayButton';
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
export default class GooglePayPreviewButton extends PreviewButton {
|
||||
/**
|
||||
* Instance of the preview button.
|
||||
*
|
||||
* @type {?PaymentButton}
|
||||
*/
|
||||
#button = null;
|
||||
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${ args.selector }GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton( buttonConfig ) {
|
||||
const contextHandler = ContextHandlerFactory.create(
|
||||
'preview',
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
null
|
||||
);
|
||||
|
||||
if ( ! this.#button ) {
|
||||
/* Intentionally using `new` keyword, instead of the `.createButton()` factory,
|
||||
* as the factory is designed to only create a single button per context, while a single
|
||||
* page can contain multiple instances of a preview button.
|
||||
*/
|
||||
this.#button = new GooglepayButton(
|
||||
'preview',
|
||||
null,
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
}
|
||||
|
||||
this.#button.configure( this.apiConfig, null );
|
||||
this.#button.applyButtonStyles( buttonConfig, this.ppcpConfig );
|
||||
this.#button.reinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
*
|
||||
* @param {Object} buttonConfig
|
||||
* @param {Object} ppcpConfig
|
||||
*/
|
||||
dynamicPreviewConfig( buttonConfig, ppcpConfig ) {
|
||||
// Merge the current form-values into the preview-button configuration.
|
||||
if ( ppcpConfig.button && buttonConfig.button ) {
|
||||
Object.assign( buttonConfig.button.style, ppcpConfig.button.style );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import GooglepayButton from './GooglepayButton';
|
||||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import GooglePayPreviewButton from './GooglepayPreviewButton';
|
||||
|
||||
/**
|
||||
* Accessor that creates and returns a single PreviewButtonManager instance.
|
||||
|
@ -33,7 +31,7 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
* method.
|
||||
*
|
||||
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||
* @return {Promise<{}>}
|
||||
* @return {Promise<{}>} Promise that resolves when API configuration is available.
|
||||
*/
|
||||
async fetchConfig( payPal ) {
|
||||
const apiMethod = payPal?.Googlepay()?.config;
|
||||
|
@ -59,7 +57,7 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||
*
|
||||
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||
* @return {GooglePayPreviewButton}
|
||||
* @return {GooglePayPreviewButton} The new preview button instance.
|
||||
*/
|
||||
createButtonInstance( wrapperId ) {
|
||||
return new GooglePayPreviewButton( {
|
||||
|
@ -69,64 +67,5 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
class GooglePayPreviewButton extends PreviewButton {
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${ args.selector }GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton( buttonConfig ) {
|
||||
const contextHandler = ContextHandlerFactory.create(
|
||||
'preview',
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
null
|
||||
);
|
||||
|
||||
const button = new GooglepayButton(
|
||||
'preview',
|
||||
null,
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
button.init( this.apiConfig, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
* @param buttonConfig
|
||||
* @param ppcpConfig
|
||||
*/
|
||||
dynamicPreviewConfig( buttonConfig, ppcpConfig ) {
|
||||
// Merge the current form-values into the preview-button configuration.
|
||||
if ( ppcpConfig.button && buttonConfig.button ) {
|
||||
Object.assign( buttonConfig.button.style, ppcpConfig.button.style );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the preview button manager.
|
||||
buttonManager();
|
||||
|
|
|
@ -29,12 +29,13 @@ return array(
|
|||
$apm_applies = $container->get( 'googlepay.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency();
|
||||
return $apm_applies->for_country() && $apm_applies->for_currency();
|
||||
},
|
||||
|
||||
'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
return new ApmApplies(
|
||||
$container->get( 'googlepay.supported-country-currency-matrix' ),
|
||||
$container->get( 'googlepay.supported-countries' ),
|
||||
$container->get( 'googlepay.supported-currencies' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
|
@ -81,768 +82,89 @@ return array(
|
|||
),
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* The list of which countries can be used for GooglePay.
|
||||
*/
|
||||
'googlepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
'googlepay.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for GooglePay.
|
||||
* Returns which countries can be used for GooglePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_googlepay_supported_country_currency_matrix',
|
||||
'woocommerce_paypal_payments_googlepay_supported_countries',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'AU', // Australia
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CA', // Canada
|
||||
'CN', // China
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LV', // Latvia
|
||||
'LI', // Liechtenstein
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'NO', // Norway
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
'SE', // Sweden
|
||||
'US', // United States
|
||||
'GB', // United Kingdom
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for GooglePay.
|
||||
*/
|
||||
'googlepay.supported-currencies' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which currencies can be used for GooglePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_googlepay_supported_currencies',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AUD', // Australian Dollar
|
||||
'BRL', // Brazilian Real
|
||||
'CAD', // Canadian Dollar
|
||||
'CHF', // Swiss Franc
|
||||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'GBP', // British Pound Sterling
|
||||
'HKD', // Hong Kong Dollar
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
'JPY', // Japanese Yen
|
||||
'MXN', // Mexican Peso
|
||||
'NOK', // Norwegian Krone
|
||||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SEK', // Swedish Krona
|
||||
'SGD', // Singapore Dollar
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
'USD', // United States Dollar
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -290,6 +290,7 @@ class Button implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->googlepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -303,6 +304,7 @@ class Button implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->googlepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -335,6 +337,23 @@ class Button implements ButtonInterface {
|
|||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an inline CSS style that hides the Google Pay gateway (on Classic Checkout).
|
||||
* The style is removed by `PaymentButton.js` once the eligibility of the payment method
|
||||
* is confirmed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function hide_gateway_until_eligible() : void {
|
||||
?>
|
||||
<style data-hide-gateway='<?php echo esc_attr( GooglePayGateway::ID ); ?>'>
|
||||
.wc_payment_method.payment_method_ppcp-googlepay {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues scripts/styles.
|
||||
*/
|
||||
|
|
|
@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* The list of which countries can be used for GooglePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
private $allowed_countries;
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for GooglePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_currencies;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
|
@ -39,30 +46,39 @@ class ApmApplies {
|
|||
/**
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* @param array $allowed_countries The list of which countries can be used for GooglePay.
|
||||
* @param array $allowed_currencies The list of which currencies can be used for GooglePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
array $allowed_countries,
|
||||
array $allowed_currencies,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->allowed_countries = $allowed_countries;
|
||||
$this->allowed_currencies = $allowed_currencies;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether GooglePay can be used in the current country and the current currency used.
|
||||
* Returns whether GooglePay can be used in the current country used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
public function for_country(): bool {
|
||||
return in_array( $this->country, $this->allowed_countries, true );
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
|
||||
/**
|
||||
* Returns whether GooglePay can be used in the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_currency(): bool {
|
||||
return in_array( $this->currency, $this->allowed_currencies, true );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -189,9 +189,8 @@ return array(
|
|||
$login_seller_sandbox = $container->get( 'api.endpoint.login-seller-sandbox' );
|
||||
$partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
|
||||
$cache = new Cache( 'ppcp-paypal-bearer' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new LoginSellerEndpoint(
|
||||
$request_data,
|
||||
$login_seller_production,
|
||||
|
@ -199,7 +198,8 @@ return array(
|
|||
$partner_referrals_data,
|
||||
$settings,
|
||||
$cache,
|
||||
$logger
|
||||
$logger,
|
||||
new Cache( 'ppcp-client-credentials-cache' )
|
||||
);
|
||||
},
|
||||
'onboarding.endpoint.pui' => static function( ContainerInterface $container ) : UpdateSignupLinksEndpoint {
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\Onboarding\Endpoint;
|
|||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
|
@ -76,6 +78,13 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The client credentials cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
private $client_credentials_cache;
|
||||
|
||||
/**
|
||||
* LoginSellerEndpoint constructor.
|
||||
*
|
||||
|
@ -86,6 +95,7 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
* @param Settings $settings The Settings.
|
||||
* @param Cache $cache The Cache.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param Cache $client_credentials_cache The client credentials cache.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
|
@ -94,7 +104,8 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
PartnerReferralsData $partner_referrals_data,
|
||||
Settings $settings,
|
||||
Cache $cache,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
Cache $client_credentials_cache
|
||||
) {
|
||||
|
||||
$this->request_data = $request_data;
|
||||
|
@ -104,6 +115,7 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
$this->settings = $settings;
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
$this->client_credentials_cache = $client_credentials_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,6 +187,9 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) {
|
||||
$this->cache->delete( PayPalBearer::CACHE_KEY );
|
||||
}
|
||||
if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) {
|
||||
$this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY );
|
||||
}
|
||||
|
||||
wp_schedule_single_event(
|
||||
time() + 5,
|
||||
|
|
|
@ -31,6 +31,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
|
||||
/**
|
||||
* Class SavePaymentMethodsModule
|
||||
|
@ -84,7 +85,9 @@ class SavePaymentMethodsModule implements ModuleInterface {
|
|||
add_filter(
|
||||
'woocommerce_paypal_payments_localized_script_data',
|
||||
function( array $localized_script_data ) use ( $c ) {
|
||||
if ( ! is_user_logged_in() ) {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
if ( ! is_user_logged_in() && ! $subscriptions_helper->cart_contains_subscription() ) {
|
||||
return $localized_script_data;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,11 @@ $background-ident-color: #fbfbfb;
|
|||
}
|
||||
}
|
||||
|
||||
.ppcp-notice-list {
|
||||
list-style-type: disc;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
|
|
58
modules/ppcp-wc-gateway/resources/js/helper/ConsoleLogger.js
Normal file
58
modules/ppcp-wc-gateway/resources/js/helper/ConsoleLogger.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Helper component to log debug details to the browser console.
|
||||
*
|
||||
* A utility class that is used by payment buttons on the front-end, like the GooglePayButton.
|
||||
*/
|
||||
export default class ConsoleLogger {
|
||||
/**
|
||||
* The prefix to display before every log output.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
#prefix = '';
|
||||
|
||||
/**
|
||||
* Whether logging is enabled, disabled by default.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#enabled = false;
|
||||
|
||||
constructor( ...prefixes ) {
|
||||
if ( prefixes.length ) {
|
||||
this.#prefix = `[${ prefixes.join( ' | ' ) }]`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable logging. Only impacts `log()` output.
|
||||
*
|
||||
* @param {boolean} state True to enable log output.
|
||||
*/
|
||||
set enabled( state ) {
|
||||
this.#enabled = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output log-level details to the browser console, if logging is enabled.
|
||||
*
|
||||
* @param {...any} args - All provided values are output to the browser console.
|
||||
*/
|
||||
log( ...args ) {
|
||||
if ( this.#enabled ) {
|
||||
// eslint-disable-next-line
|
||||
console.log( this.#prefix, ...args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an error message in the browser's console.
|
||||
*
|
||||
* Error messages are always output, even when logging is disabled.
|
||||
*
|
||||
* @param {...any} args - All provided values are output to the browser console.
|
||||
*/
|
||||
error( ...args ) {
|
||||
console.error( this.#prefix, ...args );
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
/* global jQuery */
|
||||
|
||||
/**
|
||||
* Returns a Map with all input fields that are relevant to render the preview of the
|
||||
* given payment button.
|
||||
*
|
||||
* @param {string} apmName - Value of the custom attribute `data-ppcp-apm-name`.
|
||||
* @return {Map<string, {val:Function, el:HTMLInputElement}>}
|
||||
* @return {Map<string, {val:Function, el:HTMLInputElement}>} List of input elements found on the current admin page.
|
||||
*/
|
||||
export function getButtonFormFields( apmName ) {
|
||||
const inputFields = document.querySelectorAll(
|
||||
|
@ -28,9 +30,9 @@ export function getButtonFormFields( apmName ) {
|
|||
|
||||
/**
|
||||
* Returns a function that triggers an update of the specified preview button, when invoked.
|
||||
|
||||
*
|
||||
* @param {string} apmName
|
||||
* @return {((object) => void)}
|
||||
* @return {((object) => void)} Trigger-function; updates preview buttons when invoked.
|
||||
*/
|
||||
export function buttonRefreshTriggerFactory( apmName ) {
|
||||
const eventName = `ppcp_paypal_render_preview_${ apmName }`;
|
||||
|
@ -44,7 +46,7 @@ export function buttonRefreshTriggerFactory( apmName ) {
|
|||
* Returns a function that gets the current form values of the specified preview button.
|
||||
*
|
||||
* @param {string} apmName
|
||||
* @return {() => {button: {wrapper:string, is_enabled:boolean, style:{}}}}
|
||||
* @return {() => {button: {wrapper:string, is_enabled:boolean, style:{}}}} Getter-function; returns preview config details when invoked.
|
||||
*/
|
||||
export function buttonSettingsGetterFactory( apmName ) {
|
||||
const fields = getButtonFormFields( apmName );
|
||||
|
|
|
@ -369,7 +369,8 @@ return array(
|
|||
$container->get( 'api.partner_merchant_id-production' ),
|
||||
$container->get( 'api.partner_merchant_id-sandbox' ),
|
||||
$container->get( 'api.endpoint.billing-agreements' ),
|
||||
$logger
|
||||
$logger,
|
||||
new Cache( 'ppcp-client-credentials-cache' )
|
||||
);
|
||||
},
|
||||
'wcgateway.order-processor' => static function ( ContainerInterface $container ): OrderProcessor {
|
||||
|
@ -1413,10 +1414,10 @@ return array(
|
|||
return $label;
|
||||
},
|
||||
'wcgateway.enable-dcc-url-sandbox' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.sandbox.paypal.com/bizsignup/entry/product/ppcp';
|
||||
return 'https://www.sandbox.paypal.com/bizsignup/entry?product=ppcp';
|
||||
},
|
||||
'wcgateway.enable-dcc-url-live' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.paypal.com/bizsignup/entry/product/ppcp';
|
||||
return 'https://www.paypal.com/bizsignup/entry?product=ppcp';
|
||||
},
|
||||
'wcgateway.enable-pui-url-sandbox' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.sandbox.paypal.com/bizsignup/entry?country.x=DE&product=payment_methods&capabilities=PAY_UPON_INVOICE';
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
|
@ -164,6 +166,13 @@ class SettingsListener {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The client credentials cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
private $client_credentials_cache;
|
||||
|
||||
/**
|
||||
* SettingsListener constructor.
|
||||
*
|
||||
|
@ -183,6 +192,7 @@ class SettingsListener {
|
|||
* @param string $partner_merchant_id_sandbox Partner merchant ID sandbox.
|
||||
* @param BillingAgreementsEndpoint $billing_agreements_endpoint Billing Agreements endpoint.
|
||||
* @param ?LoggerInterface $logger The logger.
|
||||
* @param Cache $client_credentials_cache The client credentials cache.
|
||||
*/
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
|
@ -200,7 +210,8 @@ class SettingsListener {
|
|||
string $partner_merchant_id_production,
|
||||
string $partner_merchant_id_sandbox,
|
||||
BillingAgreementsEndpoint $billing_agreements_endpoint,
|
||||
LoggerInterface $logger = null
|
||||
LoggerInterface $logger = null,
|
||||
Cache $client_credentials_cache
|
||||
) {
|
||||
|
||||
$this->settings = $settings;
|
||||
|
@ -219,6 +230,7 @@ class SettingsListener {
|
|||
$this->partner_merchant_id_sandbox = $partner_merchant_id_sandbox;
|
||||
$this->billing_agreements_endpoint = $billing_agreements_endpoint;
|
||||
$this->logger = $logger ?: new NullLogger();
|
||||
$this->client_credentials_cache = $client_credentials_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -490,6 +502,9 @@ class SettingsListener {
|
|||
if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) {
|
||||
$this->cache->delete( PayPalBearer::CACHE_KEY );
|
||||
}
|
||||
if ( $this->client_credentials_cache->has( SdkClientToken::CACHE_KEY ) ) {
|
||||
$this->client_credentials_cache->delete( SdkClientToken::CACHE_KEY );
|
||||
}
|
||||
|
||||
if ( $this->pui_status_cache->has( PayUponInvoiceProductStatus::PUI_STATUS_CACHE_KEY ) ) {
|
||||
$this->pui_status_cache->delete( PayUponInvoiceProductStatus::PUI_STATUS_CACHE_KEY );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "woocommerce-paypal-payments",
|
||||
"version": "2.8.2",
|
||||
"version": "2.8.3",
|
||||
"description": "WooCommerce PayPal Payments",
|
||||
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
|
||||
"license": "GPL-2.0",
|
||||
|
|
19
readme.txt
19
readme.txt
|
@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple
|
|||
Requires at least: 5.3
|
||||
Tested up to: 6.6
|
||||
Requires PHP: 7.2
|
||||
Stable tag: 2.8.2
|
||||
Stable tag: 2.8.3
|
||||
License: GPLv2
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
|
@ -179,6 +179,23 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 2.8.3 - 2024-08-12 =
|
||||
* Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474
|
||||
* Fix - Do not add tax info into order meta during order creation #2471
|
||||
* Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425
|
||||
* Fix - PayPal js files loaded on non PayPal pages #2411
|
||||
* Fix - Google Pay: Fix the incorrect popup triggering #2414
|
||||
* Fix - Add tax configurator when programmatically creating WC orders #2431
|
||||
* Fix - Shipping callback compatibility with WC Name Your Price plugin #2402
|
||||
* Fix - Uncaught Error: Cannot use object of type ...\Settings as array in .../AbstractPaymentMethodType.php (3253) #2334
|
||||
* Fix - Prevent displaying smart button multiple times on variable product page #2420
|
||||
* Fix - Prevent enabling Standard Card Button when ACDC is enabled #2404
|
||||
* Fix - Use client credentials for user tokens #2491
|
||||
* Fix - Apple Pay: Fix the shipping callback #2492
|
||||
* Enhancement - Separate Google Pay button for Classic Checkout #2430
|
||||
* Enhancement - Add Apple Pay and Google Pay support for China, simplify country-currency matrix #2468
|
||||
* Enhancement - Add AMEX support for Advanced Card Processing in China #2469
|
||||
|
||||
= 2.8.2 - 2024-07-22 =
|
||||
* Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415
|
||||
* Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400
|
||||
|
|
|
@ -43,6 +43,7 @@ class SettingsListenerTest extends ModularTestCase
|
|||
$billing_agreement_endpoint = Mockery::mock(BillingAgreementsEndpoint::class);
|
||||
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
|
||||
$logger = Mockery::mock(LoggerInterface::class);
|
||||
$client_credentials_cache = Mockery::mock(Cache::class);
|
||||
|
||||
$testee = new SettingsListener(
|
||||
$settings,
|
||||
|
@ -60,7 +61,8 @@ class SettingsListenerTest extends ModularTestCase
|
|||
'',
|
||||
'',
|
||||
$billing_agreement_endpoint,
|
||||
$logger
|
||||
$logger,
|
||||
$client_credentials_cache
|
||||
);
|
||||
|
||||
$_GET['section'] = PayPalGateway::ID;
|
||||
|
@ -94,6 +96,9 @@ class SettingsListenerTest extends ModularTestCase
|
|||
->andReturn(false);
|
||||
$dcc_status_cache->shouldReceive('has')
|
||||
->andReturn(false);
|
||||
$client_credentials_cache->shouldReceive('has')->andReturn(true);
|
||||
$client_credentials_cache->shouldReceive('delete');
|
||||
|
||||
|
||||
$testee->listen();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Plugin Name: WooCommerce PayPal Payments
|
||||
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
|
||||
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
|
||||
* Version: 2.8.2
|
||||
* Version: 2.8.3
|
||||
* Author: WooCommerce
|
||||
* Author URI: https://woocommerce.com/
|
||||
* License: GPL-2.0
|
||||
|
@ -26,7 +26,7 @@ define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' );
|
|||
define( 'PAYPAL_URL', 'https://www.paypal.com' );
|
||||
define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' );
|
||||
define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2024-07-17' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2024-08-07' );
|
||||
define( 'PPCP_PAYPAL_BN_CODE', 'Woo_PPCP' );
|
||||
|
||||
! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue