2020-03-26 11:11:46 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2020-04-15 15:15:56 +00:00
|
|
|
import { __, sprintf } from '@wordpress/i18n';
|
2020-03-26 11:11:46 +00:00
|
|
|
import {
|
|
|
|
getPaymentMethods,
|
|
|
|
getExpressPaymentMethods,
|
|
|
|
} from '@woocommerce/blocks-registry';
|
2020-05-12 16:40:08 +00:00
|
|
|
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
|
2020-09-18 10:27:54 +00:00
|
|
|
import {
|
|
|
|
useEmitResponse,
|
|
|
|
useShallowEqual,
|
|
|
|
useStoreCart,
|
|
|
|
useStoreNotices,
|
|
|
|
} from '@woocommerce/base-hooks';
|
2020-08-05 01:56:33 +00:00
|
|
|
import {
|
|
|
|
CURRENT_USER_IS_ADMIN,
|
|
|
|
PAYMENT_GATEWAY_SORT_ORDER,
|
|
|
|
} from '@woocommerce/block-settings';
|
2020-03-26 11:11:46 +00:00
|
|
|
|
2021-01-19 15:55:44 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { useEditorContext } from '../../editor';
|
|
|
|
import { useShippingDataContext } from '../shipping';
|
|
|
|
|
2020-04-14 22:44:31 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
2020-08-05 01:56:33 +00:00
|
|
|
* @param {Array} paymentMethodsSortOrder Array of payment method names to
|
|
|
|
* sort by. This should match keys of
|
|
|
|
* registeredPaymentMethods.
|
2020-09-18 10:27:54 +00:00
|
|
|
* @param {string} noticeContext Id of the context to append
|
|
|
|
* notices to.
|
2020-04-14 22:44:31 +00:00
|
|
|
*
|
|
|
|
* @return {boolean} Whether the payment methods have been initialized or not. True when all payment
|
|
|
|
* methods have been initialized.
|
|
|
|
*/
|
2020-03-26 11:11:46 +00:00
|
|
|
const usePaymentMethodRegistration = (
|
|
|
|
dispatcher,
|
2020-08-05 01:56:33 +00:00
|
|
|
registeredPaymentMethods,
|
2020-09-18 10:27:54 +00:00
|
|
|
paymentMethodsSortOrder,
|
|
|
|
noticeContext
|
2020-03-26 11:11:46 +00:00
|
|
|
) => {
|
|
|
|
const [ isInitialized, setIsInitialized ] = useState( false );
|
2020-05-12 16:40:08 +00:00
|
|
|
const { isEditor } = useEditorContext();
|
2020-07-13 22:52:13 +00:00
|
|
|
const { selectedRates, shippingAddress } = useShippingDataContext();
|
|
|
|
const selectedShippingMethods = useShallowEqual( selectedRates );
|
2020-08-05 01:56:33 +00:00
|
|
|
const paymentMethodsOrder = useShallowEqual( paymentMethodsSortOrder );
|
2021-01-29 06:28:44 +00:00
|
|
|
const {
|
|
|
|
cartTotals,
|
|
|
|
cartNeedsShipping,
|
|
|
|
paymentRequirements,
|
|
|
|
} = useStoreCart();
|
2020-04-09 11:44:29 +00:00
|
|
|
const canPayArgument = useRef( {
|
|
|
|
cartTotals,
|
|
|
|
cartNeedsShipping,
|
|
|
|
shippingAddress,
|
2020-07-13 22:52:13 +00:00
|
|
|
selectedShippingMethods,
|
2021-01-29 06:28:44 +00:00
|
|
|
paymentRequirements,
|
2020-04-09 11:44:29 +00:00
|
|
|
} );
|
2020-09-18 10:27:54 +00:00
|
|
|
const { addErrorNotice } = useStoreNotices();
|
2020-04-14 22:44:31 +00:00
|
|
|
|
2020-04-09 11:44:29 +00:00
|
|
|
useEffect( () => {
|
|
|
|
canPayArgument.current = {
|
|
|
|
cartTotals,
|
|
|
|
cartNeedsShipping,
|
|
|
|
shippingAddress,
|
2020-07-13 22:52:13 +00:00
|
|
|
selectedShippingMethods,
|
2021-01-29 06:28:44 +00:00
|
|
|
paymentRequirements,
|
2020-04-09 11:44:29 +00:00
|
|
|
};
|
2020-07-13 22:52:13 +00:00
|
|
|
}, [
|
|
|
|
cartTotals,
|
|
|
|
cartNeedsShipping,
|
|
|
|
shippingAddress,
|
|
|
|
selectedShippingMethods,
|
2021-01-29 06:28:44 +00:00
|
|
|
paymentRequirements,
|
2020-07-13 22:52:13 +00:00
|
|
|
] );
|
2020-04-09 11:44:29 +00:00
|
|
|
|
2020-07-13 22:52:13 +00:00
|
|
|
const refreshCanMakePayments = useCallback( async () => {
|
|
|
|
let availablePaymentMethods = {};
|
2020-11-20 10:10:04 +00:00
|
|
|
|
2020-07-13 22:52:13 +00:00
|
|
|
const addAvailablePaymentMethod = ( paymentMethod ) => {
|
|
|
|
availablePaymentMethods = {
|
|
|
|
...availablePaymentMethods,
|
2020-05-12 16:40:08 +00:00
|
|
|
[ paymentMethod.name ]: paymentMethod,
|
|
|
|
};
|
2020-03-26 11:11:46 +00:00
|
|
|
};
|
2020-08-05 01:56:33 +00:00
|
|
|
|
|
|
|
for ( let i = 0; i < paymentMethodsOrder.length; i++ ) {
|
|
|
|
const paymentMethodName = paymentMethodsOrder[ i ];
|
2020-07-13 22:52:13 +00:00
|
|
|
const paymentMethod = registeredPaymentMethods[ paymentMethodName ];
|
2020-08-05 01:56:33 +00:00
|
|
|
if ( ! paymentMethod ) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-05-12 16:40:08 +00:00
|
|
|
|
2020-07-13 22:52:13 +00:00
|
|
|
// In front end, ask payment method if it should be available.
|
2020-05-12 16:40:08 +00:00
|
|
|
try {
|
2020-07-13 22:52:13 +00:00
|
|
|
const canPay = await Promise.resolve(
|
|
|
|
paymentMethod.canMakePayment( canPayArgument.current )
|
2020-05-12 16:40:08 +00:00
|
|
|
);
|
|
|
|
if ( canPay ) {
|
|
|
|
if ( canPay.error ) {
|
|
|
|
throw new Error( canPay.error.message );
|
|
|
|
}
|
2020-07-13 22:52:13 +00:00
|
|
|
addAvailablePaymentMethod( paymentMethod );
|
2020-05-12 16:40:08 +00:00
|
|
|
}
|
|
|
|
} catch ( e ) {
|
2020-09-18 10:27:54 +00:00
|
|
|
if ( CURRENT_USER_IS_ADMIN || isEditor ) {
|
|
|
|
const errorText = sprintf(
|
|
|
|
/* translators: %s the id of the payment method being registered (bank transfer, Stripe...) */
|
|
|
|
__(
|
|
|
|
`There was an error registering the payment method with id '%s': `,
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
),
|
|
|
|
paymentMethod.paymentMethodId
|
|
|
|
);
|
|
|
|
addErrorNotice( `${ errorText } ${ e }`, {
|
|
|
|
context: noticeContext,
|
|
|
|
id: `wc-${ paymentMethod.paymentMethodId }-registration-error`,
|
|
|
|
} );
|
|
|
|
}
|
2020-03-26 11:11:46 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-13 22:52:13 +00:00
|
|
|
|
|
|
|
// Re-dispatch available payment methods to store.
|
|
|
|
dispatcher( availablePaymentMethods );
|
|
|
|
|
|
|
|
// Note: some payment methods use the `canMakePayment` callback to initialize / setup.
|
|
|
|
// Example: Stripe CC, Stripe Payment Request.
|
|
|
|
// That's why we track "is initialised" state here.
|
2020-05-12 16:40:08 +00:00
|
|
|
setIsInitialized( true );
|
2020-08-05 01:56:33 +00:00
|
|
|
}, [
|
2020-09-18 10:27:54 +00:00
|
|
|
addErrorNotice,
|
2020-08-05 01:56:33 +00:00
|
|
|
dispatcher,
|
|
|
|
isEditor,
|
2020-09-18 10:27:54 +00:00
|
|
|
noticeContext,
|
2020-08-05 01:56:33 +00:00
|
|
|
paymentMethodsOrder,
|
2020-09-18 10:27:54 +00:00
|
|
|
registeredPaymentMethods,
|
2020-08-05 01:56:33 +00:00
|
|
|
] );
|
2020-03-26 11:11:46 +00:00
|
|
|
|
2020-07-13 22:52:13 +00:00
|
|
|
// Determine which payment methods are available initially and whenever
|
2020-11-20 10:10:04 +00:00
|
|
|
// shipping methods or cart totals change.
|
2020-07-13 22:52:13 +00:00
|
|
|
// Some payment methods (e.g. COD) can be disabled for specific shipping methods.
|
2020-04-14 22:44:31 +00:00
|
|
|
useEffect( () => {
|
2020-07-13 22:52:13 +00:00
|
|
|
refreshCanMakePayments();
|
2021-01-29 06:28:44 +00:00
|
|
|
}, [
|
|
|
|
refreshCanMakePayments,
|
|
|
|
cartTotals,
|
|
|
|
selectedShippingMethods,
|
|
|
|
paymentRequirements,
|
|
|
|
] );
|
2020-04-14 22:44:31 +00:00
|
|
|
|
2020-03-26 11:11:46 +00:00
|
|
|
return isInitialized;
|
|
|
|
};
|
|
|
|
|
2020-08-05 01:56:33 +00:00
|
|
|
/**
|
|
|
|
* 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 ) => {
|
|
|
|
const standardMethods = getPaymentMethods();
|
2020-09-18 10:27:54 +00:00
|
|
|
const { noticeContexts } = useEmitResponse();
|
2020-08-05 01:56:33 +00:00
|
|
|
// Ensure all methods are present in order.
|
|
|
|
// Some payment methods may not be present in PAYMENT_GATEWAY_SORT_ORDER if they
|
|
|
|
// depend on state, e.g. COD can depend on shipping method.
|
|
|
|
const displayOrder = new Set( [
|
|
|
|
...PAYMENT_GATEWAY_SORT_ORDER,
|
|
|
|
...Object.keys( standardMethods ),
|
|
|
|
] );
|
|
|
|
return usePaymentMethodRegistration(
|
|
|
|
dispatcher,
|
|
|
|
standardMethods,
|
2020-09-18 10:27:54 +00:00
|
|
|
Array.from( displayOrder ),
|
|
|
|
noticeContexts.PAYMENTS
|
2020-08-05 01:56:33 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 ) => {
|
|
|
|
const expressMethods = getExpressPaymentMethods();
|
2020-09-18 10:27:54 +00:00
|
|
|
const { noticeContexts } = useEmitResponse();
|
2020-08-05 01:56:33 +00:00
|
|
|
return usePaymentMethodRegistration(
|
|
|
|
dispatcher,
|
|
|
|
expressMethods,
|
2020-09-18 10:27:54 +00:00
|
|
|
Object.keys( expressMethods ),
|
|
|
|
noticeContexts.EXPRESS_PAYMENTS
|
2020-08-05 01:56:33 +00:00
|
|
|
);
|
|
|
|
};
|