mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 12:25:15 +08:00
🚧 Add block integration (same as GooglePay)
This commit is contained in:
parent
1d46f6154b
commit
50922eb779
8 changed files with 254 additions and 71 deletions
|
@ -1,69 +1,15 @@
|
|||
import ApplePayButton from './ApplepayButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import ApplepayButton from './Block/components/ApplePayButton';
|
||||
|
||||
class ApplePayManagerBlockEditor {
|
||||
#namespace = '';
|
||||
#buttonConfig = null;
|
||||
#ppcpConfig = null;
|
||||
#applePayConfig = null;
|
||||
#contextHandler = null;
|
||||
#transactionInfo = null;
|
||||
|
||||
constructor( namespace, buttonConfig, ppcpConfig ) {
|
||||
this.#namespace = namespace;
|
||||
this.#buttonConfig = buttonConfig;
|
||||
this.#ppcpConfig = ppcpConfig;
|
||||
|
||||
/*
|
||||
* On the front-end, the init method is called when a new button context was detected
|
||||
* via `buttonModuleWatcher`. In the block editor, we do not need to wait for the
|
||||
* context, but can initialize the button in the next event loop.
|
||||
*/
|
||||
setTimeout( () => this.init() );
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
this.#applePayConfig = await window[ this.#namespace ]
|
||||
.Applepay()
|
||||
.config();
|
||||
|
||||
this.#contextHandler = ContextHandlerFactory.create(
|
||||
this.#ppcpConfig.context,
|
||||
this.#buttonConfig,
|
||||
this.#ppcpConfig,
|
||||
null
|
||||
const ApplePayManagerBlockEditor = ( {
|
||||
namespace,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
} ) => (
|
||||
<ApplepayButton
|
||||
namespace={ namespace }
|
||||
buttonConfig={ buttonConfig }
|
||||
ppcpConfig={ ppcpConfig }
|
||||
/>
|
||||
);
|
||||
|
||||
// Fetch transaction information.
|
||||
this.#transactionInfo = await this.fetchTransactionInfo();
|
||||
|
||||
const button = ApplePayButton.createButton(
|
||||
this.#ppcpConfig.context,
|
||||
null,
|
||||
this.#buttonConfig,
|
||||
this.#ppcpConfig,
|
||||
this.#contextHandler
|
||||
);
|
||||
|
||||
button.configure( this.#applePayConfig, this.#transactionInfo );
|
||||
button.init();
|
||||
} catch ( error ) {
|
||||
console.error( 'Failed to initialize Apple Pay:', 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ApplePayManagerBlockEditor;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { useState, useEffect } from '@wordpress/element';
|
||||
import useApiToGenerateButton from '../hooks/useApiToGenerateButton';
|
||||
import usePayPalScript from '../hooks/usePayPalScript';
|
||||
import useApplepayScript from '../hooks/useApplepayScript';
|
||||
import useApplepayConfig from '../hooks/useApplepayConfig';
|
||||
|
||||
const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
|
||||
const [ buttonHtml, setButtonHtml ] = useState( '' );
|
||||
const [ buttonElement, setButtonElement ] = useState( null );
|
||||
const [ componentFrame, setComponentFrame ] = useState( null );
|
||||
const isPayPalLoaded = usePayPalScript( namespace, ppcpConfig );
|
||||
|
||||
const isApplepayLoaded = useApplepayScript(
|
||||
componentFrame,
|
||||
buttonConfig,
|
||||
isPayPalLoaded
|
||||
);
|
||||
|
||||
const applepayConfig = useApplepayConfig( namespace, isApplepayLoaded );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! buttonElement ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setComponentFrame( buttonElement.ownerDocument );
|
||||
}, [ buttonElement ] );
|
||||
|
||||
const applepayButton = useApiToGenerateButton(
|
||||
componentFrame,
|
||||
namespace,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
applepayConfig
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
if ( applepayButton ) {
|
||||
setButtonHtml( applepayButton.outerHTML );
|
||||
}
|
||||
}, [ applepayButton ] );
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ setButtonElement }
|
||||
dangerouslySetInnerHTML={ { __html: buttonHtml } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplepayButton;
|
|
@ -0,0 +1,37 @@
|
|||
import { useEffect, useState } from '@wordpress/element';
|
||||
import useButtonStyles from './useButtonStyles';
|
||||
|
||||
const useApiToGenerateButton = (
|
||||
componentDocument,
|
||||
namespace,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
applepayConfig
|
||||
) => {
|
||||
const [ applepayButton, setApplepayButton ] = useState( null );
|
||||
const buttonStyles = useButtonStyles( buttonConfig, ppcpConfig );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! buttonConfig || ! applepayConfig ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = document.createElement( 'apple-pay-button' );
|
||||
button.setAttribute(
|
||||
'buttonstyle',
|
||||
buttonConfig.buttonColor || 'black'
|
||||
);
|
||||
button.setAttribute( 'type', buttonConfig.buttonType || 'pay' );
|
||||
button.setAttribute( 'locale', buttonConfig.buttonLocale || 'en' );
|
||||
|
||||
setApplepayButton( button );
|
||||
|
||||
return () => {
|
||||
setApplepayButton( null );
|
||||
};
|
||||
}, [ namespace, buttonConfig, ppcpConfig, applepayConfig, buttonStyles ] );
|
||||
|
||||
return applepayButton;
|
||||
};
|
||||
|
||||
export default useApiToGenerateButton;
|
|
@ -0,0 +1,26 @@
|
|||
import { useState, useEffect } from '@wordpress/element';
|
||||
|
||||
const useApplepayConfig = ( namespace, isApplepayLoaded ) => {
|
||||
const [ applePayConfig, setApplePayConfig ] = useState( null );
|
||||
|
||||
useEffect( () => {
|
||||
const fetchConfig = async () => {
|
||||
if ( ! isApplepayLoaded ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const config = await window[ namespace ].Applepay().config();
|
||||
setApplePayConfig( config );
|
||||
} catch ( error ) {
|
||||
console.error( 'Failed to fetch Apple Pay config:', error );
|
||||
}
|
||||
};
|
||||
|
||||
fetchConfig();
|
||||
}, [ namespace, isApplepayLoaded ] );
|
||||
|
||||
return applePayConfig;
|
||||
};
|
||||
|
||||
export default useApplepayConfig;
|
|
@ -0,0 +1,65 @@
|
|||
import { useState, useEffect } from '@wordpress/element';
|
||||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
|
||||
const useApplepayScript = (
|
||||
componentDocument,
|
||||
buttonConfig,
|
||||
isPayPalLoaded
|
||||
) => {
|
||||
const [ isApplepayLoaded, setIsApplepayLoaded ] = 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 loadApplepayScript = 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 );
|
||||
}
|
||||
);
|
||||
setIsApplepayLoaded( true );
|
||||
} catch ( error ) {
|
||||
console.error( 'Failed to load Applepay script:', error );
|
||||
}
|
||||
};
|
||||
|
||||
loadApplepayScript();
|
||||
}, [ componentDocument, buttonConfig, isPayPalLoaded ] );
|
||||
|
||||
return isApplepayLoaded;
|
||||
};
|
||||
|
||||
export default useApplepayScript;
|
|
@ -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;
|
|
@ -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 += ',applepay';
|
||||
|
||||
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;
|
|
@ -19,12 +19,16 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
|
|||
window.PayPalCommerceGateway = ppcpConfig;
|
||||
}
|
||||
|
||||
const ApplePayComponent = ( props ) => {
|
||||
const ApplePayComponent = ( { isEditing } ) => {
|
||||
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
|
||||
const [ applePayLoaded, setApplePayLoaded ] = useState( false );
|
||||
const wrapperRef = useRef( null );
|
||||
|
||||
useEffect( () => {
|
||||
if ( isEditing ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load ApplePay SDK
|
||||
loadCustomScript( { url: buttonConfig.sdk_url } ).then( () => {
|
||||
setApplePayLoaded( true );
|
||||
|
@ -40,21 +44,31 @@ const ApplePayComponent = ( props ) => {
|
|||
.catch( ( error ) => {
|
||||
console.error( 'Failed to load PayPal script: ', error );
|
||||
} );
|
||||
}, [] );
|
||||
}, [ isEditing ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! paypalLoaded || ! applePayLoaded ) {
|
||||
if ( isEditing || ! paypalLoaded || ! applePayLoaded ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ManagerClass = props.isEditing
|
||||
const ManagerClass = isEditing
|
||||
? ApplePayManagerBlockEditor
|
||||
: ApplePayManager;
|
||||
|
||||
buttonConfig.reactWrapper = wrapperRef.current;
|
||||
|
||||
new ManagerClass( namespace, buttonConfig, ppcpConfig );
|
||||
}, [ paypalLoaded, applePayLoaded, props.isEditing ] );
|
||||
}, [ paypalLoaded, applePayLoaded, isEditing ] );
|
||||
|
||||
if ( isEditing ) {
|
||||
return (
|
||||
<ApplePayManagerBlockEditor
|
||||
namespace={ namespace }
|
||||
buttonConfig={ buttonConfig }
|
||||
ppcpConfig={ ppcpConfig }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue