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; }