allow payment methods to disable based on shipping (API change) (https://github.com/woocommerce/woocommerce-blocks/pull/2840)
* allow payment methods to disable based on shipping or other factors: - renamed 'initialized' array 'available' to match primary purpose of `canMakePayment` api - whether payment method should be available - trigger refresh of available payment methods when shopper chooses different shipping method - rename resolveCanMakePayments => refreshCanMakePayments - tweaked some variable names and scope for clarity - added comments to clarify things Note this should not affect behaviour yet - no existing payment methods use this new feature. COD payment method will need this - woocommerce/woocommerce-blocks#2831 * optimise refreshCanMakePayments: - useShallowEqual to avoid unnecessary call when shipping methods have not actually changed (but object value has) * replace ("set") payment methods in store, was appending: - payment methods may come and go depending on cart/checkout state - the previous SET action appended provided payment methods to the collection - this prevents dynamic payment methods e.g. COD from being able to hide i.e. disable * cache test payment request to avoid unnecessary stripe API calls: - in the canMakePayment callback there's a test payment to determine if chrome pay/apple pay is set up and available - canMakePayment is now called multiple times as checkout state changes - now the results of the test payment are stored in variable, and returned on subsequent calls * set init flag to avoid additional attempts to init stripe API: + tweak naming of init flag
This commit is contained in:
parent
ea1435457a
commit
1df11f5461
|
@ -107,10 +107,7 @@ const reducer = (
|
|||
case SET_REGISTERED_PAYMENT_METHODS:
|
||||
return {
|
||||
...state,
|
||||
paymentMethods: {
|
||||
...state.paymentMethods,
|
||||
...paymentMethods,
|
||||
},
|
||||
paymentMethods,
|
||||
};
|
||||
case SET_REGISTERED_EXPRESS_PAYMENT_METHODS:
|
||||
return {
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
useEditorContext,
|
||||
useShippingDataContext,
|
||||
} from '@woocommerce/base-context';
|
||||
import { useStoreCart } from '@woocommerce/base-hooks';
|
||||
import { useStoreCart, useShallowEqual } from '@woocommerce/base-hooks';
|
||||
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
|
@ -54,12 +54,14 @@ const usePaymentMethodRegistration = (
|
|||
) => {
|
||||
const [ isInitialized, setIsInitialized ] = useState( false );
|
||||
const { isEditor } = useEditorContext();
|
||||
const { shippingAddress } = useShippingDataContext();
|
||||
const { selectedRates, shippingAddress } = useShippingDataContext();
|
||||
const selectedShippingMethods = useShallowEqual( selectedRates );
|
||||
const { cartTotals, cartNeedsShipping } = useStoreCart();
|
||||
const canPayArgument = useRef( {
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
shippingAddress,
|
||||
selectedShippingMethods,
|
||||
} );
|
||||
|
||||
useEffect( () => {
|
||||
|
@ -67,51 +69,64 @@ const usePaymentMethodRegistration = (
|
|||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
shippingAddress,
|
||||
selectedShippingMethods,
|
||||
};
|
||||
}, [ cartTotals, cartNeedsShipping, shippingAddress ] );
|
||||
}, [
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
shippingAddress,
|
||||
selectedShippingMethods,
|
||||
] );
|
||||
|
||||
const resolveCanMakePayments = useCallback( async () => {
|
||||
let initializedPaymentMethods = {},
|
||||
canPay;
|
||||
const setInitializedPaymentMethods = ( paymentMethod ) => {
|
||||
initializedPaymentMethods = {
|
||||
...initializedPaymentMethods,
|
||||
const refreshCanMakePayments = useCallback( async () => {
|
||||
let availablePaymentMethods = {};
|
||||
const addAvailablePaymentMethod = ( paymentMethod ) => {
|
||||
availablePaymentMethods = {
|
||||
...availablePaymentMethods,
|
||||
[ paymentMethod.name ]: paymentMethod,
|
||||
};
|
||||
};
|
||||
for ( const paymentMethodName in registeredPaymentMethods ) {
|
||||
const current = registeredPaymentMethods[ paymentMethodName ];
|
||||
const paymentMethod = registeredPaymentMethods[ paymentMethodName ];
|
||||
|
||||
// In editor, shortcut so all payment methods show as available.
|
||||
if ( isEditor ) {
|
||||
setInitializedPaymentMethods( current );
|
||||
addAvailablePaymentMethod( paymentMethod );
|
||||
continue;
|
||||
}
|
||||
|
||||
// In front end, ask payment method if it should be available.
|
||||
try {
|
||||
canPay = await Promise.resolve(
|
||||
current.canMakePayment( canPayArgument.current )
|
||||
const canPay = await Promise.resolve(
|
||||
paymentMethod.canMakePayment( canPayArgument.current )
|
||||
);
|
||||
if ( canPay ) {
|
||||
if ( canPay.error ) {
|
||||
throw new Error( canPay.error.message );
|
||||
}
|
||||
setInitializedPaymentMethods( current );
|
||||
addAvailablePaymentMethod( paymentMethod );
|
||||
}
|
||||
} catch ( e ) {
|
||||
// If user is admin, show payment `canMakePayment` errors as a notice.
|
||||
handleRegistrationError( e );
|
||||
}
|
||||
}
|
||||
// all payment methods have been initialized so dispatch and set
|
||||
dispatcher( initializedPaymentMethods );
|
||||
|
||||
// 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.
|
||||
setIsInitialized( true );
|
||||
}, [ dispatcher, isEditor, registeredPaymentMethods ] );
|
||||
|
||||
// if not initialized invoke the callback to kick off resolving the payments.
|
||||
// Determine which payment methods are available initially and whenever
|
||||
// shipping methods change.
|
||||
// Some payment methods (e.g. COD) can be disabled for specific shipping methods.
|
||||
useEffect( () => {
|
||||
if ( ! isInitialized ) {
|
||||
resolveCanMakePayments();
|
||||
}
|
||||
}, [ resolveCanMakePayments, isInitialized ] );
|
||||
refreshCanMakePayments();
|
||||
}, [ refreshCanMakePayments, selectedShippingMethods ] );
|
||||
|
||||
return isInitialized;
|
||||
};
|
||||
|
|
|
@ -16,30 +16,48 @@ const ApplePayPreview = () => <img src={ applePayImage } alt="" />;
|
|||
const canPayStripePromise = loadStripe();
|
||||
const componentStripePromise = loadStripe();
|
||||
|
||||
let isStripeInitialized = false,
|
||||
canPay = false;
|
||||
|
||||
// Initialise stripe API client and determine if payment method can be used
|
||||
// in current environment (e.g. geo + shopper has payment settings configured).
|
||||
function paymentRequestAvailable( currencyCode ) {
|
||||
// If we've already initialised, return the cached results.
|
||||
if ( isStripeInitialized ) {
|
||||
return canPay;
|
||||
}
|
||||
|
||||
return canPayStripePromise.then( ( stripe ) => {
|
||||
if ( stripe === null ) {
|
||||
isStripeInitialized = true;
|
||||
return canPay;
|
||||
}
|
||||
// Do a test payment to confirm if payment method is available.
|
||||
const paymentRequest = stripe.paymentRequest( {
|
||||
total: {
|
||||
label: 'Test total',
|
||||
amount: 1000,
|
||||
},
|
||||
country: getSetting( 'baseLocation', {} )?.country,
|
||||
currency: currencyCode,
|
||||
} );
|
||||
return paymentRequest.canMakePayment().then( ( result ) => {
|
||||
canPay = !! result;
|
||||
isStripeInitialized = true;
|
||||
return canPay;
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
const PaymentRequestPaymentMethod = {
|
||||
name: PAYMENT_METHOD_NAME,
|
||||
content: <PaymentRequestExpress stripe={ componentStripePromise } />,
|
||||
edit: <ApplePayPreview />,
|
||||
canMakePayment: ( cartData ) =>
|
||||
canPayStripePromise.then( ( stripe ) => {
|
||||
if ( stripe === null ) {
|
||||
return false;
|
||||
}
|
||||
// do a test payment request to check if payment request payment can be
|
||||
// done.
|
||||
const paymentRequest = stripe.paymentRequest( {
|
||||
total: {
|
||||
label: 'Test total',
|
||||
amount: 1000,
|
||||
},
|
||||
country: getSetting( 'baseLocation', {} )?.country,
|
||||
// eslint-disable-next-line camelcase
|
||||
currency: cartData?.cartTotals?.currency_code?.toLowerCase(),
|
||||
} );
|
||||
return paymentRequest
|
||||
.canMakePayment()
|
||||
.then( ( result ) => !! result );
|
||||
} ),
|
||||
paymentRequestAvailable(
|
||||
// eslint-disable-next-line camelcase
|
||||
cartData?.cartTotals?.currency_code?.toLowerCase()
|
||||
),
|
||||
paymentMethodId: 'stripe',
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue