mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'trunk'
# Conflicts: # modules/ppcp-googlepay/resources/js/GooglepayButton.js
This commit is contained in:
commit
fe39793e9a
86 changed files with 6565 additions and 2433 deletions
|
@ -1,15 +1,120 @@
|
|||
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
import {
|
||||
combineStyles,
|
||||
combineWrapperIds,
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
|
||||
import PaymentButton from '../../../ppcp-button/resources/js/modules/Renderer/PaymentButton';
|
||||
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
|
||||
import UpdatePaymentData from './Helper/UpdatePaymentData';
|
||||
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
||||
import TransactionInfo from './Helper/TransactionInfo';
|
||||
import { PaymentMethods } from '../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState';
|
||||
|
||||
class GooglepayButton {
|
||||
/**
|
||||
* Plugin-specific styling.
|
||||
*
|
||||
* Note that most properties of this object do not apply to the Google Pay button.
|
||||
*
|
||||
* @typedef {Object} PPCPStyle
|
||||
* @property {string} shape - Outline shape.
|
||||
* @property {?number} height - Button height in pixel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Style options that are defined by the Google Pay SDK and are required to render the button.
|
||||
*
|
||||
* @typedef {Object} GooglePayStyle
|
||||
* @property {string} type - Defines the button label.
|
||||
* @property {string} color - Button color
|
||||
* @property {string} language - The locale; an empty string will apply the user-agent's language.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Google Pay JS SDK
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects
|
||||
* @typedef {Object} GooglePaySDK
|
||||
* @property {typeof PaymentsClient} PaymentsClient - Main API client for payment actions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Payments Client class, generated by the Google Pay SDK.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
* @typedef {Object} PaymentsClient
|
||||
* @property {Function} createButton - The convenience method is used to generate a Google Pay payment button styled with the latest Google Pay branding for insertion into a webpage.
|
||||
* @property {Function} isReadyToPay - Use the isReadyToPay(isReadyToPayRequest) method to determine a user's ability to return a form of payment from the Google Pay API.
|
||||
* @property {Function} loadPaymentData - This method presents a Google Pay payment sheet that allows selection of a payment method and optionally configured parameters
|
||||
* @property {Function} onPaymentAuthorized - This method is called when a payment is authorized in the payment sheet.
|
||||
* @property {Function} onPaymentDataChanged - This method handles payment data changes in the payment sheet such as shipping address and shipping options.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This object describes the transaction details.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo
|
||||
* @typedef {Object} TransactionInfo
|
||||
* @property {string} currencyCode - Required. The ISO 4217 alphabetic currency code.
|
||||
* @property {string} countryCode - Optional. required for EEA countries,
|
||||
* @property {string} transactionId - Optional. A unique ID that identifies a facilitation attempt. Highly encouraged for troubleshooting.
|
||||
* @property {string} totalPriceStatus - Required. [ESTIMATED|FINAL] The status of the total price used:
|
||||
* @property {string} totalPrice - Required. Total monetary value of the transaction with an optional decimal precision of two decimal places.
|
||||
* @property {Array} displayItems - Optional. A list of cart items shown in the payment sheet (e.g. subtotals, sales taxes, shipping charges, discounts etc.).
|
||||
* @property {string} totalPriceLabel - Optional. Custom label for the total price within the display items.
|
||||
* @property {string} checkoutOption - Optional. Affects the submit button text displayed in the Google Pay payment sheet.
|
||||
*/
|
||||
|
||||
class GooglepayButton extends PaymentButton {
|
||||
/**
|
||||
* @type {TransactionInfo}
|
||||
* @inheritDoc
|
||||
*/
|
||||
transactionInfo;
|
||||
static methodId = PaymentMethods.GOOGLEPAY;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static cssClass = 'google-pay';
|
||||
|
||||
/**
|
||||
* Client reference, provided by the Google Pay JS SDK.
|
||||
*/
|
||||
#paymentsClient = null;
|
||||
|
||||
/**
|
||||
* Details about the processed transaction.
|
||||
*
|
||||
* @type {?TransactionInfo}
|
||||
*/
|
||||
#transactionInfo = null;
|
||||
|
||||
googlePayConfig = null;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||
return combineWrapperIds(
|
||||
buttonConfig?.button?.wrapper || '',
|
||||
buttonConfig?.button?.mini_cart_wrapper || '',
|
||||
ppcpConfig?.button?.wrapper || '',
|
||||
'ppc-button-googlepay-container',
|
||||
'ppc-button-ppcp-googlepay'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getStyles( buttonConfig, ppcpConfig ) {
|
||||
const styles = combineStyles(
|
||||
ppcpConfig?.button || {},
|
||||
buttonConfig?.button || {}
|
||||
);
|
||||
|
||||
if ( 'buy' === styles.MiniCart.type ) {
|
||||
styles.MiniCart.type = 'pay';
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context,
|
||||
|
@ -18,274 +123,257 @@ class GooglepayButton {
|
|||
ppcpConfig,
|
||||
contextHandler
|
||||
) {
|
||||
apmButtonsInit( ppcpConfig );
|
||||
// Disable debug output in the browser console:
|
||||
// buttonConfig.is_debug = false;
|
||||
|
||||
this.isInitialized = false;
|
||||
super(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.contextHandler = contextHandler;
|
||||
this.init = this.init.bind( this );
|
||||
this.onPaymentAuthorized = this.onPaymentAuthorized.bind( this );
|
||||
this.onPaymentDataChanged = this.onPaymentDataChanged.bind( this );
|
||||
this.onButtonClick = this.onButtonClick.bind( this );
|
||||
|
||||
this.paymentsClient = null;
|
||||
this.log( 'Create instance' );
|
||||
}
|
||||
|
||||
this.log = function () {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
get requiresShipping() {
|
||||
return super.requiresShipping && this.buttonConfig.shipping?.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay API.
|
||||
*
|
||||
* @return {?GooglePaySDK} API for the Google Pay JS SDK, or null when SDK is not ready yet.
|
||||
*/
|
||||
get googlePayApi() {
|
||||
return window.google?.payments?.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay PaymentsClient instance created by this button.
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
*
|
||||
* @return {?PaymentsClient} The SDK object, or null when SDK is not ready yet.
|
||||
*/
|
||||
get paymentsClient() {
|
||||
return this.#paymentsClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Details about the processed transaction.
|
||||
*
|
||||
* This object defines the price that is charged, and text that is displayed inside the
|
||||
* payment sheet.
|
||||
*
|
||||
* @return {?TransactionInfo} The TransactionInfo object.
|
||||
*/
|
||||
get transactionInfo() {
|
||||
return this.#transactionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the new transaction details to the payment button.
|
||||
*
|
||||
* @param {TransactionInfo} newTransactionInfo - Transaction details.
|
||||
*/
|
||||
set transactionInfo( newTransactionInfo ) {
|
||||
this.#transactionInfo = newTransactionInfo;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
validateConfiguration( silent = false ) {
|
||||
const validEnvs = [ 'PRODUCTION', 'TEST' ];
|
||||
|
||||
const isInvalid = ( ...args ) => {
|
||||
if ( ! silent ) {
|
||||
this.error( ...args );
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
init( config, transactionInfo ) {
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
|
||||
if ( ! this.validateConfig() ) {
|
||||
return;
|
||||
if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
|
||||
return isInvalid(
|
||||
'Invalid environment:',
|
||||
this.buttonConfig.environment
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler.validateContext() ) {
|
||||
return;
|
||||
// Preview buttons only need a valid environment.
|
||||
if ( this.isPreview ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.googlePayConfig = config;
|
||||
this.transactionInfo = transactionInfo;
|
||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
|
||||
this.initClient();
|
||||
this.initEventHandlers();
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
config
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
if ( response.result ) {
|
||||
if (
|
||||
( this.context === 'checkout' ||
|
||||
this.context === 'pay-now' ) &&
|
||||
this.buttonConfig.is_wc_gateway_enabled === '1'
|
||||
) {
|
||||
const wrapper = document.getElementById(
|
||||
'ppc-button-ppcp-googlepay'
|
||||
);
|
||||
|
||||
if ( wrapper ) {
|
||||
const { ppcpStyle, buttonStyle } =
|
||||
this.contextConfig();
|
||||
|
||||
wrapper.classList.add(
|
||||
`ppcp-button-${ ppcpStyle.shape }`,
|
||||
'ppcp-button-apm',
|
||||
'ppcp-button-googlepay'
|
||||
);
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
wrapper.style.height = `${ ppcpStyle.height }px`;
|
||||
}
|
||||
|
||||
this.addButtonCheckout(
|
||||
this.baseCardPaymentMethod,
|
||||
wrapper,
|
||||
buttonStyle
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.addButton( this.baseCardPaymentMethod );
|
||||
}
|
||||
} )
|
||||
.catch( function ( err ) {
|
||||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
return;
|
||||
return isInvalid(
|
||||
'No API configuration - missing configure() call?'
|
||||
);
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.init( this.googlePayConfig, this.transactionInfo );
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
if (
|
||||
[ 'PRODUCTION', 'TEST' ].indexOf(
|
||||
this.buttonConfig.environment
|
||||
) === -1
|
||||
) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid environment.',
|
||||
this.buttonConfig.environment
|
||||
if ( ! this.transactionInfo ) {
|
||||
return isInvalid(
|
||||
'No transactionInfo - missing configure() call?'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler ) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid context handler.',
|
||||
this.contextHandler
|
||||
);
|
||||
return false;
|
||||
if ( ! typeof this.contextHandler?.validateContext() ) {
|
||||
return isInvalid( 'Invalid context handler.', this.contextHandler );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configurations relative to this button context.
|
||||
* Configures the button instance. Must be called before the initial `init()`.
|
||||
*
|
||||
* @param {Object} apiConfig - API configuration.
|
||||
* @param {Object} transactionInfo - Transaction details; required before "init" call.
|
||||
*/
|
||||
contextConfig() {
|
||||
const config = {
|
||||
wrapper: this.buttonConfig.button.wrapper,
|
||||
ppcpStyle: this.ppcpConfig.button.style,
|
||||
buttonStyle: this.buttonConfig.button.style,
|
||||
ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
|
||||
};
|
||||
configure( apiConfig, transactionInfo ) {
|
||||
this.googlePayConfig = apiConfig;
|
||||
this.#transactionInfo = transactionInfo;
|
||||
|
||||
if ( this.context === 'mini-cart' ) {
|
||||
config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
|
||||
config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
|
||||
config.buttonStyle = this.buttonConfig.button.mini_cart_style;
|
||||
config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
|
||||
|
||||
// Handle incompatible types.
|
||||
if ( config.buttonStyle.type === 'buy' ) {
|
||||
config.buttonStyle.type = 'pay';
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
[ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
|
||||
) {
|
||||
config.ppcpButtonWrapper =
|
||||
'#express-payment-method-ppcp-gateway-paypal';
|
||||
}
|
||||
|
||||
return config;
|
||||
this.allowedPaymentMethods = this.googlePayConfig.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
}
|
||||
|
||||
initClient() {
|
||||
const callbacks = {
|
||||
onPaymentAuthorized: this.onPaymentAuthorized.bind( this ),
|
||||
};
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
callbacks.onPaymentDataChanged =
|
||||
this.onPaymentDataChanged.bind( this );
|
||||
init() {
|
||||
// Use `reinit()` to force a full refresh of an initialized button.
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient = new google.payments.api.PaymentsClient( {
|
||||
// Stop, if configuration is invalid.
|
||||
if ( ! this.validateConfiguration() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.init();
|
||||
this.#paymentsClient = this.createPaymentsClient();
|
||||
|
||||
if ( ! this.isPresent ) {
|
||||
this.log( 'Payment wrapper not found', this.wrapperId );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.paymentsClient ) {
|
||||
this.log( 'Could not initialize the payments client' );
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
this.googlePayConfig
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
this.log( 'PaymentsClient.isReadyToPay response:', response );
|
||||
this.isEligible = !! response.result;
|
||||
} )
|
||||
.catch( ( err ) => {
|
||||
this.error( err );
|
||||
this.isEligible = false;
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
// Missing (invalid) configuration indicates, that the first `init()` call did not happen yet.
|
||||
if ( ! this.validateConfiguration( true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.reinit();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an object with relevant paymentDataCallbacks for the current button instance.
|
||||
*
|
||||
* @return {Object} An object containing callbacks for the current scope & configuration.
|
||||
*/
|
||||
preparePaymentDataCallbacks() {
|
||||
const callbacks = {};
|
||||
|
||||
// We do not attach any callbacks to preview buttons.
|
||||
if ( this.isPreview ) {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
callbacks.onPaymentAuthorized = this.onPaymentAuthorized;
|
||||
|
||||
if ( this.requiresShipping ) {
|
||||
callbacks.onPaymentDataChanged = this.onPaymentDataChanged;
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
createPaymentsClient() {
|
||||
if ( ! this.googlePayApi ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const callbacks = this.preparePaymentDataCallbacks();
|
||||
|
||||
/**
|
||||
* Consider providing merchant info here:
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#PaymentOptions
|
||||
*/
|
||||
return new this.googlePayApi.PaymentsClient( {
|
||||
environment: this.buttonConfig.environment,
|
||||
// add merchant info maybe
|
||||
paymentDataCallbacks: callbacks,
|
||||
} );
|
||||
}
|
||||
|
||||
initEventHandlers() {
|
||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||
|
||||
if ( wrapper === ppcpButtonWrapper ) {
|
||||
throw new Error(
|
||||
`[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper }"`
|
||||
);
|
||||
}
|
||||
|
||||
const syncButtonVisibility = () => {
|
||||
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
||||
setVisible( wrapper, $ppcpButtonWrapper.is( ':visible' ) );
|
||||
setEnabled(
|
||||
wrapper,
|
||||
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
||||
);
|
||||
};
|
||||
|
||||
jQuery( document ).on(
|
||||
'ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled',
|
||||
( ev, data ) => {
|
||||
if ( jQuery( data.selector ).is( ppcpButtonWrapper ) ) {
|
||||
syncButtonVisibility();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
syncButtonVisibility();
|
||||
}
|
||||
|
||||
buildReadyToPayRequest( allowedPaymentMethods, baseRequest ) {
|
||||
this.log( 'Ready To Pay request', baseRequest, allowedPaymentMethods );
|
||||
|
||||
return Object.assign( {}, baseRequest, {
|
||||
allowedPaymentMethods,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Google Pay purchase button
|
||||
* @param baseCardPaymentMethod
|
||||
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
|
||||
* correct wrapper.
|
||||
*/
|
||||
addButton( baseCardPaymentMethod ) {
|
||||
this.log( 'addButton', this.context );
|
||||
addButton() {
|
||||
if ( ! this.paymentsClient ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig();
|
||||
const baseCardPaymentMethod = this.baseCardPaymentMethod;
|
||||
const { color, type, language } = this.style;
|
||||
|
||||
this.waitForWrapper( wrapper, () => {
|
||||
jQuery( wrapper ).addClass( 'ppcp-button-' + ppcpStyle.shape );
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
jQuery( wrapper ).css( 'height', `${ ppcpStyle.height }px` );
|
||||
}
|
||||
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick.bind( this ),
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: buttonStyle.color || 'black',
|
||||
buttonType: buttonStyle.type || 'pay',
|
||||
buttonLocale: buttonStyle.language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
|
||||
jQuery( wrapper ).append( button );
|
||||
} );
|
||||
}
|
||||
|
||||
addButtonCheckout( baseCardPaymentMethod, wrapper, buttonStyle ) {
|
||||
/**
|
||||
* @see https://developers.google.com/pay/api/web/reference/client#createButton
|
||||
*/
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick.bind( this ),
|
||||
onClick: this.onButtonClick,
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: buttonStyle.color || 'black',
|
||||
buttonType: buttonStyle.type || 'pay',
|
||||
buttonLocale: buttonStyle.language || 'en',
|
||||
buttonColor: color || 'black',
|
||||
buttonType: type || 'pay',
|
||||
buttonLocale: language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
|
||||
wrapper.appendChild( button );
|
||||
}
|
||||
|
||||
waitForWrapper( selector, callback, delay = 100, timeout = 2000 ) {
|
||||
const startTime = Date.now();
|
||||
const interval = setInterval( () => {
|
||||
const el = document.querySelector( selector );
|
||||
const timeElapsed = Date.now() - startTime;
|
||||
|
||||
if ( el ) {
|
||||
clearInterval( interval );
|
||||
callback( el );
|
||||
} else if ( timeElapsed > timeout ) {
|
||||
clearInterval( interval );
|
||||
}
|
||||
}, delay );
|
||||
this.insertButton( button );
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -296,35 +384,49 @@ class GooglepayButton {
|
|||
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
||||
*/
|
||||
onButtonClick() {
|
||||
this.log( 'onButtonClick', this.context );
|
||||
this.log( 'onButtonClick' );
|
||||
|
||||
const initiatePaymentRequest = () => {
|
||||
window.ppcpFundingSource = 'googlepay';
|
||||
|
||||
const paymentDataRequest = this.paymentDataRequest();
|
||||
|
||||
this.log(
|
||||
'onButtonClick: paymentDataRequest',
|
||||
paymentDataRequest,
|
||||
this.context
|
||||
);
|
||||
|
||||
this.paymentsClient.loadPaymentData( paymentDataRequest );
|
||||
};
|
||||
|
||||
if ( 'function' === typeof this.contextHandler.validateForm ) {
|
||||
// During regular checkout, validate the checkout form before initiating the payment.
|
||||
this.contextHandler
|
||||
.validateForm()
|
||||
.then( initiatePaymentRequest, () => {
|
||||
console.error(
|
||||
'[GooglePayButton] Form validation failed.'
|
||||
);
|
||||
const validateForm = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.validateForm ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler.validateForm().catch( ( error ) => {
|
||||
this.error( 'Form validation failed:', error );
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
const getTransactionInfo = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.transactionInfo ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler
|
||||
.transactionInfo()
|
||||
.then( ( transactionInfo ) => {
|
||||
this.transactionInfo = transactionInfo;
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
this.error( 'Failed to get transaction info:', error );
|
||||
throw error;
|
||||
} );
|
||||
} else {
|
||||
// This is the flow on product page, cart, and other non-checkout pages.
|
||||
initiatePaymentRequest();
|
||||
}
|
||||
};
|
||||
|
||||
validateForm()
|
||||
.then( getTransactionInfo )
|
||||
.then( initiatePaymentRequest );
|
||||
}
|
||||
|
||||
paymentDataRequest() {
|
||||
|
@ -340,10 +442,7 @@ class GooglepayButton {
|
|||
paymentDataRequest.transactionInfo = this.transactionInfo.finalObject;
|
||||
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
if ( this.requiresShipping ) {
|
||||
paymentDataRequest.callbackIntents = [
|
||||
'SHIPPING_ADDRESS',
|
||||
'SHIPPING_OPTION',
|
||||
|
@ -372,8 +471,7 @@ class GooglepayButton {
|
|||
}
|
||||
|
||||
onPaymentDataChanged( paymentData ) {
|
||||
this.log( 'onPaymentDataChanged', this.context );
|
||||
this.log( 'paymentData', paymentData );
|
||||
this.log( 'onPaymentDataChanged', paymentData );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
|
@ -437,7 +535,7 @@ class GooglepayButton {
|
|||
|
||||
resolve( paymentDataRequestUpdate );
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during onPaymentDataChanged:', error );
|
||||
this.error( 'Error during onPaymentDataChanged:', error );
|
||||
reject( error );
|
||||
}
|
||||
} );
|
||||
|
@ -508,18 +606,18 @@ class GooglepayButton {
|
|||
//------------------------
|
||||
|
||||
onPaymentAuthorized( paymentData ) {
|
||||
this.log( 'onPaymentAuthorized', this.context );
|
||||
this.log( 'onPaymentAuthorized' );
|
||||
return this.processPayment( paymentData );
|
||||
}
|
||||
|
||||
async processPayment( paymentData ) {
|
||||
this.log( 'processPayment', this.context );
|
||||
this.log( 'processPayment' );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const id = await this.contextHandler.createOrder();
|
||||
|
||||
this.log( 'processPayment: createOrder', id, this.context );
|
||||
this.log( 'processPayment: createOrder', id );
|
||||
|
||||
const confirmOrderResponse = await widgetBuilder.paypal
|
||||
.Googlepay()
|
||||
|
@ -530,8 +628,7 @@ class GooglepayButton {
|
|||
|
||||
this.log(
|
||||
'processPayment: confirmOrder',
|
||||
confirmOrderResponse,
|
||||
this.context
|
||||
confirmOrderResponse
|
||||
);
|
||||
|
||||
/** Capture the Order on the Server */
|
||||
|
@ -601,7 +698,7 @@ class GooglepayButton {
|
|||
};
|
||||
}
|
||||
|
||||
this.log( 'processPaymentResponse', response, this.context );
|
||||
this.log( 'processPaymentResponse', response );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class GooglepayManager {
|
|||
bootstrap.handler
|
||||
);
|
||||
|
||||
const button = new GooglepayButton(
|
||||
const button = GooglepayButton.createButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
|
@ -30,13 +30,19 @@ class GooglepayManager {
|
|||
|
||||
this.buttons.push( button );
|
||||
|
||||
const initButton = () => {
|
||||
button.configure( this.googlePayConfig, this.transactionInfo );
|
||||
button.init();
|
||||
};
|
||||
|
||||
// Initialize button only if googlePayConfig and transactionInfo are already fetched.
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
initButton();
|
||||
} else {
|
||||
await this.init();
|
||||
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
initButton();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
@ -53,8 +59,18 @@ class GooglepayManager {
|
|||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
}
|
||||
|
||||
for ( const button of this.buttons ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
if ( ! this.googlePayConfig ) {
|
||||
console.error( 'No GooglePayConfig received during init' );
|
||||
} else if ( ! this.transactionInfo ) {
|
||||
console.error( 'No transactionInfo found during init' );
|
||||
} else {
|
||||
for ( const button of this.buttons ) {
|
||||
button.configure(
|
||||
this.googlePayConfig,
|
||||
this.transactionInfo
|
||||
);
|
||||
button.init();
|
||||
}
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during initialization:', error );
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import GooglepayButton from './GooglepayButton';
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
export default class GooglePayPreviewButton extends PreviewButton {
|
||||
/**
|
||||
* Instance of the preview button.
|
||||
*
|
||||
* @type {?PaymentButton}
|
||||
*/
|
||||
#button = null;
|
||||
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${ args.selector }GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton( buttonConfig ) {
|
||||
const contextHandler = ContextHandlerFactory.create(
|
||||
'preview',
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
null
|
||||
);
|
||||
|
||||
if ( ! this.#button ) {
|
||||
/* Intentionally using `new` keyword, instead of the `.createButton()` factory,
|
||||
* as the factory is designed to only create a single button per context, while a single
|
||||
* page can contain multiple instances of a preview button.
|
||||
*/
|
||||
this.#button = new GooglepayButton(
|
||||
'preview',
|
||||
null,
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
}
|
||||
|
||||
this.#button.configure( this.apiConfig, null );
|
||||
this.#button.applyButtonStyles( buttonConfig, this.ppcpConfig );
|
||||
this.#button.reinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
*
|
||||
* @param {Object} buttonConfig
|
||||
* @param {Object} ppcpConfig
|
||||
*/
|
||||
dynamicPreviewConfig( buttonConfig, ppcpConfig ) {
|
||||
// Merge the current form-values into the preview-button configuration.
|
||||
if ( ppcpConfig.button && buttonConfig.button ) {
|
||||
Object.assign( buttonConfig.button.style, ppcpConfig.button.style );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import GooglepayButton from './GooglepayButton';
|
||||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import GooglePayPreviewButton from './GooglepayPreviewButton';
|
||||
|
||||
/**
|
||||
* Accessor that creates and returns a single PreviewButtonManager instance.
|
||||
|
@ -33,7 +31,7 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
* method.
|
||||
*
|
||||
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||
* @return {Promise<{}>}
|
||||
* @return {Promise<{}>} Promise that resolves when API configuration is available.
|
||||
*/
|
||||
async fetchConfig( payPal ) {
|
||||
const apiMethod = payPal?.Googlepay()?.config;
|
||||
|
@ -59,7 +57,7 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||
*
|
||||
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||
* @return {GooglePayPreviewButton}
|
||||
* @return {GooglePayPreviewButton} The new preview button instance.
|
||||
*/
|
||||
createButtonInstance( wrapperId ) {
|
||||
return new GooglePayPreviewButton( {
|
||||
|
@ -69,64 +67,5 @@ class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
class GooglePayPreviewButton extends PreviewButton {
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${ args.selector }GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton( buttonConfig ) {
|
||||
const contextHandler = ContextHandlerFactory.create(
|
||||
'preview',
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
null
|
||||
);
|
||||
|
||||
const button = new GooglepayButton(
|
||||
'preview',
|
||||
null,
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
button.init( this.apiConfig, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
* @param buttonConfig
|
||||
* @param ppcpConfig
|
||||
*/
|
||||
dynamicPreviewConfig( buttonConfig, ppcpConfig ) {
|
||||
// Merge the current form-values into the preview-button configuration.
|
||||
if ( ppcpConfig.button && buttonConfig.button ) {
|
||||
Object.assign( buttonConfig.button.style, ppcpConfig.button.style );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the preview button manager.
|
||||
buttonManager();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue