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

242 lines
8.5 KiB
JavaScript

/**
* External dependencies
*/
import { useEffect, useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import {
normalizeShippingOptions,
getTotalPaymentItem,
normalizeLineItems,
getBillingData,
getPaymentMethodData,
getShippingData,
} from '../stripe-utils';
/**
* @typedef {import('@woocommerce/type-defs/registered-payment-method-props').EventRegistrationProps} EventRegistrationProps
* @typedef {import('@woocommerce/type-defs/registered-payment-method-props').BillingDataProps} BillingDataProps
* @typedef {import('@woocommerce/type-defs/registered-payment-method-props').ShippingDataProps} ShippingDataProps
* @typedef {import('@woocommerce/type-defs/registered-payment-method-props').EmitResponseProps} EmitResponseProps
*/
/**
* @param {Object} props
*
* @param {boolean} props.canMakePayment Whether the payment request
* can make payment or not.
* @param {boolean} props.isProcessing Whether the express payment
* method is processing or not.
* @param {EventRegistrationProps} props.eventRegistration Various functions for
* registering observers to
* events.
* @param {Object} props.paymentRequestEventHandlers Cached handlers registered
* for paymentRequest events.
* @param {function(string):void} props.clearPaymentRequestEventHandler Clears the cached payment
* request event handler.
* @param {BillingDataProps} props.billing
* @param {ShippingDataProps} props.shippingData
* @param {EmitResponseProps} props.emitResponse
* @param {string} props.paymentRequestType The derived payment request
* type for the express
* payment being processed.
* @param {function(any):void} props.completePayment This is a callback
* receiving the source event
* and setting it to
* successful payment.
* @param {function(any,string):any} props.abortPayment This is a callback
* receiving the source
* event and setting it to
* failed payment.
*/
export const useCheckoutSubscriptions = ( {
canMakePayment,
isProcessing,
eventRegistration,
paymentRequestEventHandlers,
clearPaymentRequestEventHandler,
billing,
shippingData,
emitResponse,
paymentRequestType,
completePayment,
abortPayment,
} ) => {
const {
onShippingRateSuccess,
onShippingRateFail,
onShippingRateSelectSuccess,
onShippingRateSelectFail,
onPaymentProcessing,
onCheckoutAfterProcessingWithSuccess,
onCheckoutAfterProcessingWithError,
} = eventRegistration;
const { noticeContexts, responseTypes } = emitResponse;
const eventHandlers = useRef( paymentRequestEventHandlers );
const currentBilling = useRef( billing );
const currentShipping = useRef( shippingData );
const currentPaymentRequestType = useRef( paymentRequestType );
useEffect( () => {
eventHandlers.current = paymentRequestEventHandlers;
currentBilling.current = billing;
currentShipping.current = shippingData;
currentPaymentRequestType.current = paymentRequestType;
}, [
paymentRequestEventHandlers,
billing,
shippingData,
paymentRequestType,
] );
// subscribe to events.
useEffect( () => {
const onShippingRatesEvent = ( shippingRates ) => {
const handlers = eventHandlers.current;
const billingData = currentBilling.current;
if ( handlers.shippingAddressChange && isProcessing ) {
handlers.shippingAddressChange.updateWith( {
status: 'success',
shippingOptions: normalizeShippingOptions( shippingRates ),
total: getTotalPaymentItem( billingData.cartTotal ),
displayItems: normalizeLineItems(
billingData.cartTotalItems
),
} );
clearPaymentRequestEventHandler( 'shippingAddressChange' );
}
};
const onShippingRatesEventFail = ( currentErrorStatus ) => {
const handlers = eventHandlers.current;
if ( handlers.shippingAddressChange && isProcessing ) {
handlers.shippingAddressChange.updateWith( {
status: currentErrorStatus.hasInvalidAddress
? 'invalid_shipping_address'
: 'fail',
shippingOptions: [],
} );
}
clearPaymentRequestEventHandler( 'shippingAddressChange' );
};
const onShippingSelectedRate = ( forSuccess = true ) => () => {
const handlers = eventHandlers.current;
const shipping = currentShipping.current;
const billingData = currentBilling.current;
if (
handlers.shippingOptionChange &&
! shipping.isSelectingRate &&
isProcessing
) {
const updateObject = forSuccess
? {
status: 'success',
total: getTotalPaymentItem( billingData.cartTotal ),
displayItems: normalizeLineItems(
billingData.cartTotalItems
),
}
: {
status: 'fail',
};
handlers.shippingOptionChange.updateWith( updateObject );
clearPaymentRequestEventHandler( 'shippingOptionChange' );
}
};
const onProcessingPayment = () => {
const handlers = eventHandlers.current;
if ( handlers.sourceEvent && isProcessing ) {
const response = {
type: responseTypes.SUCCESS,
meta: {
billingData: getBillingData( handlers.sourceEvent ),
paymentMethodData: getPaymentMethodData(
handlers.sourceEvent,
currentPaymentRequestType.current
),
shippingData: getShippingData( handlers.sourceEvent ),
},
};
return response;
}
return { type: responseTypes.SUCCESS };
};
const onCheckoutComplete = ( checkoutResponse ) => {
const handlers = eventHandlers.current;
let response = { type: responseTypes.SUCCESS };
if ( handlers.sourceEvent && isProcessing ) {
const { paymentStatus, paymentDetails } = checkoutResponse;
if ( paymentStatus === responseTypes.SUCCESS ) {
completePayment( handlers.sourceEvent );
}
if (
paymentStatus === responseTypes.ERROR ||
paymentStatus === responseTypes.FAIL
) {
const paymentResponse = abortPayment(
handlers.sourceEvent,
paymentDetails?.errorMessage
);
response = {
type: responseTypes.ERROR,
message: paymentResponse.message,
messageContext: noticeContexts.EXPRESS_PAYMENTS,
retry: true,
};
}
clearPaymentRequestEventHandler( 'sourceEvent' );
}
return response;
};
if ( canMakePayment && isProcessing ) {
const unsubscribeShippingRateSuccess = onShippingRateSuccess(
onShippingRatesEvent
);
const unsubscribeShippingRateFail = onShippingRateFail(
onShippingRatesEventFail
);
const unsubscribeShippingRateSelectSuccess = onShippingRateSelectSuccess(
onShippingSelectedRate()
);
const unsubscribeShippingRateSelectFail = onShippingRateSelectFail(
onShippingRatesEventFail
);
const unsubscribePaymentProcessing = onPaymentProcessing(
onProcessingPayment
);
const unsubscribeCheckoutCompleteSuccess = onCheckoutAfterProcessingWithSuccess(
onCheckoutComplete
);
const unsubscribeCheckoutCompleteFail = onCheckoutAfterProcessingWithError(
onCheckoutComplete
);
return () => {
unsubscribeCheckoutCompleteFail();
unsubscribeCheckoutCompleteSuccess();
unsubscribePaymentProcessing();
unsubscribeShippingRateFail();
unsubscribeShippingRateSuccess();
unsubscribeShippingRateSelectSuccess();
unsubscribeShippingRateSelectFail();
};
}
return undefined;
}, [
canMakePayment,
isProcessing,
onShippingRateSuccess,
onShippingRateFail,
onShippingRateSelectSuccess,
onShippingRateSelectFail,
onPaymentProcessing,
onCheckoutAfterProcessingWithSuccess,
onCheckoutAfterProcessingWithError,
responseTypes,
noticeContexts,
completePayment,
abortPayment,
clearPaymentRequestEventHandler,
] );
};