Skip to content

Commit

Permalink
Merge pull request #2737 from woocommerce/PCP-3770-fastlane-implement…
Browse files Browse the repository at this point in the history
…-insights-sdk-for-block-checkout-ver2

Axo: Add PayPal Insights integration to the Block Checkout (3770)
  • Loading branch information
Dinamiko authored Nov 19, 2024
2 parents e725e42 + 55d98fd commit d09d7f7
Show file tree
Hide file tree
Showing 12 changed files with 633 additions and 104 deletions.
259 changes: 259 additions & 0 deletions modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js
Original file line number Diff line number Diff line change
@@ -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;
16 changes: 9 additions & 7 deletions modules/ppcp-axo-block/resources/js/stores/axoStore.js
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -108,13 +108,15 @@ const selectors = {
};

// Create and register the Redux store for the AXO block
const store = createReduxStore( STORE_NAME, {
reducer,
actions,
selectors,
} );
if ( ! select( STORE_NAME ) ) {
const store = createReduxStore( STORE_NAME, {
reducer,
actions,
selectors,
} );

register( store );
register( store );
}

// Action dispatchers

Expand Down
3 changes: 2 additions & 1 deletion modules/ppcp-axo-block/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@
$container->get( 'axoblock.url' ),
$container->get( 'ppcp.asset-version' ),
$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.configuration.dcc' ),
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.url' ),
$container->get( 'axo.payment_method_selected_map' ),
$container->get( 'axo.supported-country-card-type-matrix' ),
$container->get( 'axo.shipping-wc-enabled-locations' )
);
Expand Down
42 changes: 42 additions & 0 deletions modules/ppcp-axo-block/src/AxoBlockModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ static function () use ( $c ) {
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;
}

Expand Down Expand Up @@ -166,4 +175,37 @@ private function add_sdk_client_token_to_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' );
}
}
Loading

0 comments on commit d09d7f7

Please sign in to comment.