mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-31 06:52:50 +08:00
This change keeps the checkout & cart forms in-sync with the Google Pay form, to ensure the form submits the same details that the user can see inside Google Pay
787 lines
21 KiB
JavaScript
787 lines
21 KiB
JavaScript
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 TransactionInfo from './Helper/TransactionInfo';
|
|
import { PaymentMethods } from '../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState';
|
|
|
|
/**
|
|
* Plugin-specific styling.
|
|
*
|
|
* Note that most properties of this object do not apply to the Google Pay button.
|
|
*
|
|
* @typedef {Object} PPCPStyle
|
|
* @property {string} shape - Outline shape.
|
|
* @property {?number} height - Button height in pixel.
|
|
*/
|
|
|
|
/**
|
|
* Style options that are defined by the Google Pay SDK and are required to render the button.
|
|
*
|
|
* @typedef {Object} GooglePayStyle
|
|
* @property {string} type - Defines the button label.
|
|
* @property {string} color - Button color
|
|
* @property {string} language - The locale; an empty string will apply the user-agent's language.
|
|
*/
|
|
|
|
/**
|
|
* Google Pay JS SDK
|
|
*
|
|
* @see https://developers.google.com/pay/api/web/reference/request-objects
|
|
* @typedef {Object} GooglePaySDK
|
|
* @property {typeof PaymentsClient} PaymentsClient - Main API client for payment actions.
|
|
*/
|
|
|
|
/**
|
|
* The Payments Client class, generated by the Google Pay SDK.
|
|
*
|
|
* @see https://developers.google.com/pay/api/web/reference/client
|
|
* @typedef {Object} PaymentsClient
|
|
* @property {Function} createButton - The convenience method is used to generate a Google Pay payment button styled with the latest Google Pay branding for insertion into a webpage.
|
|
* @property {Function} isReadyToPay - Use the isReadyToPay(isReadyToPayRequest) method to determine a user's ability to return a form of payment from the Google Pay API.
|
|
* @property {Function} loadPaymentData - This method presents a Google Pay payment sheet that allows selection of a payment method and optionally configured parameters
|
|
* @property {Function} onPaymentAuthorized - This method is called when a payment is authorized in the payment sheet.
|
|
* @property {Function} onPaymentDataChanged - This method handles payment data changes in the payment sheet such as shipping address and shipping options.
|
|
*/
|
|
|
|
/**
|
|
* This object describes the transaction details.
|
|
*
|
|
* @see https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo
|
|
* @typedef {Object} TransactionInfo
|
|
* @property {string} currencyCode - Required. The ISO 4217 alphabetic currency code.
|
|
* @property {string} countryCode - Optional. required for EEA countries,
|
|
* @property {string} transactionId - Optional. A unique ID that identifies a facilitation attempt. Highly encouraged for troubleshooting.
|
|
* @property {string} totalPriceStatus - Required. [ESTIMATED|FINAL] The status of the total price used:
|
|
* @property {string} totalPrice - Required. Total monetary value of the transaction with an optional decimal precision of two decimal places.
|
|
* @property {Array} displayItems - Optional. A list of cart items shown in the payment sheet (e.g. subtotals, sales taxes, shipping charges, discounts etc.).
|
|
* @property {string} totalPriceLabel - Optional. Custom label for the total price within the display items.
|
|
* @property {string} checkoutOption - Optional. Affects the submit button text displayed in the Google Pay payment sheet.
|
|
*/
|
|
|
|
class GooglepayButton extends PaymentButton {
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
static methodId = PaymentMethods.GOOGLEPAY;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
static cssClass = 'google-pay';
|
|
|
|
/**
|
|
* Client reference, provided by the Google Pay JS SDK.
|
|
*/
|
|
#paymentsClient = null;
|
|
|
|
/**
|
|
* Details about the processed transaction.
|
|
*
|
|
* @type {?TransactionInfo}
|
|
*/
|
|
#transactionInfo = null;
|
|
|
|
googlePayConfig = null;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
static getWrappers( buttonConfig, ppcpConfig ) {
|
|
return combineWrapperIds(
|
|
buttonConfig?.button?.wrapper || '',
|
|
buttonConfig?.button?.mini_cart_wrapper || '',
|
|
ppcpConfig?.button?.wrapper || '',
|
|
'ppc-button-googlepay-container',
|
|
'ppc-button-ppcp-googlepay'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
static getStyles( buttonConfig, ppcpConfig ) {
|
|
const styles = combineStyles(
|
|
ppcpConfig?.button || {},
|
|
buttonConfig?.button || {}
|
|
);
|
|
|
|
if ( 'buy' === styles.MiniCart.type ) {
|
|
styles.MiniCart.type = 'pay';
|
|
}
|
|
|
|
return styles;
|
|
}
|
|
|
|
constructor(
|
|
context,
|
|
externalHandler,
|
|
buttonConfig,
|
|
ppcpConfig,
|
|
contextHandler
|
|
) {
|
|
// Disable debug output in the browser console:
|
|
// buttonConfig.is_debug = false;
|
|
|
|
super(
|
|
context,
|
|
externalHandler,
|
|
buttonConfig,
|
|
ppcpConfig,
|
|
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.log( 'Create instance' );
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
get requiresShipping() {
|
|
return super.requiresShipping && this.buttonConfig.shipping?.enabled;
|
|
}
|
|
|
|
/**
|
|
* The Google Pay API.
|
|
*
|
|
* @return {?GooglePaySDK} API for the Google Pay JS SDK, or null when SDK is not ready yet.
|
|
*/
|
|
get googlePayApi() {
|
|
return window.google?.payments?.api;
|
|
}
|
|
|
|
/**
|
|
* The Google Pay PaymentsClient instance created by this button.
|
|
* @see https://developers.google.com/pay/api/web/reference/client
|
|
*
|
|
* @return {?PaymentsClient} The SDK object, or null when SDK is not ready yet.
|
|
*/
|
|
get paymentsClient() {
|
|
return this.#paymentsClient;
|
|
}
|
|
|
|
/**
|
|
* Details about the processed transaction.
|
|
*
|
|
* This object defines the price that is charged, and text that is displayed inside the
|
|
* payment sheet.
|
|
*
|
|
* @return {?TransactionInfo} The TransactionInfo object.
|
|
*/
|
|
get transactionInfo() {
|
|
return this.#transactionInfo;
|
|
}
|
|
|
|
/**
|
|
* Assign the new transaction details to the payment button.
|
|
*
|
|
* @param {TransactionInfo} newTransactionInfo - Transaction details.
|
|
*/
|
|
set transactionInfo( newTransactionInfo ) {
|
|
this.#transactionInfo = newTransactionInfo;
|
|
|
|
this.refresh();
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
validateConfiguration( silent = false ) {
|
|
const validEnvs = [ 'PRODUCTION', 'TEST' ];
|
|
|
|
const isInvalid = ( ...args ) => {
|
|
if ( ! silent ) {
|
|
this.error( ...args );
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
|
|
return isInvalid(
|
|
'Invalid environment:',
|
|
this.buttonConfig.environment
|
|
);
|
|
}
|
|
|
|
// Preview buttons only need a valid environment.
|
|
if ( this.isPreview ) {
|
|
return true;
|
|
}
|
|
|
|
if ( ! this.googlePayConfig ) {
|
|
return isInvalid(
|
|
'No API configuration - missing configure() call?'
|
|
);
|
|
}
|
|
|
|
if ( ! this.transactionInfo ) {
|
|
return isInvalid(
|
|
'No transactionInfo - missing configure() call?'
|
|
);
|
|
}
|
|
|
|
if ( ! typeof this.contextHandler?.validateContext() ) {
|
|
return isInvalid( 'Invalid context handler.', this.contextHandler );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
configure( apiConfig, transactionInfo ) {
|
|
this.googlePayConfig = apiConfig;
|
|
this.#transactionInfo = transactionInfo;
|
|
|
|
this.allowedPaymentMethods = this.googlePayConfig.allowedPaymentMethods;
|
|
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
|
}
|
|
|
|
init() {
|
|
// Use `reinit()` to force a full refresh of an initialized button.
|
|
if ( this.isInitialized ) {
|
|
return;
|
|
}
|
|
|
|
// 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,
|
|
paymentDataCallbacks: callbacks,
|
|
} );
|
|
}
|
|
|
|
buildReadyToPayRequest( allowedPaymentMethods, baseRequest ) {
|
|
this.log( 'Ready To Pay request', baseRequest, allowedPaymentMethods );
|
|
|
|
return Object.assign( {}, baseRequest, {
|
|
allowedPaymentMethods,
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
|
|
* correct wrapper.
|
|
*/
|
|
addButton() {
|
|
if ( ! this.paymentsClient ) {
|
|
return;
|
|
}
|
|
|
|
const baseCardPaymentMethod = this.baseCardPaymentMethod;
|
|
const { color, type, language } = this.style;
|
|
|
|
/**
|
|
* @see https://developers.google.com/pay/api/web/reference/client#createButton
|
|
*/
|
|
const button = this.paymentsClient.createButton( {
|
|
onClick: this.onButtonClick,
|
|
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
|
buttonColor: color || 'black',
|
|
buttonType: type || 'pay',
|
|
buttonLocale: language || 'en',
|
|
buttonSizeMode: 'fill',
|
|
} );
|
|
|
|
this.insertButton( button );
|
|
}
|
|
|
|
//------------------------
|
|
// Button click
|
|
//------------------------
|
|
|
|
/**
|
|
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
|
*/
|
|
onButtonClick() {
|
|
this.log( 'onButtonClick' );
|
|
|
|
const initiatePaymentRequest = () => {
|
|
window.ppcpFundingSource = 'googlepay';
|
|
const paymentDataRequest = this.paymentDataRequest();
|
|
this.log(
|
|
'onButtonClick: paymentDataRequest',
|
|
paymentDataRequest,
|
|
this.context
|
|
);
|
|
this.paymentsClient.loadPaymentData( paymentDataRequest );
|
|
};
|
|
|
|
const validateForm = () => {
|
|
if ( 'function' !== typeof this.contextHandler.validateForm ) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return this.contextHandler.validateForm().catch( ( error ) => {
|
|
this.error( 'Form validation failed:', error );
|
|
throw error;
|
|
} );
|
|
};
|
|
|
|
const getTransactionInfo = () => {
|
|
if ( 'function' !== typeof this.contextHandler.transactionInfo ) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return this.contextHandler
|
|
.transactionInfo()
|
|
.then( ( transactionInfo ) => {
|
|
this.transactionInfo = transactionInfo;
|
|
} )
|
|
.catch( ( error ) => {
|
|
this.error( 'Failed to get transaction info:', error );
|
|
throw error;
|
|
} );
|
|
};
|
|
|
|
validateForm()
|
|
.then( getTransactionInfo )
|
|
.then( initiatePaymentRequest );
|
|
}
|
|
|
|
paymentDataRequest() {
|
|
const baseRequest = {
|
|
apiVersion: 2,
|
|
apiVersionMinor: 0,
|
|
};
|
|
|
|
const googlePayConfig = this.googlePayConfig;
|
|
const paymentDataRequest = Object.assign( {}, baseRequest );
|
|
paymentDataRequest.allowedPaymentMethods =
|
|
googlePayConfig.allowedPaymentMethods;
|
|
paymentDataRequest.transactionInfo = this.transactionInfo.finalObject;
|
|
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
|
|
|
if ( this.requiresShipping ) {
|
|
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', 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;
|
|
|
|
// Check, if the current context uses the WC cart.
|
|
const hasRealCart = [
|
|
'checkout-block',
|
|
'checkout',
|
|
'cart-block',
|
|
'cart',
|
|
'mini-cart',
|
|
'pay-now',
|
|
].includes( this.context );
|
|
|
|
this.log( 'onPaymentDataChanged:updatedData', updatedData );
|
|
this.log(
|
|
'onPaymentDataChanged:transactionInfo',
|
|
transactionInfo
|
|
);
|
|
|
|
updatedData.country_code = transactionInfo.countryCode;
|
|
updatedData.currency_code = transactionInfo.currencyCode;
|
|
|
|
// Handle unserviceable address.
|
|
if ( ! updatedData.shipping_options?.shippingOptions?.length ) {
|
|
paymentDataRequestUpdate.error =
|
|
this.unserviceableShippingAddressError();
|
|
resolve( paymentDataRequestUpdate );
|
|
return;
|
|
}
|
|
|
|
if (
|
|
[ 'INITIALIZE', 'SHIPPING_ADDRESS' ].includes(
|
|
paymentData.callbackTrigger
|
|
)
|
|
) {
|
|
paymentDataRequestUpdate.newShippingOptionParameters =
|
|
this.sanitizeShippingOptions(
|
|
updatedData.shipping_options
|
|
);
|
|
}
|
|
|
|
if ( updatedData.total && hasRealCart ) {
|
|
transactionInfo.setTotal(
|
|
updatedData.total,
|
|
updatedData.shipping_fee
|
|
);
|
|
|
|
// This page contains a real cart and potentially a form for shipping options.
|
|
this.syncShippingOptionWithForm(
|
|
paymentData?.shippingOptionData?.id
|
|
);
|
|
} else {
|
|
transactionInfo.shippingFee = this.getShippingCosts(
|
|
paymentData?.shippingOptionData?.id,
|
|
updatedData.shipping_options
|
|
);
|
|
}
|
|
|
|
paymentDataRequestUpdate.newTransactionInfo =
|
|
this.calculateNewTransactionInfo( transactionInfo );
|
|
|
|
resolve( paymentDataRequestUpdate );
|
|
} catch ( error ) {
|
|
this.error( 'Error during onPaymentDataChanged:', error );
|
|
reject( error );
|
|
}
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Google Pay throws an error, when the shippingOptions entries contain
|
|
* custom properties. This function strips unsupported properties from the
|
|
* provided ajax response.
|
|
*
|
|
* @param {Object} responseData Data returned from the ajax endpoint.
|
|
* @return {Object} Sanitized object.
|
|
*/
|
|
sanitizeShippingOptions( responseData ) {
|
|
const cleanOptions = [];
|
|
|
|
responseData.shippingOptions.forEach( ( item ) => {
|
|
cleanOptions.push( {
|
|
id: item.id,
|
|
label: item.label,
|
|
description: item.description,
|
|
} );
|
|
} );
|
|
|
|
responseData.shippingOptions = cleanOptions;
|
|
|
|
return { ...responseData, shippingOptions: cleanOptions };
|
|
}
|
|
|
|
/**
|
|
* Returns the shipping costs as numeric value.
|
|
*
|
|
* TODO - Move this to the PaymentButton base class
|
|
*
|
|
* @param {string} shippingId - The shipping method ID.
|
|
* @param {Object} shippingData - The PaymentDataRequest object that
|
|
* contains shipping options.
|
|
* @param {Array} shippingData.shippingOptions
|
|
* @param {string} shippingData.defaultSelectedOptionId
|
|
*
|
|
* @return {number} The shipping costs.
|
|
*/
|
|
getShippingCosts(
|
|
shippingId,
|
|
{ shippingOptions = [], defaultSelectedOptionId = '' } = {}
|
|
) {
|
|
if ( ! shippingOptions?.length ) {
|
|
this.log( 'Cannot calculate shipping cost: No Shipping Options' );
|
|
return 0;
|
|
}
|
|
|
|
const findOptionById = ( id ) =>
|
|
shippingOptions.find( ( option ) => option.id === id );
|
|
|
|
const getValidShippingId = () => {
|
|
if (
|
|
'shipping_option_unselected' === shippingId ||
|
|
! findOptionById( shippingId )
|
|
) {
|
|
// Entered on initial call, and when changing the shipping country.
|
|
return defaultSelectedOptionId;
|
|
}
|
|
|
|
return shippingId;
|
|
};
|
|
|
|
const currentOption = findOptionById( getValidShippingId() );
|
|
|
|
return Number( currentOption?.cost ) || 0;
|
|
}
|
|
|
|
unserviceableShippingAddressError() {
|
|
return {
|
|
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
|
|
message: 'Cannot ship to the selected address',
|
|
intent: 'SHIPPING_ADDRESS',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Recalculates and returns the plain transaction info object.
|
|
*
|
|
* @param {TransactionInfo} transactionInfo - Internal transactionInfo instance.
|
|
* @return {{totalPrice: string, countryCode: string, totalPriceStatus: string, currencyCode: string}} Updated details.
|
|
*/
|
|
calculateNewTransactionInfo( transactionInfo ) {
|
|
return transactionInfo.finalObject;
|
|
}
|
|
|
|
//------------------------
|
|
// Payment process
|
|
//------------------------
|
|
|
|
onPaymentAuthorized( paymentData ) {
|
|
this.log( 'onPaymentAuthorized' );
|
|
return this.processPayment( paymentData );
|
|
}
|
|
|
|
async processPayment( paymentData ) {
|
|
this.log( 'processPayment' );
|
|
|
|
return new Promise( async ( resolve, reject ) => {
|
|
try {
|
|
const id = await this.contextHandler.createOrder();
|
|
|
|
this.log( 'processPayment: createOrder', id );
|
|
|
|
const confirmOrderResponse = await widgetBuilder.paypal
|
|
.Googlepay()
|
|
.confirmOrder( {
|
|
orderId: id,
|
|
paymentMethodData: paymentData.paymentMethodData,
|
|
} );
|
|
|
|
this.log(
|
|
'processPayment: confirmOrder',
|
|
confirmOrderResponse
|
|
);
|
|
|
|
/** 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 );
|
|
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Updates the shipping option in the checkout form, if a form with shipping options is
|
|
* detected.
|
|
*
|
|
* @param {string} shippingOption - The shipping option ID, e.g. "flat_rate:4".
|
|
* @return {boolean} - True if a shipping option was found and selected, false otherwise.
|
|
*/
|
|
syncShippingOptionWithForm( shippingOption ) {
|
|
const wrappers = [
|
|
// Classic checkout, Classic cart.
|
|
'.woocommerce-shipping-methods',
|
|
// Block checkout.
|
|
'.wc-block-components-shipping-rates-control',
|
|
// Block cart.
|
|
'.wc-block-components-totals-shipping',
|
|
];
|
|
|
|
const sanitizedShippingOption = shippingOption.replace( /"/g, '' );
|
|
|
|
// Check for radio buttons with shipping options.
|
|
for ( const wrapper of wrappers ) {
|
|
const selector = `${ wrapper } input[type="radio"][value="${ sanitizedShippingOption }"]`;
|
|
const radioInput = document.querySelector( selector );
|
|
|
|
if ( radioInput ) {
|
|
radioInput.click();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check for select list with shipping options.
|
|
for ( const wrapper of wrappers ) {
|
|
const selector = `${ wrapper } select option[value="${ sanitizedShippingOption }"]`;
|
|
const selectOption = document.querySelector( selector );
|
|
|
|
if ( selectOption ) {
|
|
const selectElement = selectOption.closest( 'select' );
|
|
|
|
if ( selectElement ) {
|
|
selectElement.value = sanitizedShippingOption;
|
|
selectElement.dispatchEvent( new Event( 'change' ) );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export default GooglepayButton;
|