woocommerce/plugins/woocommerce-blocks/assets/js/payment-method-extensions/payment-methods/stripe/payment-request/use-initialization.js

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,
};
};