251 lines
6.5 KiB
JavaScript
251 lines
6.5 KiB
JavaScript
|
/**
|
||
|
* External dependencies
|
||
|
*/
|
||
|
import { useEffect, useState, useRef, useCallback } from '@wordpress/element';
|
||
|
import { useStripe } from '@stripe/react-stripe-js';
|
||
|
import { getSetting } from '@woocommerce/settings';
|
||
|
import { __ } from '@wordpress/i18n';
|
||
|
import isShallowEqual from '@wordpress/is-shallow-equal';
|
||
|
|
||
|
/**
|
||
|
* Internal dependencies
|
||
|
*/
|
||
|
import {
|
||
|
getPaymentRequest,
|
||
|
updatePaymentRequest,
|
||
|
canDoPaymentRequest,
|
||
|
normalizeShippingAddressForCheckout,
|
||
|
normalizeShippingOptionSelectionsForCheckout,
|
||
|
getStripeServerData,
|
||
|
pluckAddress,
|
||
|
normalizeShippingOptions,
|
||
|
} from '../stripe-utils';
|
||
|
import { useEventHandlers } from './use-event-handlers';
|
||
|
|
||
|
/**
|
||
|
* @typedef {import('../stripe-utils/type-defs').StripePaymentRequest} StripePaymentRequest
|
||
|
*/
|
||
|
|
||
|
export const useInitialization = ( {
|
||
|
billing,
|
||
|
shippingData,
|
||
|
setExpressPaymentError,
|
||
|
onClick,
|
||
|
onClose,
|
||
|
onSubmit,
|
||
|
} ) => {
|
||
|
const stripe = useStripe();
|
||
|
/**
|
||
|
* @type {[ StripePaymentRequest|null, function( StripePaymentRequest ):void]}
|
||
|
*/
|
||
|
// @ts-ignore
|
||
|
const [ paymentRequest, setPaymentRequest ] = useState( null );
|
||
|
const [ isFinished, setIsFinished ] = useState( false );
|
||
|
const [ isProcessing, setIsProcessing ] = useState( false );
|
||
|
const [ canMakePayment, setCanMakePayment ] = useState( false );
|
||
|
const [ paymentRequestType, setPaymentRequestType ] = useState( '' );
|
||
|
const currentShipping = useRef( shippingData );
|
||
|
const {
|
||
|
paymentRequestEventHandlers,
|
||
|
clearPaymentRequestEventHandler,
|
||
|
setPaymentRequestEventHandler,
|
||
|
} = useEventHandlers();
|
||
|
|
||
|
// Update refs when any change.
|
||
|
useEffect( () => {
|
||
|
currentShipping.current = shippingData;
|
||
|
}, [ shippingData ] );
|
||
|
|
||
|
// Create the initial paymentRequest object. Note, we can't do anything if stripe isn't available yet or we have zero total.
|
||
|
useEffect( () => {
|
||
|
if (
|
||
|
! stripe ||
|
||
|
! billing.cartTotal.value ||
|
||
|
isFinished ||
|
||
|
isProcessing ||
|
||
|
paymentRequest
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
const pr = getPaymentRequest( {
|
||
|
total: billing.cartTotal,
|
||
|
currencyCode: billing.currency.code.toLowerCase(),
|
||
|
countryCode: getSetting( 'baseLocation', {} )?.country,
|
||
|
shippingRequired: shippingData.needsShipping,
|
||
|
cartTotalItems: billing.cartTotalItems,
|
||
|
stripe,
|
||
|
} );
|
||
|
canDoPaymentRequest( pr ).then( ( result ) => {
|
||
|
setPaymentRequest( pr );
|
||
|
setPaymentRequestType( result.requestType || '' );
|
||
|
setCanMakePayment( result.canPay );
|
||
|
} );
|
||
|
}, [
|
||
|
billing.cartTotal,
|
||
|
billing.currency.code,
|
||
|
shippingData.needsShipping,
|
||
|
billing.cartTotalItems,
|
||
|
stripe,
|
||
|
isProcessing,
|
||
|
isFinished,
|
||
|
paymentRequest,
|
||
|
] );
|
||
|
|
||
|
// When the payment button is clicked, update the request and show it.
|
||
|
const onButtonClick = useCallback( () => {
|
||
|
setIsProcessing( true );
|
||
|
setIsFinished( false );
|
||
|
setExpressPaymentError( '' );
|
||
|
updatePaymentRequest( {
|
||
|
// @ts-ignore
|
||
|
paymentRequest,
|
||
|
total: billing.cartTotal,
|
||
|
currencyCode: billing.currency.code.toLowerCase(),
|
||
|
cartTotalItems: billing.cartTotalItems,
|
||
|
} );
|
||
|
onClick();
|
||
|
}, [
|
||
|
onClick,
|
||
|
paymentRequest,
|
||
|
setExpressPaymentError,
|
||
|
billing.cartTotal,
|
||
|
billing.currency.code,
|
||
|
billing.cartTotalItems,
|
||
|
] );
|
||
|
|
||
|
const abortPayment = useCallback( ( paymentMethod ) => {
|
||
|
paymentMethod.complete( 'fail' );
|
||
|
setIsProcessing( false );
|
||
|
setIsFinished( true );
|
||
|
}, [] );
|
||
|
|
||
|
const completePayment = useCallback( ( paymentMethod ) => {
|
||
|
paymentMethod.complete( 'success' );
|
||
|
setIsFinished( true );
|
||
|
setIsProcessing( false );
|
||
|
}, [] );
|
||
|
|
||
|
// whenever paymentRequest changes, hook in event listeners.
|
||
|
useEffect( () => {
|
||
|
const noop = { removeAllListeners: () => void null };
|
||
|
let shippingAddressChangeEvent = noop,
|
||
|
shippingOptionChangeEvent = noop,
|
||
|
sourceChangeEvent = noop,
|
||
|
cancelChangeEvent = noop;
|
||
|
|
||
|
if ( paymentRequest ) {
|
||
|
const cancelHandler = () => {
|
||
|
setIsFinished( false );
|
||
|
setIsProcessing( false );
|
||
|
setPaymentRequest( null );
|
||
|
onClose();
|
||
|
};
|
||
|
|
||
|
const shippingAddressChangeHandler = ( event ) => {
|
||
|
const newShippingAddress = normalizeShippingAddressForCheckout(
|
||
|
event.shippingAddress
|
||
|
);
|
||
|
if (
|
||
|
isShallowEqual(
|
||
|
pluckAddress( newShippingAddress ),
|
||
|
pluckAddress( currentShipping.current.shippingAddress )
|
||
|
)
|
||
|
) {
|
||
|
// the address is the same so no change needed.
|
||
|
event.updateWith( {
|
||
|
status: 'success',
|
||
|
shippingOptions: normalizeShippingOptions(
|
||
|
currentShipping.current.shippingRates
|
||
|
),
|
||
|
} );
|
||
|
} else {
|
||
|
// the address is different so let's set the new address and
|
||
|
// register the handler to be picked up by the shipping rate
|
||
|
// change event.
|
||
|
currentShipping.current.setShippingAddress(
|
||
|
normalizeShippingAddressForCheckout(
|
||
|
event.shippingAddress
|
||
|
)
|
||
|
);
|
||
|
setPaymentRequestEventHandler(
|
||
|
'shippingAddressChange',
|
||
|
event
|
||
|
);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const shippingOptionChangeHandler = ( event ) => {
|
||
|
currentShipping.current.setSelectedRates(
|
||
|
normalizeShippingOptionSelectionsForCheckout(
|
||
|
event.shippingOption
|
||
|
)
|
||
|
);
|
||
|
setPaymentRequestEventHandler( 'shippingOptionChange', event );
|
||
|
};
|
||
|
|
||
|
const sourceHandler = ( paymentMethod ) => {
|
||
|
if (
|
||
|
// eslint-disable-next-line no-undef
|
||
|
! getStripeServerData().allowPrepaidCard &&
|
||
|
paymentMethod.source.card.funding
|
||
|
) {
|
||
|
setExpressPaymentError(
|
||
|
__(
|
||
|
"Sorry, we're not accepting prepaid cards at this time.",
|
||
|
'woocommerce-gateway-stripe'
|
||
|
)
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
setPaymentRequestEventHandler( 'sourceEvent', paymentMethod );
|
||
|
// kick off checkout processing step.
|
||
|
onSubmit();
|
||
|
};
|
||
|
|
||
|
// @ts-ignore
|
||
|
shippingAddressChangeEvent = paymentRequest.on(
|
||
|
'shippingaddresschange',
|
||
|
shippingAddressChangeHandler
|
||
|
);
|
||
|
// @ts-ignore
|
||
|
shippingOptionChangeEvent = paymentRequest.on(
|
||
|
'shippingoptionchange',
|
||
|
shippingOptionChangeHandler
|
||
|
);
|
||
|
// @ts-ignore
|
||
|
sourceChangeEvent = paymentRequest.on( 'source', sourceHandler );
|
||
|
// @ts-ignore
|
||
|
cancelChangeEvent = paymentRequest.on( 'cancel', cancelHandler );
|
||
|
}
|
||
|
|
||
|
return () => {
|
||
|
if ( paymentRequest ) {
|
||
|
shippingAddressChangeEvent.removeAllListeners();
|
||
|
shippingOptionChangeEvent.removeAllListeners();
|
||
|
sourceChangeEvent.removeAllListeners();
|
||
|
cancelChangeEvent.removeAllListeners();
|
||
|
}
|
||
|
};
|
||
|
}, [
|
||
|
paymentRequest,
|
||
|
canMakePayment,
|
||
|
isProcessing,
|
||
|
setPaymentRequestEventHandler,
|
||
|
setExpressPaymentError,
|
||
|
onSubmit,
|
||
|
onClose,
|
||
|
] );
|
||
|
|
||
|
return {
|
||
|
paymentRequest,
|
||
|
paymentRequestEventHandlers,
|
||
|
clearPaymentRequestEventHandler,
|
||
|
isProcessing,
|
||
|
canMakePayment,
|
||
|
onButtonClick,
|
||
|
abortPayment,
|
||
|
completePayment,
|
||
|
paymentRequestType,
|
||
|
};
|
||
|
};
|