🔀 Merge branch 'trunk'
|
@ -92,6 +92,24 @@ class ApplePayButton extends PaymentButton {
|
||||||
*/
|
*/
|
||||||
#product = {};
|
#product = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start time of the configuration process.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
#configureStartTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum time to wait for buttonAttributes before proceeding with initialization.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
#maxWaitTime = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stored button attributes.
|
||||||
|
* @type {Object|null}
|
||||||
|
*/
|
||||||
|
#storedButtonAttributes = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -125,7 +143,8 @@ class ApplePayButton extends PaymentButton {
|
||||||
externalHandler,
|
externalHandler,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
ppcpConfig,
|
ppcpConfig,
|
||||||
contextHandler
|
contextHandler,
|
||||||
|
buttonAttributes
|
||||||
) {
|
) {
|
||||||
// Disable debug output in the browser console:
|
// Disable debug output in the browser console:
|
||||||
// buttonConfig.is_debug = false;
|
// buttonConfig.is_debug = false;
|
||||||
|
@ -135,7 +154,8 @@ class ApplePayButton extends PaymentButton {
|
||||||
externalHandler,
|
externalHandler,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
ppcpConfig,
|
ppcpConfig,
|
||||||
contextHandler
|
contextHandler,
|
||||||
|
buttonAttributes
|
||||||
);
|
);
|
||||||
|
|
||||||
this.init = this.init.bind( this );
|
this.init = this.init.bind( this );
|
||||||
|
@ -220,6 +240,20 @@ class ApplePayButton extends PaymentButton {
|
||||||
'No transactionInfo - missing configure() call?'
|
'No transactionInfo - missing configure() call?'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
invalidIf(
|
||||||
|
() =>
|
||||||
|
this.buttonAttributes?.height &&
|
||||||
|
isNaN( parseInt( this.buttonAttributes.height ) ),
|
||||||
|
'Invalid height in buttonAttributes'
|
||||||
|
);
|
||||||
|
|
||||||
|
invalidIf(
|
||||||
|
() =>
|
||||||
|
this.buttonAttributes?.borderRadius &&
|
||||||
|
isNaN( parseInt( this.buttonAttributes.borderRadius ) ),
|
||||||
|
'Invalid borderRadius in buttonAttributes'
|
||||||
|
);
|
||||||
|
|
||||||
invalidIf(
|
invalidIf(
|
||||||
() => ! this.contextHandler?.validateContext(),
|
() => ! this.contextHandler?.validateContext(),
|
||||||
`Invalid context handler.`
|
`Invalid context handler.`
|
||||||
|
@ -231,10 +265,58 @@ class ApplePayButton extends PaymentButton {
|
||||||
*
|
*
|
||||||
* @param {Object} apiConfig - API configuration.
|
* @param {Object} apiConfig - API configuration.
|
||||||
* @param {TransactionInfo} transactionInfo - Transaction details.
|
* @param {TransactionInfo} transactionInfo - Transaction details.
|
||||||
|
* @param {Object} buttonAttributes - Button attributes.
|
||||||
*/
|
*/
|
||||||
configure( apiConfig, transactionInfo ) {
|
configure( apiConfig, transactionInfo, buttonAttributes = {} ) {
|
||||||
|
// Start timing on first configure call
|
||||||
|
if ( ! this.#configureStartTime ) {
|
||||||
|
this.#configureStartTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If valid buttonAttributes, store them
|
||||||
|
if ( buttonAttributes?.height && buttonAttributes?.borderRadius ) {
|
||||||
|
this.#storedButtonAttributes = { ...buttonAttributes };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use stored attributes if current ones are missing
|
||||||
|
const attributes = buttonAttributes?.height
|
||||||
|
? buttonAttributes
|
||||||
|
: this.#storedButtonAttributes;
|
||||||
|
|
||||||
|
// Check if we've exceeded wait time
|
||||||
|
const timeWaited = Date.now() - this.#configureStartTime;
|
||||||
|
if ( timeWaited > this.#maxWaitTime ) {
|
||||||
|
this.log(
|
||||||
|
'ApplePay: Timeout waiting for buttonAttributes - proceeding with initialization'
|
||||||
|
);
|
||||||
this.#applePayConfig = apiConfig;
|
this.#applePayConfig = apiConfig;
|
||||||
this.#transactionInfo = transactionInfo;
|
this.#transactionInfo = transactionInfo;
|
||||||
|
this.buttonAttributes = attributes || buttonAttributes;
|
||||||
|
this.init();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block any initialization until we have valid buttonAttributes
|
||||||
|
if ( ! attributes?.height || ! attributes?.borderRadius ) {
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
this.configure(
|
||||||
|
apiConfig,
|
||||||
|
transactionInfo,
|
||||||
|
buttonAttributes
|
||||||
|
),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset timer for future configure calls
|
||||||
|
this.#configureStartTime = 0;
|
||||||
|
|
||||||
|
this.#applePayConfig = apiConfig;
|
||||||
|
this.#transactionInfo = transactionInfo;
|
||||||
|
this.buttonAttributes = attributes;
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -321,17 +403,43 @@ class ApplePayButton extends PaymentButton {
|
||||||
applyWrapperStyles() {
|
applyWrapperStyles() {
|
||||||
super.applyWrapperStyles();
|
super.applyWrapperStyles();
|
||||||
|
|
||||||
const { height } = this.style;
|
|
||||||
|
|
||||||
if ( height ) {
|
|
||||||
const wrapper = this.wrapperElement;
|
const wrapper = this.wrapperElement;
|
||||||
|
if ( ! wrapper ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try stored attributes if current ones are missing
|
||||||
|
const attributes =
|
||||||
|
this.buttonAttributes?.height || this.buttonAttributes?.borderRadius
|
||||||
|
? this.buttonAttributes
|
||||||
|
: this.#storedButtonAttributes;
|
||||||
|
|
||||||
|
const defaultHeight = 48;
|
||||||
|
const defaultBorderRadius = 4;
|
||||||
|
|
||||||
|
const height = attributes?.height
|
||||||
|
? parseInt( attributes.height, 10 )
|
||||||
|
: defaultHeight;
|
||||||
|
|
||||||
|
if ( ! isNaN( height ) ) {
|
||||||
wrapper.style.setProperty(
|
wrapper.style.setProperty(
|
||||||
'--apple-pay-button-height',
|
'--apple-pay-button-height',
|
||||||
`${ height }px`
|
`${ height }px`
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.style.height = `${ height }px`;
|
wrapper.style.height = `${ height }px`;
|
||||||
|
} else {
|
||||||
|
wrapper.style.setProperty(
|
||||||
|
'--apple-pay-button-height',
|
||||||
|
`${ defaultHeight }px`
|
||||||
|
);
|
||||||
|
wrapper.style.height = `${ defaultHeight }px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const borderRadius = attributes?.borderRadius
|
||||||
|
? parseInt( attributes.borderRadius, 10 )
|
||||||
|
: defaultBorderRadius;
|
||||||
|
if ( ! isNaN( borderRadius ) ) {
|
||||||
|
wrapper.style.borderRadius = `${ borderRadius }px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,12 +450,23 @@ class ApplePayButton extends PaymentButton {
|
||||||
addButton() {
|
addButton() {
|
||||||
const { color, type, language } = this.style;
|
const { color, type, language } = this.style;
|
||||||
|
|
||||||
|
// If current buttonAttributes are missing, try to use stored ones
|
||||||
|
if (
|
||||||
|
! this.buttonAttributes?.height &&
|
||||||
|
this.#storedButtonAttributes?.height
|
||||||
|
) {
|
||||||
|
this.buttonAttributes = { ...this.#storedButtonAttributes };
|
||||||
|
}
|
||||||
|
|
||||||
const button = document.createElement( 'apple-pay-button' );
|
const button = document.createElement( 'apple-pay-button' );
|
||||||
button.id = 'apple-' + this.wrapperId;
|
button.id = 'apple-' + this.wrapperId;
|
||||||
|
|
||||||
button.setAttribute( 'buttonstyle', color );
|
button.setAttribute( 'buttonstyle', color );
|
||||||
button.setAttribute( 'type', type );
|
button.setAttribute( 'type', type );
|
||||||
button.setAttribute( 'locale', language );
|
button.setAttribute( 'locale', language );
|
||||||
|
|
||||||
|
button.style.display = 'block';
|
||||||
|
|
||||||
button.addEventListener( 'click', ( evt ) => {
|
button.addEventListener( 'click', ( evt ) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
this.onButtonClick();
|
this.onButtonClick();
|
||||||
|
|
|
@ -3,65 +3,83 @@ import ApplePayButton from './ApplepayButton';
|
||||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||||
|
|
||||||
class ApplePayManager {
|
class ApplePayManager {
|
||||||
#namespace = '';
|
constructor( namespace, buttonConfig, ppcpConfig, buttonAttributes = {} ) {
|
||||||
#buttonConfig = null;
|
this.namespace = namespace;
|
||||||
#ppcpConfig = null;
|
this.buttonConfig = buttonConfig;
|
||||||
#applePayConfig = null;
|
this.ppcpConfig = ppcpConfig;
|
||||||
#contextHandler = null;
|
this.buttonAttributes = buttonAttributes;
|
||||||
#transactionInfo = null;
|
this.applePayConfig = null;
|
||||||
#buttons = [];
|
this.transactionInfo = null;
|
||||||
|
this.contextHandler = null;
|
||||||
|
|
||||||
constructor( namespace, buttonConfig, ppcpConfig ) {
|
this.buttons = [];
|
||||||
this.#namespace = namespace;
|
|
||||||
this.#buttonConfig = buttonConfig;
|
|
||||||
this.#ppcpConfig = ppcpConfig;
|
|
||||||
|
|
||||||
this.onContextBootstrap = this.onContextBootstrap.bind( this );
|
buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => {
|
||||||
buttonModuleWatcher.watchContextBootstrap( this.onContextBootstrap );
|
this.contextHandler = ContextHandlerFactory.create(
|
||||||
}
|
|
||||||
|
|
||||||
async onContextBootstrap( bootstrap ) {
|
|
||||||
this.#contextHandler = ContextHandlerFactory.create(
|
|
||||||
bootstrap.context,
|
bootstrap.context,
|
||||||
this.#buttonConfig,
|
buttonConfig,
|
||||||
this.#ppcpConfig,
|
ppcpConfig,
|
||||||
bootstrap.handler
|
bootstrap.handler
|
||||||
);
|
);
|
||||||
|
|
||||||
const button = ApplePayButton.createButton(
|
const button = ApplePayButton.createButton(
|
||||||
bootstrap.context,
|
bootstrap.context,
|
||||||
bootstrap.handler,
|
bootstrap.handler,
|
||||||
this.#buttonConfig,
|
buttonConfig,
|
||||||
this.#ppcpConfig,
|
ppcpConfig,
|
||||||
this.#contextHandler
|
this.contextHandler,
|
||||||
|
this.buttonAttributes
|
||||||
);
|
);
|
||||||
|
|
||||||
this.#buttons.push( button );
|
this.buttons.push( button );
|
||||||
|
const initButton = () => {
|
||||||
|
button.configure(
|
||||||
|
this.applePayConfig,
|
||||||
|
this.transactionInfo,
|
||||||
|
this.buttonAttributes
|
||||||
|
);
|
||||||
|
button.init();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize button only if applePayConfig and transactionInfo are already fetched.
|
||||||
|
if ( this.applePayConfig && this.transactionInfo ) {
|
||||||
|
initButton();
|
||||||
|
} else {
|
||||||
// Ensure ApplePayConfig is loaded before proceeding.
|
// Ensure ApplePayConfig is loaded before proceeding.
|
||||||
await this.init();
|
await this.init();
|
||||||
|
|
||||||
button.configure( this.#applePayConfig, this.#transactionInfo );
|
if ( this.applePayConfig && this.transactionInfo ) {
|
||||||
button.init();
|
initButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
try {
|
try {
|
||||||
if ( ! this.#applePayConfig ) {
|
if ( ! this.applePayConfig ) {
|
||||||
this.#applePayConfig = await window[ this.#namespace ]
|
// Gets ApplePay configuration of the PayPal merchant.
|
||||||
|
this.applePayConfig = await window[ this.namespace ]
|
||||||
.Applepay()
|
.Applepay()
|
||||||
.config();
|
.config();
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! this.#applePayConfig ) {
|
if ( ! this.transactionInfo ) {
|
||||||
|
this.transactionInfo = await this.fetchTransactionInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! this.applePayConfig ) {
|
||||||
console.error( 'No ApplePayConfig received during init' );
|
console.error( 'No ApplePayConfig received during init' );
|
||||||
}
|
} else if ( ! this.transactionInfo ) {
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! this.#transactionInfo ) {
|
|
||||||
this.#transactionInfo = await this.fetchTransactionInfo();
|
|
||||||
|
|
||||||
if ( ! this.#applePayConfig ) {
|
|
||||||
console.error( 'No transactionInfo found during init' );
|
console.error( 'No transactionInfo found during init' );
|
||||||
|
} else {
|
||||||
|
for ( const button of this.buttons ) {
|
||||||
|
button.configure(
|
||||||
|
this.applePayConfig,
|
||||||
|
this.transactionInfo,
|
||||||
|
this.buttonAttributes
|
||||||
|
);
|
||||||
|
button.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch ( error ) {
|
} catch ( error ) {
|
||||||
|
@ -71,10 +89,10 @@ class ApplePayManager {
|
||||||
|
|
||||||
async fetchTransactionInfo() {
|
async fetchTransactionInfo() {
|
||||||
try {
|
try {
|
||||||
if ( ! this.#contextHandler ) {
|
if ( ! this.contextHandler ) {
|
||||||
throw new Error( 'ContextHandler is not initialized' );
|
throw new Error( 'ContextHandler is not initialized' );
|
||||||
}
|
}
|
||||||
return await this.#contextHandler.transactionInfo();
|
return await this.contextHandler.transactionInfo();
|
||||||
} catch ( error ) {
|
} catch ( error ) {
|
||||||
console.error( 'Error fetching transaction info:', error );
|
console.error( 'Error fetching transaction info:', error );
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -82,7 +100,7 @@ class ApplePayManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
reinit() {
|
reinit() {
|
||||||
for ( const button of this.#buttons ) {
|
for ( const button of this.buttons ) {
|
||||||
button.reinit();
|
button.reinit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@ const ApplePayManagerBlockEditor = ( {
|
||||||
namespace,
|
namespace,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
ppcpConfig,
|
ppcpConfig,
|
||||||
|
buttonAttributes,
|
||||||
} ) => (
|
} ) => (
|
||||||
<ApplepayButton
|
<ApplepayButton
|
||||||
namespace={ namespace }
|
namespace={ namespace }
|
||||||
buttonConfig={ buttonConfig }
|
buttonConfig={ buttonConfig }
|
||||||
ppcpConfig={ ppcpConfig }
|
ppcpConfig={ ppcpConfig }
|
||||||
|
buttonAttributes={ buttonAttributes }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,12 @@ import usePayPalScript from '../hooks/usePayPalScript';
|
||||||
import useApplepayScript from '../hooks/useApplepayScript';
|
import useApplepayScript from '../hooks/useApplepayScript';
|
||||||
import useApplepayConfig from '../hooks/useApplepayConfig';
|
import useApplepayConfig from '../hooks/useApplepayConfig';
|
||||||
|
|
||||||
const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
|
const ApplepayButton = ( {
|
||||||
|
namespace,
|
||||||
|
buttonConfig,
|
||||||
|
ppcpConfig,
|
||||||
|
buttonAttributes,
|
||||||
|
} ) => {
|
||||||
const [ buttonHtml, setButtonHtml ] = useState( '' );
|
const [ buttonHtml, setButtonHtml ] = useState( '' );
|
||||||
const [ buttonElement, setButtonElement ] = useState( null );
|
const [ buttonElement, setButtonElement ] = useState( null );
|
||||||
const [ componentFrame, setComponentFrame ] = useState( null );
|
const [ componentFrame, setComponentFrame ] = useState( null );
|
||||||
|
@ -31,19 +36,42 @@ const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => {
|
||||||
namespace,
|
namespace,
|
||||||
buttonConfig,
|
buttonConfig,
|
||||||
ppcpConfig,
|
ppcpConfig,
|
||||||
applepayConfig
|
applepayConfig,
|
||||||
|
buttonAttributes
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
if ( applepayButton ) {
|
if ( ! applepayButton || ! buttonElement ) {
|
||||||
setButtonHtml( applepayButton.outerHTML );
|
return;
|
||||||
}
|
}
|
||||||
}, [ applepayButton ] );
|
|
||||||
|
setButtonHtml( applepayButton.outerHTML );
|
||||||
|
|
||||||
|
// Add timeout to ensure button is displayed after render
|
||||||
|
setTimeout( () => {
|
||||||
|
const button = buttonElement.querySelector( 'apple-pay-button' );
|
||||||
|
if ( button ) {
|
||||||
|
button.style.display = 'block';
|
||||||
|
}
|
||||||
|
}, 100 ); // Add a small delay to ensure DOM is ready
|
||||||
|
}, [ applepayButton, buttonElement ] );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ setButtonElement }
|
ref={ setButtonElement }
|
||||||
dangerouslySetInnerHTML={ { __html: buttonHtml } }
|
dangerouslySetInnerHTML={ { __html: buttonHtml } }
|
||||||
|
style={ {
|
||||||
|
height: buttonAttributes?.height
|
||||||
|
? `${ buttonAttributes.height }px`
|
||||||
|
: '48px',
|
||||||
|
'--apple-pay-button-height': buttonAttributes?.height
|
||||||
|
? `${ buttonAttributes.height }px`
|
||||||
|
: '48px',
|
||||||
|
borderRadius: buttonAttributes?.borderRadius
|
||||||
|
? `${ buttonAttributes.borderRadius }px`
|
||||||
|
: undefined,
|
||||||
|
overflow: 'hidden',
|
||||||
|
} }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) {
|
||||||
window.PayPalCommerceGateway = ppcpConfig;
|
window.PayPalCommerceGateway = ppcpConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApplePayComponent = ( { isEditing } ) => {
|
const ApplePayComponent = ( { isEditing, buttonAttributes } ) => {
|
||||||
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
|
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
|
||||||
const [ applePayLoaded, setApplePayLoaded ] = useState( false );
|
const [ applePayLoaded, setApplePayLoaded ] = useState( false );
|
||||||
const wrapperRef = useRef( null );
|
const wrapperRef = useRef( null );
|
||||||
|
@ -57,8 +57,13 @@ const ApplePayComponent = ( { isEditing } ) => {
|
||||||
|
|
||||||
buttonConfig.reactWrapper = wrapperRef.current;
|
buttonConfig.reactWrapper = wrapperRef.current;
|
||||||
|
|
||||||
new ManagerClass( namespace, buttonConfig, ppcpConfig );
|
new ManagerClass(
|
||||||
}, [ paypalLoaded, applePayLoaded, isEditing ] );
|
namespace,
|
||||||
|
buttonConfig,
|
||||||
|
ppcpConfig,
|
||||||
|
buttonAttributes
|
||||||
|
);
|
||||||
|
}, [ paypalLoaded, applePayLoaded, isEditing, buttonAttributes ] );
|
||||||
|
|
||||||
if ( isEditing ) {
|
if ( isEditing ) {
|
||||||
return (
|
return (
|
||||||
|
@ -66,6 +71,7 @@ const ApplePayComponent = ( { isEditing } ) => {
|
||||||
namespace={ namespace }
|
namespace={ namespace }
|
||||||
buttonConfig={ buttonConfig }
|
buttonConfig={ buttonConfig }
|
||||||
ppcpConfig={ ppcpConfig }
|
ppcpConfig={ ppcpConfig }
|
||||||
|
buttonAttributes={ buttonAttributes }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -102,5 +108,6 @@ registerExpressPaymentMethod( {
|
||||||
canMakePayment: () => buttonData.enabled,
|
canMakePayment: () => buttonData.enabled,
|
||||||
supports: {
|
supports: {
|
||||||
features,
|
features,
|
||||||
|
style: [ 'height', 'borderRadius' ],
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
import { registerPlugin } from '@wordpress/plugins';
|
||||||
|
import { useEffect, useCallback, useState, useRef } from '@wordpress/element';
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
|
||||||
|
import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights';
|
||||||
|
import { STORE_NAME } from '../stores/axoStore';
|
||||||
|
import usePayPalCommerceGateway from '../hooks/usePayPalCommerceGateway';
|
||||||
|
|
||||||
|
const GATEWAY_HANDLE = 'ppcp-axo-gateway';
|
||||||
|
|
||||||
|
const useEventTracking = () => {
|
||||||
|
const [ triggeredEvents, setTriggeredEvents ] = useState( {
|
||||||
|
initialized: false,
|
||||||
|
jsLoaded: false,
|
||||||
|
beginCheckout: false,
|
||||||
|
emailSubmitted: false,
|
||||||
|
} );
|
||||||
|
|
||||||
|
const currentPaymentMethod = useRef( null );
|
||||||
|
|
||||||
|
const setEventTriggered = useCallback( ( eventName, value = true ) => {
|
||||||
|
setTriggeredEvents( ( prev ) => ( {
|
||||||
|
...prev,
|
||||||
|
[ eventName ]: value,
|
||||||
|
} ) );
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const isEventTriggered = useCallback(
|
||||||
|
( eventName ) => triggeredEvents[ eventName ],
|
||||||
|
[ triggeredEvents ]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentPaymentMethod = useCallback( ( methodName ) => {
|
||||||
|
currentPaymentMethod.current = methodName;
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const getCurrentPaymentMethod = useCallback(
|
||||||
|
() => currentPaymentMethod.current,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
setEventTriggered,
|
||||||
|
isEventTriggered,
|
||||||
|
setCurrentPaymentMethod,
|
||||||
|
getCurrentPaymentMethod,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForPayPalInsight = () => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
// If already loaded, resolve immediately
|
||||||
|
if ( window.paypalInsight ) {
|
||||||
|
resolve( window.paypalInsight );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a reasonable timeout
|
||||||
|
const timeoutId = setTimeout( () => {
|
||||||
|
observer.disconnect();
|
||||||
|
reject( new Error( 'PayPal Insights script load timeout' ) );
|
||||||
|
}, 10000 );
|
||||||
|
|
||||||
|
// Create MutationObserver to watch for script initialization
|
||||||
|
const observer = new MutationObserver( () => {
|
||||||
|
if ( window.paypalInsight ) {
|
||||||
|
observer.disconnect();
|
||||||
|
clearTimeout( timeoutId );
|
||||||
|
resolve( window.paypalInsight );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Start observing
|
||||||
|
observer.observe( document, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePayPalInsightsInit = ( axoConfig, ppcpConfig, eventTracking ) => {
|
||||||
|
const { setEventTriggered, isEventTriggered } = eventTracking;
|
||||||
|
const initialized = useRef( false );
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
if (
|
||||||
|
! axoConfig?.insights?.enabled ||
|
||||||
|
! axoConfig?.insights?.client_id ||
|
||||||
|
! axoConfig?.insights?.session_id ||
|
||||||
|
initialized.current ||
|
||||||
|
isEventTriggered( 'initialized' )
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initializePayPalInsights = async () => {
|
||||||
|
try {
|
||||||
|
await waitForPayPalInsight();
|
||||||
|
|
||||||
|
if ( initialized.current ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track JS load first
|
||||||
|
PayPalInsights.trackJsLoad();
|
||||||
|
setEventTriggered( 'jsLoaded' );
|
||||||
|
|
||||||
|
PayPalInsights.config( axoConfig.insights.client_id, {
|
||||||
|
debug: axoConfig?.wp_debug === '1',
|
||||||
|
} );
|
||||||
|
|
||||||
|
PayPalInsights.setSessionId( axoConfig.insights.session_id );
|
||||||
|
initialized.current = true;
|
||||||
|
setEventTriggered( 'initialized' );
|
||||||
|
|
||||||
|
if (
|
||||||
|
isEventTriggered( 'jsLoaded' ) &&
|
||||||
|
! isEventTriggered( 'beginCheckout' )
|
||||||
|
) {
|
||||||
|
PayPalInsights.trackBeginCheckout( {
|
||||||
|
amount: axoConfig.insights.amount,
|
||||||
|
page_type: 'checkout',
|
||||||
|
user_data: {
|
||||||
|
country: 'US',
|
||||||
|
is_store_member: false,
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
setEventTriggered( 'beginCheckout' );
|
||||||
|
}
|
||||||
|
} catch ( error ) {
|
||||||
|
console.error(
|
||||||
|
'PayPal Insights initialization failed:',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
initializePayPalInsights();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
initialized.current = false;
|
||||||
|
};
|
||||||
|
}, [ axoConfig, ppcpConfig, setEventTriggered, isEventTriggered ] );
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePaymentMethodTracking = ( axoConfig, eventTracking ) => {
|
||||||
|
const { setCurrentPaymentMethod } = eventTracking;
|
||||||
|
const lastPaymentMethod = useRef( null );
|
||||||
|
const isInitialMount = useRef( true );
|
||||||
|
|
||||||
|
const activePaymentMethod = useSelect( ( select ) => {
|
||||||
|
return select( PAYMENT_STORE_KEY )?.getActivePaymentMethod();
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const handlePaymentMethodChange = useCallback(
|
||||||
|
async ( paymentMethod ) => {
|
||||||
|
// Skip if no payment method or same as last one
|
||||||
|
if (
|
||||||
|
! paymentMethod ||
|
||||||
|
paymentMethod === lastPaymentMethod.current
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await waitForPayPalInsight();
|
||||||
|
|
||||||
|
// Only track if it's not the initial mount, and we have a previous payment method
|
||||||
|
if ( ! isInitialMount.current && lastPaymentMethod.current ) {
|
||||||
|
PayPalInsights.trackSelectPaymentMethod( {
|
||||||
|
payment_method_selected:
|
||||||
|
axoConfig?.insights?.payment_method_selected_map[
|
||||||
|
paymentMethod
|
||||||
|
] || 'other',
|
||||||
|
page_type: 'checkout',
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPaymentMethod.current = paymentMethod;
|
||||||
|
setCurrentPaymentMethod( paymentMethod );
|
||||||
|
} catch ( error ) {
|
||||||
|
console.error( 'Failed to track payment method:', error );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
axoConfig?.insights?.payment_method_selected_map,
|
||||||
|
setCurrentPaymentMethod,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
if ( activePaymentMethod ) {
|
||||||
|
if ( isInitialMount.current ) {
|
||||||
|
// Just set the initial payment method without tracking
|
||||||
|
lastPaymentMethod.current = activePaymentMethod;
|
||||||
|
setCurrentPaymentMethod( activePaymentMethod );
|
||||||
|
isInitialMount.current = false;
|
||||||
|
} else {
|
||||||
|
handlePaymentMethodChange( activePaymentMethod );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
activePaymentMethod,
|
||||||
|
handlePaymentMethodChange,
|
||||||
|
setCurrentPaymentMethod,
|
||||||
|
] );
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
return () => {
|
||||||
|
lastPaymentMethod.current = null;
|
||||||
|
isInitialMount.current = true;
|
||||||
|
};
|
||||||
|
}, [] );
|
||||||
|
};
|
||||||
|
|
||||||
|
const PayPalInsightsLoader = () => {
|
||||||
|
const eventTracking = useEventTracking();
|
||||||
|
const { setEventTriggered, isEventTriggered } = eventTracking;
|
||||||
|
|
||||||
|
const initialConfig =
|
||||||
|
window?.wc?.wcSettings?.getSetting( `${ GATEWAY_HANDLE }_data` ) || {};
|
||||||
|
|
||||||
|
const { ppcpConfig } = usePayPalCommerceGateway( initialConfig );
|
||||||
|
const axoConfig = window?.wc_ppcp_axo;
|
||||||
|
|
||||||
|
const { isEmailSubmitted } = useSelect( ( select ) => {
|
||||||
|
const storeSelect = select( STORE_NAME );
|
||||||
|
return {
|
||||||
|
isEmailSubmitted: storeSelect?.getIsEmailSubmitted?.() ?? false,
|
||||||
|
};
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
usePayPalInsightsInit( axoConfig, ppcpConfig, eventTracking );
|
||||||
|
usePaymentMethodTracking( axoConfig, eventTracking );
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
const trackEmail = async () => {
|
||||||
|
if ( isEmailSubmitted && ! isEventTriggered( 'emailSubmitted' ) ) {
|
||||||
|
try {
|
||||||
|
await waitForPayPalInsight();
|
||||||
|
PayPalInsights.trackSubmitCheckoutEmail();
|
||||||
|
setEventTriggered( 'emailSubmitted' );
|
||||||
|
} catch ( error ) {
|
||||||
|
console.error( 'Failed to track email submission:', error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trackEmail();
|
||||||
|
}, [ isEmailSubmitted, setEventTriggered, isEventTriggered ] );
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
registerPlugin( 'wc-ppcp-paypal-insights', {
|
||||||
|
render: PayPalInsightsLoader,
|
||||||
|
scope: 'woocommerce-checkout',
|
||||||
|
} );
|
||||||
|
|
||||||
|
export default PayPalInsightsLoader;
|
|
@ -1,4 +1,4 @@
|
||||||
import { createReduxStore, register, dispatch } from '@wordpress/data';
|
import { createReduxStore, register, dispatch, select } from '@wordpress/data';
|
||||||
|
|
||||||
export const STORE_NAME = 'woocommerce-paypal-payments/axo-block';
|
export const STORE_NAME = 'woocommerce-paypal-payments/axo-block';
|
||||||
|
|
||||||
|
@ -108,13 +108,15 @@ const selectors = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and register the Redux store for the AXO block
|
// Create and register the Redux store for the AXO block
|
||||||
const store = createReduxStore( STORE_NAME, {
|
if ( ! select( STORE_NAME ) ) {
|
||||||
|
const store = createReduxStore( STORE_NAME, {
|
||||||
reducer,
|
reducer,
|
||||||
actions,
|
actions,
|
||||||
selectors,
|
selectors,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
register( store );
|
register( store );
|
||||||
|
}
|
||||||
|
|
||||||
// Action dispatchers
|
// Action dispatchers
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,12 @@ return array(
|
||||||
$container->get( 'axoblock.url' ),
|
$container->get( 'axoblock.url' ),
|
||||||
$container->get( 'ppcp.asset-version' ),
|
$container->get( 'ppcp.asset-version' ),
|
||||||
$container->get( 'axo.gateway' ),
|
$container->get( 'axo.gateway' ),
|
||||||
fn() : SmartButtonInterface => $container->get( 'button.smart-button' ),
|
fn(): SmartButtonInterface => $container->get( 'button.smart-button' ),
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'wcgateway.configuration.dcc' ),
|
$container->get( 'wcgateway.configuration.dcc' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'onboarding.environment' ),
|
||||||
$container->get( 'wcgateway.url' ),
|
$container->get( 'wcgateway.url' ),
|
||||||
|
$container->get( 'axo.payment_method_selected_map' ),
|
||||||
$container->get( 'axo.supported-country-card-type-matrix' ),
|
$container->get( 'axo.supported-country-card-type-matrix' ),
|
||||||
$container->get( 'axo.shipping-wc-enabled-locations' )
|
$container->get( 'axo.shipping-wc-enabled-locations' )
|
||||||
);
|
);
|
||||||
|
|
|
@ -133,6 +133,15 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
|
||||||
wp_enqueue_style( 'wc-ppcp-axo-block' );
|
wp_enqueue_style( 'wc-ppcp-axo-block' );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enqueue the PayPal Insights script.
|
||||||
|
add_action(
|
||||||
|
'wp_enqueue_scripts',
|
||||||
|
function () use ( $c ) {
|
||||||
|
$this->enqueue_paypal_insights_script( $c );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,4 +175,37 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
|
||||||
|
|
||||||
return $localized_script_data;
|
return $localized_script_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues PayPal Insights analytics script for the Checkout block.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $c The service container.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function enqueue_paypal_insights_script( ContainerInterface $c ): void {
|
||||||
|
if ( ! has_block( 'woocommerce/checkout' ) || WC()->cart->is_empty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$module_url = $c->get( 'axoblock.url' );
|
||||||
|
$asset_version = $c->get( 'ppcp.asset-version' );
|
||||||
|
|
||||||
|
wp_register_script(
|
||||||
|
'wc-ppcp-paypal-insights',
|
||||||
|
untrailingslashit( $module_url ) . '/assets/js/PayPalInsightsLoader.js',
|
||||||
|
array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ),
|
||||||
|
$asset_version,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_localize_script(
|
||||||
|
'wc-ppcp-paypal-insights',
|
||||||
|
'ppcpPayPalInsightsData',
|
||||||
|
array(
|
||||||
|
'isAxoEnabled' => true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_enqueue_script( 'wc-ppcp-paypal-insights' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
|
||||||
*/
|
*/
|
||||||
private $environment;
|
private $environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $payment_method_selected_map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The WcGateway module URL.
|
* The WcGateway module URL.
|
||||||
*
|
*
|
||||||
|
@ -99,12 +106,12 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
|
||||||
* @param string $module_url The URL of this module.
|
* @param string $module_url The URL of this module.
|
||||||
* @param string $version The assets version.
|
* @param string $version The assets version.
|
||||||
* @param WC_Payment_Gateway $gateway Credit card gateway.
|
* @param WC_Payment_Gateway $gateway Credit card gateway.
|
||||||
* @param SmartButtonInterface|callable $smart_button The smart button script loading
|
* @param SmartButtonInterface|callable $smart_button The smart button script loading handler.
|
||||||
* handler.
|
|
||||||
* @param Settings $settings The settings.
|
* @param Settings $settings The settings.
|
||||||
* @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings.
|
* @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings.
|
||||||
* @param Environment $environment The environment object.
|
* @param Environment $environment The environment object.
|
||||||
* @param string $wcgateway_module_url The WcGateway module URL.
|
* @param string $wcgateway_module_url The WcGateway module URL.
|
||||||
|
* @param array $payment_method_selected_map Mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
|
||||||
* @param array $supported_country_card_type_matrix The supported country card type matrix for Axo.
|
* @param array $supported_country_card_type_matrix The supported country card type matrix for Axo.
|
||||||
* @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations.
|
* @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations.
|
||||||
*/
|
*/
|
||||||
|
@ -117,6 +124,7 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
|
||||||
DCCGatewayConfiguration $dcc_configuration,
|
DCCGatewayConfiguration $dcc_configuration,
|
||||||
Environment $environment,
|
Environment $environment,
|
||||||
string $wcgateway_module_url,
|
string $wcgateway_module_url,
|
||||||
|
array $payment_method_selected_map,
|
||||||
array $supported_country_card_type_matrix,
|
array $supported_country_card_type_matrix,
|
||||||
array $enabled_shipping_locations
|
array $enabled_shipping_locations
|
||||||
) {
|
) {
|
||||||
|
@ -129,10 +137,10 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
|
||||||
$this->dcc_configuration = $dcc_configuration;
|
$this->dcc_configuration = $dcc_configuration;
|
||||||
$this->environment = $environment;
|
$this->environment = $environment;
|
||||||
$this->wcgateway_module_url = $wcgateway_module_url;
|
$this->wcgateway_module_url = $wcgateway_module_url;
|
||||||
|
$this->payment_method_selected_map = $payment_method_selected_map;
|
||||||
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
|
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
|
||||||
$this->enabled_shipping_locations = $enabled_shipping_locations;
|
$this->enabled_shipping_locations = $enabled_shipping_locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -223,8 +231,9 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType {
|
||||||
'currency_code' => get_woocommerce_currency(),
|
'currency_code' => get_woocommerce_currency(),
|
||||||
'value' => ( WC()->cart && method_exists( WC()->cart, 'get_total' ) )
|
'value' => ( WC()->cart && method_exists( WC()->cart, 'get_total' ) )
|
||||||
? WC()->cart->get_total( 'numeric' )
|
? WC()->cart->get_total( 'numeric' )
|
||||||
: null, // Set to null if WC()->cart is null or get_total doesn't exist.
|
: null,
|
||||||
),
|
),
|
||||||
|
'payment_method_selected_map' => $this->payment_method_selected_map,
|
||||||
),
|
),
|
||||||
'allowed_cards' => $this->supported_country_card_type_matrix,
|
'allowed_cards' => $this->supported_country_card_type_matrix,
|
||||||
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
|
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
|
||||||
|
|
|
@ -9,7 +9,10 @@ module.exports = {
|
||||||
target: 'web',
|
target: 'web',
|
||||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||||
entry: {
|
entry: {
|
||||||
'index': path.resolve( './resources/js/index.js' ),
|
index: path.resolve( './resources/js/index.js' ),
|
||||||
|
PayPalInsightsLoader: path.resolve(
|
||||||
|
'./resources/js/plugins/PayPalInsightsLoader.js'
|
||||||
|
),
|
||||||
gateway: path.resolve( './resources/css/gateway.scss' ),
|
gateway: path.resolve( './resources/css/gateway.scss' ),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|
|
@ -121,7 +121,7 @@ class AxoManager {
|
||||||
this.axoConfig?.insights?.session_id
|
this.axoConfig?.insights?.session_id
|
||||||
) {
|
) {
|
||||||
PayPalInsights.config( this.axoConfig?.insights?.client_id, {
|
PayPalInsights.config( this.axoConfig?.insights?.client_id, {
|
||||||
debug: true,
|
debug: axoConfig?.wp_debug === '1',
|
||||||
} );
|
} );
|
||||||
PayPalInsights.setSessionId( this.axoConfig?.insights?.session_id );
|
PayPalInsights.setSessionId( this.axoConfig?.insights?.session_id );
|
||||||
PayPalInsights.trackJsLoad();
|
PayPalInsights.trackJsLoad();
|
||||||
|
@ -164,19 +164,25 @@ class AxoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
registerEventHandlers() {
|
registerEventHandlers() {
|
||||||
|
// Payment method change tracking with duplicate prevention
|
||||||
|
let lastSelectedPaymentMethod = document.querySelector(
|
||||||
|
'input[name=payment_method]:checked'
|
||||||
|
)?.value;
|
||||||
this.$( document ).on(
|
this.$( document ).on(
|
||||||
'change',
|
'change',
|
||||||
'input[name=payment_method]',
|
'input[name=payment_method]',
|
||||||
( ev ) => {
|
( ev ) => {
|
||||||
const map = {
|
if ( lastSelectedPaymentMethod !== ev.target.value ) {
|
||||||
'ppcp-axo-gateway': 'card',
|
|
||||||
'ppcp-gateway': 'paypal',
|
|
||||||
};
|
|
||||||
|
|
||||||
PayPalInsights.trackSelectPaymentMethod( {
|
PayPalInsights.trackSelectPaymentMethod( {
|
||||||
payment_method_selected: map[ ev.target.value ] || 'other',
|
payment_method_selected:
|
||||||
|
this.axoConfig?.insights
|
||||||
|
?.payment_method_selected_map[
|
||||||
|
ev.target.value
|
||||||
|
] || 'other',
|
||||||
page_type: 'checkout',
|
page_type: 'checkout',
|
||||||
} );
|
} );
|
||||||
|
lastSelectedPaymentMethod = ev.target.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1166,16 +1172,6 @@ class AxoManager {
|
||||||
|
|
||||||
this.el.axoNonceInput.get().value = nonce;
|
this.el.axoNonceInput.get().value = nonce;
|
||||||
|
|
||||||
PayPalInsights.trackEndCheckout( {
|
|
||||||
amount: this.axoConfig?.insights?.amount,
|
|
||||||
page_type: 'checkout',
|
|
||||||
payment_method_selected: 'card',
|
|
||||||
user_data: {
|
|
||||||
country: 'US',
|
|
||||||
is_store_member: false,
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( data ) {
|
if ( data ) {
|
||||||
// Ryan flow.
|
// Ryan flow.
|
||||||
const form = document.querySelector( 'form.woocommerce-checkout' );
|
const form = document.querySelector( 'form.woocommerce-checkout' );
|
||||||
|
|
99
modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights';
|
||||||
|
|
||||||
|
class EndCheckoutTracker {
|
||||||
|
constructor() {
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize() {
|
||||||
|
const axoConfig = window.wc_ppcp_axo_insights_data || {};
|
||||||
|
|
||||||
|
if (
|
||||||
|
axoConfig?.enabled === '1' &&
|
||||||
|
axoConfig?.client_id &&
|
||||||
|
axoConfig?.session_id &&
|
||||||
|
axoConfig?.orderTotal &&
|
||||||
|
axoConfig?.orderCurrency
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await this.waitForPayPalInsight();
|
||||||
|
|
||||||
|
PayPalInsights.config( axoConfig?.client_id, {
|
||||||
|
debug: axoConfig?.wp_debug === '1',
|
||||||
|
} );
|
||||||
|
PayPalInsights.setSessionId( axoConfig.session_id );
|
||||||
|
PayPalInsights.trackJsLoad();
|
||||||
|
|
||||||
|
const trackingData = {
|
||||||
|
amount: {
|
||||||
|
currency_code: axoConfig?.orderCurrency,
|
||||||
|
value: axoConfig?.orderTotal,
|
||||||
|
},
|
||||||
|
page_type: 'checkout',
|
||||||
|
payment_method_selected:
|
||||||
|
axoConfig?.payment_method_selected_map[
|
||||||
|
axoConfig?.paymentMethod
|
||||||
|
] || 'other',
|
||||||
|
user_data: {
|
||||||
|
country: 'US',
|
||||||
|
is_store_member: false,
|
||||||
|
},
|
||||||
|
order_id: axoConfig?.orderId,
|
||||||
|
order_key: axoConfig?.orderKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
PayPalInsights.trackEndCheckout( trackingData );
|
||||||
|
} catch ( error ) {
|
||||||
|
console.error(
|
||||||
|
'EndCheckoutTracker: Error during tracking:',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
console.error( 'PayPalInsights object:', window.paypalInsight );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
'EndCheckoutTracker: Missing required configuration',
|
||||||
|
{
|
||||||
|
enabled: axoConfig?.enabled,
|
||||||
|
hasClientId: !! axoConfig?.client_id,
|
||||||
|
hasSessionId: !! axoConfig?.session_id,
|
||||||
|
hasOrderTotal: !! axoConfig?.orderTotal,
|
||||||
|
hasOrderCurrency: !! axoConfig?.orderCurrency,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForPayPalInsight() {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
// If already loaded, resolve immediately
|
||||||
|
if ( window.paypalInsight ) {
|
||||||
|
resolve( window.paypalInsight );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeoutId = setTimeout( () => {
|
||||||
|
observer.disconnect();
|
||||||
|
reject( new Error( 'PayPal Insights script load timeout' ) );
|
||||||
|
}, 10000 );
|
||||||
|
|
||||||
|
// Create MutationObserver to watch for script initialization
|
||||||
|
const observer = new MutationObserver( () => {
|
||||||
|
if ( window.paypalInsight ) {
|
||||||
|
observer.disconnect();
|
||||||
|
clearTimeout( timeoutId );
|
||||||
|
resolve( window.paypalInsight );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
observer.observe( document, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener( 'DOMContentLoaded', () => {
|
||||||
|
new EndCheckoutTracker();
|
||||||
|
} );
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter;
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ return array(
|
||||||
$container->get( 'session.handler' ),
|
$container->get( 'session.handler' ),
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'onboarding.environment' ),
|
$container->get( 'onboarding.environment' ),
|
||||||
|
$container->get( 'axo.insights' ),
|
||||||
$container->get( 'wcgateway.settings.status' ),
|
$container->get( 'wcgateway.settings.status' ),
|
||||||
$container->get( 'api.shop.currency.getter' ),
|
$container->get( 'api.shop.currency.getter' ),
|
||||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||||
|
@ -91,6 +93,55 @@ return array(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Data needed for the PayPal Insights.
|
||||||
|
'axo.insights' => static function ( ContainerInterface $container ): array {
|
||||||
|
$settings = $container->get( 'wcgateway.settings' );
|
||||||
|
assert( $settings instanceof Settings );
|
||||||
|
|
||||||
|
$currency = $container->get( 'api.shop.currency.getter' );
|
||||||
|
assert( $currency instanceof CurrencyGetter );
|
||||||
|
|
||||||
|
$session_id = '';
|
||||||
|
if ( isset( WC()->session ) && method_exists( WC()->session, 'get_customer_unique_id' ) ) {
|
||||||
|
$session_id = substr(
|
||||||
|
md5( WC()->session->get_customer_unique_id() ),
|
||||||
|
0,
|
||||||
|
16
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||||
|
'client_id' => ( $settings->has( 'client_id' ) ? $settings->get( 'client_id' ) : null ),
|
||||||
|
'session_id' => $session_id,
|
||||||
|
'amount' => array(
|
||||||
|
'currency_code' => $currency->get(),
|
||||||
|
),
|
||||||
|
'payment_method_selected_map' => $container->get( 'axo.payment_method_selected_map' ),
|
||||||
|
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// The mapping of payment methods to the PayPal Insights 'payment_method_selected' types.
|
||||||
|
'axo.payment_method_selected_map' => static function ( ContainerInterface $container ): array {
|
||||||
|
return array(
|
||||||
|
'ppcp-axo-gateway' => 'card',
|
||||||
|
'ppcp-credit-card-gateway' => 'card',
|
||||||
|
'ppcp-gateway' => 'paypal',
|
||||||
|
'ppcp-googlepay' => 'google_pay',
|
||||||
|
'ppcp-applepay' => 'apple_pay',
|
||||||
|
'ppcp-multibanco' => 'other',
|
||||||
|
'ppcp-trustly' => 'other',
|
||||||
|
'ppcp-p24' => 'other',
|
||||||
|
'ppcp-mybank' => 'other',
|
||||||
|
'ppcp-ideal' => 'other',
|
||||||
|
'ppcp-eps' => 'other',
|
||||||
|
'ppcp-blik' => 'other',
|
||||||
|
'ppcp-bancontact' => 'other',
|
||||||
|
'ppcp-card-button-gateway' => 'card',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The matrix which countries and currency combinations can be used for AXO.
|
* The matrix which countries and currency combinations can be used for AXO.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -52,6 +52,13 @@ class AxoManager {
|
||||||
*/
|
*/
|
||||||
private Environment $environment;
|
private Environment $environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data needed for the PayPal Insights.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $insights_data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Settings status helper.
|
* The Settings status helper.
|
||||||
*
|
*
|
||||||
|
@ -107,6 +114,7 @@ class AxoManager {
|
||||||
* @param SessionHandler $session_handler The Session handler.
|
* @param SessionHandler $session_handler The Session handler.
|
||||||
* @param Settings $settings The Settings.
|
* @param Settings $settings The Settings.
|
||||||
* @param Environment $environment The environment object.
|
* @param Environment $environment The environment object.
|
||||||
|
* @param array $insights_data Data needed for the PayPal Insights.
|
||||||
* @param SettingsStatus $settings_status The Settings status helper.
|
* @param SettingsStatus $settings_status The Settings status helper.
|
||||||
* @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop.
|
* @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop.
|
||||||
* @param LoggerInterface $logger The logger.
|
* @param LoggerInterface $logger The logger.
|
||||||
|
@ -120,6 +128,7 @@ class AxoManager {
|
||||||
SessionHandler $session_handler,
|
SessionHandler $session_handler,
|
||||||
Settings $settings,
|
Settings $settings,
|
||||||
Environment $environment,
|
Environment $environment,
|
||||||
|
array $insights_data,
|
||||||
SettingsStatus $settings_status,
|
SettingsStatus $settings_status,
|
||||||
CurrencyGetter $currency,
|
CurrencyGetter $currency,
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
|
@ -133,12 +142,13 @@ class AxoManager {
|
||||||
$this->session_handler = $session_handler;
|
$this->session_handler = $session_handler;
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
$this->environment = $environment;
|
$this->environment = $environment;
|
||||||
|
$this->insights_data = $insights_data;
|
||||||
$this->settings_status = $settings_status;
|
$this->settings_status = $settings_status;
|
||||||
$this->currency = $currency;
|
$this->currency = $currency;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->wcgateway_module_url = $wcgateway_module_url;
|
$this->wcgateway_module_url = $wcgateway_module_url;
|
||||||
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
|
|
||||||
$this->enabled_shipping_locations = $enabled_shipping_locations;
|
$this->enabled_shipping_locations = $enabled_shipping_locations;
|
||||||
|
$this->supported_country_card_type_matrix = $supported_country_card_type_matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,7 +189,7 @@ class AxoManager {
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function script_data() {
|
private function script_data(): array {
|
||||||
return array(
|
return array(
|
||||||
'environment' => array(
|
'environment' => array(
|
||||||
'is_sandbox' => $this->environment->current_environment() === 'sandbox',
|
'is_sandbox' => $this->environment->current_environment() === 'sandbox',
|
||||||
|
@ -187,20 +197,10 @@ class AxoManager {
|
||||||
'widgets' => array(
|
'widgets' => array(
|
||||||
'email' => 'render',
|
'email' => 'render',
|
||||||
),
|
),
|
||||||
'insights' => array(
|
// The amount is not available when setting the insights data, so we need to merge it here.
|
||||||
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
'insights' => ( function( array $data ): array {
|
||||||
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
|
$data['amount']['value'] = WC()->cart->get_total( 'numeric' );
|
||||||
'session_id' =>
|
return $data; } )( $this->insights_data ),
|
||||||
substr(
|
|
||||||
method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '',
|
|
||||||
0,
|
|
||||||
16
|
|
||||||
),
|
|
||||||
'amount' => array(
|
|
||||||
'currency_code' => $this->currency->get(),
|
|
||||||
'value' => WC()->cart->get_total( 'numeric' ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'allowed_cards' => $this->supported_country_card_type_matrix,
|
'allowed_cards' => $this->supported_country_card_type_matrix,
|
||||||
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
|
'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(),
|
||||||
'enabled_shipping_locations' => $this->enabled_shipping_locations,
|
'enabled_shipping_locations' => $this->enabled_shipping_locations,
|
||||||
|
|
|
@ -320,6 +320,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||||
$endpoint->handle_request();
|
$endpoint->handle_request();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enqueue the PayPal Insights script.
|
||||||
|
add_action(
|
||||||
|
'wp_enqueue_scripts',
|
||||||
|
function () use ( $c ) {
|
||||||
|
$this->enqueue_paypal_insights_script_on_order_received( $c );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,8 +444,8 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function is_excluded_endpoint(): bool {
|
private function is_excluded_endpoint(): bool {
|
||||||
// Exclude the Order Pay endpoint.
|
// Exclude the Order Pay and Order Received endpoints.
|
||||||
return is_wc_endpoint_url( 'order-pay' );
|
return is_wc_endpoint_url( 'order-pay' ) || is_wc_endpoint_url( 'order-received' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -457,4 +466,57 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||||
$axo_enabled ? 'enabled' : 'disabled'
|
$axo_enabled ? 'enabled' : 'disabled'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues PayPal Insights on the Order Received endpoint.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $c The service container.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function enqueue_paypal_insights_script_on_order_received( ContainerInterface $c ): void {
|
||||||
|
global $wp;
|
||||||
|
|
||||||
|
if ( ! isset( $wp->query_vars['order-received'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$order_id = absint( $wp->query_vars['order-received'] );
|
||||||
|
if ( ! $order_id ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$order = wc_get_order( $order_id );
|
||||||
|
if ( ! $order || ! $order instanceof \WC_Order ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$module_url = $c->get( 'axo.url' );
|
||||||
|
$asset_version = $c->get( 'ppcp.asset-version' );
|
||||||
|
$insights_data = $c->get( 'axo.insights' );
|
||||||
|
|
||||||
|
wp_register_script(
|
||||||
|
'wc-ppcp-paypal-insights-end-checkout',
|
||||||
|
untrailingslashit( $module_url ) . '/assets/js/TrackEndCheckout.js',
|
||||||
|
array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ),
|
||||||
|
$asset_version,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_localize_script(
|
||||||
|
'wc-ppcp-paypal-insights-end-checkout',
|
||||||
|
'wc_ppcp_axo_insights_data',
|
||||||
|
array_merge(
|
||||||
|
$insights_data,
|
||||||
|
array(
|
||||||
|
'orderId' => $order_id,
|
||||||
|
'orderTotal' => (string) $order->get_total(),
|
||||||
|
'orderCurrency' => (string) $order->get_currency(),
|
||||||
|
'paymentMethod' => (string) $order->get_payment_method(),
|
||||||
|
'orderKey' => (string) $order->get_order_key(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_enqueue_script( 'wc-ppcp-paypal-insights-end-checkout' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const path = require('path');
|
const path = require( 'path' );
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
|
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
|
||||||
|
@ -9,15 +9,19 @@ module.exports = {
|
||||||
target: 'web',
|
target: 'web',
|
||||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||||
entry: {
|
entry: {
|
||||||
'boot': path.resolve('./resources/js/boot.js'),
|
boot: path.resolve( './resources/js/boot.js' ),
|
||||||
'styles': path.resolve('./resources/css/styles.scss')
|
styles: path.resolve( './resources/css/styles.scss' ),
|
||||||
|
TrackEndCheckout: path.resolve(
|
||||||
|
'./resources/js/Insights/EndCheckoutTracker.js'
|
||||||
|
),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'assets/'),
|
path: path.resolve( __dirname, 'assets/' ),
|
||||||
filename: 'js/[name].js',
|
filename: 'js/[name].js',
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [
|
||||||
|
{
|
||||||
test: /\.js?$/,
|
test: /\.js?$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
|
@ -30,10 +34,11 @@ module.exports = {
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: 'css/[name].css',
|
name: 'css/[name].css',
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{loader:'sass-loader'}
|
},
|
||||||
]
|
{ loader: 'sass-loader' },
|
||||||
}]
|
],
|
||||||
}
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,6 +44,12 @@ class OnboardingModule implements ServiceModule, ExtendingModule, ExecutableModu
|
||||||
*/
|
*/
|
||||||
public function run( ContainerInterface $c ): bool {
|
public function run( ContainerInterface $c ): bool {
|
||||||
|
|
||||||
|
if ( ! apply_filters(
|
||||||
|
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||||
|
'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled',
|
||||||
|
getenv( 'PCP_SETTINGS_ENABLED' ) === '1'
|
||||||
|
) ) {
|
||||||
|
|
||||||
$asset_loader = $c->get( 'onboarding.assets' );
|
$asset_loader = $c->get( 'onboarding.assets' );
|
||||||
/**
|
/**
|
||||||
* The OnboardingAssets.
|
* The OnboardingAssets.
|
||||||
|
@ -64,6 +70,7 @@ class OnboardingModule implements ServiceModule, ExtendingModule, ExecutableModu
|
||||||
'enqueue',
|
'enqueue',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_form_field',
|
'woocommerce_form_field',
|
||||||
|
|
|
@ -82,9 +82,8 @@ import {
|
||||||
renderFields( cardFields );
|
renderFields( cardFields );
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
const placeOrderButton = document.querySelector( '#place_order' );
|
||||||
.querySelector( '#place_order' )
|
placeOrderButton?.addEventListener( 'click', ( event ) => {
|
||||||
?.addEventListener( 'click', ( event ) => {
|
|
||||||
const cardPaymentToken = document.querySelector(
|
const cardPaymentToken = document.querySelector(
|
||||||
'input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked'
|
'input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked'
|
||||||
)?.value;
|
)?.value;
|
||||||
|
@ -95,11 +94,11 @@ import {
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
placeOrderButton.disabled = true;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
cardFields.submit().catch( ( error ) => {
|
cardFields.submit().catch( ( error ) => {
|
||||||
console.error( error );
|
console.error( error );
|
||||||
|
placeOrderButton.disabled = false;
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -18,7 +18,7 @@ $color-gradient-dark: #001435;
|
||||||
$gradient-header: linear-gradient(87.03deg, #003087 -0.49%, #001E51 29.22%, $color-gradient-dark 100%);
|
$gradient-header: linear-gradient(87.03deg, #003087 -0.49%, #001E51 29.22%, $color-gradient-dark 100%);
|
||||||
|
|
||||||
$max-width-onboarding: 1024px;
|
$max-width-onboarding: 1024px;
|
||||||
$max-width-onboarding-content: 662px;
|
$max-width-onboarding-content: 500px;
|
||||||
$max-width-settings: 938px;
|
$max-width-settings: 938px;
|
||||||
|
|
||||||
#ppcp-settings-container {
|
#ppcp-settings-container {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.ppcp-r-onboarding-header{
|
.ppcp-r-onboarding-header{
|
||||||
margin: 0 0 32px 0;
|
margin: 0 0 24px 0;
|
||||||
|
|
||||||
&__logo {
|
&__logo {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -8,14 +8,12 @@
|
||||||
.ppcp-r-select-box {
|
.ppcp-r-select-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid $color-gray-500;
|
border: 1px solid $color-gray-200;
|
||||||
outline: 1px solid transparent;
|
outline: 1px solid transparent;
|
||||||
border-radius: 8px;
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 32px;
|
gap: 16px;
|
||||||
align-items: center;
|
padding: 24px 16px 24px 16px;
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 28px 16px 28px 32px;
|
|
||||||
|
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
|
@ -59,20 +57,23 @@
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
@include font(16, 24, 600);
|
@include font(14, 20, 700);
|
||||||
color: $color-blueberry;
|
color: $color-black;
|
||||||
margin: 0 0 4px 0;
|
margin: 0 0 4px 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
@include font(14, 20, 400);
|
@include font(13, 20, 400);
|
||||||
color: $color-gray-800;
|
color: $color-gray-700;
|
||||||
margin: 0 0 18px 0;
|
margin:0;
|
||||||
|
|
||||||
|
&:not(:last-child){
|
||||||
|
margin-block-end:18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__radio-presentation {
|
&__radio-presentation {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
padding: 0 16px 48px;
|
padding: 0 16px 48px;
|
||||||
box-sizing: border-box;
|
box-sizing: content-box;
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
@media screen and (max-width: 480px) {
|
||||||
padding-bottom: 36px;
|
padding-bottom: 36px;
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
color: $color-gray-700;
|
@include font(13, 20, 400);
|
||||||
@include font(14, 20, 400);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
|
@ -29,7 +28,7 @@
|
||||||
|
|
||||||
.ppcp-r-select-box__additional-content {
|
.ppcp-r-select-box__additional-content {
|
||||||
a {
|
a {
|
||||||
@include font(12, 20, 400);
|
@include font(13, 20, 500);
|
||||||
color: $color-blueberry;
|
color: $color-blueberry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.ppcp-r-tab-dashboard-todo {
|
.ppcp-r-tab-overview-todo {
|
||||||
margin: 0 0 48px 0;
|
margin: 0 0 48px 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
@import './components/reusable-components/spinner-overlay';
|
@import './components/reusable-components/spinner-overlay';
|
||||||
@import './components/reusable-components/welcome-docs';
|
@import './components/reusable-components/welcome-docs';
|
||||||
@import './components/screens/onboarding';
|
@import './components/screens/onboarding';
|
||||||
@import './components/screens/dashboard/tab-dashboard';
|
@import './components/screens/overview/tab-overview';
|
||||||
@import './components/screens/dashboard/tab-payment-methods';
|
@import './components/screens/overview/tab-payment-methods';
|
||||||
@import 'components/screens/dashboard/tab-settings';
|
@import 'components/screens/overview/tab-settings';
|
||||||
}
|
}
|
||||||
|
|
||||||
@import './components/reusable-components/payment-method-modal';
|
@import './components/reusable-components/payment-method-modal';
|
||||||
|
|
|
@ -31,7 +31,6 @@ const SelectBox = ( props ) => {
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
<div className="ppcp-r-select-box__content">
|
<div className="ppcp-r-select-box__content">
|
||||||
{ data().getImage( props.icon ) }
|
|
||||||
<div className="ppcp-r-select-box__content-inner">
|
<div className="ppcp-r-select-box__content-inner">
|
||||||
<span className="ppcp-r-select-box__title">
|
<span className="ppcp-r-select-box__title">
|
||||||
{ props.title }
|
{ props.title }
|
||||||
|
|
|
@ -33,50 +33,21 @@ const StepBusiness = ( {
|
||||||
<div className="ppcp-r-page-business">
|
<div className="ppcp-r-page-business">
|
||||||
<OnboardingHeader
|
<OnboardingHeader
|
||||||
title={ __(
|
title={ __(
|
||||||
'Tell Us About Your Business',
|
'Choose your account type',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
<div className="ppcp-r-inner-container">
|
<div className="ppcp-r-inner-container">
|
||||||
<SelectBoxWrapper>
|
<SelectBoxWrapper>
|
||||||
<SelectBox
|
|
||||||
title={ __(
|
|
||||||
'Casual Seller',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
) }
|
|
||||||
description={ __(
|
|
||||||
'I sell occasionally and mainly use PayPal for personal transactions.',
|
|
||||||
'woocommerce-paypal-payments'
|
|
||||||
) }
|
|
||||||
icon="icon-business-casual-seller.svg"
|
|
||||||
name={ BUSINESS_RADIO_GROUP_NAME }
|
|
||||||
value={ BUSINESS_TYPES.CASUAL_SELLER }
|
|
||||||
changeCallback={ handleSellerTypeChange }
|
|
||||||
currentValue={ getCurrentValue() }
|
|
||||||
checked={ isCasualSeller === true }
|
|
||||||
type="radio"
|
|
||||||
>
|
|
||||||
<PaymentMethodIcons
|
|
||||||
icons={ [
|
|
||||||
'paypal',
|
|
||||||
'venmo',
|
|
||||||
'visa',
|
|
||||||
'mastercard',
|
|
||||||
'amex',
|
|
||||||
'discover',
|
|
||||||
] }
|
|
||||||
/>
|
|
||||||
</SelectBox>
|
|
||||||
<SelectBox
|
<SelectBox
|
||||||
title={ __(
|
title={ __(
|
||||||
'Business',
|
'Business',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'I run a registered business and sell full-time.',
|
'Recommended for individuals and organizations that primarily use PayPal to sell goods or services or receive donations, even if your business is not incorporated.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
icon="icon-business-business.svg"
|
|
||||||
name={ BUSINESS_RADIO_GROUP_NAME }
|
name={ BUSINESS_RADIO_GROUP_NAME }
|
||||||
value={ BUSINESS_TYPES.BUSINESS }
|
value={ BUSINESS_TYPES.BUSINESS }
|
||||||
changeCallback={ handleSellerTypeChange }
|
changeCallback={ handleSellerTypeChange }
|
||||||
|
@ -84,19 +55,23 @@ const StepBusiness = ( {
|
||||||
checked={ isCasualSeller === false }
|
checked={ isCasualSeller === false }
|
||||||
type="radio"
|
type="radio"
|
||||||
>
|
>
|
||||||
<PaymentMethodIcons
|
</SelectBox>
|
||||||
icons={ [
|
<SelectBox
|
||||||
'paypal',
|
title={ __(
|
||||||
'venmo',
|
'Personal Account',
|
||||||
'visa',
|
'woocommerce-paypal-payments'
|
||||||
'mastercard',
|
) }
|
||||||
'amex',
|
description={ __(
|
||||||
'discover',
|
'Ideal for those who primarily make purchases or send personal transactions to family and friends.',
|
||||||
'apple-pay',
|
'woocommerce-paypal-payments'
|
||||||
'google-pay',
|
) }
|
||||||
'ideal',
|
name={ BUSINESS_RADIO_GROUP_NAME }
|
||||||
] }
|
value={ BUSINESS_TYPES.CASUAL_SELLER }
|
||||||
/>
|
changeCallback={ handleSellerTypeChange }
|
||||||
|
currentValue={ getCurrentValue() }
|
||||||
|
checked={ isCasualSeller === true }
|
||||||
|
type="radio"
|
||||||
|
>
|
||||||
</SelectBox>
|
</SelectBox>
|
||||||
</SelectBoxWrapper>
|
</SelectBoxWrapper>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,7 +18,7 @@ const StepProducts = ( {
|
||||||
<div className="ppcp-r-page-products">
|
<div className="ppcp-r-page-products">
|
||||||
<OnboardingHeader
|
<OnboardingHeader
|
||||||
title={ __(
|
title={ __(
|
||||||
'Tell Us About the Products You Sell',
|
'Tell us about the products you sell',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
|
@ -27,10 +27,9 @@ const StepProducts = ( {
|
||||||
<SelectBox
|
<SelectBox
|
||||||
title={ __( 'Virtual', 'woocommerce-paypal-payments' ) }
|
title={ __( 'Virtual', 'woocommerce-paypal-payments' ) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'Digital items or services that don’t require shipping.',
|
'Items do not require shipping.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
icon="icon-product-virtual.svg"
|
|
||||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||||
value={ PRODUCT_TYPES.VIRTUAL }
|
value={ PRODUCT_TYPES.VIRTUAL }
|
||||||
changeCallback={ toggleProduct }
|
changeCallback={ toggleProduct }
|
||||||
|
@ -70,10 +69,9 @@ const StepProducts = ( {
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'Items that need to be shipped.',
|
'Items require shipping.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
icon="icon-product-physical.svg"
|
|
||||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||||
value={ PRODUCT_TYPES.PHYSICAL }
|
value={ PRODUCT_TYPES.PHYSICAL }
|
||||||
changeCallback={ toggleProduct }
|
changeCallback={ toggleProduct }
|
||||||
|
@ -98,10 +96,9 @@ const StepProducts = ( {
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'Recurring payments for physical goods or services.',
|
'Recurring payments for either physical goods or services.',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
) }
|
) }
|
||||||
icon="icon-product-subscription.svg"
|
|
||||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||||
value={ PRODUCT_TYPES.SUBSCRIPTIONS }
|
value={ PRODUCT_TYPES.SUBSCRIPTIONS }
|
||||||
changeCallback={ toggleProduct }
|
changeCallback={ toggleProduct }
|
||||||
|
|
|
@ -15,16 +15,16 @@ import ConnectionInfo, {
|
||||||
connectionStatusDataDefault,
|
connectionStatusDataDefault,
|
||||||
} from '../../ReusableComponents/ConnectionInfo';
|
} from '../../ReusableComponents/ConnectionInfo';
|
||||||
|
|
||||||
const TabDashboard = () => {
|
const TabOverview = () => {
|
||||||
const [ todos, setTodos ] = useState( [] );
|
const [ todos, setTodos ] = useState( [] );
|
||||||
const [ todosData, setTodosData ] = useState( todosDataDefault );
|
const [ todosData, setTodosData ] = useState( todosDataDefault );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ppcp-r-tab-dashboard">
|
<div className="ppcp-r-tab-overview">
|
||||||
{ todosData.length > 0 && (
|
{ todosData.length > 0 && (
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
className="ppcp-r-tab-dashboard-todo"
|
className="ppcp-r-tab-overview-todo"
|
||||||
icon="icon-dashboard-list.svg"
|
icon="icon-overview-list.svg"
|
||||||
title={ __(
|
title={ __(
|
||||||
'Things to do next',
|
'Things to do next',
|
||||||
'woocommerce-paypal-payments'
|
'woocommerce-paypal-payments'
|
||||||
|
@ -51,8 +51,8 @@ const TabDashboard = () => {
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
) }
|
) }
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
className="ppcp-r-tab-dashboard-support"
|
className="ppcp-r-tab-overview-support"
|
||||||
icon="icon-dashboard-support.svg"
|
icon="icon-overview-support.svg"
|
||||||
title={ __( 'Status', 'woocommerce-paypal-payments' ) }
|
title={ __( 'Status', 'woocommerce-paypal-payments' ) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'Your PayPal account connection details, along with available products and features.',
|
'Your PayPal account connection details, along with available products and features.',
|
||||||
|
@ -400,4 +400,4 @@ const featuresDefault = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
export default TabDashboard;
|
export default TabOverview;
|
|
@ -1,16 +1,16 @@
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import TabDashboard from './Dashboard/TabDashboard';
|
import TabOverview from './Overview/TabOverview';
|
||||||
import TabPaymentMethods from './Dashboard/TabPaymentMethods';
|
import TabPaymentMethods from './Overview/TabPaymentMethods';
|
||||||
import TabSettings from './Dashboard/TabSettings';
|
import TabSettings from './Overview/TabSettings';
|
||||||
import TabStyling from './Dashboard/TabStyling';
|
import TabStyling from './Overview/TabStyling';
|
||||||
|
|
||||||
export const getSettingsTabs = () => {
|
export const getSettingsTabs = () => {
|
||||||
const tabs = [];
|
const tabs = [];
|
||||||
|
|
||||||
tabs.push( {
|
tabs.push( {
|
||||||
name: 'dashboard',
|
name: 'overview',
|
||||||
title: __( 'Dashboard', 'woocommerce-paypal-payments' ),
|
title: __( 'Overview', 'woocommerce-paypal-payments' ),
|
||||||
component: <TabDashboard />,
|
component: <TabOverview />,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
tabs.push( {
|
tabs.push( {
|
||||||
|
|