File "use-payment-method-registration.ts"
Full Path: /home/warrior1/public_html/plugins/woocommerce/packages/woocommerce-blocks/assets/js/base/context/providers/cart-checkout/payment-methods/use-payment-method-registration.ts
File size: 7.51 KB
MIME-type: text/x-java
Charset: utf-8
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import {
getPaymentMethods,
getExpressPaymentMethods,
} from '@woocommerce/blocks-registry';
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
import { useShallowEqual } from '@woocommerce/base-hooks';
import { CURRENT_USER_IS_ADMIN, getSetting } from '@woocommerce/settings';
import type {
PaymentMethods,
ExpressPaymentMethods,
PaymentMethodConfigInstance,
ExpressPaymentMethodConfigInstance,
} from '@woocommerce/type-defs/payments';
import { useDebouncedCallback } from 'use-debounce';
import { useDispatch } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import { useEditorContext } from '../../editor-context';
import { useCustomerDataContext } from '../customer';
import { useStoreCart } from '../../../hooks/cart/use-store-cart';
import { useEmitResponse } from '../../../hooks/use-emit-response';
import type { PaymentMethodsDispatcherType } from './types';
import { useShippingData } from '../../../hooks/shipping/use-shipping-data';
/**
* This hook handles initializing registered payment methods and exposing all
* registered payment methods that can be used in the current environment (via
* the payment method's `canMakePayment` property).
*
* @param {function(Object):undefined} dispatcher A dispatcher for setting registered payment methods to an external state.
* @param {Object} registeredPaymentMethods Registered payment methods to process.
* @param {Array} paymentMethodsSortOrder Array of payment method names to sort by. This should match keys of registeredPaymentMethods.
* @param {string} noticeContext Id of the context to append notices to.
*
* @return {boolean} Whether the payment methods have been initialized or not. True when all payment methods have been initialized.
*/
const usePaymentMethodRegistration = (
dispatcher: PaymentMethodsDispatcherType,
registeredPaymentMethods: PaymentMethods | ExpressPaymentMethods,
paymentMethodsSortOrder: string[],
noticeContext: string
) => {
const [ isInitialized, setIsInitialized ] = useState( false );
const { isEditor } = useEditorContext();
const { selectedRates } = useShippingData();
const { billingAddress, shippingAddress } = useCustomerDataContext();
const selectedShippingMethods = useShallowEqual( selectedRates );
const paymentMethodsOrder = useShallowEqual( paymentMethodsSortOrder );
const cart = useStoreCart();
const {
cartTotals,
cartIsLoading,
cartNeedsShipping,
paymentRequirements,
} = cart;
const canPayArgument = useRef( {
cart,
cartTotals,
cartNeedsShipping,
billingData: billingAddress,
billingAddress,
shippingAddress,
selectedShippingMethods,
paymentRequirements,
} );
const { createErrorNotice } = useDispatch( 'core/notices' );
useEffect( () => {
canPayArgument.current = {
cart,
cartTotals,
cartNeedsShipping,
get billingData() {
// prettier-ignore
deprecated(
'billingData',
{
alternative: 'billingAddress',
plugin: 'woocommerce-gutenberg-products-block',
link:
'https://github.com/woocommerce/woocommerce-blocks/pull/6369',
}
);
return this.billingAddress;
},
billingAddress,
shippingAddress,
selectedShippingMethods,
paymentRequirements,
};
}, [
cart,
cartTotals,
cartNeedsShipping,
billingAddress,
shippingAddress,
selectedShippingMethods,
paymentRequirements,
] );
const refreshCanMakePayments = useCallback( async () => {
let availablePaymentMethods = {};
const addAvailablePaymentMethod = (
paymentMethod:
| PaymentMethodConfigInstance
| ExpressPaymentMethodConfigInstance
) => {
availablePaymentMethods = {
...availablePaymentMethods,
[ paymentMethod.name ]: paymentMethod,
};
};
for ( let i = 0; i < paymentMethodsOrder.length; i++ ) {
const paymentMethodName = paymentMethodsOrder[ i ];
const paymentMethod = registeredPaymentMethods[ paymentMethodName ];
if ( ! paymentMethod ) {
continue;
}
// See if payment method should be available. This always evaluates to true in the editor context.
try {
const canPay = isEditor
? true
: await Promise.resolve(
paymentMethod.canMakePayment(
canPayArgument.current
)
);
if ( canPay ) {
if (
typeof canPay === 'object' &&
canPay !== null &&
canPay.error
) {
throw new Error( canPay.error.message );
}
addAvailablePaymentMethod( paymentMethod );
}
} catch ( e ) {
if ( CURRENT_USER_IS_ADMIN || isEditor ) {
const errorText = sprintf(
/* translators: %s the id of the payment method being registered (bank transfer, cheque...) */
__(
`There was an error registering the payment method with id '%s': `,
'woo-gutenberg-products-block'
),
paymentMethod.paymentMethodId
);
createErrorNotice( `${ errorText } ${ e }`, {
context: noticeContext,
id: `wc-${ paymentMethod.paymentMethodId }-registration-error`,
} );
}
}
}
// Re-dispatch available payment methods to store.
dispatcher( availablePaymentMethods );
// Note: Some 4rd party payment methods use the `canMakePayment` callback to initialize / setup.
// That's why we track "is initialized" state here.
setIsInitialized( true );
}, [
createErrorNotice,
dispatcher,
isEditor,
noticeContext,
paymentMethodsOrder,
registeredPaymentMethods,
] );
const debouncedRefreshCanMakePayments = useDebouncedCallback(
refreshCanMakePayments,
500,
{
leading: true,
}
);
// Determine which payment methods are available initially and whenever
// shipping methods, cart or the billing data change.
// Some payment methods (e.g. COD) can be disabled for specific shipping methods.
useEffect( () => {
if ( ! cartIsLoading ) {
debouncedRefreshCanMakePayments();
}
}, [
debouncedRefreshCanMakePayments,
cart,
selectedShippingMethods,
billingAddress,
cartIsLoading,
] );
return isInitialized;
};
/**
* Custom hook for setting up payment methods (standard, non-express).
*
* @param {function(Object):undefined} dispatcher
*
* @return {boolean} True when standard payment methods have been initialized.
*/
export const usePaymentMethods = (
dispatcher: PaymentMethodsDispatcherType
): boolean => {
const standardMethods: PaymentMethods =
getPaymentMethods() as PaymentMethods;
const { noticeContexts } = useEmitResponse();
// Ensure all methods are present in order.
// Some payment methods may not be present in paymentGatewaySortOrder if they
// depend on state, e.g. COD can depend on shipping method.
const displayOrder = new Set( [
...( getSetting( 'paymentGatewaySortOrder', [] ) as [] ),
...Object.keys( standardMethods ),
] );
return usePaymentMethodRegistration(
dispatcher,
standardMethods,
Array.from( displayOrder ),
noticeContexts.PAYMENTS
);
};
/**
* Custom hook for setting up express payment methods.
*
* @param {function(Object):undefined} dispatcher
*
* @return {boolean} True when express payment methods have been initialized.
*/
export const useExpressPaymentMethods = (
dispatcher: PaymentMethodsDispatcherType
): boolean => {
const expressMethods: ExpressPaymentMethods =
getExpressPaymentMethods() as ExpressPaymentMethods;
const { noticeContexts } = useEmitResponse();
return usePaymentMethodRegistration(
dispatcher,
expressMethods,
Object.keys( expressMethods ),
noticeContexts.EXPRESS_PAYMENTS
);
};