mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-03 08:37:53 +08:00
Merge pull request #2457 from woocommerce/PCP-3321-separate-apple-pay-button-for-classic-checkout
Separate Apple Pay button for Classic Checkout (3321)
This commit is contained in:
commit
fbd7dc1857
19 changed files with 929 additions and 394 deletions
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
|
"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"wc": true
|
"wc": true,
|
||||||
|
"jQuery": "readonly"
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": ["error", { "allow": ["warn", "error"] }]
|
"no-console": ["error", { "allow": ["warn", "error"] }]
|
||||||
|
|
|
@ -52,3 +52,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ppc-button-ppcp-applepay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
/* global ApplePaySession */
|
||||||
|
/* global PayPalCommerceGateway */
|
||||||
|
|
||||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||||
import { createAppleErrors } from './Helper/applePayError';
|
import { createAppleErrors } from './Helper/applePayError';
|
||||||
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
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 widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
|
||||||
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
||||||
|
|
||||||
class ApplepayButton {
|
/**
|
||||||
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
|
* Plugin-specific styling.
|
||||||
apmButtonsInit( ppcpConfig );
|
*
|
||||||
|
* 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.context = context;
|
||||||
this.externalHandler = externalHandler;
|
this.externalHandler = externalHandler;
|
||||||
this.buttonConfig = buttonConfig;
|
this.buttonConfig = buttonConfig;
|
||||||
this.ppcpConfig = ppcpConfig;
|
this.ppcpConfig = ppcpConfig;
|
||||||
this.paymentsClient = null;
|
|
||||||
this.formData = null;
|
|
||||||
|
|
||||||
this.contextHandler = ContextHandlerFactory.create(
|
this.contextHandler = ContextHandlerFactory.create(
|
||||||
this.context,
|
this.context,
|
||||||
|
@ -26,36 +106,226 @@ class ApplepayButton {
|
||||||
this.ppcpConfig
|
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();
|
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 = document.ppcpApplepayButtons || {};
|
||||||
document.ppcpApplepayButtons[ this.context ] = this;
|
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 ) {
|
init( config ) {
|
||||||
if ( this.isInitialized ) {
|
if ( this.#isInitialized ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,41 +333,35 @@ class ApplepayButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log( 'Init', this.context );
|
this.log( 'Init' );
|
||||||
this.initEventHandlers();
|
this.initEventHandlers();
|
||||||
this.isInitialized = true;
|
|
||||||
|
this.#isInitialized = true;
|
||||||
this.applePayConfig = config;
|
this.applePayConfig = config;
|
||||||
this.isEligible =
|
|
||||||
( this.applePayConfig.isEligible && window.ApplePaySession ) ||
|
|
||||||
this.buttonConfig.is_admin;
|
|
||||||
|
|
||||||
if ( this.isEligible ) {
|
if ( ! this.isEligible ) {
|
||||||
this.fetchTransactionInfo().then( () => {
|
this.hide();
|
||||||
this.addButton();
|
|
||||||
const id_minicart =
|
|
||||||
'#apple-' + this.buttonConfig.button.mini_cart_wrapper;
|
|
||||||
const id = '#apple-' + this.buttonConfig.button.wrapper;
|
|
||||||
|
|
||||||
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();
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
} else {
|
} else {
|
||||||
jQuery( '#' + this.buttonConfig.button.wrapper ).hide();
|
// Bail if the button wrapper is not present; handles mini-cart logic on checkout page.
|
||||||
jQuery( '#' + this.buttonConfig.button.mini_cart_wrapper ).hide();
|
if ( ! this.isPresent ) {
|
||||||
jQuery( '#express-payment-method-ppcp-applepay' ).hide();
|
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 +370,51 @@ class ApplepayButton {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isInitialized = false;
|
this.#isInitialized = false;
|
||||||
this.init( this.applePayConfig );
|
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() {
|
async fetchTransactionInfo() {
|
||||||
this.transactionInfo = await this.contextHandler.transactionInfo();
|
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() {
|
initEventHandlers() {
|
||||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
const ppcpButtonWrapper = `#${ this.ppcpButtonWrapperId }`;
|
||||||
const wrapper_id = '#' + wrapper;
|
const wrapperId = `#${ this.wrapperId }`;
|
||||||
|
|
||||||
if ( wrapper_id === ppcpButtonWrapper ) {
|
if ( wrapperId === ppcpButtonWrapper ) {
|
||||||
throw new Error(
|
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 +424,9 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
||||||
setVisible( wrapper_id, $ppcpButtonWrapper.is( ':visible' ) );
|
setVisible( wrapperId, $ppcpButtonWrapper.is( ':visible' ) );
|
||||||
setEnabled(
|
setEnabled(
|
||||||
wrapper_id,
|
wrapperId,
|
||||||
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -178,8 +444,9 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an ApplePay session.
|
* Starts an Apple Pay session.
|
||||||
* @param paymentRequest
|
*
|
||||||
|
* @param {Object} paymentRequest The payment request object.
|
||||||
*/
|
*/
|
||||||
applePaySession( paymentRequest ) {
|
applePaySession( paymentRequest ) {
|
||||||
this.log( 'applePaySession', paymentRequest );
|
this.log( 'applePaySession', paymentRequest );
|
||||||
|
@ -192,6 +459,7 @@ class ApplepayButton {
|
||||||
session.onshippingcontactselected =
|
session.onshippingcontactselected =
|
||||||
this.onShippingContactSelected( session );
|
this.onShippingContactSelected( session );
|
||||||
}
|
}
|
||||||
|
|
||||||
session.onvalidatemerchant = this.onValidateMerchant( session );
|
session.onvalidatemerchant = this.onValidateMerchant( session );
|
||||||
session.onpaymentauthorized = this.onPaymentAuthorized( session );
|
session.onpaymentauthorized = this.onPaymentAuthorized( session );
|
||||||
return session;
|
return session;
|
||||||
|
@ -199,32 +467,38 @@ class ApplepayButton {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an Apple Pay purchase button.
|
* Adds an Apple Pay purchase button.
|
||||||
|
*
|
||||||
|
* @return {HTMLElement|null} The newly created `<apple-pay-button>` element. Null on failure.
|
||||||
*/
|
*/
|
||||||
addButton() {
|
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 );
|
if ( ! wrapper ) {
|
||||||
const type = this.buttonConfig.button.type;
|
return null;
|
||||||
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 }">`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const $wrapper = jQuery( '#' + wrapper );
|
const ppcpStyle = this.ppcpStyle;
|
||||||
$wrapper.addClass( 'ppcp-button-' + ppcpStyle.shape );
|
|
||||||
|
wrapper.innerHTML = `<apple-pay-button id='${ id }' buttonstyle='${ style.color }' type='${ style.type }' locale='${ style.lang }' />`;
|
||||||
|
wrapper.classList.add(
|
||||||
|
`ppcp-button-${ ppcpStyle.shape }`,
|
||||||
|
'ppcp-button-apm',
|
||||||
|
'ppcp-button-applepay'
|
||||||
|
);
|
||||||
|
|
||||||
if ( ppcpStyle.height ) {
|
if ( ppcpStyle.height ) {
|
||||||
$wrapper.css(
|
wrapper.style.setProperty(
|
||||||
'--apple-pay-button-height',
|
'--apple-pay-button-height',
|
||||||
`${ ppcpStyle.height }px`
|
`${ ppcpStyle.height }px`
|
||||||
);
|
);
|
||||||
$wrapper.css( 'height', `${ ppcpStyle.height }px` );
|
wrapper.style.height = `${ ppcpStyle.height }px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return wrapper.querySelector( 'apple-pay-button' );
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------
|
//------------------------
|
||||||
|
@ -235,19 +509,21 @@ class ApplepayButton {
|
||||||
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
|
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
|
||||||
*/
|
*/
|
||||||
async onButtonClick() {
|
async onButtonClick() {
|
||||||
this.log( 'onButtonClick', this.context );
|
this.log( 'onButtonClick' );
|
||||||
|
|
||||||
const paymentRequest = this.paymentRequest();
|
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.
|
// 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 checkoutFormSelector = 'form.woocommerce-checkout';
|
||||||
const errorHandler = new ErrorHandler(
|
const errorHandler = new ErrorHandler(
|
||||||
PayPalCommerceGateway.labels.error.generic,
|
PayPalCommerceGateway.labels.error.generic,
|
||||||
document.querySelector( '.woocommerce-notices-wrapper' )
|
document.querySelector( '.woocommerce-notices-wrapper' )
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData(
|
const formData = new FormData(
|
||||||
document.querySelector( checkoutFormSelector )
|
document.querySelector( checkoutFormSelector )
|
||||||
|
@ -269,6 +545,7 @@ class ApplepayButton {
|
||||||
PayPalCommerceGateway.ajax.validate_checkout.nonce
|
PayPalCommerceGateway.ajax.validate_checkout.nonce
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if ( formValidator ) {
|
if ( formValidator ) {
|
||||||
try {
|
try {
|
||||||
const errors = await formValidator.validate(
|
const errors = await formValidator.validate(
|
||||||
|
@ -296,13 +573,13 @@ class ApplepayButton {
|
||||||
/**
|
/**
|
||||||
* If the button should show the shipping fields.
|
* If the button should show the shipping fields.
|
||||||
*
|
*
|
||||||
* @return {false|*}
|
* @return {boolean} True, if shipping fields should be captured by ApplePay.
|
||||||
*/
|
*/
|
||||||
shouldRequireShippingInButton() {
|
shouldRequireShippingInButton() {
|
||||||
return (
|
return (
|
||||||
this.contextHandler.shippingAllowed() &&
|
this.contextHandler.shippingAllowed() &&
|
||||||
this.buttonConfig.product.needShipping &&
|
this.buttonConfig.product.needShipping &&
|
||||||
( this.context !== 'checkout' ||
|
( CONTEXT.Checkout !== this.context ||
|
||||||
this.shouldUpdateButtonWithFormData() )
|
this.shouldUpdateButtonWithFormData() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -310,10 +587,10 @@ class ApplepayButton {
|
||||||
/**
|
/**
|
||||||
* If the button should be updated with the form addresses.
|
* 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() {
|
shouldUpdateButtonWithFormData() {
|
||||||
if ( this.context !== 'checkout' ) {
|
if ( CONTEXT.Checkout !== this.context ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -323,29 +600,28 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates how payment completion should be handled if with the context handler default actions.
|
* Indicates how payment completion should be handled if with the context handler default
|
||||||
* Or with ApplePay module specific completion.
|
* actions. Or with Apple Pay module specific completion.
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @return {boolean} True, when the Apple Pay data should be submitted to WooCommerce.
|
||||||
*/
|
*/
|
||||||
shouldCompletePaymentWithContextHandler() {
|
shouldCompletePaymentWithContextHandler() {
|
||||||
// Data already handled, ex: PayNow
|
// Data already handled, ex: PayNow
|
||||||
if ( ! this.contextHandler.shippingAllowed() ) {
|
if ( ! this.contextHandler.shippingAllowed() ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use WC form data mode in Checkout.
|
// Use WC form data mode in Checkout.
|
||||||
if (
|
return (
|
||||||
this.context === 'checkout' &&
|
CONTEXT.Checkout === this.context &&
|
||||||
! this.shouldUpdateButtonWithFormData()
|
! this.shouldUpdateButtonWithFormData()
|
||||||
) {
|
);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates ApplePay paymentRequest with form data.
|
* Updates Apple Pay paymentRequest with form data.
|
||||||
* @param paymentRequest
|
*
|
||||||
|
* @param {Object} paymentRequest Object to extend with form data.
|
||||||
*/
|
*/
|
||||||
updateRequestDataWithForm( paymentRequest ) {
|
updateRequestDataWithForm( paymentRequest ) {
|
||||||
if ( ! this.shouldUpdateButtonWithFormData() ) {
|
if ( ! this.shouldUpdateButtonWithFormData() ) {
|
||||||
|
@ -358,8 +634,9 @@ class ApplepayButton {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add custom data.
|
// Add custom data.
|
||||||
// "applicationData" is originating a "PayPalApplePayError: An internal server error has occurred" on paypal.Applepay().confirmOrder().
|
// "applicationData" is originating a "PayPalApplePayError: An internal server error has
|
||||||
// paymentRequest.applicationData = this.fillApplicationData(this.formData);
|
// occurred" on paypal.Applepay().confirmOrder(). paymentRequest.applicationData =
|
||||||
|
// this.fillApplicationData(this.formData);
|
||||||
|
|
||||||
if ( ! this.shouldRequireShippingInButton() ) {
|
if ( ! this.shouldRequireShippingInButton() ) {
|
||||||
return;
|
return;
|
||||||
|
@ -425,7 +702,8 @@ class ApplepayButton {
|
||||||
'email',
|
'email',
|
||||||
'phone',
|
'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() ) {
|
if ( ! this.shouldRequireShippingInButton() ) {
|
||||||
|
@ -453,14 +731,11 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshContextData() {
|
refreshContextData() {
|
||||||
switch ( this.context ) {
|
if ( CONTEXT.Product === this.context ) {
|
||||||
case 'product':
|
// Refresh product data that makes the price change.
|
||||||
// Refresh product data that makes the price change.
|
this.productQuantity = document.querySelector( 'input.qty' )?.value;
|
||||||
this.productQuantity =
|
this.products = this.contextHandler.products();
|
||||||
document.querySelector( 'input.qty' )?.value;
|
this.log( 'Products updated', this.products );
|
||||||
this.products = this.contextHandler.products();
|
|
||||||
this.log( 'Products updated', this.products );
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,8 +743,36 @@ class ApplepayButton {
|
||||||
// Payment process
|
// 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 ) {
|
onValidateMerchant( session ) {
|
||||||
this.log( 'onvalidatemerchant', this.buttonConfig.ajax_url );
|
|
||||||
return ( applePayValidateMerchantEvent ) => {
|
return ( applePayValidateMerchantEvent ) => {
|
||||||
this.log( 'onvalidatemerchant call' );
|
this.log( 'onvalidatemerchant call' );
|
||||||
|
|
||||||
|
@ -479,34 +782,15 @@ class ApplepayButton {
|
||||||
validationUrl: applePayValidateMerchantEvent.validationURL,
|
validationUrl: applePayValidateMerchantEvent.validationURL,
|
||||||
} )
|
} )
|
||||||
.then( ( validateResult ) => {
|
.then( ( validateResult ) => {
|
||||||
this.log( 'onvalidatemerchant ok' );
|
|
||||||
session.completeMerchantValidation(
|
session.completeMerchantValidation(
|
||||||
validateResult.merchantSession
|
validateResult.merchantSession
|
||||||
);
|
);
|
||||||
//call backend to update validation to true
|
|
||||||
jQuery.ajax( {
|
this.adminValidation( true );
|
||||||
url: this.buttonConfig.ajax_url,
|
|
||||||
type: 'POST',
|
|
||||||
data: {
|
|
||||||
action: 'ppcp_validate',
|
|
||||||
validation: true,
|
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
} )
|
} )
|
||||||
.catch( ( validateError ) => {
|
.catch( ( validateError ) => {
|
||||||
this.log( 'onvalidatemerchant error', validateError );
|
|
||||||
console.error( validateError );
|
console.error( validateError );
|
||||||
//call backend to update validation to false
|
this.adminValidation( false );
|
||||||
jQuery.ajax( {
|
|
||||||
url: this.buttonConfig.ajax_url,
|
|
||||||
type: 'POST',
|
|
||||||
data: {
|
|
||||||
action: 'ppcp_validate',
|
|
||||||
validation: false,
|
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
this.log( 'onvalidatemerchant session abort' );
|
this.log( 'onvalidatemerchant session abort' );
|
||||||
session.abort();
|
session.abort();
|
||||||
} );
|
} );
|
||||||
|
@ -515,14 +799,14 @@ class ApplepayButton {
|
||||||
|
|
||||||
onShippingMethodSelected( session ) {
|
onShippingMethodSelected( session ) {
|
||||||
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
|
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
|
||||||
const ajax_url = this.buttonConfig.ajax_url;
|
const ajaxUrl = this.buttonConfig.ajax_url;
|
||||||
return ( event ) => {
|
return ( event ) => {
|
||||||
this.log( 'onshippingmethodselected call' );
|
this.log( 'onshippingmethodselected call' );
|
||||||
|
|
||||||
const data = this.getShippingMethodData( event );
|
const data = this.getShippingMethodData( event );
|
||||||
|
|
||||||
jQuery.ajax( {
|
jQuery.ajax( {
|
||||||
url: ajax_url,
|
url: ajaxUrl,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
success: (
|
success: (
|
||||||
|
@ -537,7 +821,8 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
this.selectedShippingMethod = event.shippingMethod;
|
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 =
|
||||||
response.newShippingMethods.sort( ( a, b ) => {
|
response.newShippingMethods.sort( ( a, b ) => {
|
||||||
if (
|
if (
|
||||||
|
@ -565,7 +850,7 @@ class ApplepayButton {
|
||||||
onShippingContactSelected( session ) {
|
onShippingContactSelected( session ) {
|
||||||
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
|
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
|
||||||
|
|
||||||
const ajax_url = this.buttonConfig.ajax_url;
|
const ajaxUrl = this.buttonConfig.ajax_url;
|
||||||
|
|
||||||
return ( event ) => {
|
return ( event ) => {
|
||||||
this.log( 'onshippingcontactselected call' );
|
this.log( 'onshippingcontactselected call' );
|
||||||
|
@ -573,7 +858,7 @@ class ApplepayButton {
|
||||||
const data = this.getShippingContactData( event );
|
const data = this.getShippingContactData( event );
|
||||||
|
|
||||||
jQuery.ajax( {
|
jQuery.ajax( {
|
||||||
url: ajax_url,
|
url: ajaxUrl,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
success: (
|
success: (
|
||||||
|
@ -603,15 +888,15 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
getShippingContactData( event ) {
|
getShippingContactData( event ) {
|
||||||
const product_id = this.buttonConfig.product.id;
|
const productId = this.buttonConfig.product.id;
|
||||||
|
|
||||||
this.refreshContextData();
|
this.refreshContextData();
|
||||||
|
|
||||||
switch ( this.context ) {
|
switch ( this.context ) {
|
||||||
case 'product':
|
case CONTEXT.Product:
|
||||||
return {
|
return {
|
||||||
action: 'ppcp_update_shipping_contact',
|
action: 'ppcp_update_shipping_contact',
|
||||||
product_id,
|
product_id: productId,
|
||||||
products: JSON.stringify( this.products ),
|
products: JSON.stringify( this.products ),
|
||||||
caller_page: 'productDetail',
|
caller_page: 'productDetail',
|
||||||
product_quantity: this.productQuantity,
|
product_quantity: this.productQuantity,
|
||||||
|
@ -619,11 +904,12 @@ class ApplepayButton {
|
||||||
need_shipping: this.shouldRequireShippingInButton(),
|
need_shipping: this.shouldRequireShippingInButton(),
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
};
|
};
|
||||||
case 'cart':
|
|
||||||
case 'checkout':
|
case CONTEXT.Cart:
|
||||||
case 'cart-block':
|
case CONTEXT.Checkout:
|
||||||
case 'checkout-block':
|
case CONTEXT.BlockCart:
|
||||||
case 'mini-cart':
|
case CONTEXT.BlockCheckout:
|
||||||
|
case CONTEXT.MiniCart:
|
||||||
return {
|
return {
|
||||||
action: 'ppcp_update_shipping_contact',
|
action: 'ppcp_update_shipping_contact',
|
||||||
simplified_contact: event.shippingContact,
|
simplified_contact: event.shippingContact,
|
||||||
|
@ -635,12 +921,12 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
getShippingMethodData( event ) {
|
getShippingMethodData( event ) {
|
||||||
const product_id = this.buttonConfig.product.id;
|
const productId = this.buttonConfig.product.id;
|
||||||
|
|
||||||
this.refreshContextData();
|
this.refreshContextData();
|
||||||
|
|
||||||
switch ( this.context ) {
|
switch ( this.context ) {
|
||||||
case 'product':
|
case CONTEXT.Product:
|
||||||
return {
|
return {
|
||||||
action: 'ppcp_update_shipping_method',
|
action: 'ppcp_update_shipping_method',
|
||||||
shipping_method: event.shippingMethod,
|
shipping_method: event.shippingMethod,
|
||||||
|
@ -650,17 +936,18 @@ class ApplepayButton {
|
||||||
? this.updatedContactInfo
|
? this.updatedContactInfo
|
||||||
: this.initialPaymentRequest?.shippingContact ??
|
: this.initialPaymentRequest?.shippingContact ??
|
||||||
this.initialPaymentRequest?.billingContact,
|
this.initialPaymentRequest?.billingContact,
|
||||||
product_id,
|
product_id: productId,
|
||||||
products: JSON.stringify( this.products ),
|
products: JSON.stringify( this.products ),
|
||||||
caller_page: 'productDetail',
|
caller_page: 'productDetail',
|
||||||
product_quantity: this.productQuantity,
|
product_quantity: this.productQuantity,
|
||||||
'woocommerce-process-checkout-nonce': this.nonce,
|
'woocommerce-process-checkout-nonce': this.nonce,
|
||||||
};
|
};
|
||||||
case 'cart':
|
|
||||||
case 'checkout':
|
case CONTEXT.Cart:
|
||||||
case 'cart-block':
|
case CONTEXT.Checkout:
|
||||||
case 'checkout-block':
|
case CONTEXT.BlockCart:
|
||||||
case 'mini-cart':
|
case CONTEXT.BlockCheckout:
|
||||||
|
case CONTEXT.MiniCart:
|
||||||
return {
|
return {
|
||||||
action: 'ppcp_update_shipping_method',
|
action: 'ppcp_update_shipping_method',
|
||||||
shipping_method: event.shippingMethod,
|
shipping_method: event.shippingMethod,
|
||||||
|
@ -681,9 +968,6 @@ class ApplepayButton {
|
||||||
return async ( event ) => {
|
return async ( event ) => {
|
||||||
this.log( 'onpaymentauthorized call' );
|
this.log( 'onpaymentauthorized call' );
|
||||||
|
|
||||||
function form() {
|
|
||||||
return document.querySelector( 'form.cart' );
|
|
||||||
}
|
|
||||||
const processInWooAndCapture = async ( data ) => {
|
const processInWooAndCapture = async ( data ) => {
|
||||||
return new Promise( ( resolve, reject ) => {
|
return new Promise( ( resolve, reject ) => {
|
||||||
try {
|
try {
|
||||||
|
@ -698,7 +982,7 @@ class ApplepayButton {
|
||||||
( this.initialPaymentRequest.shippingMethods ||
|
( this.initialPaymentRequest.shippingMethods ||
|
||||||
[] )[ 0 ];
|
[] )[ 0 ];
|
||||||
|
|
||||||
const request_data = {
|
const requestData = {
|
||||||
action: 'ppcp_create_order',
|
action: 'ppcp_create_order',
|
||||||
caller_page: this.context,
|
caller_page: this.context,
|
||||||
product_id: this.buttonConfig.product.id ?? null,
|
product_id: this.buttonConfig.product.id ?? null,
|
||||||
|
@ -723,7 +1007,7 @@ class ApplepayButton {
|
||||||
jQuery.ajax( {
|
jQuery.ajax( {
|
||||||
url: this.buttonConfig.ajax_url,
|
url: this.buttonConfig.ajax_url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: request_data,
|
data: requestData,
|
||||||
complete: ( jqXHR, textStatus ) => {
|
complete: ( jqXHR, textStatus ) => {
|
||||||
this.log( 'onpaymentauthorized complete' );
|
this.log( 'onpaymentauthorized complete' );
|
||||||
},
|
},
|
||||||
|
@ -785,7 +1069,8 @@ class ApplepayButton {
|
||||||
if (
|
if (
|
||||||
this.shouldCompletePaymentWithContextHandler()
|
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;
|
let approveFailed = false;
|
||||||
await this.contextHandler.approveOrder(
|
await this.contextHandler.approveOrder(
|
||||||
|
@ -960,4 +1245,4 @@ class ApplepayButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ApplepayButton;
|
export default ApplePayButton;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
/* global paypal */
|
||||||
import ApplepayButton from './ApplepayButton';
|
|
||||||
|
|
||||||
class ApplepayManager {
|
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||||
|
import ApplePayButton from './ApplepayButton';
|
||||||
|
|
||||||
|
class ApplePayManager {
|
||||||
constructor( buttonConfig, ppcpConfig ) {
|
constructor( buttonConfig, ppcpConfig ) {
|
||||||
this.buttonConfig = buttonConfig;
|
this.buttonConfig = buttonConfig;
|
||||||
this.ppcpConfig = ppcpConfig;
|
this.ppcpConfig = ppcpConfig;
|
||||||
|
@ -9,7 +11,7 @@ class ApplepayManager {
|
||||||
this.buttons = [];
|
this.buttons = [];
|
||||||
|
|
||||||
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
|
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
|
||||||
const button = new ApplepayButton(
|
const button = new ApplePayButton(
|
||||||
bootstrap.context,
|
bootstrap.context,
|
||||||
bootstrap.handler,
|
bootstrap.handler,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
|
@ -40,8 +42,7 @@ class ApplepayManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets ApplePay configuration of the PayPal merchant.
|
* Gets Apple Pay configuration of the PayPal merchant.
|
||||||
* @return {Promise<null>}
|
|
||||||
*/
|
*/
|
||||||
async config() {
|
async config() {
|
||||||
this.ApplePayConfig = await paypal.Applepay().config();
|
this.ApplePayConfig = await paypal.Applepay().config();
|
||||||
|
@ -49,4 +50,4 @@ class ApplepayManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ApplepayManager;
|
export default ApplePayManager;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import ApplepayButton from './ApplepayButton';
|
/* global paypal */
|
||||||
|
|
||||||
class ApplepayManagerBlockEditor {
|
import ApplePayButton from './ApplepayButton';
|
||||||
|
|
||||||
|
class ApplePayManagerBlockEditor {
|
||||||
constructor( buttonConfig, ppcpConfig ) {
|
constructor( buttonConfig, ppcpConfig ) {
|
||||||
this.buttonConfig = buttonConfig;
|
this.buttonConfig = buttonConfig;
|
||||||
this.ppcpConfig = ppcpConfig;
|
this.ppcpConfig = ppcpConfig;
|
||||||
|
@ -17,7 +19,7 @@ class ApplepayManagerBlockEditor {
|
||||||
try {
|
try {
|
||||||
this.applePayConfig = await paypal.Applepay().config();
|
this.applePayConfig = await paypal.Applepay().config();
|
||||||
|
|
||||||
const button = new ApplepayButton(
|
const button = new ApplePayButton(
|
||||||
this.ppcpConfig.context,
|
this.ppcpConfig.context,
|
||||||
null,
|
null,
|
||||||
this.buttonConfig,
|
this.buttonConfig,
|
||||||
|
@ -31,4 +33,4 @@ class ApplepayManagerBlockEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ApplepayManagerBlockEditor;
|
export default ApplePayManagerBlockEditor;
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import BaseHandler from './BaseHandler';
|
import BaseHandler from './BaseHandler';
|
||||||
|
|
||||||
class PreviewHandler extends BaseHandler {
|
class PreviewHandler extends BaseHandler {
|
||||||
constructor( buttonConfig, ppcpConfig, externalHandler ) {
|
|
||||||
super( buttonConfig, ppcpConfig, externalHandler );
|
|
||||||
}
|
|
||||||
|
|
||||||
transactionInfo() {
|
transactionInfo() {
|
||||||
// We need to return something as ApplePay button initialization expects valid data.
|
// We need to return something as ApplePay button initialization expects valid data.
|
||||||
return {
|
return {
|
||||||
|
@ -19,7 +15,7 @@ class PreviewHandler extends BaseHandler {
|
||||||
throw new Error( 'Create order fail. This is just a preview.' );
|
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.' );
|
throw new Error( 'Approve order fail. This is just a preview.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const buttonID = 'applepay-container';
|
export const buttonID = 'ppc-button-applepay-container';
|
||||||
export const endpoints = {
|
export const endpoints = {
|
||||||
validation: '_apple_pay_validation',
|
validation: '_apple_pay_validation',
|
||||||
createOrderCart: '_apple_pay_create_order_cart',
|
createOrderCart: '_apple_pay_create_order_cart',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import ApplepayButton from './ApplepayButton';
|
import ApplePayButton from './ApplepayButton';
|
||||||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||||
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class ApplePayPreviewButton extends PreviewButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
createButton( buttonConfig ) {
|
createButton( buttonConfig ) {
|
||||||
const button = new ApplepayButton(
|
const button = new ApplePayButton(
|
||||||
'preview',
|
'preview',
|
||||||
null,
|
null,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Help
|
||||||
import { cartHasSubscriptionProducts } from '../../../ppcp-blocks/resources/js/Helper/Subscription';
|
import { cartHasSubscriptionProducts } from '../../../ppcp-blocks/resources/js/Helper/Subscription';
|
||||||
import { loadCustomScript } from '@paypal/paypal-js';
|
import { loadCustomScript } from '@paypal/paypal-js';
|
||||||
import CheckoutHandler from './Context/CheckoutHandler';
|
import CheckoutHandler from './Context/CheckoutHandler';
|
||||||
import ApplepayManager from './ApplepayManager';
|
import ApplePayManager from './ApplepayManager';
|
||||||
import ApplepayManagerBlockEditor from './ApplepayManagerBlockEditor';
|
import ApplePayManagerBlockEditor from './ApplepayManagerBlockEditor';
|
||||||
|
|
||||||
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
|
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
|
||||||
const ppcpConfig = ppcpData.scriptData;
|
const ppcpConfig = ppcpData.scriptData;
|
||||||
|
@ -24,8 +24,8 @@ const ApplePayComponent = ( props ) => {
|
||||||
|
|
||||||
const bootstrap = function () {
|
const bootstrap = function () {
|
||||||
const ManagerClass = props.isEditing
|
const ManagerClass = props.isEditing
|
||||||
? ApplepayManagerBlockEditor
|
? ApplePayManagerBlockEditor
|
||||||
: ApplepayManager;
|
: ApplePayManager;
|
||||||
const manager = new ManagerClass( buttonConfig, ppcpConfig );
|
const manager = new ManagerClass( buttonConfig, ppcpConfig );
|
||||||
manager.init();
|
manager.init();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { loadCustomScript } from '@paypal/paypal-js';
|
import { loadCustomScript } from '@paypal/paypal-js';
|
||||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
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';
|
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
|
||||||
|
|
||||||
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
|
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
|
||||||
let manager;
|
let manager;
|
||||||
|
|
||||||
const bootstrap = function () {
|
const bootstrap = function () {
|
||||||
manager = new ApplepayManager( buttonConfig, ppcpConfig );
|
manager = new ApplePayManager( buttonConfig, ppcpConfig );
|
||||||
manager.init();
|
manager.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -299,5 +299,15 @@ return array(
|
||||||
esc_html( $button_text )
|
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' )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
231
modules/ppcp-applepay/src/ApplePayGateway.php
Normal file
231
modules/ppcp-applepay/src/ApplePayGateway.php
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Apple Pay Payment Gateway
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\Applepay
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare( strict_types = 1 );
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\Applepay;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
OrderProcessor $order_processor,
|
||||||
|
callable $paypal_checkout_url_factory,
|
||||||
|
RefundProcessor $refund_processor,
|
||||||
|
TransactionUrlProvider $transaction_url_provider,
|
||||||
|
SessionHandler $session_handler,
|
||||||
|
string $module_url
|
||||||
|
) {
|
||||||
|
$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.png';
|
||||||
|
|
||||||
|
$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;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Applepay;
|
namespace WooCommerce\PayPalCommerce\Applepay;
|
||||||
|
|
||||||
|
use WC_Payment_Gateway;
|
||||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||||
use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
|
use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
|
||||||
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
|
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
|
||||||
|
@ -117,6 +118,48 @@ class ApplepayModule implements ModuleInterface {
|
||||||
100,
|
100,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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>';
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -306,7 +349,7 @@ class ApplepayModule implements ModuleInterface {
|
||||||
* @param bool $is_sandbox The environment for this merchant.
|
* @param bool $is_sandbox The environment for this merchant.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function validation_string( bool $is_sandbox ) {
|
public function validation_string( bool $is_sandbox ) : string {
|
||||||
$sandbox_string = $this->sandbox_validation_string();
|
$sandbox_string = $this->sandbox_validation_string();
|
||||||
$live_string = $this->live_validation_string();
|
$live_string = $this->live_validation_string();
|
||||||
return $is_sandbox ? $sandbox_string : $live_string;
|
return $is_sandbox ? $sandbox_string : $live_string;
|
||||||
|
|
|
@ -20,12 +20,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
|
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
|
||||||
|
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ApplePayButton
|
* Class ApplePayButton
|
||||||
*/
|
*/
|
||||||
class ApplePayButton implements ButtonInterface {
|
class ApplePayButton implements ButtonInterface {
|
||||||
use RequestHandlerTrait;
|
use RequestHandlerTrait, ContextTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The settings.
|
* The settings.
|
||||||
|
@ -340,7 +341,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
}
|
}
|
||||||
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
||||||
$this->response_templates->response_success( $response );
|
$this->response_templates->response_success( $response );
|
||||||
} catch ( \Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
|
@ -382,7 +383,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
}
|
}
|
||||||
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
||||||
$this->response_templates->response_success( $response );
|
$this->response_templates->response_success( $response );
|
||||||
} catch ( \Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
|
@ -399,7 +400,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
* On error returns an array of errors to be handled by the script
|
* On error returns an array of errors to be handled by the script
|
||||||
* On success returns the new order data
|
* On success returns the new order data
|
||||||
*
|
*
|
||||||
* @throws \Exception When validation fails.
|
* @throws Exception When validation fails.
|
||||||
*/
|
*/
|
||||||
public function create_wc_order(): void {
|
public function create_wc_order(): void {
|
||||||
$applepay_request_data_object = $this->applepay_data_object_http();
|
$applepay_request_data_object = $this->applepay_data_object_http();
|
||||||
|
@ -420,15 +421,18 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$applepay_request_data_object->order_data( $context );
|
$applepay_request_data_object->order_data( $context );
|
||||||
|
|
||||||
$this->update_posted_data( $applepay_request_data_object );
|
$this->update_posted_data( $applepay_request_data_object );
|
||||||
|
|
||||||
if ( $context === 'product' ) {
|
if ( $context === 'product' ) {
|
||||||
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
|
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
|
||||||
$cart = WC()->cart;
|
$cart = WC()->cart;
|
||||||
$address = $applepay_request_data_object->shipping_address();
|
$address = $applepay_request_data_object->shipping_address();
|
||||||
|
|
||||||
$this->calculate_totals_single_product(
|
$this->calculate_totals_single_product(
|
||||||
$cart,
|
$cart,
|
||||||
$address,
|
$address,
|
||||||
$applepay_request_data_object->shipping_method()
|
$applepay_request_data_object->shipping_method()
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( ! $cart_item_key ) {
|
if ( ! $cart_item_key ) {
|
||||||
$this->response_templates->response_with_data_errors(
|
$this->response_templates->response_with_data_errors(
|
||||||
array(
|
array(
|
||||||
|
@ -438,19 +442,16 @@ class ApplePayButton implements ButtonInterface {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return;
|
} else {
|
||||||
}
|
add_filter(
|
||||||
add_filter(
|
'woocommerce_payment_successful_result',
|
||||||
'woocommerce_payment_successful_result',
|
function ( array $result ) use ( $cart, $cart_item_key ) : array {
|
||||||
function ( array $result ) use ( $cart, $cart_item_key ) : array {
|
$this->clear_current_cart( $cart, $cart_item_key );
|
||||||
if ( ! is_string( $cart_item_key ) ) {
|
$this->reload_cart( $cart );
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
$this->clear_current_cart( $cart, $cart_item_key );
|
);
|
||||||
$this->reload_cart( $cart );
|
}
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WC()->checkout()->process_checkout();
|
WC()->checkout()->process_checkout();
|
||||||
|
@ -460,17 +461,20 @@ class ApplePayButton implements ButtonInterface {
|
||||||
/**
|
/**
|
||||||
* Checks if the nonce in the data object is valid
|
* Checks if the nonce in the data object is valid
|
||||||
*
|
*
|
||||||
* @return bool|int
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function is_nonce_valid(): bool {
|
protected function is_nonce_valid(): bool {
|
||||||
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
|
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
|
||||||
if ( ! $nonce ) {
|
if ( ! $nonce ) {
|
||||||
return false;
|
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,
|
$nonce,
|
||||||
'woocommerce-process_checkout'
|
'woocommerce-process_checkout'
|
||||||
) === 1;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -511,7 +515,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$address,
|
$address,
|
||||||
$applepay_request_data_object->shipping_method()
|
$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->clear_current_cart( $cart, $cart_item_key );
|
||||||
$this->reload_cart( $cart );
|
$this->reload_cart( $cart );
|
||||||
}
|
}
|
||||||
|
@ -819,9 +823,9 @@ class ApplePayButton implements ButtonInterface {
|
||||||
/**
|
/**
|
||||||
* Removes the old cart, saves it, and creates a new one
|
* 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.
|
* @param ApplePayDataObjectHttp $applepay_request_data_object The request data object.
|
||||||
* @return bool | string The cart item key after adding to the new cart.
|
* @return string The cart item key after adding to the new cart.
|
||||||
* @throws \Exception If it cannot be added to cart.
|
|
||||||
*/
|
*/
|
||||||
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
|
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
|
||||||
$this->save_old_cart();
|
$this->save_old_cart();
|
||||||
|
@ -838,7 +842,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->cart_products->add_products( array( $product ) );
|
$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,
|
$render_placeholder,
|
||||||
function () {
|
function () {
|
||||||
$this->applepay_button();
|
$this->applepay_button();
|
||||||
|
$this->hide_gateway_until_eligible();
|
||||||
},
|
},
|
||||||
21
|
21
|
||||||
);
|
);
|
||||||
|
@ -961,6 +966,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
$render_placeholder,
|
$render_placeholder,
|
||||||
function () {
|
function () {
|
||||||
$this->applepay_button();
|
$this->applepay_button();
|
||||||
|
$this->hide_gateway_until_eligible();
|
||||||
},
|
},
|
||||||
21
|
21
|
||||||
);
|
);
|
||||||
|
@ -973,7 +979,7 @@ class ApplePayButton implements ButtonInterface {
|
||||||
add_action(
|
add_action(
|
||||||
$render_placeholder,
|
$render_placeholder,
|
||||||
function () {
|
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
|
21
|
||||||
);
|
);
|
||||||
|
@ -981,24 +987,29 @@ class ApplePayButton implements ButtonInterface {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ApplePay button markup
|
* ApplePay button markup
|
||||||
*/
|
*/
|
||||||
protected function applepay_button(): void {
|
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' ); ?>
|
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?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 {
|
protected function hide_gateway_until_eligible(): void {
|
||||||
return true;
|
?>
|
||||||
|
<style id="ppcp-hide-apple-pay">.wc_payment_method.payment_method_ppcp-applepay{display:none}</style>
|
||||||
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
* @package WooCommerce\PayPalCommerce\Applepay
|
* @package WooCommerce\PayPalCommerce\Applepay
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare( strict_types = 1 );
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\Applepay\Assets;
|
namespace WooCommerce\PayPalCommerce\Applepay\Assets;
|
||||||
|
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||||
|
use WC_Product;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class DataToAppleButtonScripts
|
* Class DataToAppleButtonScripts
|
||||||
|
@ -33,7 +35,7 @@ class DataToAppleButtonScripts {
|
||||||
/**
|
/**
|
||||||
* DataToAppleButtonScripts constructor.
|
* 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.
|
* @param Settings $settings The settings.
|
||||||
*/
|
*/
|
||||||
public function __construct( string $sdk_url, Settings $settings ) {
|
public function __construct( string $sdk_url, Settings $settings ) {
|
||||||
|
@ -45,57 +47,92 @@ class DataToAppleButtonScripts {
|
||||||
* Sets the appropriate data to send to ApplePay script
|
* Sets the appropriate data to send to ApplePay script
|
||||||
* Data differs between product page and cart page
|
* Data differs between product page and cart page
|
||||||
*
|
*
|
||||||
* @param bool $is_block Whether the button is in a block or not.
|
|
||||||
* @return array
|
* @return array
|
||||||
* @throws NotFoundException When the setting is not found.
|
|
||||||
*/
|
*/
|
||||||
public function apple_pay_script_data( bool $is_block = false ): array {
|
public function apple_pay_script_data() : array {
|
||||||
$base_location = wc_get_base_location();
|
|
||||||
$shop_country_code = $base_location['country'];
|
|
||||||
$currency_code = get_woocommerce_currency();
|
|
||||||
$total_label = get_bloginfo( 'name' );
|
|
||||||
if ( is_product() ) {
|
if ( is_product() ) {
|
||||||
return $this->data_for_product_page(
|
return $this->data_for_product_page();
|
||||||
$shop_country_code,
|
|
||||||
$currency_code,
|
|
||||||
$total_label
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->data_for_cart_page(
|
return $this->data_for_cart_page();
|
||||||
$shop_country_code,
|
|
||||||
$currency_code,
|
|
||||||
$total_label
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the appropriate admin data to send to ApplePay script
|
* Returns the appropriate admin data to send to ApplePay script
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @throws NotFoundException When the setting is not found.
|
|
||||||
*/
|
*/
|
||||||
public function apple_pay_script_data_for_admin() : array {
|
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();
|
$base_location = wc_get_base_location();
|
||||||
$shop_country_code = $base_location['country'];
|
$shop_country_code = $base_location['country'];
|
||||||
$currency_code = get_woocommerce_currency();
|
$currency_code = get_woocommerce_currency();
|
||||||
$total_label = get_bloginfo( 'name' );
|
$total_label = get_bloginfo( 'name' );
|
||||||
|
|
||||||
return $this->data_for_admin_page(
|
// Button layout (label, color, language).
|
||||||
$shop_country_code,
|
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
||||||
$currency_code,
|
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
||||||
$total_label
|
$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
|
* Check if the product needs shipping
|
||||||
*
|
*
|
||||||
* @param \WC_Product $product The product.
|
* @param WC_Product $product Product to check.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function check_if_need_shipping( $product ) {
|
protected function check_if_need_shipping( WC_Product $product ) : bool {
|
||||||
if (
|
if (
|
||||||
! wc_shipping_enabled()
|
! wc_shipping_enabled()
|
||||||
|| 0 === wc_get_shipping_method_count(
|
|| 0 === wc_get_shipping_method_count(
|
||||||
|
@ -104,30 +141,20 @@ class DataToAppleButtonScripts {
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$needs_shipping = false;
|
|
||||||
|
|
||||||
if ( $product->needs_shipping() ) {
|
if ( $product->needs_shipping() ) {
|
||||||
$needs_shipping = true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $needs_shipping;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares the data for the product page.
|
* 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
|
* @return array
|
||||||
* @throws NotFoundException When the setting is not found.
|
|
||||||
*/
|
*/
|
||||||
protected function data_for_product_page(
|
protected function data_for_product_page() : array {
|
||||||
$shop_country_code,
|
|
||||||
$currency_code,
|
|
||||||
$total_label
|
|
||||||
) {
|
|
||||||
$product = wc_get_product( get_the_id() );
|
$product = wc_get_product( get_the_id() );
|
||||||
if ( ! $product ) {
|
if ( ! $product ) {
|
||||||
return array();
|
return array();
|
||||||
|
@ -136,146 +163,59 @@ class DataToAppleButtonScripts {
|
||||||
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
|
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
|
||||||
$is_variation = true;
|
$is_variation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$product_need_shipping = $this->check_if_need_shipping( $product );
|
$product_need_shipping = $this->check_if_need_shipping( $product );
|
||||||
$product_id = get_the_id();
|
$product_id = get_the_id();
|
||||||
$product_price = $product->get_price();
|
$product_price = $product->get_price();
|
||||||
$product_stock = $product->get_stock_status();
|
$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(
|
return $this->get_apple_pay_data(
|
||||||
'sdk_url' => $this->sdk_url,
|
array(
|
||||||
'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(
|
|
||||||
'needShipping' => $product_need_shipping,
|
'needShipping' => $product_need_shipping,
|
||||||
'id' => $product_id,
|
'id' => $product_id,
|
||||||
'price' => $product_price,
|
'price' => $product_price,
|
||||||
'isVariation' => $is_variation,
|
'isVariation' => $is_variation,
|
||||||
'stock' => $product_stock,
|
'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.
|
* 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
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function data_for_cart_page(
|
protected function data_for_cart_page() : array {
|
||||||
$shop_country_code,
|
|
||||||
$currency_code,
|
|
||||||
$total_label
|
|
||||||
) {
|
|
||||||
$cart = WC()->cart;
|
$cart = WC()->cart;
|
||||||
if ( ! $cart ) {
|
if ( ! $cart ) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
return $this->get_apple_pay_data(
|
||||||
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
array(
|
||||||
$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(
|
|
||||||
'needShipping' => $cart->needs_shipping(),
|
'needShipping' => $cart->needs_shipping(),
|
||||||
'subtotal' => $cart->get_subtotal(),
|
'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.
|
* Prepares the data for the cart page.
|
||||||
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page() methods.
|
* 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.
|
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function data_for_admin_page(
|
protected function data_for_admin_page() : array {
|
||||||
$shop_country_code,
|
$data = $this->get_apple_pay_data(
|
||||||
$currency_code,
|
array(
|
||||||
$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(
|
|
||||||
'needShipping' => false,
|
'needShipping' => false,
|
||||||
'subtotal' => 0,
|
'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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* global PayPalCommerceGateway */
|
||||||
|
|
||||||
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
|
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
|
||||||
import { setVisible, setVisibleByClass } from '../Helper/Hiding';
|
import { setVisible, setVisibleByClass } from '../Helper/Hiding';
|
||||||
import {
|
import {
|
||||||
|
@ -197,12 +199,15 @@ class CheckoutBootstap {
|
||||||
);
|
);
|
||||||
const isGooglePayMethod =
|
const isGooglePayMethod =
|
||||||
currentPaymentMethod === PaymentMethods.GOOGLEPAY;
|
currentPaymentMethod === PaymentMethods.GOOGLEPAY;
|
||||||
|
const isApplePayMethod =
|
||||||
|
currentPaymentMethod === PaymentMethods.APPLEPAY;
|
||||||
const isSavedCard = isCard && isSavedCardSelected();
|
const isSavedCard = isCard && isSavedCardSelected();
|
||||||
const isNotOurGateway =
|
const isNotOurGateway =
|
||||||
! isPaypal &&
|
! isPaypal &&
|
||||||
! isCard &&
|
! isCard &&
|
||||||
! isSeparateButtonGateway &&
|
! isSeparateButtonGateway &&
|
||||||
! isGooglePayMethod;
|
! isGooglePayMethod &&
|
||||||
|
! isApplePayMethod;
|
||||||
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
||||||
const hasVaultedPaypal =
|
const hasVaultedPaypal =
|
||||||
PayPalCommerceGateway.vaulted_paypal_email !== '';
|
PayPalCommerceGateway.vaulted_paypal_email !== '';
|
||||||
|
@ -257,6 +262,8 @@ class CheckoutBootstap {
|
||||||
paymentMethod: currentPaymentMethod,
|
paymentMethod: currentPaymentMethod,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
setVisible( '#ppc-button-ppcp-applepay', isApplePayMethod );
|
||||||
|
|
||||||
document.body.dispatchEvent( new Event( 'ppcp_checkout_rendered' ) );
|
document.body.dispatchEvent( new Event( 'ppcp_checkout_rendered' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const PaymentMethods = {
|
||||||
OXXO: 'ppcp-oxxo-gateway',
|
OXXO: 'ppcp-oxxo-gateway',
|
||||||
CARD_BUTTON: 'ppcp-card-button-gateway',
|
CARD_BUTTON: 'ppcp-card-button-gateway',
|
||||||
GOOGLEPAY: 'ppcp-googlepay',
|
GOOGLEPAY: 'ppcp-googlepay',
|
||||||
|
APPLEPAY: 'ppcp-applepay',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,6 +71,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
||||||
|
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway {
|
'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway {
|
||||||
|
@ -198,6 +199,7 @@ return array(
|
||||||
Settings::PAY_LATER_TAB_ID,
|
Settings::PAY_LATER_TAB_ID,
|
||||||
AxoGateway::ID,
|
AxoGateway::ID,
|
||||||
GooglePayGateway::ID,
|
GooglePayGateway::ID,
|
||||||
|
ApplePayGateway::ID,
|
||||||
),
|
),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
@ -220,6 +222,7 @@ return array(
|
||||||
Settings::PAY_LATER_TAB_ID,
|
Settings::PAY_LATER_TAB_ID,
|
||||||
Settings::CONNECTION_TAB_ID,
|
Settings::CONNECTION_TAB_ID,
|
||||||
GooglePayGateway::ID,
|
GooglePayGateway::ID,
|
||||||
|
ApplePayGateway::ID,
|
||||||
),
|
),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
|
@ -118,18 +118,15 @@ class Settings implements ContainerInterface {
|
||||||
* Stores the settings to the database.
|
* Stores the settings to the database.
|
||||||
*/
|
*/
|
||||||
public function persist() {
|
public function persist() {
|
||||||
|
|
||||||
return update_option( self::KEY, $this->settings );
|
return update_option( self::KEY, $this->settings );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the settings.
|
* Loads the settings.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function load(): bool {
|
private function load(): bool {
|
||||||
|
|
||||||
if ( $this->settings ) {
|
if ( $this->settings ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue