From 5b054581036fab0486bf08ac9cf00f84d23671ae Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 27 Aug 2024 12:34:50 +0200
Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Decouple=20init=20logic=20?=
=?UTF-8?q?from=20global=20PPCP=20config?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Allow Google Pay logic to initialize on pages that do not provide a global PayPalCommerceGateway object. Required to use CheckoutBootstrap to popuplate billing fields in continuation mode.
---
.../js/ContextBootstrap/CheckoutBootstrap.js | 61 +++++++++++++------
modules/ppcp-googlepay/resources/js/boot.js | 56 ++++++++++++-----
2 files changed, 83 insertions(+), 34 deletions(-)
diff --git a/modules/ppcp-googlepay/resources/js/ContextBootstrap/CheckoutBootstrap.js b/modules/ppcp-googlepay/resources/js/ContextBootstrap/CheckoutBootstrap.js
index 9e1c30e9f..1e1933a10 100644
--- a/modules/ppcp-googlepay/resources/js/ContextBootstrap/CheckoutBootstrap.js
+++ b/modules/ppcp-googlepay/resources/js/ContextBootstrap/CheckoutBootstrap.js
@@ -13,12 +13,34 @@ export class CheckoutBootstrap {
#storage;
/**
- * @type {null|HTMLFormElement}
+ * @type {HTMLFormElement|null}
*/
- #checkoutForm = null;
+ #checkoutForm;
+ /**
+ * @param {GooglePayStorage} storage
+ */
constructor( storage ) {
this.#storage = storage;
+ this.#checkoutForm = CheckoutBootstrap.getCheckoutForm();
+ }
+
+ /**
+ * Indicates if the current page contains a checkout form.
+ *
+ * @return {boolean} True if a checkout form is present.
+ */
+ static isPageWithCheckoutForm() {
+ return null !== CheckoutBootstrap.getCheckoutForm();
+ }
+
+ /**
+ * Retrieves the WooCommerce checkout form element.
+ *
+ * @return {HTMLFormElement|null} The form, or null if not a checkout page.
+ */
+ static getCheckoutForm() {
+ return document.querySelector( CHECKOUT_FORM_SELECTOR );
}
/**
@@ -27,37 +49,32 @@ export class CheckoutBootstrap {
* @return {HTMLFormElement|null} The form, or null if not a checkout page.
*/
get checkoutForm() {
- if ( null === this.#checkoutForm ) {
- this.#checkoutForm = document.querySelector(
- CHECKOUT_FORM_SELECTOR
- );
- }
-
return this.#checkoutForm;
}
/**
- * Indicates, if the current page contains a checkout form.
+ * Initializes the checkout process.
*
- * @return {boolean} True, if a checkout form is present.
+ * @throws {Error} If called on a page without a checkout form.
*/
- get isPageWithCheckoutForm() {
- return null !== this.checkoutForm;
- }
-
init() {
- if ( ! this.isPageWithCheckoutForm ) {
- return;
+ if ( ! this.#checkoutForm ) {
+ throw new Error(
+ 'Checkout form not found. Cannot initialize CheckoutBootstrap.'
+ );
}
this.#populateCheckoutFields();
}
+ /**
+ * Populates checkout fields with stored or customer data.
+ */
#populateCheckoutFields() {
const loggedInData = getWooCommerceCustomerDetails();
- // If customer is logged in, we use the details from the customer profile.
if ( loggedInData ) {
+ // If customer is logged in, we use the details from the customer profile.
return;
}
@@ -68,11 +85,17 @@ export class CheckoutBootstrap {
}
setPayerData( billingData, true );
- this.checkoutForm.addEventListener( 'submit', () =>
- this.#onFormSubmit()
+ this.checkoutForm.addEventListener(
+ 'submit',
+ this.#onFormSubmit.bind( this )
);
}
+ /**
+ * Clean-up when checkout form is submitted.
+ *
+ * Immediately removes the payer details from the localStorage.
+ */
#onFormSubmit() {
this.#storage.clearPayer();
}
diff --git a/modules/ppcp-googlepay/resources/js/boot.js b/modules/ppcp-googlepay/resources/js/boot.js
index 3071998a9..666286ed4 100644
--- a/modules/ppcp-googlepay/resources/js/boot.js
+++ b/modules/ppcp-googlepay/resources/js/boot.js
@@ -1,3 +1,12 @@
+/**
+ * Initialize the GooglePay module in the front end.
+ * In some cases, this module is loaded when the `window.PayPalCommerceGateway` object is not
+ * present. In that case, the page does not contain a Google Pay button, but some other logic
+ * that is related to Google Pay (e.g., the CheckoutBootstrap module)
+ *
+ * @file
+ */
+
import { loadCustomScript } from '@paypal/paypal-js';
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
import GooglepayManager from './GooglepayManager';
@@ -5,31 +14,48 @@ import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Hel
import { CheckoutBootstrap } from './ContextBootstrap/CheckoutBootstrap';
import moduleStorage from './Helper/GooglePayStorage';
-( function ( { buttonConfig, ppcpConfig } ) {
+( function ( { buttonConfig, ppcpConfig = {} } ) {
const context = ppcpConfig.context;
- let manager;
+ function bootstrapPayButton() {
+ if ( ! buttonConfig || ! ppcpConfig ) {
+ return;
+ }
- const bootstrap = function () {
- manager = new GooglepayManager( buttonConfig, ppcpConfig );
+ const manager = new GooglepayManager( buttonConfig, ppcpConfig );
manager.init();
- if ( 'continuation' === context || 'checkout' === context ) {
- const checkoutBootstap = new CheckoutBootstrap( moduleStorage );
-
- checkoutBootstap.init();
- }
- };
-
- setupButtonEvents( function () {
- if ( manager ) {
+ setupButtonEvents( function () {
manager.reinit();
+ } );
+ }
+
+ function bootstrapCheckout() {
+ if ( context && ! [ 'continuation', 'checkout' ].includes( context ) ) {
+ // Context must be missing/empty, or "continuation"/"checkout" to proceed.
+ return;
}
- } );
+ if ( ! CheckoutBootstrap.isPageWithCheckoutForm() ) {
+ return;
+ }
+
+ const checkoutBootstrap = new CheckoutBootstrap( moduleStorage );
+ checkoutBootstrap.init();
+ }
+
+ function bootstrap() {
+ bootstrapPayButton();
+ bootstrapCheckout();
+ }
document.addEventListener( 'DOMContentLoaded', () => {
if ( ! buttonConfig || ! ppcpConfig ) {
- // No PayPal buttons present on this page.
+ /*
+ * No PayPal buttons present on this page, but maybe a bootstrap module needs to be
+ * initialized. Run bootstrap without trying to load an SDK or payment configuration.
+ */
+ bootstrap();
+
return;
}