Merge pull request #2688 from woocommerce/PCP-3374-google-pay-button-not-displayed-in-the-word-press-editor-v2

Google Pay: Fix button preview in the editor (3374)
This commit is contained in:
Emili Castells 2024-10-11 14:54:25 +02:00 committed by GitHub
commit 469d42f6bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 287 additions and 90 deletions

View file

@ -0,0 +1,53 @@
import { useState, useEffect } from '@wordpress/element';
import useGooglepayApiToGenerateButton from '../hooks/useGooglepayApiToGenerateButton';
import usePayPalScript from '../hooks/usePayPalScript';
import useGooglepayScript from '../hooks/useGooglepayScript';
import useGooglepayConfig from '../hooks/useGooglepayConfig';
const GooglepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
const [ buttonHtml, setButtonHtml ] = useState( '' );
const [ buttonElement, setButtonElement ] = useState( null );
const [ componentFrame, setComponentFrame ] = useState( null );
const isPayPalLoaded = usePayPalScript( namespace, ppcpConfig );
const isGooglepayLoaded = useGooglepayScript(
componentFrame,
buttonConfig,
isPayPalLoaded
);
const googlepayConfig = useGooglepayConfig( namespace, isGooglepayLoaded );
useEffect( () => {
if ( ! buttonElement ) {
return;
}
setComponentFrame( buttonElement.ownerDocument );
}, [ buttonElement ] );
const googlepayButton = useGooglepayApiToGenerateButton(
componentFrame,
namespace,
buttonConfig,
ppcpConfig,
googlepayConfig
);
useEffect( () => {
if ( googlepayButton ) {
const hideLoader =
'<style>.block-editor-iframe__html .gpay-card-info-animated-progress-bar-container {display:none !important}</style>';
setButtonHtml( googlepayButton.outerHTML + hideLoader );
}
}, [ googlepayButton ] );
return (
<div
ref={ setButtonElement }
dangerouslySetInnerHTML={ { __html: buttonHtml } }
/>
);
};
export default GooglepayButton;

View file

@ -0,0 +1,19 @@
import { useMemo } from '@wordpress/element';
import { combineStyles } from '../../../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
const useButtonStyles = ( buttonConfig, ppcpConfig ) => {
return useMemo( () => {
const styles = combineStyles(
ppcpConfig?.button || {},
buttonConfig?.button || {}
);
if ( styles.MiniCart && styles.MiniCart.type === 'buy' ) {
styles.MiniCart.type = 'pay';
}
return styles;
}, [ buttonConfig, ppcpConfig ] );
};
export default useButtonStyles;

View file

@ -0,0 +1,57 @@
import { useEffect, useState } from '@wordpress/element';
import useButtonStyles from './useButtonStyles';
const useGooglepayApiToGenerateButton = (
componentDocument,
namespace,
buttonConfig,
ppcpConfig,
googlepayConfig
) => {
const [ googlepayButton, setGooglepayButton ] = useState( null );
const buttonStyles = useButtonStyles( buttonConfig, ppcpConfig );
useEffect( () => {
if (
! componentDocument?.defaultView ||
! buttonConfig ||
! googlepayConfig
) {
return;
}
const api = componentDocument.defaultView.google?.payments?.api;
if ( ! api ) {
return;
}
const paymentsClient = new api.PaymentsClient( {
environment: 'TEST',
} );
const googlePayButtonOptions = {
allowedPaymentMethods: googlepayConfig.allowedPaymentMethods,
buttonColor: buttonConfig.buttonColor || 'black',
buttonType: buttonConfig.buttonType || 'pay',
buttonLocale: buttonConfig.buttonLocale || 'en',
buttonSizeMode: 'fill',
};
const button = paymentsClient.createButton( {
...googlePayButtonOptions,
onClick: ( event ) => {
event.preventDefault();
},
} );
setGooglepayButton( button );
return () => {
setGooglepayButton( null );
};
}, [ namespace, buttonConfig, ppcpConfig, googlepayConfig, buttonStyles ] );
return googlepayButton;
};
export default useGooglepayApiToGenerateButton;

View file

@ -0,0 +1,26 @@
import { useState, useEffect } from '@wordpress/element';
const useGooglepayConfig = ( namespace, isGooglepayLoaded ) => {
const [ googlePayConfig, setGooglePayConfig ] = useState( null );
useEffect( () => {
const fetchConfig = async () => {
if ( ! isGooglepayLoaded ) {
return;
}
try {
const config = await window[ namespace ].Googlepay().config();
setGooglePayConfig( config );
} catch ( error ) {
console.error( 'Failed to fetch Google Pay config:', error );
}
};
fetchConfig();
}, [ namespace, isGooglepayLoaded ] );
return googlePayConfig;
};
export default useGooglepayConfig;

View file

@ -0,0 +1,65 @@
import { useState, useEffect } from '@wordpress/element';
import { loadCustomScript } from '@paypal/paypal-js';
const useGooglepayScript = (
componentDocument,
buttonConfig,
isPayPalLoaded
) => {
const [ isGooglepayLoaded, setIsGooglepayLoaded ] = useState( false );
useEffect( () => {
if ( ! componentDocument ) {
return;
}
const injectScriptToFrame = ( scriptSrc ) => {
if ( document === componentDocument ) {
return;
}
const script = document.querySelector(
`script[src^="${ scriptSrc }"]`
);
if ( script ) {
const newScript = componentDocument.createElement( 'script' );
newScript.src = script.src;
newScript.async = script.async;
newScript.type = script.type;
componentDocument.head.appendChild( newScript );
} else {
console.error( 'Script not found in the document:', scriptSrc );
}
};
const loadGooglepayScript = async () => {
if ( ! isPayPalLoaded ) {
return;
}
if ( ! buttonConfig || ! buttonConfig.sdk_url ) {
console.error( 'Invalid buttonConfig or missing sdk_url' );
return;
}
try {
await loadCustomScript( { url: buttonConfig.sdk_url } ).then(
() => {
injectScriptToFrame( buttonConfig.sdk_url );
}
);
setIsGooglepayLoaded( true );
} catch ( error ) {
console.error( 'Failed to load Googlepay script:', error );
}
};
loadGooglepayScript();
}, [ componentDocument, buttonConfig, isPayPalLoaded ] );
return isGooglepayLoaded;
};
export default useGooglepayScript;

View file

@ -0,0 +1,25 @@
import { useState, useEffect } from '@wordpress/element';
import { loadPayPalScript } from '../../../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
const usePayPalScript = ( namespace, ppcpConfig ) => {
const [ isPayPalLoaded, setIsPayPalLoaded ] = useState( false );
ppcpConfig.url_params.components += ',googlepay';
useEffect( () => {
const loadScript = async () => {
try {
await loadPayPalScript( namespace, ppcpConfig );
setIsPayPalLoaded( true );
} catch ( error ) {
console.error( `Error loading PayPal script: ${ error }` );
}
};
loadScript();
}, [ namespace, ppcpConfig ] );
return isPayPalLoaded;
};
export default usePayPalScript;

View file

@ -1,62 +1,15 @@
import GooglepayButton from './GooglepayButton';
import ContextHandlerFactory from './Context/ContextHandlerFactory';
import GooglepayButton from './Block/components/GooglepayButton';
class GooglepayManagerBlockEditor {
constructor( namespace, buttonConfig, ppcpConfig ) {
this.namespace = namespace;
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
this.googlePayConfig = null;
this.transactionInfo = null;
this.contextHandler = null;
}
init() {
( async () => {
await this.config();
} )();
}
async config() {
try {
// Gets GooglePay configuration of the PayPal merchant.
this.googlePayConfig = await window[ this.namespace ]
.Googlepay()
.config();
// Fetch transaction information.
this.transactionInfo = await this.fetchTransactionInfo();
const button = new GooglepayButton(
this.ppcpConfig.context,
null,
this.buttonConfig,
this.ppcpConfig,
this.contextHandler
);
button.init( this.googlePayConfig, this.transactionInfo );
} catch ( error ) {
console.error( 'Failed to initialize Google Pay:', error );
}
}
async fetchTransactionInfo() {
try {
if ( ! this.contextHandler ) {
this.contextHandler = ContextHandlerFactory.create(
this.ppcpConfig.context,
this.buttonConfig,
this.ppcpConfig,
null
);
}
return null;
} catch ( error ) {
console.error( 'Error fetching transaction info:', error );
throw error;
}
}
}
const GooglepayManagerBlockEditor = ( {
namespace,
buttonConfig,
ppcpConfig,
} ) => (
<GooglepayButton
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
/>
);
export default GooglepayManagerBlockEditor;

View file

@ -1,4 +1,5 @@
import { useEffect, useState } from '@wordpress/element';
import { loadCustomScript } from '@paypal/paypal-js';
import {
registerExpressPaymentMethod,
registerPaymentMethod,
@ -6,7 +7,6 @@ import {
import { __ } from '@wordpress/i18n';
import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
import GooglepayManager from './GooglepayManager';
import { loadCustomScript } from '@paypal/paypal-js';
import GooglepayManagerBlockEditor from './GooglepayManagerBlockEditor';
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
@ -20,56 +20,55 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
window.PayPalCommerceGateway = ppcpConfig;
}
const GooglePayComponent = ( props ) => {
const [ bootstrapped, setBootstrapped ] = useState( false );
const GooglePayComponent = ( { isEditing } ) => {
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
const [ googlePayLoaded, setGooglePayLoaded ] = useState( false );
const [ manager, setManager ] = useState( null );
useEffect( () => {
// Load GooglePay SDK
loadCustomScript( { url: buttonConfig.sdk_url } ).then( () => {
setGooglePayLoaded( true );
} );
ppcpConfig.url_params.components += ',googlepay';
// Load PayPal
loadPayPalScript( namespace, ppcpConfig )
.then( () => {
setPaypalLoaded( true );
} )
.catch( ( error ) => {
console.error( 'Failed to load PayPal script: ', error );
if ( ! isEditing ) {
loadCustomScript( { url: buttonConfig.sdk_url } ).then( () => {
setGooglePayLoaded( true );
} );
}, [] );
ppcpConfig.url_params.components += ',googlepay';
loadPayPalScript( namespace, ppcpConfig )
.then( () => {
setPaypalLoaded( true );
} )
.catch( ( error ) => {
console.error( 'Failed to load PayPal script: ', error );
} );
}
}, [ isEditing, buttonConfig, ppcpConfig ] );
useEffect( () => {
if ( paypalLoaded && googlePayLoaded && ! manager ) {
const ManagerClass = props.isEditing
? GooglepayManagerBlockEditor
: GooglepayManager;
const newManager = new ManagerClass(
if ( ! isEditing && paypalLoaded && googlePayLoaded && ! manager ) {
const newManager = new GooglepayManager(
namespace,
buttonConfig,
ppcpConfig
);
setManager( newManager );
}
}, [ paypalLoaded, googlePayLoaded, props.isEditing ] );
}, [ paypalLoaded, googlePayLoaded, isEditing, manager ] );
useEffect( () => {
if ( manager && ! bootstrapped ) {
setBootstrapped( true );
manager.init();
}
}, [ manager, bootstrapped ] );
if ( isEditing ) {
return (
<GooglepayManagerBlockEditor
namespace={ namespace }
buttonConfig={ buttonConfig }
ppcpConfig={ ppcpConfig }
/>
);
}
return (
<div
id={ buttonConfig.button.wrapper.replace( '#', '' ) }
className="ppcp-button-apm ppcp-button-googlepay"
></div>
/>
);
};