Merge pull request #2414 from woocommerce/PCP-2645-when-selecting-google-pay-as-a-payment-option-in-safari-a-developer-error-is-displayed

Google Pay: Fix the incorrect popup triggering (2645)
This commit is contained in:
Emili Castells 2024-07-23 12:57:00 +02:00 committed by GitHub
commit 2e44e11dbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 113 additions and 59 deletions

View file

@ -1,4 +1,3 @@
import ContextHandlerFactory from './Context/ContextHandlerFactory';
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler'; import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder'; import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
@ -6,7 +5,13 @@ import UpdatePaymentData from './Helper/UpdatePaymentData';
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons'; import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
class GooglepayButton { class GooglepayButton {
constructor( context, externalHandler, buttonConfig, ppcpConfig ) { constructor(
context,
externalHandler,
buttonConfig,
ppcpConfig,
contextHandler
) {
apmButtonsInit( ppcpConfig ); apmButtonsInit( ppcpConfig );
this.isInitialized = false; this.isInitialized = false;
@ -15,16 +20,10 @@ class GooglepayButton {
this.externalHandler = externalHandler; this.externalHandler = externalHandler;
this.buttonConfig = buttonConfig; this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig; this.ppcpConfig = ppcpConfig;
this.contextHandler = contextHandler;
this.paymentsClient = null; this.paymentsClient = null;
this.contextHandler = ContextHandlerFactory.create(
this.context,
this.buttonConfig,
this.ppcpConfig,
this.externalHandler
);
this.log = function () { this.log = function () {
if ( this.buttonConfig.is_debug ) { if ( this.buttonConfig.is_debug ) {
//console.log('[GooglePayButton]', ...arguments); //console.log('[GooglePayButton]', ...arguments);
@ -32,7 +31,7 @@ class GooglepayButton {
}; };
} }
init( config ) { init( config, transactionInfo ) {
if ( this.isInitialized ) { if ( this.isInitialized ) {
return; return;
} }
@ -47,6 +46,7 @@ class GooglepayButton {
} }
this.googlePayConfig = config; this.googlePayConfig = config;
this.transactionInfo = transactionInfo;
this.allowedPaymentMethods = config.allowedPaymentMethods; this.allowedPaymentMethods = config.allowedPaymentMethods;
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ]; this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
@ -109,7 +109,7 @@ class GooglepayButton {
} }
this.isInitialized = false; this.isInitialized = false;
this.init( this.googlePayConfig ); this.init( this.googlePayConfig, this.transactionInfo );
} }
validateConfig() { validateConfig() {
@ -289,10 +289,11 @@ class GooglepayButton {
/** /**
* Show Google Pay payment sheet when Google Pay payment button is clicked * Show Google Pay payment sheet when Google Pay payment button is clicked
*/ */
async onButtonClick() { onButtonClick() {
this.log( 'onButtonClick', this.context ); this.log( 'onButtonClick', this.context );
const paymentDataRequest = await this.paymentDataRequest(); const paymentDataRequest = this.paymentDataRequest();
this.log( this.log(
'onButtonClick: paymentDataRequest', 'onButtonClick: paymentDataRequest',
paymentDataRequest, paymentDataRequest,
@ -304,7 +305,7 @@ class GooglepayButton {
this.paymentsClient.loadPaymentData( paymentDataRequest ); this.paymentsClient.loadPaymentData( paymentDataRequest );
} }
async paymentDataRequest() { paymentDataRequest() {
const baseRequest = { const baseRequest = {
apiVersion: 2, apiVersion: 2,
apiVersionMinor: 0, apiVersionMinor: 0,
@ -314,8 +315,7 @@ class GooglepayButton {
const paymentDataRequest = Object.assign( {}, baseRequest ); const paymentDataRequest = Object.assign( {}, baseRequest );
paymentDataRequest.allowedPaymentMethods = paymentDataRequest.allowedPaymentMethods =
googlePayConfig.allowedPaymentMethods; googlePayConfig.allowedPaymentMethods;
paymentDataRequest.transactionInfo = paymentDataRequest.transactionInfo = this.transactionInfo;
await this.contextHandler.transactionInfo();
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo; paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
if ( if (
@ -354,43 +354,51 @@ class GooglepayButton {
this.log( 'paymentData', paymentData ); this.log( 'paymentData', paymentData );
return new Promise( async ( resolve, reject ) => { return new Promise( async ( resolve, reject ) => {
const paymentDataRequestUpdate = {}; try {
const paymentDataRequestUpdate = {};
const updatedData = await new UpdatePaymentData( const updatedData = await new UpdatePaymentData(
this.buttonConfig.ajax.update_payment_data this.buttonConfig.ajax.update_payment_data
).update( paymentData ); ).update( paymentData );
const transactionInfo = await this.contextHandler.transactionInfo(); const transactionInfo = this.transactionInfo;
this.log( 'onPaymentDataChanged:updatedData', updatedData ); this.log( 'onPaymentDataChanged:updatedData', updatedData );
this.log( 'onPaymentDataChanged:transactionInfo', transactionInfo ); this.log(
'onPaymentDataChanged:transactionInfo',
transactionInfo
);
updatedData.country_code = transactionInfo.countryCode; updatedData.country_code = transactionInfo.countryCode;
updatedData.currency_code = transactionInfo.currencyCode; updatedData.currency_code = transactionInfo.currencyCode;
updatedData.total_str = transactionInfo.totalPrice; 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;
}
// Handle unserviceable address.
if ( ! updatedData.shipping_options?.shippingOptions?.length ) {
paymentDataRequestUpdate.error =
this.unserviceableShippingAddressError();
resolve( paymentDataRequestUpdate ); resolve( paymentDataRequestUpdate );
return; } catch ( error ) {
console.error( 'Error during onPaymentDataChanged:', error );
reject( error );
} }
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 );
} ); } );
} }

View file

@ -1,39 +1,76 @@
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
import GooglepayButton from './GooglepayButton'; import GooglepayButton from './GooglepayButton';
import ContextHandlerFactory from './Context/ContextHandlerFactory';
class GooglepayManager { class GooglepayManager {
constructor( buttonConfig, ppcpConfig ) { constructor( buttonConfig, ppcpConfig ) {
this.buttonConfig = buttonConfig; this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig; this.ppcpConfig = ppcpConfig;
this.googlePayConfig = null; this.googlePayConfig = null;
this.transactionInfo = null;
this.contextHandler = null;
this.buttons = []; this.buttons = [];
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => { buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => {
this.contextHandler = ContextHandlerFactory.create(
bootstrap.context,
buttonConfig,
ppcpConfig,
bootstrap.handler
);
const button = new GooglepayButton( const button = new GooglepayButton(
bootstrap.context, bootstrap.context,
bootstrap.handler, bootstrap.handler,
buttonConfig, buttonConfig,
ppcpConfig ppcpConfig,
this.contextHandler
); );
this.buttons.push( button ); this.buttons.push( button );
if ( this.googlePayConfig ) { // Initialize button only if googlePayConfig and transactionInfo are already fetched.
button.init( this.googlePayConfig ); 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 );
}
} }
} ); } );
} }
init() { async init() {
( async () => { try {
// Gets GooglePay configuration of the PayPal merchant. if ( ! this.googlePayConfig ) {
this.googlePayConfig = await paypal.Googlepay().config(); // Gets GooglePay configuration of the PayPal merchant.
this.googlePayConfig = await paypal.Googlepay().config();
}
if ( ! this.transactionInfo ) {
this.transactionInfo = await this.fetchTransactionInfo();
}
for ( const button of this.buttons ) { for ( const button of this.buttons ) {
button.init( this.googlePayConfig ); 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() { reinit() {

View file

@ -1,6 +1,7 @@
import GooglepayButton from './GooglepayButton'; import GooglepayButton from './GooglepayButton';
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton'; import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager'; import PreviewButtonManager from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButtonManager';
import ContextHandlerFactory from './Context/ContextHandlerFactory';
/** /**
* Accessor that creates and returns a single PreviewButtonManager instance. * Accessor that creates and returns a single PreviewButtonManager instance.
@ -95,14 +96,22 @@ class GooglePayPreviewButton extends PreviewButton {
} }
createButton( buttonConfig ) { createButton( buttonConfig ) {
const contextHandler = ContextHandlerFactory.create(
'preview',
buttonConfig,
this.ppcpConfig,
null
);
const button = new GooglepayButton( const button = new GooglepayButton(
'preview', 'preview',
null, null,
buttonConfig, buttonConfig,
this.ppcpConfig this.ppcpConfig,
contextHandler
); );
button.init( this.apiConfig ); button.init( this.apiConfig, null );
} }
/** /**