mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Fix JS linting
This commit is contained in:
parent
525fee7edd
commit
8b43a06f9f
3 changed files with 638 additions and 545 deletions
|
@ -1,397 +1,479 @@
|
|||
import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
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 { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
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';
|
||||
|
||||
class GooglepayButton {
|
||||
|
||||
constructor(context, externalHandler, buttonConfig, ppcpConfig, contextHandler) {
|
||||
apmButtonsInit(ppcpConfig);
|
||||
|
||||
this.isInitialized = false;
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.contextHandler = contextHandler;
|
||||
|
||||
this.paymentsClient = null;
|
||||
|
||||
this.log = function() {
|
||||
if (this.buttonConfig.is_debug) {
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(config, transactionInfo) {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
|
||||
if (!this.validateConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.contextHandler.validateContext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.googlePayConfig = config;
|
||||
this.transactionInfo = transactionInfo;
|
||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[0];
|
||||
|
||||
this.initClient();
|
||||
this.initEventHandlers();
|
||||
|
||||
this.paymentsClient.isReadyToPay(
|
||||
this.buildReadyToPayRequest(this.allowedPaymentMethods, config)
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.result) {
|
||||
this.addButton(this.baseCardPaymentMethod);
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
reinit() {
|
||||
if (!this.googlePayConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.init(this.googlePayConfig, this.transactionInfo);
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
if (['PRODUCTION', 'TEST'].indexOf(this.buttonConfig.environment) === -1) {
|
||||
console.error('[GooglePayButton] Invalid environment.', this.buttonConfig.environment);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.contextHandler) {
|
||||
console.error('[GooglePayButton] Invalid context handler.', this.contextHandler);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configurations relative to this button context.
|
||||
*/
|
||||
contextConfig() {
|
||||
let 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
initClient() {
|
||||
const callbacks = {
|
||||
onPaymentAuthorized: this.onPaymentAuthorized.bind(this)
|
||||
}
|
||||
|
||||
if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) {
|
||||
callbacks['onPaymentDataChanged'] = this.onPaymentDataChanged.bind(this);
|
||||
}
|
||||
|
||||
this.paymentsClient = new google.payments.api.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) {
|
||||
return Object.assign({}, baseRequest, {
|
||||
allowedPaymentMethods: allowedPaymentMethods,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Google Pay purchase button
|
||||
*/
|
||||
addButton(baseCardPaymentMethod) {
|
||||
this.log('addButton', this.context);
|
||||
|
||||
const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig();
|
||||
|
||||
this.waitForWrapper(wrapper, () => {
|
||||
jQuery(wrapper).addClass('ppcp-button-' + ppcpStyle.shape);
|
||||
|
||||
if (ppcpStyle.height) {
|
||||
jQuery(wrapper).css('height', `${ppcpStyle.height}px`)
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// Button click
|
||||
//------------------------
|
||||
|
||||
/**
|
||||
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
||||
*/
|
||||
onButtonClick() {
|
||||
this.log('onButtonClick', this.context);
|
||||
|
||||
const paymentDataRequest = this.paymentDataRequest();
|
||||
this.log('onButtonClick: paymentDataRequest', paymentDataRequest, this.context);
|
||||
|
||||
window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler.
|
||||
|
||||
this.paymentsClient.loadPaymentData(paymentDataRequest);
|
||||
}
|
||||
|
||||
paymentDataRequest() {
|
||||
let baseRequest = {
|
||||
apiVersion: 2,
|
||||
apiVersionMinor: 0
|
||||
}
|
||||
|
||||
const googlePayConfig = this.googlePayConfig;
|
||||
const paymentDataRequest = Object.assign({}, baseRequest);
|
||||
paymentDataRequest.allowedPaymentMethods = googlePayConfig.allowedPaymentMethods;
|
||||
paymentDataRequest.transactionInfo = this.transactionInfo;
|
||||
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
||||
|
||||
if (this.buttonConfig.shipping.enabled && this.contextHandler.shippingAllowed()) {
|
||||
paymentDataRequest.callbackIntents = ["SHIPPING_ADDRESS", "SHIPPING_OPTION", "PAYMENT_AUTHORIZATION"];
|
||||
paymentDataRequest.shippingAddressRequired = true;
|
||||
paymentDataRequest.shippingAddressParameters = this.shippingAddressParameters();
|
||||
paymentDataRequest.shippingOptionRequired = true;
|
||||
} else {
|
||||
paymentDataRequest.callbackIntents = ['PAYMENT_AUTHORIZATION'];
|
||||
}
|
||||
|
||||
return paymentDataRequest;
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// Shipping processing
|
||||
//------------------------
|
||||
|
||||
shippingAddressParameters() {
|
||||
return {
|
||||
allowedCountryCodes: this.buttonConfig.shipping.countries,
|
||||
phoneNumberRequired: true
|
||||
};
|
||||
}
|
||||
|
||||
onPaymentDataChanged(paymentData) {
|
||||
this.log('onPaymentDataChanged', this.context);
|
||||
this.log('paymentData', paymentData);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let paymentDataRequestUpdate = {};
|
||||
|
||||
const updatedData = await (new UpdatePaymentData(this.buttonConfig.ajax.update_payment_data)).update(paymentData);
|
||||
const transactionInfo = this.transactionInfo;
|
||||
|
||||
this.log('onPaymentDataChanged:updatedData', updatedData);
|
||||
this.log('onPaymentDataChanged:transactionInfo', transactionInfo);
|
||||
|
||||
updatedData.country_code = transactionInfo.countryCode;
|
||||
updatedData.currency_code = transactionInfo.currencyCode;
|
||||
updatedData.total_str = transactionInfo.totalPrice;
|
||||
|
||||
// Handle unserviceable address.
|
||||
if (!(updatedData.shipping_options?.shippingOptions?.length)) {
|
||||
paymentDataRequestUpdate.error = this.unserviceableShippingAddressError();
|
||||
resolve(paymentDataRequestUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (paymentData.callbackTrigger) {
|
||||
case 'INITIALIZE':
|
||||
case 'SHIPPING_ADDRESS':
|
||||
paymentDataRequestUpdate.newShippingOptionParameters = updatedData.shipping_options;
|
||||
paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData);
|
||||
break;
|
||||
case 'SHIPPING_OPTION':
|
||||
paymentDataRequestUpdate.newTransactionInfo = this.calculateNewTransactionInfo(updatedData);
|
||||
break;
|
||||
}
|
||||
|
||||
resolve(paymentDataRequestUpdate);
|
||||
} catch(error) {
|
||||
console.error('Error during onPaymentDataChanged:', error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unserviceableShippingAddressError() {
|
||||
return {
|
||||
reason: "SHIPPING_ADDRESS_UNSERVICEABLE",
|
||||
message: "Cannot ship to the selected address",
|
||||
intent: "SHIPPING_ADDRESS"
|
||||
};
|
||||
}
|
||||
|
||||
calculateNewTransactionInfo(updatedData) {
|
||||
return {
|
||||
countryCode: updatedData.country_code,
|
||||
currencyCode: updatedData.currency_code,
|
||||
totalPriceStatus: 'FINAL',
|
||||
totalPrice: updatedData.total_str
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//------------------------
|
||||
// Payment process
|
||||
//------------------------
|
||||
|
||||
onPaymentAuthorized(paymentData) {
|
||||
this.log('onPaymentAuthorized', this.context);
|
||||
return this.processPayment(paymentData);
|
||||
}
|
||||
|
||||
async processPayment(paymentData) {
|
||||
this.log('processPayment', this.context);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let id = await this.contextHandler.createOrder();
|
||||
|
||||
this.log('processPayment: createOrder', id, this.context);
|
||||
|
||||
const confirmOrderResponse = await widgetBuilder.paypal.Googlepay().confirmOrder({
|
||||
orderId: id,
|
||||
paymentMethodData: paymentData.paymentMethodData
|
||||
});
|
||||
|
||||
this.log('processPayment: confirmOrder', confirmOrderResponse, this.context);
|
||||
|
||||
/** Capture the Order on the Server */
|
||||
if (confirmOrderResponse.status === "APPROVED") {
|
||||
|
||||
let approveFailed = false;
|
||||
await this.contextHandler.approveOrder({
|
||||
orderID: id
|
||||
}, { // actions mock object.
|
||||
restart: () => new Promise((resolve, reject) => {
|
||||
approveFailed = true;
|
||||
resolve();
|
||||
}),
|
||||
order: {
|
||||
get: () => new Promise((resolve, reject) => {
|
||||
resolve(null);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if (!approveFailed) {
|
||||
resolve(this.processPaymentResponse('SUCCESS'));
|
||||
} else {
|
||||
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'FAILED TO APPROVE'));
|
||||
}
|
||||
|
||||
} else {
|
||||
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'TRANSACTION FAILED'));
|
||||
}
|
||||
} catch(err) {
|
||||
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', err.message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
processPaymentResponse(state, intent = null, message = null) {
|
||||
let response = {
|
||||
transactionState: state,
|
||||
}
|
||||
|
||||
if (intent || message) {
|
||||
response.error = {
|
||||
intent: intent,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
this.log('processPaymentResponse', response, this.context);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
) {
|
||||
apmButtonsInit( ppcpConfig );
|
||||
|
||||
this.isInitialized = false;
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.contextHandler = contextHandler;
|
||||
|
||||
this.paymentsClient = null;
|
||||
|
||||
this.log = function () {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
init( config, transactionInfo ) {
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
|
||||
if ( ! this.validateConfig() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler.validateContext() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.googlePayConfig = config;
|
||||
this.transactionInfo = transactionInfo;
|
||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
|
||||
this.initClient();
|
||||
this.initEventHandlers();
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
config
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
if ( response.result ) {
|
||||
this.addButton( this.baseCardPaymentMethod );
|
||||
}
|
||||
} )
|
||||
.catch( function ( err ) {
|
||||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.init( this.googlePayConfig, this.transactionInfo );
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
if (
|
||||
[ 'PRODUCTION', 'TEST' ].indexOf(
|
||||
this.buttonConfig.environment
|
||||
) === -1
|
||||
) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid environment.',
|
||||
this.buttonConfig.environment
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler ) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid context handler.',
|
||||
this.contextHandler
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
initClient() {
|
||||
const callbacks = {
|
||||
onPaymentAuthorized: this.onPaymentAuthorized.bind( this ),
|
||||
};
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
callbacks.onPaymentDataChanged =
|
||||
this.onPaymentDataChanged.bind( this );
|
||||
}
|
||||
|
||||
this.paymentsClient = new google.payments.api.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 ) {
|
||||
return Object.assign( {}, baseRequest, {
|
||||
allowedPaymentMethods,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Google Pay purchase button
|
||||
* @param baseCardPaymentMethod
|
||||
*/
|
||||
addButton( baseCardPaymentMethod ) {
|
||||
this.log( 'addButton', this.context );
|
||||
|
||||
const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig();
|
||||
|
||||
this.waitForWrapper( wrapper, () => {
|
||||
jQuery( wrapper ).addClass( 'ppcp-button-' + ppcpStyle.shape );
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
jQuery( wrapper ).css( 'height', `${ ppcpStyle.height }px` );
|
||||
}
|
||||
|
||||
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 );
|
||||
} );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// Button click
|
||||
//------------------------
|
||||
|
||||
/**
|
||||
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
||||
*/
|
||||
onButtonClick() {
|
||||
this.log( 'onButtonClick', this.context );
|
||||
|
||||
const paymentDataRequest = this.paymentDataRequest();
|
||||
this.log(
|
||||
'onButtonClick: paymentDataRequest',
|
||||
paymentDataRequest,
|
||||
this.context
|
||||
);
|
||||
|
||||
window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler.
|
||||
|
||||
this.paymentsClient.loadPaymentData( paymentDataRequest );
|
||||
}
|
||||
|
||||
paymentDataRequest() {
|
||||
const baseRequest = {
|
||||
apiVersion: 2,
|
||||
apiVersionMinor: 0,
|
||||
};
|
||||
|
||||
const googlePayConfig = this.googlePayConfig;
|
||||
const paymentDataRequest = Object.assign( {}, baseRequest );
|
||||
paymentDataRequest.allowedPaymentMethods =
|
||||
googlePayConfig.allowedPaymentMethods;
|
||||
paymentDataRequest.transactionInfo = this.transactionInfo;
|
||||
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
paymentDataRequest.callbackIntents = [
|
||||
'SHIPPING_ADDRESS',
|
||||
'SHIPPING_OPTION',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
];
|
||||
paymentDataRequest.shippingAddressRequired = true;
|
||||
paymentDataRequest.shippingAddressParameters =
|
||||
this.shippingAddressParameters();
|
||||
paymentDataRequest.shippingOptionRequired = true;
|
||||
} else {
|
||||
paymentDataRequest.callbackIntents = [ 'PAYMENT_AUTHORIZATION' ];
|
||||
}
|
||||
|
||||
return paymentDataRequest;
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// Shipping processing
|
||||
//------------------------
|
||||
|
||||
shippingAddressParameters() {
|
||||
return {
|
||||
allowedCountryCodes: this.buttonConfig.shipping.countries,
|
||||
phoneNumberRequired: true,
|
||||
};
|
||||
}
|
||||
|
||||
onPaymentDataChanged( paymentData ) {
|
||||
this.log( 'onPaymentDataChanged', this.context );
|
||||
this.log( 'paymentData', paymentData );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const paymentDataRequestUpdate = {};
|
||||
|
||||
const updatedData = await new UpdatePaymentData(
|
||||
this.buttonConfig.ajax.update_payment_data
|
||||
).update( paymentData );
|
||||
const transactionInfo = this.transactionInfo;
|
||||
|
||||
this.log( 'onPaymentDataChanged:updatedData', updatedData );
|
||||
this.log(
|
||||
'onPaymentDataChanged:transactionInfo',
|
||||
transactionInfo
|
||||
);
|
||||
|
||||
updatedData.country_code = transactionInfo.countryCode;
|
||||
updatedData.currency_code = transactionInfo.currencyCode;
|
||||
updatedData.total_str = transactionInfo.totalPrice;
|
||||
|
||||
// Handle unserviceable address.
|
||||
if ( ! updatedData.shipping_options?.shippingOptions?.length ) {
|
||||
paymentDataRequestUpdate.error =
|
||||
this.unserviceableShippingAddressError();
|
||||
resolve( paymentDataRequestUpdate );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( paymentData.callbackTrigger ) {
|
||||
case 'INITIALIZE':
|
||||
case 'SHIPPING_ADDRESS':
|
||||
paymentDataRequestUpdate.newShippingOptionParameters =
|
||||
updatedData.shipping_options;
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
case 'SHIPPING_OPTION':
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
}
|
||||
|
||||
resolve( paymentDataRequestUpdate );
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during onPaymentDataChanged:', error );
|
||||
reject( error );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
unserviceableShippingAddressError() {
|
||||
return {
|
||||
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
|
||||
message: 'Cannot ship to the selected address',
|
||||
intent: 'SHIPPING_ADDRESS',
|
||||
};
|
||||
}
|
||||
|
||||
calculateNewTransactionInfo( updatedData ) {
|
||||
return {
|
||||
countryCode: updatedData.country_code,
|
||||
currencyCode: updatedData.currency_code,
|
||||
totalPriceStatus: 'FINAL',
|
||||
totalPrice: updatedData.total_str,
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// Payment process
|
||||
//------------------------
|
||||
|
||||
onPaymentAuthorized( paymentData ) {
|
||||
this.log( 'onPaymentAuthorized', this.context );
|
||||
return this.processPayment( paymentData );
|
||||
}
|
||||
|
||||
async processPayment( paymentData ) {
|
||||
this.log( 'processPayment', this.context );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const id = await this.contextHandler.createOrder();
|
||||
|
||||
this.log( 'processPayment: createOrder', id, this.context );
|
||||
|
||||
const confirmOrderResponse = await widgetBuilder.paypal
|
||||
.Googlepay()
|
||||
.confirmOrder( {
|
||||
orderId: id,
|
||||
paymentMethodData: paymentData.paymentMethodData,
|
||||
} );
|
||||
|
||||
this.log(
|
||||
'processPayment: confirmOrder',
|
||||
confirmOrderResponse,
|
||||
this.context
|
||||
);
|
||||
|
||||
/** Capture the Order on the Server */
|
||||
if ( confirmOrderResponse.status === 'APPROVED' ) {
|
||||
let approveFailed = false;
|
||||
await this.contextHandler.approveOrder(
|
||||
{
|
||||
orderID: id,
|
||||
},
|
||||
{
|
||||
// actions mock object.
|
||||
restart: () =>
|
||||
new Promise( ( resolve, reject ) => {
|
||||
approveFailed = true;
|
||||
resolve();
|
||||
} ),
|
||||
order: {
|
||||
get: () =>
|
||||
new Promise( ( resolve, reject ) => {
|
||||
resolve( null );
|
||||
} ),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if ( ! approveFailed ) {
|
||||
resolve( this.processPaymentResponse( 'SUCCESS' ) );
|
||||
} else {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
'FAILED TO APPROVE'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
'TRANSACTION FAILED'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch ( err ) {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
err.message
|
||||
)
|
||||
);
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
processPaymentResponse( state, intent = null, message = null ) {
|
||||
const response = {
|
||||
transactionState: state,
|
||||
};
|
||||
|
||||
if ( intent || message ) {
|
||||
response.error = {
|
||||
intent,
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
this.log( 'processPaymentResponse', response, this.context );
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export default GooglepayButton;
|
||||
|
|
|
@ -1,88 +1,85 @@
|
|||
import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher";
|
||||
import GooglepayButton from "./GooglepayButton";
|
||||
import ContextHandlerFactory from "./Context/ContextHandlerFactory";
|
||||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||
import GooglepayButton from './GooglepayButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
|
||||
class GooglepayManager {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.googlePayConfig = null;
|
||||
this.transactionInfo = null;
|
||||
this.contextHandler = null;
|
||||
|
||||
constructor(buttonConfig, ppcpConfig) {
|
||||
this.buttons = [];
|
||||
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.googlePayConfig = null;
|
||||
this.transactionInfo = null;
|
||||
this.contextHandler = null;
|
||||
buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => {
|
||||
if ( ! this.contextHandler ) {
|
||||
this.contextHandler = ContextHandlerFactory.create(
|
||||
bootstrap.context,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
bootstrap.handler
|
||||
);
|
||||
}
|
||||
|
||||
this.buttons = [];
|
||||
const button = new GooglepayButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
this.contextHandler
|
||||
);
|
||||
|
||||
buttonModuleWatcher.watchContextBootstrap(async (bootstrap) => {
|
||||
if (!this.contextHandler) {
|
||||
this.contextHandler = ContextHandlerFactory.create(
|
||||
bootstrap.context,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
bootstrap.handler
|
||||
);
|
||||
}
|
||||
this.buttons.push( button );
|
||||
|
||||
const button = new GooglepayButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
this.contextHandler
|
||||
);
|
||||
// Initialize button only if googlePayConfig and transactionInfo are already fetched.
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
} else {
|
||||
await this.init();
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
this.buttons.push(button);
|
||||
async init() {
|
||||
try {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await paypal.Googlepay().config();
|
||||
}
|
||||
|
||||
// Initialize button only if googlePayConfig and transactionInfo are already fetched.
|
||||
if (this.googlePayConfig && this.transactionInfo) {
|
||||
button.init(this.googlePayConfig, this.transactionInfo);
|
||||
} else {
|
||||
await this.init();
|
||||
if (this.googlePayConfig && this.transactionInfo) {
|
||||
button.init(this.googlePayConfig, this.transactionInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if ( ! this.transactionInfo ) {
|
||||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
if (!this.googlePayConfig) {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await paypal.Googlepay().config();
|
||||
}
|
||||
for ( const button of this.buttons ) {
|
||||
button.init( this.googlePayConfig, this.transactionInfo );
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during initialization:', error );
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.transactionInfo) {
|
||||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
}
|
||||
|
||||
for (const button of this.buttons) {
|
||||
button.init(this.googlePayConfig, this.transactionInfo);
|
||||
}
|
||||
} catch(error) {
|
||||
console.error('Error during initialization:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchTransactionInfo() {
|
||||
try {
|
||||
if (!this.contextHandler) {
|
||||
throw new Error('ContextHandler is not initialized');
|
||||
}
|
||||
return await this.contextHandler.transactionInfo();
|
||||
} catch(error) {
|
||||
console.error('Error fetching transaction info:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
reinit() {
|
||||
for (const button of this.buttons) {
|
||||
button.reinit();
|
||||
}
|
||||
}
|
||||
async fetchTransactionInfo() {
|
||||
try {
|
||||
if ( ! this.contextHandler ) {
|
||||
throw new Error( 'ContextHandler is not initialized' );
|
||||
}
|
||||
return await this.contextHandler.transactionInfo();
|
||||
} catch ( error ) {
|
||||
console.error( 'Error fetching transaction info:', error );
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
reinit() {
|
||||
for ( const button of this.buttons ) {
|
||||
button.reinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GooglepayManager;
|
||||
|
|
|
@ -7,110 +7,124 @@ import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
|||
* Accessor that creates and returns a single PreviewButtonManager instance.
|
||||
*/
|
||||
const buttonManager = () => {
|
||||
if (!GooglePayPreviewButtonManager.instance) {
|
||||
GooglePayPreviewButtonManager.instance = new GooglePayPreviewButtonManager();
|
||||
}
|
||||
if ( ! GooglePayPreviewButtonManager.instance ) {
|
||||
GooglePayPreviewButtonManager.instance =
|
||||
new GooglePayPreviewButtonManager();
|
||||
}
|
||||
|
||||
return GooglePayPreviewButtonManager.instance;
|
||||
return GooglePayPreviewButtonManager.instance;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Manages all GooglePay preview buttons on this page.
|
||||
*/
|
||||
class GooglePayPreviewButtonManager extends PreviewButtonManager {
|
||||
constructor() {
|
||||
const args = {
|
||||
methodName: 'GooglePay',
|
||||
buttonConfig: window.wc_ppcp_googlepay_admin,
|
||||
};
|
||||
constructor() {
|
||||
const args = {
|
||||
methodName: 'GooglePay',
|
||||
buttonConfig: window.wc_ppcp_googlepay_admin,
|
||||
};
|
||||
|
||||
super(args);
|
||||
}
|
||||
super( args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||
* method.
|
||||
*
|
||||
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||
* @return {Promise<{}>}
|
||||
*/
|
||||
async fetchConfig(payPal) {
|
||||
const apiMethod = payPal?.Googlepay()?.config;
|
||||
/**
|
||||
* Responsible for fetching and returning the PayPal configuration object for this payment
|
||||
* method.
|
||||
*
|
||||
* @param {{}} payPal - The PayPal SDK object provided by WidgetBuilder.
|
||||
* @return {Promise<{}>}
|
||||
*/
|
||||
async fetchConfig( payPal ) {
|
||||
const apiMethod = payPal?.Googlepay()?.config;
|
||||
|
||||
if (!apiMethod) {
|
||||
this.error('configuration object cannot be retrieved from PayPal');
|
||||
return {};
|
||||
}
|
||||
if ( ! apiMethod ) {
|
||||
this.error(
|
||||
'configuration object cannot be retrieved from PayPal'
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return await apiMethod();
|
||||
} catch (error) {
|
||||
if (error.message.includes('Not Eligible')) {
|
||||
this.apiError = 'Not Eligible';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return await apiMethod();
|
||||
} catch ( error ) {
|
||||
if ( error.message.includes( 'Not Eligible' ) ) {
|
||||
this.apiError = 'Not Eligible';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||
*
|
||||
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||
* @return {GooglePayPreviewButton}
|
||||
*/
|
||||
createButtonInstance(wrapperId) {
|
||||
return new GooglePayPreviewButton({
|
||||
selector: wrapperId,
|
||||
apiConfig: this.apiConfig,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* This method is responsible for creating a new PreviewButton instance and returning it.
|
||||
*
|
||||
* @param {string} wrapperId - CSS ID of the wrapper element.
|
||||
* @return {GooglePayPreviewButton}
|
||||
*/
|
||||
createButtonInstance( wrapperId ) {
|
||||
return new GooglePayPreviewButton( {
|
||||
selector: wrapperId,
|
||||
apiConfig: this.apiConfig,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
class GooglePayPreviewButton extends PreviewButton {
|
||||
constructor(args) {
|
||||
super(args);
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${args.selector}GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
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');
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton(buttonConfig) {
|
||||
const contextHandler = ContextHandlerFactory.create('preview', buttonConfig, this.ppcpConfig, null);
|
||||
const button = new GooglepayButton('preview', null, buttonConfig, this.ppcpConfig, contextHandler);
|
||||
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, contextHandler.transactionInfo());
|
||||
}
|
||||
button.init( this.apiConfig, contextHandler.transactionInfo() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue