mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Merge pull request #2675 from woocommerce/PCP-3771-axo-refactor-pay-pal-script-loading-to-prevent-conflicts
Add a new namespaced script loader (3771)
This commit is contained in:
commit
be6b8a39ec
17 changed files with 416 additions and 97 deletions
|
@ -15,12 +15,20 @@ import useCardChange from './useCardChange';
|
|||
/**
|
||||
* Custom hook to set up AXO functionality.
|
||||
*
|
||||
* @param {Object} ppcpConfig - PayPal Checkout configuration.
|
||||
* @param {Object} fastlaneSdk - Fastlane SDK instance.
|
||||
* @param {Object} paymentComponent - Payment component instance.
|
||||
* @param {string} namespace - Namespace for the PayPal script.
|
||||
* @param {Object} ppcpConfig - PayPal Checkout configuration.
|
||||
* @param {boolean} isConfigLoaded - Whether the PayPal config has loaded.
|
||||
* @param {Object} fastlaneSdk - Fastlane SDK instance.
|
||||
* @param {Object} paymentComponent - Payment component instance.
|
||||
* @return {boolean} Whether PayPal script has loaded.
|
||||
*/
|
||||
const useAxoSetup = ( ppcpConfig, fastlaneSdk, paymentComponent ) => {
|
||||
const useAxoSetup = (
|
||||
namespace,
|
||||
ppcpConfig,
|
||||
isConfigLoaded,
|
||||
fastlaneSdk,
|
||||
paymentComponent
|
||||
) => {
|
||||
// Get dispatch functions from the AXO store
|
||||
const {
|
||||
setIsAxoActive,
|
||||
|
@ -30,7 +38,11 @@ const useAxoSetup = ( ppcpConfig, fastlaneSdk, paymentComponent ) => {
|
|||
} = useDispatch( STORE_NAME );
|
||||
|
||||
// Check if PayPal script has loaded
|
||||
const paypalLoaded = usePayPalScript( ppcpConfig );
|
||||
const paypalLoaded = usePayPalScript(
|
||||
namespace,
|
||||
ppcpConfig,
|
||||
isConfigLoaded
|
||||
);
|
||||
|
||||
// Set up card and shipping address change handlers
|
||||
const onChangeCardButtonClick = useCardChange( fastlaneSdk );
|
||||
|
|
|
@ -1,24 +1,31 @@
|
|||
import { useEffect, useRef, useState, useMemo } from '@wordpress/element';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import Fastlane from '../../../../ppcp-axo/resources/js/Connection/Fastlane';
|
||||
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
|
||||
import { useDeleteEmptyKeys } from './useDeleteEmptyKeys';
|
||||
import { STORE_NAME } from '../stores/axoStore';
|
||||
|
||||
/**
|
||||
* Custom hook to initialize and manage the Fastlane SDK.
|
||||
*
|
||||
* @param {string} namespace - Namespace for the PayPal script.
|
||||
* @param {Object} axoConfig - Configuration for AXO.
|
||||
* @param {Object} ppcpConfig - Configuration for PPCP.
|
||||
* @return {Object|null} The initialized Fastlane SDK instance or null.
|
||||
*/
|
||||
const useFastlaneSdk = ( axoConfig, ppcpConfig ) => {
|
||||
const useFastlaneSdk = ( namespace, axoConfig, ppcpConfig ) => {
|
||||
const [ fastlaneSdk, setFastlaneSdk ] = useState( null );
|
||||
// Ref to prevent multiple simultaneous initializations
|
||||
const initializingRef = useRef( false );
|
||||
// Ref to hold the latest config values
|
||||
const configRef = useRef( { axoConfig, ppcpConfig } );
|
||||
// Custom hook to remove empty keys from an object
|
||||
const deleteEmptyKeys = useDeleteEmptyKeys();
|
||||
|
||||
const { isPayPalLoaded } = useSelect(
|
||||
( select ) => ( {
|
||||
isPayPalLoaded: select( STORE_NAME ).getIsPayPalLoaded(),
|
||||
} ),
|
||||
[]
|
||||
);
|
||||
|
||||
const styleOptions = useMemo( () => {
|
||||
return deleteEmptyKeys( configRef.current.axoConfig.style_options );
|
||||
}, [ deleteEmptyKeys ] );
|
||||
|
@ -26,7 +33,7 @@ const useFastlaneSdk = ( axoConfig, ppcpConfig ) => {
|
|||
// Effect to initialize Fastlane SDK
|
||||
useEffect( () => {
|
||||
const initFastlane = async () => {
|
||||
if ( initializingRef.current || fastlaneSdk ) {
|
||||
if ( initializingRef.current || fastlaneSdk || ! isPayPalLoaded ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,7 +41,7 @@ const useFastlaneSdk = ( axoConfig, ppcpConfig ) => {
|
|||
log( 'Init Fastlane' );
|
||||
|
||||
try {
|
||||
const fastlane = new Fastlane();
|
||||
const fastlane = new Fastlane( namespace );
|
||||
|
||||
// Set sandbox environment if configured
|
||||
if ( configRef.current.axoConfig.environment.is_sandbox ) {
|
||||
|
@ -59,7 +66,7 @@ const useFastlaneSdk = ( axoConfig, ppcpConfig ) => {
|
|||
};
|
||||
|
||||
initFastlane();
|
||||
}, [ fastlaneSdk, styleOptions ] );
|
||||
}, [ fastlaneSdk, styleOptions, isPayPalLoaded, namespace ] );
|
||||
|
||||
// Effect to update the config ref when configs change
|
||||
useEffect( () => {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import { useState, useEffect } from '@wordpress/element';
|
||||
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
|
||||
|
||||
/**
|
||||
* Custom hook to load and manage the PayPal Commerce Gateway configuration.
|
||||
*
|
||||
* @param {Object} initialConfig - Initial configuration object.
|
||||
* @return {Object} An object containing the loaded config and a boolean indicating if it's loaded.
|
||||
*/
|
||||
const usePayPalCommerceGateway = ( initialConfig ) => {
|
||||
const [ isConfigLoaded, setIsConfigLoaded ] = useState( false );
|
||||
const [ ppcpConfig, setPpcpConfig ] = useState( initialConfig );
|
||||
|
||||
useEffect( () => {
|
||||
/**
|
||||
* Function to load the PayPal Commerce Gateway configuration.
|
||||
*/
|
||||
const loadConfig = () => {
|
||||
if ( typeof window.PayPalCommerceGateway !== 'undefined' ) {
|
||||
setPpcpConfig( window.PayPalCommerceGateway );
|
||||
setIsConfigLoaded( true );
|
||||
} else {
|
||||
log( 'PayPal Commerce Gateway config not loaded.', 'error' );
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the DOM is still loading
|
||||
if ( document.readyState === 'loading' ) {
|
||||
// If it's loading, add an event listener for when the DOM is fully loaded
|
||||
document.addEventListener( 'DOMContentLoaded', loadConfig );
|
||||
} else {
|
||||
// If it's already loaded, call the loadConfig function immediately
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
// Cleanup function to remove the event listener
|
||||
return () => {
|
||||
document.removeEventListener( 'DOMContentLoaded', loadConfig );
|
||||
};
|
||||
}, [] );
|
||||
|
||||
// Return the loaded configuration and the loading status
|
||||
return { isConfigLoaded, ppcpConfig };
|
||||
};
|
||||
|
||||
export default usePayPalCommerceGateway;
|
|
@ -1,29 +1,48 @@
|
|||
import { useState, useEffect } from '@wordpress/element';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
|
||||
import { loadPaypalScript } from '../../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import { loadPayPalScript } from '../../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
|
||||
import { STORE_NAME } from '../stores/axoStore';
|
||||
|
||||
/**
|
||||
* Custom hook to load the PayPal script.
|
||||
*
|
||||
* @param {Object} ppcpConfig - Configuration object for PayPal script.
|
||||
* @param {string} namespace - Namespace for the PayPal script.
|
||||
* @param {Object} ppcpConfig - Configuration object for PayPal script.
|
||||
* @param {boolean} isConfigLoaded - Whether the PayPal Commerce Gateway config is loaded.
|
||||
* @return {boolean} True if the PayPal script has loaded, false otherwise.
|
||||
*/
|
||||
const usePayPalScript = ( ppcpConfig ) => {
|
||||
const [ isLoaded, setIsLoaded ] = useState( false );
|
||||
const usePayPalScript = ( namespace, ppcpConfig, isConfigLoaded ) => {
|
||||
// Get dispatch functions from the AXO store
|
||||
const { setIsPayPalLoaded } = useDispatch( STORE_NAME );
|
||||
|
||||
// Select relevant states from the AXO store
|
||||
const { isPayPalLoaded } = useSelect(
|
||||
( select ) => ( {
|
||||
isPayPalLoaded: select( STORE_NAME ).getIsPayPalLoaded(),
|
||||
} ),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! isLoaded ) {
|
||||
log( 'Loading PayPal script' );
|
||||
const loadScript = async () => {
|
||||
if ( ! isPayPalLoaded && isConfigLoaded ) {
|
||||
try {
|
||||
await loadPayPalScript( namespace, ppcpConfig );
|
||||
setIsPayPalLoaded( true );
|
||||
} catch ( error ) {
|
||||
log(
|
||||
`Error loading PayPal script for namespace: ${ namespace }. Error: ${ error }`,
|
||||
'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load the PayPal script using the provided configuration
|
||||
loadPaypalScript( ppcpConfig, () => {
|
||||
log( 'PayPal script loaded' );
|
||||
setIsLoaded( true );
|
||||
} );
|
||||
}
|
||||
}, [ ppcpConfig, isLoaded ] );
|
||||
loadScript();
|
||||
}, [ ppcpConfig, isConfigLoaded, isPayPalLoaded ] );
|
||||
|
||||
return isLoaded;
|
||||
return isPayPalLoaded;
|
||||
};
|
||||
|
||||
export default usePayPalScript;
|
||||
|
|
|
@ -9,25 +9,25 @@ import useAxoSetup from './hooks/useAxoSetup';
|
|||
import useAxoCleanup from './hooks/useAxoCleanup';
|
||||
import useHandlePaymentSetup from './hooks/useHandlePaymentSetup';
|
||||
import usePaymentSetupEffect from './hooks/usePaymentSetupEffect';
|
||||
import usePayPalCommerceGateway from './hooks/usePayPalCommerceGateway';
|
||||
|
||||
// Components
|
||||
import { Payment } from './components/Payment/Payment';
|
||||
|
||||
const gatewayHandle = 'ppcp-axo-gateway';
|
||||
const ppcpConfig = wc.wcSettings.getSetting( `${ gatewayHandle }_data` );
|
||||
|
||||
if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
|
||||
window.PayPalCommerceGateway = ppcpConfig;
|
||||
}
|
||||
|
||||
const axoConfig = window.wc_ppcp_axo;
|
||||
|
||||
const namespace = 'ppcpBlocksPaypalAxo';
|
||||
const initialConfig = wc.wcSettings.getSetting( `${ gatewayHandle }_data` );
|
||||
const Axo = ( props ) => {
|
||||
const { eventRegistration, emitResponse } = props;
|
||||
const { onPaymentSetup } = eventRegistration;
|
||||
const [ paymentComponent, setPaymentComponent ] = useState( null );
|
||||
|
||||
const fastlaneSdk = useFastlaneSdk( axoConfig, ppcpConfig );
|
||||
const { isConfigLoaded, ppcpConfig } =
|
||||
usePayPalCommerceGateway( initialConfig );
|
||||
|
||||
const axoConfig = window.wc_ppcp_axo;
|
||||
|
||||
const fastlaneSdk = useFastlaneSdk( namespace, axoConfig, ppcpConfig );
|
||||
const tokenizedCustomerData = useTokenizeCustomerData();
|
||||
const handlePaymentSetup = useHandlePaymentSetup(
|
||||
emitResponse,
|
||||
|
@ -35,7 +35,13 @@ const Axo = ( props ) => {
|
|||
tokenizedCustomerData
|
||||
);
|
||||
|
||||
useAxoSetup( ppcpConfig, fastlaneSdk, paymentComponent );
|
||||
const isScriptLoaded = useAxoSetup(
|
||||
namespace,
|
||||
ppcpConfig,
|
||||
isConfigLoaded,
|
||||
fastlaneSdk,
|
||||
paymentComponent
|
||||
);
|
||||
|
||||
const { handlePaymentLoad } = usePaymentSetupEffect(
|
||||
onPaymentSetup,
|
||||
|
@ -45,31 +51,57 @@ const Axo = ( props ) => {
|
|||
|
||||
useAxoCleanup();
|
||||
|
||||
return fastlaneSdk ? (
|
||||
if ( ! isConfigLoaded ) {
|
||||
return (
|
||||
<>
|
||||
{ __(
|
||||
'Loading configuration…',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! isScriptLoaded ) {
|
||||
return (
|
||||
<>
|
||||
{ __(
|
||||
'Loading PayPal script…',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! fastlaneSdk ) {
|
||||
return (
|
||||
<>{ __( 'Loading Fastlane…', 'woocommerce-paypal-payments' ) }</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Payment
|
||||
fastlaneSdk={ fastlaneSdk }
|
||||
onPaymentLoad={ handlePaymentLoad }
|
||||
/>
|
||||
) : (
|
||||
<>{ __( 'Loading Fastlane…', 'woocommerce-paypal-payments' ) }</>
|
||||
);
|
||||
};
|
||||
|
||||
registerPaymentMethod( {
|
||||
name: ppcpConfig.id,
|
||||
name: initialConfig.id,
|
||||
label: (
|
||||
<div
|
||||
id="ppcp-axo-block-radio-label"
|
||||
dangerouslySetInnerHTML={ { __html: ppcpConfig.title } }
|
||||
dangerouslySetInnerHTML={ { __html: initialConfig.title } }
|
||||
/>
|
||||
),
|
||||
content: <Axo />,
|
||||
edit: createElement( ppcpConfig.title ),
|
||||
ariaLabel: ppcpConfig.title,
|
||||
edit: createElement( initialConfig.title ),
|
||||
ariaLabel: initialConfig.title,
|
||||
canMakePayment: () => true,
|
||||
supports: {
|
||||
showSavedCards: true,
|
||||
features: ppcpConfig.supports,
|
||||
features: initialConfig.supports,
|
||||
},
|
||||
} );
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createReduxStore, register, dispatch } from '@wordpress/data';
|
|||
export const STORE_NAME = 'woocommerce-paypal-payments/axo-block';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
isPayPalLoaded: false,
|
||||
isGuest: true,
|
||||
isAxoActive: false,
|
||||
isAxoScriptLoaded: false,
|
||||
|
@ -15,6 +16,10 @@ const DEFAULT_STATE = {
|
|||
|
||||
// Action creators for updating the store state
|
||||
const actions = {
|
||||
setIsPayPalLoaded: ( isPayPalLoaded ) => ( {
|
||||
type: 'SET_IS_PAYPAL_LOADED',
|
||||
payload: isPayPalLoaded,
|
||||
} ),
|
||||
setIsGuest: ( isGuest ) => ( {
|
||||
type: 'SET_IS_GUEST',
|
||||
payload: isGuest,
|
||||
|
@ -58,6 +63,8 @@ const actions = {
|
|||
*/
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case 'SET_IS_PAYPAL_LOADED':
|
||||
return { ...state, isPayPalLoaded: action.payload };
|
||||
case 'SET_IS_GUEST':
|
||||
return { ...state, isGuest: action.payload };
|
||||
case 'SET_IS_AXO_ACTIVE':
|
||||
|
@ -81,6 +88,7 @@ const reducer = ( state = DEFAULT_STATE, action ) => {
|
|||
|
||||
// Selector functions to retrieve specific pieces of state
|
||||
const selectors = {
|
||||
getIsPayPalLoaded: ( state ) => state.isPayPalLoaded,
|
||||
getIsGuest: ( state ) => state.isGuest,
|
||||
getIsAxoActive: ( state ) => state.isAxoActive,
|
||||
getIsAxoScriptLoaded: ( state ) => state.isAxoScriptLoaded,
|
||||
|
@ -102,6 +110,15 @@ register( store );
|
|||
|
||||
// Action dispatchers
|
||||
|
||||
/**
|
||||
* Action dispatcher to update the PayPal script load status in the store.
|
||||
*
|
||||
* @param {boolean} isPayPalLoaded - Whether the PayPal script has loaded.
|
||||
*/
|
||||
export const setIsPayPalLoaded = ( isPayPalLoaded ) => {
|
||||
dispatch( STORE_NAME ).setIsPayPalLoaded( isPayPalLoaded );
|
||||
};
|
||||
|
||||
/**
|
||||
* Action dispatcher to update the guest status in the store.
|
||||
*
|
||||
|
|
|
@ -52,11 +52,12 @@ class AxoManager {
|
|||
billingView = null;
|
||||
cardView = null;
|
||||
|
||||
constructor( axoConfig, ppcpConfig ) {
|
||||
constructor( namespace, axoConfig, ppcpConfig ) {
|
||||
this.namespace = namespace;
|
||||
this.axoConfig = axoConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
|
||||
this.fastlane = new Fastlane();
|
||||
this.fastlane = new Fastlane( namespace );
|
||||
this.$ = jQuery;
|
||||
|
||||
this.status = {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class Fastlane {
|
||||
construct() {
|
||||
constructor( namespace ) {
|
||||
this.namespace = namespace;
|
||||
this.connection = null;
|
||||
this.identity = null;
|
||||
this.profile = null;
|
||||
|
@ -10,7 +11,16 @@ class Fastlane {
|
|||
|
||||
connect( config ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
window.paypal
|
||||
if ( ! window[ this.namespace ] ) {
|
||||
reject(
|
||||
new Error(
|
||||
`Namespace ${ this.namespace } not found on window object`
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
window[ this.namespace ]
|
||||
.Fastlane( config )
|
||||
.then( ( result ) => {
|
||||
this.init( result );
|
||||
|
@ -18,7 +28,7 @@ class Fastlane {
|
|||
} )
|
||||
.catch( ( error ) => {
|
||||
console.error( error );
|
||||
reject();
|
||||
reject( error );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -1,21 +1,27 @@
|
|||
import AxoManager from './AxoManager';
|
||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
|
||||
import { log } from './Helper/Debug';
|
||||
|
||||
( function ( { axoConfig, ppcpConfig, jQuery } ) {
|
||||
const namespace = 'ppcpPaypalClassicAxo';
|
||||
const bootstrap = () => {
|
||||
new AxoManager( axoConfig, ppcpConfig );
|
||||
new AxoManager( namespace, axoConfig, ppcpConfig );
|
||||
};
|
||||
|
||||
document.addEventListener( 'DOMContentLoaded', () => {
|
||||
if ( ! typeof PayPalCommerceGateway ) {
|
||||
if ( typeof PayPalCommerceGateway === 'undefined' ) {
|
||||
console.error( 'AXO could not be configured.' );
|
||||
return;
|
||||
}
|
||||
|
||||
// Load PayPal
|
||||
loadPaypalScript( ppcpConfig, () => {
|
||||
bootstrap();
|
||||
} );
|
||||
loadPayPalScript( namespace, ppcpConfig )
|
||||
.then( () => {
|
||||
bootstrap();
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
log( `Failed to load PayPal script: ${ error }`, 'error' );
|
||||
} );
|
||||
} );
|
||||
} )( {
|
||||
axoConfig: window.wc_ppcp_axo,
|
||||
|
|
|
@ -378,7 +378,8 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
|||
return ! is_user_logged_in()
|
||||
&& CartCheckoutDetector::has_classic_checkout()
|
||||
&& $dcc_configuration->use_fastlane()
|
||||
&& ! $this->is_excluded_endpoint();
|
||||
&& ! $this->is_excluded_endpoint()
|
||||
&& is_checkout();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,11 +15,12 @@ import {
|
|||
cartHasSubscriptionProducts,
|
||||
isPayPalSubscription,
|
||||
} from './Helper/Subscription';
|
||||
import { loadPaypalScriptPromise } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
|
||||
import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js';
|
||||
import { normalizeStyleForFundingSource } from '../../../ppcp-button/resources/js/modules/Helper/Style';
|
||||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||
import BlockCheckoutMessagesBootstrap from './Bootstrap/BlockCheckoutMessagesBootstrap';
|
||||
const namespace = 'ppcpBlocksPaypalExpressButtons';
|
||||
const config = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
|
||||
|
||||
window.ppcpFundingSource = config.fundingSource;
|
||||
|
@ -55,7 +56,10 @@ const PayPalComponent = ( {
|
|||
if ( ! paypalScriptLoaded ) {
|
||||
if ( ! paypalScriptPromise ) {
|
||||
// for editor, since canMakePayment was not called
|
||||
paypalScriptPromise = loadPaypalScriptPromise( config.scriptData );
|
||||
paypalScriptPromise = loadPayPalScript(
|
||||
namespace,
|
||||
config.scriptData
|
||||
);
|
||||
}
|
||||
paypalScriptPromise.then( () => setPaypalScriptLoaded( true ) );
|
||||
}
|
||||
|
@ -614,7 +618,10 @@ const PayPalComponent = ( {
|
|||
return null;
|
||||
}
|
||||
|
||||
const PayPalButton = paypal.Buttons.driver( 'react', { React, ReactDOM } );
|
||||
const PayPalButton = ppcpBlocksPaypalExpressButtons.Buttons.driver(
|
||||
'react',
|
||||
{ React, ReactDOM }
|
||||
);
|
||||
|
||||
const getOnShippingOptionsChange = ( fundingSource ) => {
|
||||
if ( fundingSource === 'venmo' ) {
|
||||
|
@ -818,7 +825,8 @@ if ( block_enabled && config.enabled ) {
|
|||
ariaLabel: config.title,
|
||||
canMakePayment: async () => {
|
||||
if ( ! paypalScriptPromise ) {
|
||||
paypalScriptPromise = loadPaypalScriptPromise(
|
||||
paypalScriptPromise = loadPayPalScript(
|
||||
namespace,
|
||||
config.scriptData
|
||||
);
|
||||
paypalScriptPromise.then( () => {
|
||||
|
@ -831,7 +839,9 @@ if ( block_enabled && config.enabled ) {
|
|||
}
|
||||
await paypalScriptPromise;
|
||||
|
||||
return paypal.Buttons( { fundingSource } ).isEligible();
|
||||
return ppcpBlocksPaypalExpressButtons
|
||||
.Buttons( { fundingSource } )
|
||||
.isEligible();
|
||||
},
|
||||
supports: {
|
||||
features,
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import merge from 'deepmerge';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { keysToCamelCase } from './Utils';
|
||||
|
||||
const processAxoConfig = ( config ) => {
|
||||
const scriptOptions = {};
|
||||
const sdkClientToken = config?.axo?.sdk_client_token;
|
||||
const uuid = uuidv4().replace( /-/g, '' );
|
||||
if ( sdkClientToken ) {
|
||||
scriptOptions[ 'data-sdk-client-token' ] = sdkClientToken;
|
||||
scriptOptions[ 'data-client-metadata-id' ] = uuid;
|
||||
}
|
||||
return scriptOptions;
|
||||
};
|
||||
|
||||
const processUserIdToken = ( config, sdkClientToken ) => {
|
||||
const userIdToken = config?.save_payment_methods?.id_token;
|
||||
return userIdToken && ! sdkClientToken
|
||||
? { 'data-user-id-token': userIdToken }
|
||||
: {};
|
||||
};
|
||||
|
||||
export const processConfig = ( config ) => {
|
||||
let scriptOptions = keysToCamelCase( config.url_params );
|
||||
if ( config.script_attributes ) {
|
||||
scriptOptions = merge( scriptOptions, config.script_attributes );
|
||||
}
|
||||
const axoOptions = processAxoConfig( config );
|
||||
const userIdTokenOptions = processUserIdToken(
|
||||
config,
|
||||
axoOptions[ 'data-sdk-client-token' ]
|
||||
);
|
||||
return merge.all( [ scriptOptions, axoOptions, userIdTokenOptions ] );
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
import { loadScript } from '@paypal/paypal-js';
|
||||
import dataClientIdAttributeHandler from '../DataClientIdAttributeHandler';
|
||||
import widgetBuilder from '../Renderer/WidgetBuilder';
|
||||
import { processConfig } from './ConfigProcessor';
|
||||
|
||||
const loadedScripts = new Map();
|
||||
const scriptPromises = new Map();
|
||||
|
||||
const handleDataClientIdAttribute = async ( scriptOptions, config ) => {
|
||||
if (
|
||||
config.data_client_id?.set_attribute &&
|
||||
config.vault_v3_enabled !== '1'
|
||||
) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
dataClientIdAttributeHandler(
|
||||
scriptOptions,
|
||||
config.data_client_id,
|
||||
( paypal ) => {
|
||||
widgetBuilder.setPaypal( paypal );
|
||||
resolve( paypal );
|
||||
},
|
||||
reject
|
||||
);
|
||||
} );
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loadPayPalScript = async ( namespace, config ) => {
|
||||
if ( ! namespace ) {
|
||||
throw new Error( 'Namespace is required' );
|
||||
}
|
||||
|
||||
if ( loadedScripts.has( namespace ) ) {
|
||||
console.log( `Script already loaded for namespace: ${ namespace }` );
|
||||
return loadedScripts.get( namespace );
|
||||
}
|
||||
|
||||
if ( scriptPromises.has( namespace ) ) {
|
||||
console.log(
|
||||
`Script loading in progress for namespace: ${ namespace }`
|
||||
);
|
||||
return scriptPromises.get( namespace );
|
||||
}
|
||||
|
||||
const scriptOptions = {
|
||||
...processConfig( config ),
|
||||
'data-namespace': namespace,
|
||||
};
|
||||
|
||||
const dataClientIdResult = await handleDataClientIdAttribute(
|
||||
scriptOptions,
|
||||
config
|
||||
);
|
||||
if ( dataClientIdResult ) {
|
||||
return dataClientIdResult;
|
||||
}
|
||||
|
||||
const scriptPromise = new Promise( ( resolve, reject ) => {
|
||||
loadScript( scriptOptions )
|
||||
.then( ( script ) => {
|
||||
widgetBuilder.setPaypal( script );
|
||||
loadedScripts.set( namespace, script );
|
||||
console.log( `Script loaded for namespace: ${ namespace }` );
|
||||
resolve( script );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
console.error(
|
||||
`Failed to load script for namespace: ${ namespace }`,
|
||||
error
|
||||
);
|
||||
reject( error );
|
||||
} )
|
||||
.finally( () => {
|
||||
scriptPromises.delete( namespace );
|
||||
} );
|
||||
} );
|
||||
|
||||
scriptPromises.set( namespace, scriptPromise );
|
||||
return scriptPromise;
|
||||
};
|
||||
|
||||
export const loadAndRenderPayPalScript = async (
|
||||
namespace,
|
||||
options,
|
||||
renderFunction,
|
||||
renderTarget
|
||||
) => {
|
||||
if ( ! namespace ) {
|
||||
throw new Error( 'Namespace is required' );
|
||||
}
|
||||
|
||||
const scriptOptions = {
|
||||
...options,
|
||||
'data-namespace': namespace,
|
||||
};
|
||||
|
||||
const script = await loadScript( scriptOptions );
|
||||
widgetBuilder.setPaypal( script );
|
||||
await renderFunction( script, renderTarget );
|
||||
};
|
|
@ -3,7 +3,8 @@ import GooglepayButton from './GooglepayButton';
|
|||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
|
||||
class GooglepayManager {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
constructor( namespace, buttonConfig, ppcpConfig ) {
|
||||
this.namespace = namespace;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.googlePayConfig = null;
|
||||
|
@ -52,7 +53,9 @@ class GooglepayManager {
|
|||
try {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await paypal.Googlepay().config();
|
||||
this.googlePayConfig = await window[ this.namespace ]
|
||||
.Googlepay()
|
||||
.config();
|
||||
}
|
||||
|
||||
if ( ! this.transactionInfo ) {
|
||||
|
|
|
@ -2,7 +2,8 @@ import GooglepayButton from './GooglepayButton';
|
|||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
|
||||
class GooglepayManagerBlockEditor {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
constructor( namespace, buttonConfig, ppcpConfig ) {
|
||||
this.namespace = namespace;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.googlePayConfig = null;
|
||||
|
@ -19,7 +20,9 @@ class GooglepayManagerBlockEditor {
|
|||
async config() {
|
||||
try {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await ppcpBlocksEditorPaypalGooglepay.Googlepay().config();
|
||||
this.googlePayConfig = await window[ this.namespace ]
|
||||
.Googlepay()
|
||||
.config();
|
||||
|
||||
// Fetch transaction information.
|
||||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
registerPaymentMethod,
|
||||
} from '@woocommerce/blocks-registry';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
|
||||
import GooglepayManager from './GooglepayManager';
|
||||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import GooglepayManagerBlockEditor from './GooglepayManagerBlockEditor';
|
||||
|
@ -14,7 +14,7 @@ const ppcpConfig = ppcpData.scriptData;
|
|||
|
||||
const buttonData = wc.wcSettings.getSetting( 'ppcp-googlepay_data' );
|
||||
const buttonConfig = buttonData.scriptData;
|
||||
const dataNamespace = 'ppcpBlocksEditorPaypalGooglepay';
|
||||
const namespace = 'ppcpBlocksPaypalGooglepay';
|
||||
|
||||
if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
|
||||
window.PayPalCommerceGateway = ppcpConfig;
|
||||
|
@ -24,14 +24,7 @@ const GooglePayComponent = ( props ) => {
|
|||
const [ bootstrapped, setBootstrapped ] = useState( false );
|
||||
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
|
||||
const [ googlePayLoaded, setGooglePayLoaded ] = useState( false );
|
||||
|
||||
const bootstrap = function () {
|
||||
const ManagerClass = props.isEditing
|
||||
? GooglepayManagerBlockEditor
|
||||
: GooglepayManager;
|
||||
const manager = new ManagerClass( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
};
|
||||
const [ manager, setManager ] = useState( null );
|
||||
|
||||
useEffect( () => {
|
||||
// Load GooglePay SDK
|
||||
|
@ -41,22 +34,36 @@ const GooglePayComponent = ( props ) => {
|
|||
|
||||
ppcpConfig.url_params.components += ',googlepay';
|
||||
|
||||
if ( props.isEditing ) {
|
||||
ppcpConfig.data_namespace = dataNamespace;
|
||||
}
|
||||
|
||||
// Load PayPal
|
||||
loadPaypalScript( ppcpConfig, () => {
|
||||
setPaypalLoaded( true );
|
||||
} );
|
||||
loadPayPalScript( namespace, ppcpConfig )
|
||||
.then( () => {
|
||||
setPaypalLoaded( true );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
console.error( 'Failed to load PayPal script: ', error );
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! bootstrapped && paypalLoaded && googlePayLoaded ) {
|
||||
setBootstrapped( true );
|
||||
bootstrap();
|
||||
if ( paypalLoaded && googlePayLoaded && ! manager ) {
|
||||
const ManagerClass = props.isEditing
|
||||
? GooglepayManagerBlockEditor
|
||||
: GooglepayManager;
|
||||
const newManager = new ManagerClass(
|
||||
namespace,
|
||||
buttonConfig,
|
||||
ppcpConfig
|
||||
);
|
||||
setManager( newManager );
|
||||
}
|
||||
}, [ paypalLoaded, googlePayLoaded ] );
|
||||
}, [ paypalLoaded, googlePayLoaded, props.isEditing ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( manager && ! bootstrapped ) {
|
||||
setBootstrapped( true );
|
||||
manager.init();
|
||||
}
|
||||
}, [ manager, bootstrapped ] );
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading';
|
||||
import GooglepayManager from './GooglepayManager';
|
||||
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
|
||||
import { CheckoutBootstrap } from './ContextBootstrap/CheckoutBootstrap';
|
||||
|
@ -16,14 +16,18 @@ import moduleStorage from './Helper/GooglePayStorage';
|
|||
|
||||
( function ( { buttonConfig, ppcpConfig = {} } ) {
|
||||
const context = ppcpConfig.context;
|
||||
const namespace = 'ppcpPaypalGooglepay';
|
||||
|
||||
function bootstrapPayButton() {
|
||||
if ( ! buttonConfig || ! ppcpConfig ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const manager = new GooglepayManager( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
const manager = new GooglepayManager(
|
||||
namespace,
|
||||
buttonConfig,
|
||||
ppcpConfig
|
||||
);
|
||||
|
||||
setupButtonEvents( function () {
|
||||
manager.reinit();
|
||||
|
@ -31,9 +35,11 @@ import moduleStorage from './Helper/GooglePayStorage';
|
|||
}
|
||||
|
||||
function bootstrapCheckout() {
|
||||
if ( context
|
||||
&& ! [ 'checkout' ].includes( context )
|
||||
&& ! (context === 'mini-cart' && ppcpConfig.continuation) ) {
|
||||
if (
|
||||
context &&
|
||||
! [ 'checkout' ].includes( context ) &&
|
||||
! ( context === 'mini-cart' && ppcpConfig.continuation )
|
||||
) {
|
||||
// Context must be missing/empty, or "checkout"/checkout continuation to proceed.
|
||||
return;
|
||||
}
|
||||
|
@ -80,10 +86,14 @@ import moduleStorage from './Helper/GooglePayStorage';
|
|||
} );
|
||||
|
||||
// Load PayPal
|
||||
loadPaypalScript( ppcpConfig, () => {
|
||||
paypalLoaded = true;
|
||||
tryToBoot();
|
||||
} );
|
||||
loadPayPalScript( namespace, ppcpConfig )
|
||||
.then( () => {
|
||||
paypalLoaded = true;
|
||||
tryToBoot();
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
console.error( 'Failed to load PayPal script: ', error );
|
||||
} );
|
||||
} );
|
||||
} )( {
|
||||
buttonConfig: window.wc_ppcp_googlepay,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue