mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Merge 3a20cb433a
into 098a79279d
This commit is contained in:
commit
17a1b7c865
4 changed files with 209 additions and 8 deletions
|
@ -11,6 +11,7 @@ import { snapshotFields } from '../helpers/fieldHelpers';
|
|||
import useCustomerData from './useCustomerData';
|
||||
import useShippingAddressChange from './useShippingAddressChange';
|
||||
import useCardChange from './useCardChange';
|
||||
import useSessionRestoration from './useSessionRestoration';
|
||||
|
||||
/**
|
||||
* Custom hook to set up AXO functionality.
|
||||
|
@ -63,6 +64,9 @@ const useAxoSetup = (
|
|||
// Set up phone sync handler
|
||||
usePhoneSyncHandler( paymentComponent );
|
||||
|
||||
// Set up session restoration
|
||||
useSessionRestoration( fastlaneSdk );
|
||||
|
||||
// Initialize class toggles on mount
|
||||
useEffect( () => {
|
||||
initializeClassToggles();
|
||||
|
@ -104,6 +108,7 @@ const useAxoSetup = (
|
|||
setShippingAddress,
|
||||
setCardDetails,
|
||||
paymentComponent,
|
||||
setCardChangeHandler,
|
||||
] );
|
||||
|
||||
return paypalLoaded;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import { useEffect, useRef } from '@wordpress/element';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { setIsEmailLookupCompleted, STORE_NAME } from '../stores/axoStore';
|
||||
import { log } from '../../../../ppcp-axo/resources/js/Helper/Debug';
|
||||
|
||||
/**
|
||||
* Hook to restore Fastlane session after payment failures using triggerAuthenticationFlow
|
||||
* Only runs when ppcp_fastlane_error=1 URL parameter is present
|
||||
* @param {Object} fastlaneSdk - The Fastlane SDK instance
|
||||
*/
|
||||
const useSessionRestoration = ( fastlaneSdk ) => {
|
||||
const { setShippingAddress, setCardDetails, setIsGuest } =
|
||||
useDispatch( STORE_NAME );
|
||||
const hasProcessed = useRef( false );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! fastlaneSdk || hasProcessed.current ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlParams = new URLSearchParams( window.location.search );
|
||||
const hasErrorParam = urlParams.get( 'ppcp_fastlane_error' ) === '1';
|
||||
|
||||
if ( ! hasErrorParam ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the error parameter from URL
|
||||
urlParams.delete( 'ppcp_fastlane_error' );
|
||||
const newUrl = new URL( window.location );
|
||||
newUrl.search = urlParams.toString();
|
||||
window.history.replaceState( {}, '', newUrl );
|
||||
|
||||
hasProcessed.current = true;
|
||||
|
||||
const restoreSession = async () => {
|
||||
try {
|
||||
const emailInput = document.getElementById( 'email' );
|
||||
|
||||
if ( emailInput?.value ) {
|
||||
const lookupResult =
|
||||
await fastlaneSdk.identity.lookupCustomerByEmail(
|
||||
emailInput.value
|
||||
);
|
||||
|
||||
wp.data.dispatch( STORE_NAME ).setIsEmailSubmitted( true );
|
||||
|
||||
if ( lookupResult?.customerContextId ) {
|
||||
const customerContextId =
|
||||
lookupResult.customerContextId;
|
||||
|
||||
const authenticatedCustomerResult =
|
||||
await fastlaneSdk.identity.triggerAuthenticationFlow(
|
||||
customerContextId
|
||||
);
|
||||
|
||||
if (
|
||||
authenticatedCustomerResult?.authenticationState ===
|
||||
'succeeded'
|
||||
) {
|
||||
const { profileData } = authenticatedCustomerResult;
|
||||
setIsGuest( false );
|
||||
|
||||
if ( profileData?.shippingAddress ) {
|
||||
setShippingAddress(
|
||||
profileData.shippingAddress
|
||||
);
|
||||
}
|
||||
|
||||
if ( profileData?.card ) {
|
||||
setCardDetails( profileData.card );
|
||||
}
|
||||
|
||||
setIsEmailLookupCompleted( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( error ) {
|
||||
log( 'Failed to restore Fastlane session', 'warn' );
|
||||
}
|
||||
};
|
||||
|
||||
restoreSession();
|
||||
}, [ fastlaneSdk, setShippingAddress, setCardDetails, setIsGuest ] );
|
||||
};
|
||||
|
||||
export default useSessionRestoration;
|
|
@ -60,6 +60,8 @@ class AxoManager {
|
|||
this.fastlane = new Fastlane( namespace );
|
||||
this.$ = jQuery;
|
||||
|
||||
this.hasProcessedSessionRestore = false;
|
||||
|
||||
this.status = {
|
||||
active: false,
|
||||
validEmail: false,
|
||||
|
@ -529,7 +531,15 @@ class AxoManager {
|
|||
log(
|
||||
`this.lastEmailCheckedIdentity: ${ this.lastEmailCheckedIdentity }`
|
||||
);
|
||||
if (
|
||||
|
||||
const urlParams = new URLSearchParams( window.location.search );
|
||||
const hasErrorParam = urlParams.get( 'ppcp_fastlane_error' ) === '1';
|
||||
|
||||
if ( hasErrorParam ) {
|
||||
log(
|
||||
'Payment failure detected, session restoration will be attempted'
|
||||
);
|
||||
} else if (
|
||||
this.emailInput &&
|
||||
this.lastEmailCheckedIdentity !== this.emailInput.value
|
||||
) {
|
||||
|
@ -662,6 +672,8 @@ class AxoManager {
|
|||
await this.renderWatermark();
|
||||
this.renderEmailSubmitButton();
|
||||
this.watchEmail();
|
||||
|
||||
await this.restoreSessionAfterFailure();
|
||||
}
|
||||
|
||||
async connect() {
|
||||
|
@ -1383,6 +1395,94 @@ class AxoManager {
|
|||
this.$( '#billing_email_field input' ).on( 'input', reEnableInput );
|
||||
this.$( '#billing_email_field input' ).on( 'click', reEnableInput );
|
||||
}
|
||||
|
||||
async restoreSessionAfterFailure() {
|
||||
if ( ! this.fastlane || this.hasProcessedSessionRestore ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlParams = new URLSearchParams( window.location.search );
|
||||
const hasErrorParam = urlParams.get( 'ppcp_fastlane_error' ) === '1';
|
||||
|
||||
if ( ! hasErrorParam ) {
|
||||
return;
|
||||
}
|
||||
|
||||
urlParams.delete( 'ppcp_fastlane_error' );
|
||||
const newUrl = new URL( window.location );
|
||||
newUrl.search = urlParams.toString();
|
||||
window.history.replaceState( {}, '', newUrl );
|
||||
|
||||
this.hasProcessedSessionRestore = true;
|
||||
|
||||
try {
|
||||
if ( this.emailInput?.value ) {
|
||||
log(
|
||||
`Restoring Fastlane session for email: ${ this.emailInput.value }`
|
||||
);
|
||||
|
||||
const lookupResult =
|
||||
await this.fastlane.identity.lookupCustomerByEmail(
|
||||
this.emailInput.value
|
||||
);
|
||||
|
||||
if ( lookupResult?.customerContextId ) {
|
||||
const authenticatedCustomerResult =
|
||||
await this.fastlane.identity.triggerAuthenticationFlow(
|
||||
lookupResult.customerContextId
|
||||
);
|
||||
|
||||
if (
|
||||
authenticatedCustomerResult?.authenticationState ===
|
||||
'succeeded'
|
||||
) {
|
||||
const { profileData } = authenticatedCustomerResult;
|
||||
|
||||
if ( profileData?.shippingAddress ) {
|
||||
this.setShipping( profileData.shippingAddress );
|
||||
}
|
||||
|
||||
if ( profileData?.card ) {
|
||||
this.setCard( profileData.card );
|
||||
this.setStatus( 'hasCard', true );
|
||||
|
||||
const cardBillingAddress =
|
||||
profileData.card?.paymentSource?.card
|
||||
?.billingAddress;
|
||||
if ( cardBillingAddress ) {
|
||||
const billingData = {
|
||||
address: cardBillingAddress,
|
||||
};
|
||||
|
||||
const phoneNumber =
|
||||
profileData.shippingAddress?.phoneNumber
|
||||
?.nationalNumber;
|
||||
if ( phoneNumber ) {
|
||||
billingData.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
this.setBilling( billingData );
|
||||
}
|
||||
}
|
||||
|
||||
this.setStatus( 'validEmail', true );
|
||||
this.setStatus( 'hasProfile', true );
|
||||
|
||||
this.hideGatewaySelection = true;
|
||||
this.$( '.wc_payment_methods label' ).hide();
|
||||
this.$( '.wc_payment_methods input' ).hide();
|
||||
|
||||
await this.renderWatermark( false );
|
||||
|
||||
log( 'Fastlane session successfully restored' );
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( error ) {
|
||||
log( 'Failed to restore Fastlane session', 'warn' );
|
||||
console.warn( 'Fastlane session restoration error:', error );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AxoManager;
|
||||
|
|
|
@ -82,7 +82,7 @@ class ReturnUrlEndpoint {
|
|||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
if ( ! isset( $_GET['token'] ) ) {
|
||||
wc_add_notice( __( 'Payment session expired. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
$token = sanitize_text_field( wp_unslash( $_GET['token'] ) );
|
||||
|
@ -93,7 +93,7 @@ class ReturnUrlEndpoint {
|
|||
} catch ( Exception $exception ) {
|
||||
$this->logger->warning( "Return URL endpoint failed to fetch order $token: " . $exception->getMessage() );
|
||||
wc_add_notice( __( 'Could not retrieve payment information. Please try again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ class ReturnUrlEndpoint {
|
|||
} catch ( Exception $e ) {
|
||||
$this->logger->warning( "3DS completion failed for order $token: " . $e->getMessage() );
|
||||
wc_add_notice( $this->get_3ds_error_message( $e ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ class ReturnUrlEndpoint {
|
|||
|
||||
$this->logger->warning( "Return URL endpoint $token: no WC order ID." );
|
||||
wc_add_notice( __( 'Order information is missing. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ class ReturnUrlEndpoint {
|
|||
$this->logger->warning( "Return URL endpoint $token: WC order $wc_order_id not found." );
|
||||
|
||||
wc_add_notice( __( 'Order not found. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ class ReturnUrlEndpoint {
|
|||
$payment_gateway = $this->get_payment_gateway( $wc_order->get_payment_method() );
|
||||
if ( ! $payment_gateway ) {
|
||||
wc_add_notice( __( 'Payment gateway is unavailable. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -170,10 +170,19 @@ class ReturnUrlEndpoint {
|
|||
}
|
||||
|
||||
wc_add_notice( __( 'Payment processing failed. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
wp_safe_redirect( $this->get_checkout_url_with_error() );
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checkout URL with Fastlane error parameter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_checkout_url_with_error(): string {
|
||||
return add_query_arg( 'ppcp_fastlane_error', '1', wc_get_checkout_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if order needs 3DS completion.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue