🔀 Merge branch 'trunk'

# Conflicts:
#	modules/ppcp-applepay/resources/js/ApplepayButton.js
#	modules/ppcp-applepay/resources/js/boot-admin.js
#	modules/ppcp-button/resources/js/modules/Preview/PreviewButtonManager.js
#	modules/ppcp-googlepay/resources/js/GooglepayButton.js
#	modules/ppcp-googlepay/resources/js/boot-admin.js
This commit is contained in:
Philipp Stracker 2024-09-06 15:19:17 +02:00
commit a128228c86
No known key found for this signature in database
303 changed files with 21512 additions and 5378 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="78.102px" height="50px" viewBox="-5 -5 175.52107 115.9651" enable-background="new 0 0 165.52107 105.9651"
xml:space="preserve">
<g>
<path id="XMLID_4_" d="M150.69807,0H14.82318c-0.5659,0-1.1328,0-1.69769,0.0033c-0.47751,0.0034-0.95391,0.0087-1.43031,0.0217
c-1.039,0.0281-2.0869,0.0894-3.1129,0.2738c-1.0424,0.1876-2.0124,0.4936-2.9587,0.9754
c-0.9303,0.4731-1.782,1.0919-2.52009,1.8303c-0.73841,0.7384-1.35721,1.5887-1.83021,2.52
c-0.4819,0.9463-0.7881,1.9166-0.9744,2.9598c-0.18539,1.0263-0.2471,2.074-0.2751,3.1119
c-0.0128,0.4764-0.01829,0.9528-0.0214,1.4291c-0.0033,0.5661-0.0022,1.1318-0.0022,1.6989V91.142
c0,0.5671-0.0011,1.13181,0.0022,1.69901c0.00311,0.4763,0.0086,0.9527,0.0214,1.4291
c0.028,1.03699,0.08971,2.08469,0.2751,3.11069c0.1863,1.0436,0.4925,2.0135,0.9744,2.9599
c0.473,0.9313,1.0918,1.7827,1.83021,2.52c0.73809,0.7396,1.58979,1.3583,2.52009,1.8302
c0.9463,0.4831,1.9163,0.7892,2.9587,0.9767c1.026,0.1832,2.0739,0.2456,3.1129,0.2737c0.4764,0.0108,0.9528,0.0172,1.43031,0.0194
c0.56489,0.0044,1.13179,0.0044,1.69769,0.0044h135.87489c0.5649,0,1.13181,0,1.69659-0.0044
c0.47641-0.0022,0.95282-0.0086,1.4314-0.0194c1.0368-0.0281,2.0845-0.0905,3.11301-0.2737
c1.041-0.1875,2.0112-0.4936,2.9576-0.9767c0.9313-0.4719,1.7805-1.0906,2.52011-1.8302c0.7372-0.7373,1.35599-1.5887,1.8302-2.52
c0.48299-0.9464,0.78889-1.9163,0.97429-2.9599c0.1855-1.026,0.2457-2.0737,0.2738-3.11069
c0.013-0.4764,0.01941-0.9528,0.02161-1.4291c0.00439-0.5672,0.00439-1.1319,0.00439-1.69901V14.8242
c0-0.5671,0-1.1328-0.00439-1.6989c-0.0022-0.4763-0.00861-0.9527-0.02161-1.4291c-0.02811-1.0379-0.0883-2.0856-0.2738-3.1119
c-0.18539-1.0432-0.4913-2.0135-0.97429-2.9598c-0.47421-0.9313-1.093-1.7816-1.8302-2.52
c-0.73961-0.7384-1.58881-1.3572-2.52011-1.8303c-0.9464-0.4818-1.9166-0.7878-2.9576-0.9754
c-1.0285-0.1844-2.0762-0.2457-3.11301-0.2738c-0.47858-0.013-0.95499-0.0183-1.4314-0.0217C151.82988,0,151.26297,0,150.69807,0
L150.69807,0z"/>
<path id="XMLID_3_" fill="#FFFFFF" d="M150.69807,3.532l1.67149,0.0032c0.4528,0.0032,0.90561,0.0081,1.36092,0.0205
c0.79201,0.0214,1.71849,0.0643,2.58209,0.2191c0.7507,0.1352,1.38029,0.3408,1.9845,0.6484
c0.5965,0.3031,1.14301,0.7003,1.62019,1.1768c0.479,0.4797,0.87671,1.0271,1.18381,1.6302
c0.30589,0.5995,0.51019,1.2261,0.64459,1.9823c0.1544,0.8542,0.1971,1.7832,0.21881,2.5801
c0.01219,0.4498,0.01819,0.8996,0.0204,1.3601c0.00429,0.5569,0.0042,1.1135,0.0042,1.6715V91.142
c0,0.558,0.00009,1.1136-0.0043,1.6824c-0.00211,0.4497-0.0081,0.8995-0.0204,1.3501c-0.02161,0.7957-0.0643,1.7242-0.2206,2.5885
c-0.13251,0.7458-0.3367,1.3725-0.64429,1.975c-0.30621,0.6016-0.70331,1.1484-1.18022,1.6251
c-0.47989,0.48-1.0246,0.876-1.62819,1.1819c-0.5997,0.3061-1.22821,0.51151-1.97151,0.6453
c-0.88109,0.157-1.84639,0.2002-2.57339,0.2199c-0.4574,0.0103-0.9126,0.01649-1.37889,0.0187
c-0.55571,0.0043-1.1134,0.0042-1.6692,0.0042H14.82318c-0.0074,0-0.0146,0-0.0221,0c-0.5494,0-1.0999,0-1.6593-0.0043
c-0.4561-0.00211-0.9112-0.0082-1.3512-0.0182c-0.7436-0.0201-1.7095-0.0632-2.5834-0.2193
c-0.74969-0.1348-1.3782-0.3402-1.9858-0.6503c-0.59789-0.3032-1.1422-0.6988-1.6223-1.1797
c-0.4764-0.4756-0.8723-1.0207-1.1784-1.6232c-0.3064-0.6019-0.5114-1.2305-0.64619-1.9852
c-0.15581-0.8626-0.19861-1.7874-0.22-2.5777c-0.01221-0.4525-0.01731-0.9049-0.02021-1.3547l-0.0022-1.3279l0.0001-0.3506V14.8242
l-0.0001-0.3506l0.0021-1.3251c0.003-0.4525,0.0081-0.9049,0.02031-1.357c0.02139-0.7911,0.06419-1.7163,0.22129-2.5861
c0.1336-0.7479,0.3385-1.3765,0.6465-1.9814c0.3037-0.5979,0.7003-1.1437,1.17921-1.6225
c0.477-0.4772,1.02309-0.8739,1.62479-1.1799c0.6011-0.3061,1.2308-0.5116,1.9805-0.6465c0.8638-0.1552,1.7909-0.198,2.5849-0.2195
c0.4526-0.0123,0.9052-0.0172,1.3544-0.0203l1.6771-0.0033H150.69807"/>
<g>
<g>
<path d="M45.1862,35.64053c1.41724-1.77266,2.37897-4.15282,2.12532-6.58506c-2.07464,0.10316-4.60634,1.36871-6.07207,3.14276
c-1.31607,1.5192-2.4809,3.99902-2.17723,6.3293C41.39111,38.72954,43.71785,37.36345,45.1862,35.64053"/>
<path d="M47.28506,38.98252c-3.38211-0.20146-6.25773,1.91951-7.87286,1.91951c-1.61602,0-4.08931-1.81799-6.76438-1.76899
c-3.48177,0.05114-6.71245,2.01976-8.4793,5.15079c-3.63411,6.2636-0.95904,15.55471,2.57494,20.65606
c1.71618,2.5238,3.78447,5.30269,6.50976,5.20287c2.57494-0.10104,3.58421-1.66732,6.71416-1.66732
c3.12765,0,4.03679,1.66732,6.76252,1.61681c2.82665-0.05054,4.59381-2.52506,6.30997-5.05132
c1.96878-2.877,2.77473-5.65498,2.82542-5.80748c-0.0507-0.05051-5.45058-2.12204-5.50065-8.33358
c-0.05098-5.20101,4.23951-7.6749,4.44144-7.82832C52.3832,39.4881,48.5975,39.08404,47.28506,38.98252"/>
</g>
<g>
<path d="M76.73385,31.94381c7.35096,0,12.4697,5.06708,12.4697,12.44437c0,7.40363-5.22407,12.49704-12.65403,12.49704h-8.13892
v12.94318h-5.88037v-37.8846H76.73385z M68.41059,51.9493h6.74732c5.11975,0,8.0336-2.75636,8.0336-7.53479
c0-4.77792-2.91385-7.50845-8.00727-7.50845h-6.77365V51.9493z"/>
<path d="M90.73997,61.97864c0-4.8311,3.70182-7.79761,10.26583-8.16526l7.56061-0.44614v-2.12639
c0-3.07185-2.07423-4.90959-5.53905-4.90959c-3.28251,0-5.33041,1.57492-5.82871,4.04313h-5.35574
c0.31499-4.98859,4.56777-8.66407,11.3941-8.66407c6.69466,0,10.97377,3.54432,10.97377,9.08388v19.03421h-5.43472v-4.54194
h-0.13065c-1.60125,3.07185-5.09341,5.01441-8.71623,5.01441C94.52078,70.30088,90.73997,66.94038,90.73997,61.97864z
M108.56641,59.4846v-2.17905l-6.8,0.41981c-3.38683,0.23649-5.30306,1.73291-5.30306,4.09579
c0,2.41504,1.99523,3.99046,5.04075,3.99046C105.46823,65.81161,108.56641,63.08108,108.56641,59.4846z"/>
<path d="M119.34167,79.9889v-4.5946c0.4193,0.10483,1.36425,0.10483,1.83723,0.10483c2.6252,0,4.04313-1.10245,4.90908-3.9378
c0-0.05267,0.49931-1.68025,0.49931-1.70658l-9.97616-27.64562h6.14268l6.98432,22.47371h0.10432l6.98433-22.47371h5.9857
l-10.34483,29.06304c-2.36186,6.69517-5.0924,8.84789-10.81577,8.84789C121.17891,80.12006,119.76098,80.06739,119.34167,79.9889
z"/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -4,7 +4,7 @@
"description": "Applepay module for PPCP",
"license": "GPL-2.0",
"require": {
"php": "^7.2 | ^8.0",
"php": "^7.4 | ^8.0",
"dhii/module-interface": "^0.3.0-alpha1"
},
"autoload": {

View file

@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array(
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
'wcgateway.settings.fields' => function ( array $fields, ContainerInterface $container ): array {
// Used in various places to mark fields for the preview button.
$apm_name = 'ApplePay';
@ -101,7 +101,7 @@ return array(
'applepay_button_enabled' => array(
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
'title_html' => sprintf(
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
'<img src="%sassets/images/applepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
$module_url,
__( 'Apple Pay', 'woocommerce-paypal-payments' )
),
@ -155,7 +155,7 @@ return array(
'applepay_button_enabled' => array(
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
'title_html' => sprintf(
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
'<img src="%sassets/images/applepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
$module_url,
__( 'Apple Pay', 'woocommerce-paypal-payments' )
),
@ -269,7 +269,7 @@ return array(
'classes' => array( 'ppcp-field-indent' ),
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'pay',
'default' => 'plain',
'options' => PropertiesDictionary::button_types(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'dcc',

View file

@ -9,8 +9,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
return static function (): ModuleInterface {
return static function (): ApplepayModule {
return new ApplepayModule();
};

View file

@ -57,3 +57,7 @@
}
}
}
#ppc-button-ppcp-applepay {
display: none;
}

View file

@ -1,3 +1,6 @@
/* global ApplePaySession */
/* global PayPalCommerceGateway */
import ContextHandlerFactory from './Context/ContextHandlerFactory';
import { createAppleErrors } from './Helper/applePayError';
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
@ -7,18 +10,95 @@ import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
class ApplepayButton {
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
apmButtonsInit( ppcpConfig );
/**
* Plugin-specific styling.
*
* Note that most properties of this object do not apply to the Apple Pay button.
*
* @typedef {Object} PPCPStyle
* @property {string} shape - Outline shape.
* @property {?number} height - Button height in pixel.
*/
this.isInitialized = false;
/**
* Style options that are defined by the Apple Pay SDK and are required to render the button.
*
* @typedef {Object} ApplePayStyle
* @property {string} type - Defines the button label.
* @property {string} color - Button color
* @property {string} lang - The locale; an empty string will apply the user-agent's language.
*/
/**
* List of valid context values that the button can have.
*
* @type {Object}
*/
const CONTEXT = {
Product: 'product',
Cart: 'cart',
Checkout: 'checkout',
PayNow: 'pay-now',
MiniCart: 'mini-cart',
BlockCart: 'cart-block',
BlockCheckout: 'checkout-block',
Preview: 'preview',
// Block editor contexts.
Blocks: [ 'cart-block', 'checkout-block' ],
// Custom gateway contexts.
Gateways: [ 'checkout', 'pay-now' ],
};
/**
* A payment button for Apple Pay.
*
* On a single page, multiple Apple Pay buttons can be displayed, which also means multiple
* ApplePayButton instances exist. A typical case is on the product page, where one Apple Pay button
* is located inside the minicart-popup, and another pay-now button is in the product context.
*/
class ApplePayButton {
/**
* Whether the payment button is initialized.
*
* @type {boolean}
*/
#isInitialized = false;
#wrapperId = '';
#ppcpButtonWrapperId = '';
/**
* Context describes the button's location on the website and what details it submits.
*
* @type {''|'product'|'cart'|'checkout'|'pay-now'|'mini-cart'|'cart-block'|'checkout-block'|'preview'}
*/
context = '';
externalHandler = null;
buttonConfig = null;
ppcpConfig = null;
paymentsClient = null;
formData = null;
contextHandler = null;
updatedContactInfo = [];
selectedShippingMethod = [];
/**
* Stores initialization data sent to the button.
*/
initialPaymentRequest = null;
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
this._initDebug( !! buttonConfig?.is_debug );
apmButtonsInit( ppcpConfig );
this.context = context;
this.externalHandler = externalHandler;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
this.paymentsClient = null;
this.formData = null;
this.contextHandler = ContextHandlerFactory.create(
this.context,
@ -26,36 +106,226 @@ class ApplepayButton {
this.ppcpConfig
);
this.updatedContactInfo = [];
this.selectedShippingMethod = [];
this.nonce =
document.getElementById( 'woocommerce-process-checkout-nonce' )
?.value || buttonConfig.nonce;
// Stores initialization data sent to the button.
this.initialPaymentRequest = null;
// Default eligibility status.
this.isEligible = true;
this.log = function () {
if ( this.buttonConfig.is_debug ) {
//console.log('[ApplePayButton]', ...arguments);
}
};
this.refreshContextData();
}
/**
* NOOP log function to avoid errors when debugging is disabled.
*/
log() {}
/**
* Enables debugging tools, when the button's is_debug flag is set.
*
* @param {boolean} enableDebugging If debugging features should be enabled for this instance.
* @private
*/
_initDebug( enableDebugging ) {
if ( ! enableDebugging || this.#isInitialized ) {
return;
}
// Debug helpers
jQuery( document ).on( 'ppcp-applepay-debug', () => {
console.log( 'ApplePayButton', this.context, this );
} );
document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
document.ppcpApplepayButtons[ this.context ] = this;
this.log = ( ...args ) => {
console.log( `[ApplePayButton | ${ this.context }]`, ...args );
};
jQuery( document ).on( 'ppcp-applepay-debug', () => {
this.log( this );
} );
}
/**
* The nonce for ajax requests.
*
* @return {string} The nonce value
*/
get nonce() {
const input = document.getElementById(
'woocommerce-process-checkout-nonce'
);
return input?.value || this.buttonConfig.nonce;
}
/**
* Whether the current page qualifies to use the Apple Pay button.
*
* In admin, the button is always eligible, to display an accurate preview.
* On front-end, PayPal's response decides if customers can use Apple Pay.
*
* @return {boolean} True, if the button can be displayed.
*/
get isEligible() {
if ( ! this.#isInitialized ) {
return true;
}
if ( CONTEXT.Preview === this.context ) {
return true;
}
/**
* Ensure the ApplePaySession is available and accepts payments
* This check is required when using Apple Pay SDK v1; canMakePayments() returns false
* if the current device is not liked to iCloud or the Apple Wallet is not available
* for a different reason.
*/
try {
if ( ! window.ApplePaySession?.canMakePayments() ) {
return false;
}
} catch ( error ) {
console.warn( error );
return false;
}
return !! this.applePayConfig.isEligible;
}
/**
* 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 &&
CONTEXT.Gateways.includes( this.context )
);
}
/**
* Returns the wrapper ID for the current button context.
* The ID varies for the MiniCart context.
*
* @return {string} The wrapper-element's ID (without the `#` prefix).
*/
get wrapperId() {
if ( ! this.#wrapperId ) {
let id;
if ( CONTEXT.MiniCart === this.context ) {
id = this.buttonConfig.button.mini_cart_wrapper;
} else if ( this.isSeparateGateway ) {
id = 'ppc-button-ppcp-applepay';
} else {
id = this.buttonConfig.button.wrapper;
}
this.#wrapperId = id.replace( /^#/, '' );
}
return this.#wrapperId;
}
/**
* Returns the wrapper ID for the ppcpButton
*
* @return {string} The wrapper-element's ID (without the `#` prefix).
*/
get ppcpButtonWrapperId() {
if ( ! this.#ppcpButtonWrapperId ) {
let id;
if ( CONTEXT.MiniCart === this.context ) {
id = this.ppcpConfig.button.mini_cart_wrapper;
} else if ( CONTEXT.Blocks.includes( this.context ) ) {
id = '#express-payment-method-ppcp-gateway-paypal';
} else {
id = this.ppcpConfig.button.wrapper;
}
this.#ppcpButtonWrapperId = id.replace( /^#/, '' );
}
return this.#ppcpButtonWrapperId;
}
/**
* Returns the context-relevant PPCP style object.
* The style for the MiniCart context can be different.
*
* The PPCP style are custom style options, that are provided by this plugin.
*
* @return {PPCPStyle} The style object.
*/
get ppcpStyle() {
if ( CONTEXT.MiniCart === this.context ) {
return this.ppcpConfig.button.mini_cart_style;
}
return this.ppcpConfig.button.style;
}
/**
* Returns default style options that are propagated to and rendered by the Apple Pay button.
*
* These styles are the official style options provided by the Apple Pay SDK.
*
* @return {ApplePayStyle} The style object.
*/
get buttonStyle() {
return {
type: this.buttonConfig.button.type,
lang: this.buttonConfig.button.lang,
color: this.buttonConfig.button.color,
};
}
/**
* Returns the HTML element that wraps the current button
*
* @return {HTMLElement|null} The wrapper element, or null.
*/
get wrapperElement() {
return document.getElementById( this.wrapperId );
}
/**
* Returns an array of HTMLElements that belong to the payment button.
*
* @return {HTMLElement[]} List of payment button wrapper elements.
*/
get allElements() {
const selectors = [];
// Payment button (Pay now, smart button block)
selectors.push( `#${ this.wrapperId }` );
// Block Checkout: Express checkout button.
if ( CONTEXT.Blocks.includes( this.context ) ) {
selectors.push( '#express-payment-method-ppcp-applepay' );
}
// Classic Checkout: Apple Pay gateway.
if ( CONTEXT.Gateways.includes( this.context ) ) {
selectors.push( '.wc_payment_method.payment_method_ppcp-applepay' );
}
this.log( 'Wrapper Elements:', selectors );
return /** @type {HTMLElement[]} */ selectors.flatMap( ( selector ) =>
Array.from( document.querySelectorAll( selector ) )
);
}
/**
* Checks whether the main button-wrapper is present in the current DOM.
*
* @return {boolean} True, if the button context (wrapper element) is found.
*/
get isPresent() {
return this.wrapperElement instanceof HTMLElement;
}
init( config ) {
if ( this.isInitialized ) {
if ( this.#isInitialized ) {
return;
}
@ -63,41 +333,41 @@ class ApplepayButton {
return;
}
this.log( 'Init', this.context );
this.log( 'Init' );
this.initEventHandlers();
this.isInitialized = true;
this.#isInitialized = true;
this.applePayConfig = config;
this.isEligible =
( this.applePayConfig.isEligible && window.ApplePaySession ) ||
this.buttonConfig.is_admin;
if ( this.isEligible ) {
this.fetchTransactionInfo().then( () => {
this.addButton();
const id_minicart =
'#apple-' + this.buttonConfig.button.mini_cart_wrapper;
const id = '#apple-' + this.buttonConfig.button.wrapper;
if ( this.isSeparateGateway ) {
document
.querySelectorAll( '#ppc-button-applepay-container' )
.forEach( ( el ) => el.remove() );
}
if ( this.context === 'mini-cart' ) {
document
.querySelector( id_minicart )
?.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();
} );
} else {
document
.querySelector( id )
?.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();
} );
}
} );
if ( ! this.isEligible ) {
this.hide();
} else {
jQuery( '#' + this.buttonConfig.button.wrapper ).hide();
jQuery( '#' + this.buttonConfig.button.mini_cart_wrapper ).hide();
jQuery( '#express-payment-method-ppcp-applepay' ).hide();
// Bail if the button wrapper is not present; handles mini-cart logic on checkout page.
if ( ! this.isPresent ) {
this.log( 'Abort init (no wrapper found)' );
return;
}
this.show();
this.fetchTransactionInfo().then( () => {
const button = this.addButton();
if ( ! button ) {
return;
}
button.addEventListener( 'click', ( evt ) => {
evt.preventDefault();
this.onButtonClick();
} );
} );
}
}
@ -106,49 +376,51 @@ class ApplepayButton {
return;
}
this.isInitialized = false;
this.#isInitialized = false;
this.init( this.applePayConfig );
}
/**
* Hides all wrappers that belong to this ApplePayButton instance.
*/
hide() {
this.log( 'Hide button' );
this.allElements.forEach( ( element ) => {
element.style.display = 'none';
} );
}
/**
* Ensures all wrapper elements of this ApplePayButton instance are visible.
*/
show() {
this.log( 'Show button' );
if ( ! this.isPresent ) {
this.log( '!! Cannot show button, wrapper is not present' );
return;
}
// Classic Checkout/PayNow: Make the Apple Pay gateway visible after page load.
document
.querySelectorAll( 'style#ppcp-hide-apple-pay' )
.forEach( ( el ) => el.remove() );
this.allElements.forEach( ( element ) => {
element.style.display = '';
} );
}
async fetchTransactionInfo() {
this.transactionInfo = await this.contextHandler.transactionInfo();
}
/**
* Returns configurations relative to this button context.
*/
contextConfig() {
const config = {
wrapper: this.buttonConfig.button.wrapper,
ppcpStyle: this.ppcpConfig.button.style,
buttonStyle: this.buttonConfig.button.style,
ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
};
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;
}
if (
[ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
) {
config.ppcpButtonWrapper =
'#express-payment-method-ppcp-gateway-paypal';
}
return config;
}
initEventHandlers() {
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
const wrapper_id = '#' + wrapper;
const ppcpButtonWrapper = `#${ this.ppcpButtonWrapperId }`;
const wrapperId = `#${ this.wrapperId }`;
if ( wrapper_id === ppcpButtonWrapper ) {
if ( wrapperId === ppcpButtonWrapper ) {
throw new Error(
`[ApplePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper_id }"`
`[ApplePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapperId }"`
);
}
@ -158,9 +430,9 @@ class ApplepayButton {
}
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
setVisible( wrapper_id, $ppcpButtonWrapper.is( ':visible' ) );
setVisible( wrapperId, $ppcpButtonWrapper.is( ':visible' ) );
setEnabled(
wrapper_id,
wrapperId,
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
);
};
@ -178,8 +450,9 @@ class ApplepayButton {
}
/**
* Starts an ApplePay session.
* @param paymentRequest
* Starts an Apple Pay session.
*
* @param {Object} paymentRequest The payment request object.
*/
applePaySession( paymentRequest ) {
this.log( 'applePaySession', paymentRequest );
@ -192,6 +465,7 @@ class ApplepayButton {
session.onshippingcontactselected =
this.onShippingContactSelected( session );
}
session.onvalidatemerchant = this.onValidateMerchant( session );
session.onpaymentauthorized = this.onPaymentAuthorized( session );
return session;
@ -199,33 +473,39 @@ class ApplepayButton {
/**
* Adds an Apple Pay purchase button.
*
* @return {HTMLElement|null} The newly created `<apple-pay-button>` element. Null on failure.
*/
addButton() {
this.log( 'addButton', this.context );
this.log( 'addButton' );
const { wrapper, ppcpStyle } = this.contextConfig();
const wrapper = this.wrapperElement;
const style = this.buttonStyle;
const id = 'apple-' + this.wrapperId;
const appleContainer = document.getElementById( wrapper );
const type = this.buttonConfig.button.type;
const language = this.buttonConfig.button.lang;
const color = this.buttonConfig.button.color;
const id = 'apple-' + wrapper;
if ( appleContainer ) {
appleContainer.innerHTML = `<apple-pay-button id="${ id }" buttonstyle="${ color }" type="${ type }" locale="${ language }">`;
if ( ! wrapper ) {
return null;
}
const $wrapper = jQuery( '#' + wrapper );
$wrapper.removeClass( 'ppcp-button-rect ppcp-button-pill' );
$wrapper.addClass( 'ppcp-button-' + ppcpStyle.shape );
const ppcpStyle = this.ppcpStyle;
wrapper.innerHTML = `<apple-pay-button id='${ id }' buttonstyle='${ style.color }' type='${ style.type }' locale='${ style.lang }' />`;
wrapper.classList.remove( 'ppcp-button-rect', 'ppcp-button-pill' );
wrapper.classList.add(
`ppcp-button-${ ppcpStyle.shape }`,
'ppcp-button-apm',
'ppcp-button-applepay'
);
if ( ppcpStyle.height ) {
$wrapper.css(
wrapper.style.setProperty(
'--apple-pay-button-height',
`${ ppcpStyle.height }px`
);
$wrapper.css( 'height', `${ ppcpStyle.height }px` );
wrapper.style.height = `${ ppcpStyle.height }px`;
}
return wrapper.querySelector( 'apple-pay-button' );
}
//------------------------
@ -236,19 +516,21 @@ class ApplepayButton {
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
async onButtonClick() {
this.log( 'onButtonClick', this.context );
this.log( 'onButtonClick' );
const paymentRequest = this.paymentRequest();
window.ppcpFundingSource = 'apple_pay'; // Do this on another place like on create order endpoint handler.
// Do this on another place like on create order endpoint handler.
window.ppcpFundingSource = 'apple_pay';
// Trigger woocommerce validation if we are in the checkout page.
if ( this.context === 'checkout' ) {
if ( CONTEXT.Checkout === this.context ) {
const checkoutFormSelector = 'form.woocommerce-checkout';
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
document.querySelector( '.woocommerce-notices-wrapper' )
);
try {
const formData = new FormData(
document.querySelector( checkoutFormSelector )
@ -270,6 +552,7 @@ class ApplepayButton {
PayPalCommerceGateway.ajax.validate_checkout.nonce
)
: null;
if ( formValidator ) {
try {
const errors = await formValidator.validate(
@ -297,13 +580,13 @@ class ApplepayButton {
/**
* If the button should show the shipping fields.
*
* @return {false|*}
* @return {boolean} True, if shipping fields should be captured by ApplePay.
*/
shouldRequireShippingInButton() {
return (
this.contextHandler.shippingAllowed() &&
this.buttonConfig.product.needShipping &&
( this.context !== 'checkout' ||
( CONTEXT.Checkout !== this.context ||
this.shouldUpdateButtonWithFormData() )
);
}
@ -311,10 +594,10 @@ class ApplepayButton {
/**
* If the button should be updated with the form addresses.
*
* @return {boolean}
* @return {boolean} True, when Apple Pay data should be submitted to WooCommerce.
*/
shouldUpdateButtonWithFormData() {
if ( this.context !== 'checkout' ) {
if ( CONTEXT.Checkout !== this.context ) {
return false;
}
return (
@ -324,29 +607,28 @@ class ApplepayButton {
}
/**
* Indicates how payment completion should be handled if with the context handler default actions.
* Or with ApplePay module specific completion.
* Indicates how payment completion should be handled if with the context handler default
* actions. Or with Apple Pay module specific completion.
*
* @return {boolean}
* @return {boolean} True, when the Apple Pay data should be submitted to WooCommerce.
*/
shouldCompletePaymentWithContextHandler() {
// Data already handled, ex: PayNow
if ( ! this.contextHandler.shippingAllowed() ) {
return true;
}
// Use WC form data mode in Checkout.
if (
this.context === 'checkout' &&
return (
CONTEXT.Checkout === this.context &&
! this.shouldUpdateButtonWithFormData()
) {
return true;
}
return false;
);
}
/**
* Updates ApplePay paymentRequest with form data.
* @param paymentRequest
* Updates Apple Pay paymentRequest with form data.
*
* @param {Object} paymentRequest Object to extend with form data.
*/
updateRequestDataWithForm( paymentRequest ) {
if ( ! this.shouldUpdateButtonWithFormData() ) {
@ -359,8 +641,9 @@ class ApplepayButton {
);
// Add custom data.
// "applicationData" is originating a "PayPalApplePayError: An internal server error has occurred" on paypal.Applepay().confirmOrder().
// paymentRequest.applicationData = this.fillApplicationData(this.formData);
// "applicationData" is originating a "PayPalApplePayError: An internal server error has
// occurred" on paypal.Applepay().confirmOrder(). paymentRequest.applicationData =
// this.fillApplicationData(this.formData);
if ( ! this.shouldRequireShippingInButton() ) {
return;
@ -426,7 +709,8 @@ class ApplepayButton {
'email',
'phone',
],
requiredBillingContactFields: [ 'postalAddress' ], // ApplePay does not implement billing email and phone fields.
requiredBillingContactFields: [ 'postalAddress' ], // ApplePay does not implement billing
// email and phone fields.
};
if ( ! this.shouldRequireShippingInButton() ) {
@ -454,14 +738,11 @@ class ApplepayButton {
}
refreshContextData() {
switch ( this.context ) {
case 'product':
// Refresh product data that makes the price change.
this.productQuantity =
document.querySelector( 'input.qty' )?.value;
this.products = this.contextHandler.products();
this.log( 'Products updated', this.products );
break;
if ( CONTEXT.Product === this.context ) {
// Refresh product data that makes the price change.
this.productQuantity = document.querySelector( 'input.qty' )?.value;
this.products = this.contextHandler.products();
this.log( 'Products updated', this.products );
}
}
@ -469,8 +750,36 @@ class ApplepayButton {
// Payment process
//------------------------
/**
* Make ajax call to change the verification-status of the current domain.
*
* @param {boolean} isValid
*/
adminValidation( isValid ) {
// eslint-disable-next-line no-unused-vars
const ignored = fetch( this.buttonConfig.ajax_url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams( {
action: 'ppcp_validate',
'woocommerce-process-checkout-nonce': this.nonce,
validation: isValid,
} ).toString(),
} );
}
/**
* Returns an event handler that Apple Pay calls when displaying the payment sheet.
*
* @see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778021-onvalidatemerchant
*
* @param {Object} session The ApplePaySession object.
*
* @return {(function(*): void)|*} Callback that runs after the merchant validation
*/
onValidateMerchant( session ) {
this.log( 'onvalidatemerchant', this.buttonConfig.ajax_url );
return ( applePayValidateMerchantEvent ) => {
this.log( 'onvalidatemerchant call' );
@ -480,34 +789,15 @@ class ApplepayButton {
validationUrl: applePayValidateMerchantEvent.validationURL,
} )
.then( ( validateResult ) => {
this.log( 'onvalidatemerchant ok' );
session.completeMerchantValidation(
validateResult.merchantSession
);
//call backend to update validation to true
jQuery.ajax( {
url: this.buttonConfig.ajax_url,
type: 'POST',
data: {
action: 'ppcp_validate',
validation: true,
'woocommerce-process-checkout-nonce': this.nonce,
},
} );
this.adminValidation( true );
} )
.catch( ( validateError ) => {
this.log( 'onvalidatemerchant error', validateError );
console.error( validateError );
//call backend to update validation to false
jQuery.ajax( {
url: this.buttonConfig.ajax_url,
type: 'POST',
data: {
action: 'ppcp_validate',
validation: false,
'woocommerce-process-checkout-nonce': this.nonce,
},
} );
this.adminValidation( false );
this.log( 'onvalidatemerchant session abort' );
session.abort();
} );
@ -516,14 +806,14 @@ class ApplepayButton {
onShippingMethodSelected( session ) {
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
const ajax_url = this.buttonConfig.ajax_url;
const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingmethodselected call' );
const data = this.getShippingMethodData( event );
jQuery.ajax( {
url: ajax_url,
url: ajaxUrl,
method: 'POST',
data,
success: (
@ -538,7 +828,8 @@ class ApplepayButton {
}
this.selectedShippingMethod = event.shippingMethod;
// Sort the response shipping methods, so that the selected shipping method is the first one.
// Sort the response shipping methods, so that the selected shipping method is
// the first one.
response.newShippingMethods =
response.newShippingMethods.sort( ( a, b ) => {
if (
@ -566,7 +857,7 @@ class ApplepayButton {
onShippingContactSelected( session ) {
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
const ajax_url = this.buttonConfig.ajax_url;
const ajaxUrl = this.buttonConfig.ajax_url;
return ( event ) => {
this.log( 'onshippingcontactselected call' );
@ -574,7 +865,7 @@ class ApplepayButton {
const data = this.getShippingContactData( event );
jQuery.ajax( {
url: ajax_url,
url: ajaxUrl,
method: 'POST',
data,
success: (
@ -604,15 +895,15 @@ class ApplepayButton {
}
getShippingContactData( event ) {
const product_id = this.buttonConfig.product.id;
const productId = this.buttonConfig.product.id;
this.refreshContextData();
switch ( this.context ) {
case 'product':
case CONTEXT.Product:
return {
action: 'ppcp_update_shipping_contact',
product_id,
product_id: productId,
products: JSON.stringify( this.products ),
caller_page: 'productDetail',
product_quantity: this.productQuantity,
@ -620,11 +911,12 @@ class ApplepayButton {
need_shipping: this.shouldRequireShippingInButton(),
'woocommerce-process-checkout-nonce': this.nonce,
};
case 'cart':
case 'checkout':
case 'cart-block':
case 'checkout-block':
case 'mini-cart':
case CONTEXT.Cart:
case CONTEXT.Checkout:
case CONTEXT.BlockCart:
case CONTEXT.BlockCheckout:
case CONTEXT.MiniCart:
return {
action: 'ppcp_update_shipping_contact',
simplified_contact: event.shippingContact,
@ -636,37 +928,42 @@ class ApplepayButton {
}
getShippingMethodData( event ) {
const product_id = this.buttonConfig.product.id;
const productId = this.buttonConfig.product.id;
this.refreshContextData();
switch ( this.context ) {
case 'product':
case CONTEXT.Product:
return {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
simplified_contact:
this.updatedContactInfo ||
this.initialPaymentRequest.shippingContact ||
this.initialPaymentRequest.billingContact,
product_id,
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',
product_quantity: this.productQuantity,
'woocommerce-process-checkout-nonce': this.nonce,
};
case 'cart':
case 'checkout':
case 'cart-block':
case 'checkout-block':
case 'mini-cart':
case CONTEXT.Cart:
case CONTEXT.Checkout:
case CONTEXT.BlockCart:
case CONTEXT.BlockCheckout:
case CONTEXT.MiniCart:
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,
};
@ -678,9 +975,6 @@ class ApplepayButton {
return async ( event ) => {
this.log( 'onpaymentauthorized call' );
function form() {
return document.querySelector( 'form.cart' );
}
const processInWooAndCapture = async ( data ) => {
return new Promise( ( resolve, reject ) => {
try {
@ -695,7 +989,7 @@ class ApplepayButton {
( this.initialPaymentRequest.shippingMethods ||
[] )[ 0 ];
const request_data = {
const requestData = {
action: 'ppcp_create_order',
caller_page: this.context,
product_id: this.buttonConfig.product.id ?? null,
@ -720,7 +1014,7 @@ class ApplepayButton {
jQuery.ajax( {
url: this.buttonConfig.ajax_url,
method: 'POST',
data: request_data,
data: requestData,
complete: ( jqXHR, textStatus ) => {
this.log( 'onpaymentauthorized complete' );
},
@ -782,7 +1076,8 @@ class ApplepayButton {
if (
this.shouldCompletePaymentWithContextHandler()
) {
// No shipping, expect immediate capture, ex: PayNow, Checkout with form data.
// No shipping, expect immediate capture, ex: PayNow, Checkout with
// form data.
let approveFailed = false;
await this.contextHandler.approveOrder(
@ -949,6 +1244,12 @@ class ApplepayButton {
return btoa( utf8Str );
}
hasValidContactInfo( value ) {
return Array.isArray( value )
? value.length > 0
: Object.keys( value || {} ).length > 0;
}
}
export default ApplepayButton;
export default ApplePayButton;

View file

@ -1,7 +1,9 @@
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
import ApplepayButton from './ApplepayButton';
/* global paypal */
class ApplepayManager {
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
import ApplePayButton from './ApplepayButton';
class ApplePayManager {
constructor( buttonConfig, ppcpConfig ) {
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
@ -9,7 +11,7 @@ class ApplepayManager {
this.buttons = [];
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
const button = new ApplepayButton(
const button = new ApplePayButton(
bootstrap.context,
bootstrap.handler,
buttonConfig,
@ -40,8 +42,7 @@ class ApplepayManager {
}
/**
* Gets ApplePay configuration of the PayPal merchant.
* @return {Promise<null>}
* Gets Apple Pay configuration of the PayPal merchant.
*/
async config() {
this.ApplePayConfig = await paypal.Applepay().config();
@ -49,4 +50,4 @@ class ApplepayManager {
}
}
export default ApplepayManager;
export default ApplePayManager;

View file

@ -1,6 +1,8 @@
import ApplepayButton from './ApplepayButton';
/* global paypal */
class ApplepayManagerBlockEditor {
import ApplePayButton from './ApplepayButton';
class ApplePayManagerBlockEditor {
constructor( buttonConfig, ppcpConfig ) {
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
@ -17,7 +19,7 @@ class ApplepayManagerBlockEditor {
try {
this.applePayConfig = await paypal.Applepay().config();
const button = new ApplepayButton(
const button = new ApplePayButton(
this.ppcpConfig.context,
null,
this.buttonConfig,
@ -31,4 +33,4 @@ class ApplepayManagerBlockEditor {
}
}
export default ApplepayManagerBlockEditor;
export default ApplePayManagerBlockEditor;

View file

@ -1,6 +1,5 @@
import ErrorHandler from '../../../../ppcp-button/resources/js/modules/ErrorHandler';
import CartActionHandler from '../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler';
import { isPayPalSubscription } from '../../../../ppcp-blocks/resources/js/Helper/Subscription';
class BaseHandler {
constructor( buttonConfig, ppcpConfig ) {
@ -24,7 +23,7 @@ class BaseHandler {
}
shippingAllowed() {
return this.buttonConfig.product.needsShipping;
return this.buttonConfig.product.needShipping;
}
transactionInfo() {
@ -76,13 +75,6 @@ class BaseHandler {
document.querySelector( '.woocommerce-notices-wrapper' )
);
}
errorHandler() {
return new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector( '.woocommerce-notices-wrapper' )
);
}
}
export default BaseHandler;

View file

@ -1,10 +1,6 @@
import BaseHandler from './BaseHandler';
class PreviewHandler extends BaseHandler {
constructor( buttonConfig, ppcpConfig, externalHandler ) {
super( buttonConfig, ppcpConfig, externalHandler );
}
transactionInfo() {
// We need to return something as ApplePay button initialization expects valid data.
return {
@ -19,7 +15,7 @@ class PreviewHandler extends BaseHandler {
throw new Error( 'Create order fail. This is just a preview.' );
}
approveOrder( data, actions ) {
approveOrder() {
throw new Error( 'Approve order fail. This is just a preview.' );
}

View file

@ -1,4 +1,4 @@
export const buttonID = 'applepay-container';
export const buttonID = 'ppc-button-applepay-container';
export const endpoints = {
validation: '_apple_pay_validation',
createOrderCart: '_apple_pay_create_order_cart',

View file

@ -4,8 +4,8 @@ import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Help
import { cartHasSubscriptionProducts } from '../../../ppcp-blocks/resources/js/Helper/Subscription';
import { loadCustomScript } from '@paypal/paypal-js';
import CheckoutHandler from './Context/CheckoutHandler';
import ApplepayManager from './ApplepayManager';
import ApplepayManagerBlockEditor from './ApplepayManagerBlockEditor';
import ApplePayManager from './ApplepayManager';
import ApplePayManagerBlockEditor from './ApplepayManagerBlockEditor';
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
const ppcpConfig = ppcpData.scriptData;
@ -24,8 +24,8 @@ const ApplePayComponent = ( props ) => {
const bootstrap = function () {
const ManagerClass = props.isEditing
? ApplepayManagerBlockEditor
: ApplepayManager;
? ApplePayManagerBlockEditor
: ApplePayManager;
const manager = new ManagerClass( buttonConfig, ppcpConfig );
manager.init();
};

View file

@ -1,13 +1,13 @@
import { loadCustomScript } from '@paypal/paypal-js';
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
import ApplepayManager from './ApplepayManager';
import ApplePayManager from './ApplepayManager';
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
let manager;
const bootstrap = function () {
manager = new ApplepayManager( buttonConfig, ppcpConfig );
manager = new ApplePayManager( buttonConfig, ppcpConfig );
manager.init();
};

View file

@ -24,32 +24,33 @@ use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
'applepay.eligible' => static function ( ContainerInterface $container ): bool {
'applepay.eligible' => static function ( ContainerInterface $container ): bool {
$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 {
'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' )
);
},
'applepay.status-cache' => static function( ContainerInterface $container ): Cache {
'applepay.status-cache' => static function( ContainerInterface $container ): Cache {
return new Cache( 'ppcp-paypal-apple-status-cache' );
},
// We assume it's a referral if we can check product status without API request failures.
'applepay.is_referral' => static function ( ContainerInterface $container ): bool {
'applepay.is_referral' => static function ( ContainerInterface $container ): bool {
$status = $container->get( 'applepay.apple-product-status' );
assert( $status instanceof AppleProductStatus );
return ! $status->has_request_failure();
},
'applepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice {
'applepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice {
$settings = $container->get( 'wcgateway.settings' );
return new AvailabilityNotice(
@ -63,17 +64,17 @@ return array(
);
},
'applepay.has_validated' => static function ( ContainerInterface $container ): bool {
'applepay.has_validated' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'applepay_validated' );
},
'applepay.is_validated' => static function ( ContainerInterface $container ): bool {
'applepay.is_validated' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'applepay_validated' ) ? $settings->get( 'applepay_validated' ) === true : false;
},
'applepay.apple-product-status' => SingletonDecorator::make(
'applepay.apple-product-status' => SingletonDecorator::make(
static function( ContainerInterface $container ): AppleProductStatus {
return new AppleProductStatus(
$container->get( 'wcgateway.settings' ),
@ -83,7 +84,7 @@ return array(
);
}
),
'applepay.available' => static function ( ContainerInterface $container ): bool {
'applepay.available' => static function ( ContainerInterface $container ): bool {
if ( apply_filters( 'woocommerce_paypal_payments_applepay_validate_product_status', true ) ) {
$status = $container->get( 'applepay.apple-product-status' );
assert( $status instanceof AppleProductStatus );
@ -94,10 +95,10 @@ return array(
}
return true;
},
'applepay.server_supported' => static function ( ContainerInterface $container ): bool {
'applepay.server_supported' => static function ( ContainerInterface $container ): bool {
return ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off';
},
'applepay.is_browser_supported' => static function ( ContainerInterface $container ): bool {
'applepay.is_browser_supported' => static function ( ContainerInterface $container ): bool {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' );
if ( $user_agent ) {
@ -127,7 +128,7 @@ return array(
}
return false;
},
'applepay.url' => static function ( ContainerInterface $container ): string {
'applepay.url' => static function ( ContainerInterface $container ): string {
$path = realpath( __FILE__ );
if ( false === $path ) {
return '';
@ -137,13 +138,13 @@ return array(
dirname( $path, 3 ) . '/woocommerce-paypal-payments.php'
);
},
'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string {
'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string {
return 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js';
},
'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts {
'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts {
return new DataToAppleButtonScripts( $container->get( 'applepay.sdk_script_url' ), $container->get( 'wcgateway.settings' ) );
},
'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton {
'applepay.button' => static function ( ContainerInterface $container ): ApplePayButton {
return new ApplePayButton(
$container->get( 'wcgateway.settings' ),
$container->get( 'woocommerce.logger.woocommerce' ),
@ -155,7 +156,7 @@ return array(
$container->get( 'button.helper.cart-products' )
);
},
'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface {
'applepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface {
return new BlocksPaymentMethod(
'ppcp-applepay',
$container->get( 'applepay.url' ),
@ -164,781 +165,103 @@ 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
);
},
'applepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string {
/**
* 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
);
},
'applepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string {
return 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY';
},
'applepay.enable-url-live' => static function ( ContainerInterface $container ): string {
'applepay.enable-url-live' => static function ( ContainerInterface $container ): string {
return 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY';
},
'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() < State::STATE_ONBOARDED ) {
return '';
@ -976,5 +299,16 @@ return array(
esc_html( $button_text )
);
},
'applepay.wc-gateway' => static function ( ContainerInterface $container ): ApplePayGateway {
return new ApplePayGateway(
$container->get( 'wcgateway.order-processor' ),
$container->get( 'api.factory.paypal-checkout-url' ),
$container->get( 'wcgateway.processor.refunds' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'session.handler' ),
$container->get( 'applepay.url' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
);

View file

@ -0,0 +1,242 @@
<?php
/**
* The Apple Pay Payment Gateway
*
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Applepay;
use Exception;
use Psr\Log\LoggerInterface;
use WC_Order;
use WC_Payment_Gateway;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages;
/**
* Class ApplePayGateway
*/
class ApplePayGateway extends WC_Payment_Gateway {
use ProcessPaymentTrait;
const ID = 'ppcp-applepay';
/**
* The processor for orders.
*
* @var OrderProcessor
*/
protected $order_processor;
/**
* The function return the PayPal checkout URL for the given order ID.
*
* @var callable(string):string
*/
private $paypal_checkout_url_factory;
/**
* The Refund Processor.
*
* @var RefundProcessor
*/
private $refund_processor;
/**
* Service able to provide transaction url for an order.
*
* @var TransactionUrlProvider
*/
protected $transaction_url_provider;
/**
* The Session Handler.
*
* @var SessionHandler
*/
protected $session_handler;
/**
* The URL to the module.
*
* @var string
*/
private $module_url;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* ApplePayGateway constructor.
*
* @param OrderProcessor $order_processor The Order Processor.
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal
* checkout URL for the given order
* ID.
* @param RefundProcessor $refund_processor The Refund Processor.
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction
* view URL based on order.
* @param SessionHandler $session_handler The Session Handler.
* @param string $module_url The URL to the module.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
OrderProcessor $order_processor,
callable $paypal_checkout_url_factory,
RefundProcessor $refund_processor,
TransactionUrlProvider $transaction_url_provider,
SessionHandler $session_handler,
string $module_url,
LoggerInterface $logger
) {
$this->id = self::ID;
$this->method_title = __( 'Apple Pay (via PayPal) ', 'woocommerce-paypal-payments' );
$this->method_description = __( 'Display Apple Pay as a standalone payment option instead of bundling it with PayPal.', 'woocommerce-paypal-payments' );
$this->title = $this->get_option( 'title', __( 'Apple Pay', 'woocommerce-paypal-payments' ) );
$this->description = $this->get_option( 'description', '' );
$this->module_url = $module_url;
$this->icon = esc_url( $this->module_url ) . 'assets/images/applepay.svg';
$this->init_form_fields();
$this->init_settings();
$this->order_processor = $order_processor;
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;
$this->refund_processor = $refund_processor;
$this->transaction_url_provider = $transaction_url_provider;
$this->session_handler = $session_handler;
$this->logger = $logger;
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array( $this, 'process_admin_options' )
);
}
/**
* Initialize the form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'label' => __( 'Enable Apple Pay', 'woocommerce-paypal-payments' ),
'default' => 'no',
'desc_tip' => true,
'description' => __( 'Enable/Disable Apple Pay payment gateway.', 'woocommerce-paypal-payments' ),
),
'title' => array(
'title' => __( 'Title', 'woocommerce-paypal-payments' ),
'type' => 'text',
'default' => $this->title,
'desc_tip' => true,
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-paypal-payments' ),
),
'description' => array(
'title' => __( 'Description', 'woocommerce-paypal-payments' ),
'type' => 'text',
'default' => $this->description,
'desc_tip' => true,
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-paypal-payments' ),
),
);
}
/**
* Process payment for a WooCommerce order.
*
* @param int $order_id The WooCommerce order id.
*
* @return array
*/
public function process_payment( $order_id ) : array {
$wc_order = wc_get_order( $order_id );
if ( ! is_a( $wc_order, WC_Order::class ) ) {
return $this->handle_payment_failure(
null,
new GatewayGenericException( new Exception( 'WC order was not found.' ) )
);
}
do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order );
try {
try {
$this->order_processor->process( $wc_order );
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
return $this->handle_payment_success( $wc_order );
} catch ( PayPalOrderMissingException $exc ) {
$order = $this->order_processor->create_order( $wc_order );
return array(
'result' => 'success',
'redirect' => ( $this->paypal_checkout_url_factory )( $order->id() ),
);
}
} catch ( PayPalApiException $error ) {
return $this->handle_payment_failure(
$wc_order,
new Exception(
Messages::generic_payment_error_message() . ' ' . $error->getMessage(),
$error->getCode(),
$error
)
);
} catch ( Exception $error ) {
return $this->handle_payment_failure( $wc_order, $error );
}
}
/**
* Process refund.
*
* If the gateway declares 'refunds' support, this will allow it to refund.
* a passed in amount.
*
* @param int $order_id Order ID.
* @param float $amount Refund amount.
* @param string $reason Refund reason.
*
* @return boolean True or false based on success, or a WP_Error object.
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) : bool {
$order = wc_get_order( $order_id );
if ( ! is_a( $order, WC_Order::class ) ) {
return false;
}
return $this->refund_processor->process( $order, (float) $amount, (string) $reason );
}
/**
* Return transaction url for this gateway and given order.
*
* @param WC_Order $order WC order to get transaction url by.
*
* @return string
*/
public function get_transaction_url( $order ) : string {
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
return parent::get_transaction_url( $order );
}
}

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use WC_Payment_Gateway;
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
@ -17,30 +18,37 @@ use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
* Class ApplepayModule
*/
class ApplepayModule implements ModuleInterface {
class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule {
use ModuleClassNameIdTrait;
/**
* {@inheritDoc}
*/
public function setup(): ServiceProviderInterface {
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
public function services(): array {
return require __DIR__ . '/../services.php';
}
/**
* {@inheritDoc}
*/
public function run( ContainerInterface $c ): void {
public function extensions(): array {
return require __DIR__ . '/../extensions.php';
}
/**
* {@inheritDoc}
*/
public function run( ContainerInterface $c ): bool {
$module = $this;
// Clears product status when appropriate.
@ -117,14 +125,50 @@ class ApplepayModule implements ModuleInterface {
100,
2
);
}
/**
* Returns the key for the module.
*
* @return string|void
*/
public function getKey() {
add_filter(
'woocommerce_payment_gateways',
/**
* Param types removed to avoid third-party issues.
*
* @psalm-suppress MissingClosureParamType
*/
static function ( $methods ) use ( $c ): array {
if ( ! is_array( $methods ) ) {
return $methods;
}
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
if ( $settings->has( 'applepay_button_enabled' ) && $settings->get( 'applepay_button_enabled' ) ) {
$applepay_gateway = $c->get( 'applepay.wc-gateway' );
assert( $applepay_gateway instanceof WC_Payment_Gateway );
$methods[] = $applepay_gateway;
}
return $methods;
}
);
add_action(
'woocommerce_review_order_after_submit',
function () {
// Wrapper ID: #ppc-button-ppcp-applepay.
echo '<div id="ppc-button-' . esc_attr( ApplePayGateway::ID ) . '"></div>';
}
);
add_action(
'woocommerce_pay_order_after_submit',
function () {
// Wrapper ID: #ppc-button-ppcp-applepay.
echo '<div id="ppc-button-' . esc_attr( ApplePayGateway::ID ) . '"></div>';
}
);
return true;
}
/**
@ -306,7 +350,7 @@ class ApplepayModule implements ModuleInterface {
* @param bool $is_sandbox The environment for this merchant.
* @return string
*/
public function validation_string( bool $is_sandbox ) {
public function validation_string( bool $is_sandbox ) : string {
$sandbox_string = $this->sandbox_validation_string();
$live_string = $this->live_validation_string();
return $is_sandbox ? $sandbox_string : $live_string;

View file

@ -20,12 +20,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
/**
* Class ApplePayButton
*/
class ApplePayButton implements ButtonInterface {
use RequestHandlerTrait;
use RequestHandlerTrait, ContextTrait;
/**
* The settings.
@ -340,7 +341,7 @@ class ApplePayButton implements ButtonInterface {
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
} catch ( \Exception $e ) {
} catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
@ -382,7 +383,7 @@ class ApplePayButton implements ButtonInterface {
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
} catch ( \Exception $e ) {
} catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
@ -399,7 +400,7 @@ class ApplePayButton implements ButtonInterface {
* On error returns an array of errors to be handled by the script
* On success returns the new order data
*
* @throws \Exception When validation fails.
* @throws Exception When validation fails.
*/
public function create_wc_order(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
@ -420,15 +421,18 @@ class ApplePayButton implements ButtonInterface {
$applepay_request_data_object->order_data( $context );
$this->update_posted_data( $applepay_request_data_object );
if ( $context === 'product' ) {
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
$cart = WC()->cart;
$address = $applepay_request_data_object->shipping_address();
$this->calculate_totals_single_product(
$cart,
$address,
$applepay_request_data_object->shipping_method()
);
if ( ! $cart_item_key ) {
$this->response_templates->response_with_data_errors(
array(
@ -438,19 +442,16 @@ class ApplePayButton implements ButtonInterface {
),
)
);
return;
}
add_filter(
'woocommerce_payment_successful_result',
function ( array $result ) use ( $cart, $cart_item_key ) : array {
if ( ! is_string( $cart_item_key ) ) {
} else {
add_filter(
'woocommerce_payment_successful_result',
function ( array $result ) use ( $cart, $cart_item_key ) : array {
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
return $result;
}
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
return $result;
}
);
);
}
}
WC()->checkout()->process_checkout();
@ -460,17 +461,20 @@ class ApplePayButton implements ButtonInterface {
/**
* Checks if the nonce in the data object is valid
*
* @return bool|int
* @return bool
*/
protected function is_nonce_valid(): bool {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $nonce ) {
return false;
}
return wp_verify_nonce(
// Return value 1 indicates "valid nonce, generated in past 12 hours".
// Return value 2 also indicated valid nonce, but older than 12 hours.
return 1 === wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
) === 1;
);
}
/**
@ -511,7 +515,7 @@ class ApplePayButton implements ButtonInterface {
$address,
$applepay_request_data_object->shipping_method()
);
if ( is_string( $cart_item_key ) ) {
if ( $cart_item_key ) {
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
}
@ -819,9 +823,9 @@ class ApplePayButton implements ButtonInterface {
/**
* Removes the old cart, saves it, and creates a new one
*
* @throws Exception If it cannot be added to cart.
* @param ApplePayDataObjectHttp $applepay_request_data_object The request data object.
* @return bool | string The cart item key after adding to the new cart.
* @throws \Exception If it cannot be added to cart.
* @return string The cart item key after adding to the new cart.
*/
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
$this->save_old_cart();
@ -838,7 +842,7 @@ class ApplePayButton implements ButtonInterface {
);
$this->cart_products->add_products( array( $product ) );
return $this->cart_products->cart_item_keys()[0];
return $this->cart_products->cart_item_keys()[0] ?? '';
}
/**
@ -949,6 +953,7 @@ class ApplePayButton implements ButtonInterface {
$render_placeholder,
function () {
$this->applepay_button();
$this->hide_gateway_until_eligible();
},
21
);
@ -961,6 +966,7 @@ class ApplePayButton implements ButtonInterface {
$render_placeholder,
function () {
$this->applepay_button();
$this->hide_gateway_until_eligible();
},
21
);
@ -973,7 +979,7 @@ class ApplePayButton implements ButtonInterface {
add_action(
$render_placeholder,
function () {
echo '<span id="applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
echo '<span id="ppc-button-applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
},
21
);
@ -981,24 +987,29 @@ class ApplePayButton implements ButtonInterface {
return true;
}
/**
* ApplePay button markup
*/
protected function applepay_button(): void {
?>
<div id="applepay-container" class="ppcp-button-apm ppcp-button-applepay">
<div id="ppc-button-applepay-container" class="ppcp-button-apm ppcp-button-applepay">
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
</div>
<?php
}
/**
* Checks if the module should load the script.
* Outputs an inline CSS style that hides the Apple Pay gateway (on Classic Checkout).
* The style is removed by `ApplepayButton.js` once the eligibility of the payment method
* is confirmed.
*
* @return bool
* @return void
*/
public function should_load_script(): bool {
return true;
protected function hide_gateway_until_eligible(): void {
?>
<style id="ppcp-hide-apple-pay">.wc_payment_method.payment_method_ppcp-applepay{display:none}</style>
<?php
}
/**

View file

@ -5,12 +5,14 @@
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare(strict_types=1);
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Applepay\Assets;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
use WC_Product;
/**
* Class DataToAppleButtonScripts
@ -33,7 +35,7 @@ class DataToAppleButtonScripts {
/**
* DataToAppleButtonScripts constructor.
*
* @param string $sdk_url The URL to the SDK.
* @param string $sdk_url The URL to the SDK.
* @param Settings $settings The settings.
*/
public function __construct( string $sdk_url, Settings $settings ) {
@ -45,57 +47,92 @@ class DataToAppleButtonScripts {
* Sets the appropriate data to send to ApplePay script
* Data differs between product page and cart page
*
* @param bool $is_block Whether the button is in a block or not.
* @return array
* @throws NotFoundException When the setting is not found.
*/
public function apple_pay_script_data( bool $is_block = false ): array {
$base_location = wc_get_base_location();
$shop_country_code = $base_location['country'];
$currency_code = get_woocommerce_currency();
$total_label = get_bloginfo( 'name' );
public function apple_pay_script_data() : array {
if ( is_product() ) {
return $this->data_for_product_page(
$shop_country_code,
$currency_code,
$total_label
);
return $this->data_for_product_page();
}
return $this->data_for_cart_page(
$shop_country_code,
$currency_code,
$total_label
);
return $this->data_for_cart_page();
}
/**
* Returns the appropriate admin data to send to ApplePay script
*
* @return array
* @throws NotFoundException When the setting is not found.
*/
public function apple_pay_script_data_for_admin() : array {
return $this->data_for_admin_page();
}
/**
* Returns the full config array for the Apple Pay integration with default values.
*
* @param array $product - Optional. Product details for the payment button.
*
* @return array
*/
private function get_apple_pay_data( array $product = array() ) : array {
// true: Use Apple Pay as distinct gateway.
// false: integrate it with the smart buttons.
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$is_wc_gateway_enabled = isset( $available_gateways[ ApplePayGateway::ID ] );
// use_wc: Use WC checkout data
// use_applepay: Use data provided by Apple Pay.
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' )
? $this->settings->get( 'applepay_checkout_data_mode' )
: PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
// Store country, currency and name.
$base_location = wc_get_base_location();
$shop_country_code = $base_location['country'];
$currency_code = get_woocommerce_currency();
$total_label = get_bloginfo( 'name' );
return $this->data_for_admin_page(
$shop_country_code,
$currency_code,
$total_label
// Button layout (label, color, language).
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
$is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
return array(
'sdk_url' => $this->sdk_url,
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'is_admin' => false,
'is_enabled' => $is_enabled,
'is_wc_gateway_enabled' => $is_wc_gateway_enabled,
'preferences' => array(
'checkout_data_mode' => $checkout_data_mode,
),
'button' => array(
'wrapper' => 'ppc-button-applepay-container',
'mini_cart_wrapper' => 'ppc-button-applepay-container-minicart',
'type' => $type,
'color' => $color,
'lang' => $lang,
),
'product' => $product,
'shop' => array(
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
);
}
/**
* Check if the product needs shipping
*
* @param \WC_Product $product The product.
* @param WC_Product $product Product to check.
*
* @return bool
*/
protected function check_if_need_shipping( $product ) {
protected function check_if_need_shipping( WC_Product $product ) : bool {
if (
! wc_shipping_enabled()
|| 0 === wc_get_shipping_method_count(
@ -104,30 +141,20 @@ class DataToAppleButtonScripts {
) {
return false;
}
$needs_shipping = false;
if ( $product->needs_shipping() ) {
$needs_shipping = true;
return true;
}
return $needs_shipping;
return false;
}
/**
* Prepares the data for the product page.
*
* @param string $shop_country_code The shop country code.
* @param string $currency_code The currency code.
* @param string $total_label The label for the total amount.
*
* @return array
* @throws NotFoundException When the setting is not found.
*/
protected function data_for_product_page(
$shop_country_code,
$currency_code,
$total_label
) {
protected function data_for_product_page() : array {
$product = wc_get_product( get_the_id() );
if ( ! $product ) {
return array();
@ -136,146 +163,59 @@ class DataToAppleButtonScripts {
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
$is_variation = true;
}
$product_need_shipping = $this->check_if_need_shipping( $product );
$product_id = get_the_id();
$product_price = $product->get_price();
$product_stock = $product->get_stock_status();
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
return array(
'sdk_url' => $this->sdk_url,
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
'is_admin' => false,
'preferences' => array(
'checkout_data_mode' => $checkout_data_mode,
),
'button' => array(
'wrapper' => 'applepay-container',
'mini_cart_wrapper' => 'applepay-container-minicart',
'type' => $type,
'color' => $color,
'lang' => $lang,
),
'product' => array(
return $this->get_apple_pay_data(
array(
'needShipping' => $product_need_shipping,
'id' => $product_id,
'price' => $product_price,
'isVariation' => $is_variation,
'stock' => $product_stock,
),
'shop' => array(
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
)
);
}
/**
* Prepares the data for the cart page.
*
* @param string $shop_country_code The shop country code.
* @param string $currency_code The currency code.
* @param string $total_label The label for the total amount.
*
* @return array
*/
protected function data_for_cart_page(
$shop_country_code,
$currency_code,
$total_label
) {
protected function data_for_cart_page() : array {
$cart = WC()->cart;
if ( ! $cart ) {
return array();
}
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
return array(
'sdk_url' => $this->sdk_url,
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
'is_admin' => false,
'preferences' => array(
'checkout_data_mode' => $checkout_data_mode,
),
'button' => array(
'wrapper' => 'applepay-container',
'mini_cart_wrapper' => 'applepay-container-minicart',
'type' => $type,
'color' => $color,
'lang' => $lang,
),
'product' => array(
return $this->get_apple_pay_data(
array(
'needShipping' => $cart->needs_shipping(),
'subtotal' => $cart->get_subtotal(),
),
'shop' => array(
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
)
);
}
/**
* Prepares the data for the cart page.
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page() methods.
*
* @param string $shop_country_code The shop country code.
* @param string $currency_code The currency code.
* @param string $total_label The label for the total amount.
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page()
* methods.
*
* @return array
*/
protected function data_for_admin_page(
$shop_country_code,
$currency_code,
$total_label
) {
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
$is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
return array(
'sdk_url' => $this->sdk_url,
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'is_admin' => true,
'is_enabled' => $is_enabled,
'preferences' => array(
'checkout_data_mode' => $checkout_data_mode,
),
'button' => array(
'wrapper' => 'applepay-container',
'mini_cart_wrapper' => 'applepay-container-minicart',
'type' => $type,
'color' => $color,
'lang' => $lang,
),
'product' => array(
protected function data_for_admin_page() : array {
$data = $this->get_apple_pay_data(
array(
'needShipping' => false,
'subtotal' => 0,
),
'shop' => array(
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
)
);
$data['is_admin'] = true;
return $data;
}
}

View file

@ -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->currency = $currency;
$this->country = $country;
$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;
}
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
public function for_country(): bool {
return in_array( $this->country, $this->allowed_countries, 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 );
}
}