Don't throw an error when registering a payment method fails (https://github.com/woocommerce/woocommerce-blocks/pull/3134)
* Show all payment methods when it's an admin and let the error boundary handle errors * Use StoreNoticesContainer in Payment method error boundary so notices have styling * Filter out saved payment methods for admin users if they don't accept payments * Simplify update options logic * For admins, only show payment methods that errored but canPay was not false * Simplify how new payment method option is appended * Wrap canMakePayment in a try catch block to handle payment methods that throw an error * Add an id to payment method error boundary errors * Add an error boundary to express payment methods * Hardcode failing content and savePaymentInfo to false if the payment method failed * Add some new comments * Add a notice instead of registering the payment method if it fails and user is admin * Throw error early if stripe failed to load * Split express and standard payment method error notices * Don't add payment methods in the editor and instead add a notice * Fix error id * Use noticeContext constant * Add missing JSdoc param * Remove unnecessary removeNotice
This commit is contained in:
parent
1871b4e573
commit
d641d2e1a4
|
@ -15,6 +15,7 @@ import {
|
||||||
useEditorContext,
|
useEditorContext,
|
||||||
usePaymentMethodDataContext,
|
usePaymentMethodDataContext,
|
||||||
} from '@woocommerce/base-context';
|
} from '@woocommerce/base-context';
|
||||||
|
import PaymentMethodErrorBoundary from './payment-method-error-boundary';
|
||||||
|
|
||||||
const ExpressPaymentMethods = () => {
|
const ExpressPaymentMethods = () => {
|
||||||
const { isEditor } = useEditorContext();
|
const { isEditor } = useEditorContext();
|
||||||
|
@ -59,9 +60,11 @@ const ExpressPaymentMethods = () => {
|
||||||
<li key="noneRegistered">No registered Payment Methods</li>
|
<li key="noneRegistered">No registered Payment Methods</li>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ul className="wc-block-components-express-payment__event-buttons">
|
<PaymentMethodErrorBoundary isEditor={ isEditor }>
|
||||||
{ content }
|
<ul className="wc-block-components-express-payment__event-buttons">
|
||||||
</ul>
|
{ content }
|
||||||
|
</ul>
|
||||||
|
</PaymentMethodErrorBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,14 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useExpressPaymentMethods } from '@woocommerce/base-hooks';
|
import {
|
||||||
import { StoreNoticesProvider } from '@woocommerce/base-context';
|
useEmitResponse,
|
||||||
|
useExpressPaymentMethods,
|
||||||
|
} from '@woocommerce/base-hooks';
|
||||||
|
import {
|
||||||
|
StoreNoticesProvider,
|
||||||
|
useEditorContext,
|
||||||
|
} from '@woocommerce/base-context';
|
||||||
import Title from '@woocommerce/base-components/title';
|
import Title from '@woocommerce/base-components/title';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,14 +17,26 @@ import Title from '@woocommerce/base-components/title';
|
||||||
*/
|
*/
|
||||||
import ExpressPaymentMethods from '../express-payment-methods';
|
import ExpressPaymentMethods from '../express-payment-methods';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
const CheckoutExpressPayment = () => {
|
const CheckoutExpressPayment = () => {
|
||||||
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
|
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
|
||||||
|
const { isEditor } = useEditorContext();
|
||||||
|
const { noticeContexts } = useEmitResponse();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
! isInitialized ||
|
! isInitialized ||
|
||||||
( isInitialized && Object.keys( paymentMethods ).length === 0 )
|
( isInitialized && Object.keys( paymentMethods ).length === 0 )
|
||||||
) {
|
) {
|
||||||
|
// Make sure errors are shown in the editor and for admins. For example,
|
||||||
|
// when a payment method fails to register.
|
||||||
|
if ( isEditor || CURRENT_USER_IS_ADMIN ) {
|
||||||
|
return (
|
||||||
|
<StoreNoticesProvider
|
||||||
|
context={ noticeContexts.EXPRESS_PAYMENTS }
|
||||||
|
></StoreNoticesProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +55,9 @@ const CheckoutExpressPayment = () => {
|
||||||
</Title>
|
</Title>
|
||||||
</div>
|
</div>
|
||||||
<div className="wc-block-components-express-payment__content">
|
<div className="wc-block-components-express-payment__content">
|
||||||
<StoreNoticesProvider context="wc/express-payment-area">
|
<StoreNoticesProvider
|
||||||
|
context={ noticeContexts.EXPRESS_PAYMENTS }
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
{ __(
|
{ __(
|
||||||
'In a hurry? Use one of our express checkout options below:',
|
'In a hurry? Use one of our express checkout options below:',
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
import { Notice } from 'wordpress-components';
|
import { StoreNoticesContainer } from '@woocommerce/base-components/store-notices-container';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
@ -36,11 +36,15 @@ class PaymentMethodErrorBoundary extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
const notices = [
|
||||||
<Notice isDismissible={ false } status="error">
|
{
|
||||||
{ errorText }
|
id: '0',
|
||||||
</Notice>
|
content: errorText,
|
||||||
);
|
isDismissible: false,
|
||||||
|
status: 'error',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return <StoreNoticesContainer notices={ notices } />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.children;
|
return this.props.children;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
usePaymentMethodDataContext,
|
usePaymentMethodDataContext,
|
||||||
} from '@woocommerce/base-context';
|
} from '@woocommerce/base-context';
|
||||||
import RadioControl from '@woocommerce/base-components/radio-control';
|
import RadioControl from '@woocommerce/base-components/radio-control';
|
||||||
|
import { getPaymentMethods } from '@woocommerce/blocks-registry';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@woocommerce/type-defs/contexts').CustomerPaymentMethod} CustomerPaymentMethod
|
* @typedef {import('@woocommerce/type-defs/contexts').CustomerPaymentMethod} CustomerPaymentMethod
|
||||||
|
@ -92,6 +93,7 @@ const SavedPaymentMethodOptions = ( { onSelect } ) => {
|
||||||
setActivePaymentMethod,
|
setActivePaymentMethod,
|
||||||
} = usePaymentMethodDataContext();
|
} = usePaymentMethodDataContext();
|
||||||
const [ selectedToken, setSelectedToken ] = useState( '' );
|
const [ selectedToken, setSelectedToken ] = useState( '' );
|
||||||
|
const standardMethods = getPaymentMethods();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Object} Options
|
* @type {Object} Options
|
||||||
|
@ -99,56 +101,45 @@ const SavedPaymentMethodOptions = ( { onSelect } ) => {
|
||||||
*/
|
*/
|
||||||
const currentOptions = useRef( [] );
|
const currentOptions = useRef( [] );
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
let options = [];
|
const types = Object.keys( customerPaymentMethods );
|
||||||
const paymentMethodKeys = Object.keys( customerPaymentMethods );
|
const options = types
|
||||||
if ( paymentMethodKeys.length > 0 ) {
|
.flatMap( ( type ) => {
|
||||||
paymentMethodKeys.forEach( ( type ) => {
|
const typeMethods = customerPaymentMethods[ type ];
|
||||||
const paymentMethods = customerPaymentMethods[ type ];
|
return typeMethods.map( ( paymentMethod ) => {
|
||||||
if ( paymentMethods.length > 0 ) {
|
const method =
|
||||||
options = options.concat(
|
standardMethods[ paymentMethod.method.gateway ];
|
||||||
paymentMethods.map( ( paymentMethod ) => {
|
if ( ! method?.supports?.savePaymentInfo ) {
|
||||||
const option =
|
return null;
|
||||||
type === 'cc' || type === 'echeck'
|
}
|
||||||
? getCcOrEcheckPaymentMethodOption(
|
const option =
|
||||||
paymentMethod,
|
type === 'cc' || type === 'echeck'
|
||||||
setActivePaymentMethod,
|
? getCcOrEcheckPaymentMethodOption(
|
||||||
setPaymentStatus
|
paymentMethod,
|
||||||
)
|
setActivePaymentMethod,
|
||||||
: getDefaultPaymentMethodOptions(
|
setPaymentStatus
|
||||||
paymentMethod,
|
)
|
||||||
setActivePaymentMethod,
|
: getDefaultPaymentMethodOptions(
|
||||||
setPaymentStatus
|
paymentMethod,
|
||||||
);
|
setActivePaymentMethod,
|
||||||
if (
|
setPaymentStatus
|
||||||
paymentMethod.is_default &&
|
);
|
||||||
selectedToken === ''
|
if ( paymentMethod.is_default && selectedToken === '' ) {
|
||||||
) {
|
setSelectedToken( paymentMethod.tokenId + '' );
|
||||||
setSelectedToken( paymentMethod.tokenId + '' );
|
option.onChange( paymentMethod.tokenId );
|
||||||
option.onChange( paymentMethod.tokenId );
|
}
|
||||||
}
|
return option;
|
||||||
return option;
|
|
||||||
} )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
if ( options.length > 0 ) {
|
|
||||||
currentOptions.current = options;
|
|
||||||
currentOptions.current.push( {
|
|
||||||
value: '0',
|
|
||||||
label: __(
|
|
||||||
'Use a new payment method',
|
|
||||||
'woo-gutenberg-product-blocks'
|
|
||||||
),
|
|
||||||
name: `wc-saved-payment-method-token-new`,
|
|
||||||
} );
|
} );
|
||||||
}
|
} )
|
||||||
}
|
.filter( Boolean );
|
||||||
|
currentOptions.current = options;
|
||||||
}, [
|
}, [
|
||||||
customerPaymentMethods,
|
customerPaymentMethods,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
setActivePaymentMethod,
|
setActivePaymentMethod,
|
||||||
setPaymentStatus,
|
setPaymentStatus,
|
||||||
|
standardMethods,
|
||||||
] );
|
] );
|
||||||
|
|
||||||
const updateToken = useCallback(
|
const updateToken = useCallback(
|
||||||
( token ) => {
|
( token ) => {
|
||||||
if ( token === '0' ) {
|
if ( token === '0' ) {
|
||||||
|
@ -167,12 +158,17 @@ const SavedPaymentMethodOptions = ( { onSelect } ) => {
|
||||||
|
|
||||||
// In the editor, show `Use a new payment method` option as selected.
|
// In the editor, show `Use a new payment method` option as selected.
|
||||||
const selectedOption = isEditor ? '0' : selectedToken + '';
|
const selectedOption = isEditor ? '0' : selectedToken + '';
|
||||||
|
const newPaymentMethodOption = {
|
||||||
|
value: '0',
|
||||||
|
label: __( 'Use a new payment method', 'woo-gutenberg-product-blocks' ),
|
||||||
|
name: `wc-saved-payment-method-token-new`,
|
||||||
|
};
|
||||||
return currentOptions.current.length > 0 ? (
|
return currentOptions.current.length > 0 ? (
|
||||||
<RadioControl
|
<RadioControl
|
||||||
id={ 'wc-payment-method-saved-tokens' }
|
id={ 'wc-payment-method-saved-tokens' }
|
||||||
selected={ selectedOption }
|
selected={ selectedOption }
|
||||||
onChange={ updateToken }
|
onChange={ updateToken }
|
||||||
options={ currentOptions.current }
|
options={ [ ...currentOptions.current, newPaymentMethodOption ] }
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,32 +11,17 @@ import {
|
||||||
useEditorContext,
|
useEditorContext,
|
||||||
useShippingDataContext,
|
useShippingDataContext,
|
||||||
} from '@woocommerce/base-context';
|
} from '@woocommerce/base-context';
|
||||||
import { useStoreCart, useShallowEqual } from '@woocommerce/base-hooks';
|
import {
|
||||||
|
useEmitResponse,
|
||||||
|
useShallowEqual,
|
||||||
|
useStoreCart,
|
||||||
|
useStoreNotices,
|
||||||
|
} from '@woocommerce/base-hooks';
|
||||||
import {
|
import {
|
||||||
CURRENT_USER_IS_ADMIN,
|
CURRENT_USER_IS_ADMIN,
|
||||||
PAYMENT_GATEWAY_SORT_ORDER,
|
PAYMENT_GATEWAY_SORT_ORDER,
|
||||||
} from '@woocommerce/block-settings';
|
} from '@woocommerce/block-settings';
|
||||||
|
|
||||||
/**
|
|
||||||
* If there was an error registering a payment method, alert the admin.
|
|
||||||
*
|
|
||||||
* @param {Object} error Error object.
|
|
||||||
*/
|
|
||||||
const handleRegistrationError = ( error ) => {
|
|
||||||
if ( CURRENT_USER_IS_ADMIN ) {
|
|
||||||
throw new Error(
|
|
||||||
sprintf(
|
|
||||||
__(
|
|
||||||
// translators: %s is the error method returned by the payment method.
|
|
||||||
'Problem with payment method initialization: %s',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
error.message
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This hook handles initializing registered payment methods and exposing all
|
* This hook handles initializing registered payment methods and exposing all
|
||||||
* registered payment methods that can be used in the current environment (via
|
* registered payment methods that can be used in the current environment (via
|
||||||
|
@ -50,6 +35,8 @@ const handleRegistrationError = ( error ) => {
|
||||||
* @param {Array} paymentMethodsSortOrder Array of payment method names to
|
* @param {Array} paymentMethodsSortOrder Array of payment method names to
|
||||||
* sort by. This should match keys of
|
* sort by. This should match keys of
|
||||||
* registeredPaymentMethods.
|
* 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
|
* @return {boolean} Whether the payment methods have been initialized or not. True when all payment
|
||||||
* methods have been initialized.
|
* methods have been initialized.
|
||||||
|
@ -57,7 +44,8 @@ const handleRegistrationError = ( error ) => {
|
||||||
const usePaymentMethodRegistration = (
|
const usePaymentMethodRegistration = (
|
||||||
dispatcher,
|
dispatcher,
|
||||||
registeredPaymentMethods,
|
registeredPaymentMethods,
|
||||||
paymentMethodsSortOrder
|
paymentMethodsSortOrder,
|
||||||
|
noticeContext
|
||||||
) => {
|
) => {
|
||||||
const [ isInitialized, setIsInitialized ] = useState( false );
|
const [ isInitialized, setIsInitialized ] = useState( false );
|
||||||
const { isEditor } = useEditorContext();
|
const { isEditor } = useEditorContext();
|
||||||
|
@ -71,6 +59,7 @@ const usePaymentMethodRegistration = (
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
selectedShippingMethods,
|
selectedShippingMethods,
|
||||||
} );
|
} );
|
||||||
|
const { addErrorNotice } = useStoreNotices();
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
canPayArgument.current = {
|
canPayArgument.current = {
|
||||||
|
@ -102,12 +91,6 @@ const usePaymentMethodRegistration = (
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In editor, shortcut so all payment methods show as available.
|
|
||||||
if ( isEditor ) {
|
|
||||||
addAvailablePaymentMethod( paymentMethod );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In front end, ask payment method if it should be available.
|
// In front end, ask payment method if it should be available.
|
||||||
try {
|
try {
|
||||||
const canPay = await Promise.resolve(
|
const canPay = await Promise.resolve(
|
||||||
|
@ -120,8 +103,20 @@ const usePaymentMethodRegistration = (
|
||||||
addAvailablePaymentMethod( paymentMethod );
|
addAvailablePaymentMethod( paymentMethod );
|
||||||
}
|
}
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
// If user is admin, show payment `canMakePayment` errors as a notice.
|
if ( CURRENT_USER_IS_ADMIN || isEditor ) {
|
||||||
handleRegistrationError( e );
|
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`,
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +128,12 @@ const usePaymentMethodRegistration = (
|
||||||
// That's why we track "is initialised" state here.
|
// That's why we track "is initialised" state here.
|
||||||
setIsInitialized( true );
|
setIsInitialized( true );
|
||||||
}, [
|
}, [
|
||||||
|
addErrorNotice,
|
||||||
dispatcher,
|
dispatcher,
|
||||||
isEditor,
|
isEditor,
|
||||||
registeredPaymentMethods,
|
noticeContext,
|
||||||
paymentMethodsOrder,
|
paymentMethodsOrder,
|
||||||
|
registeredPaymentMethods,
|
||||||
] );
|
] );
|
||||||
|
|
||||||
// Determine which payment methods are available initially and whenever
|
// Determine which payment methods are available initially and whenever
|
||||||
|
@ -158,6 +155,7 @@ const usePaymentMethodRegistration = (
|
||||||
*/
|
*/
|
||||||
export const usePaymentMethods = ( dispatcher ) => {
|
export const usePaymentMethods = ( dispatcher ) => {
|
||||||
const standardMethods = getPaymentMethods();
|
const standardMethods = getPaymentMethods();
|
||||||
|
const { noticeContexts } = useEmitResponse();
|
||||||
// Ensure all methods are present in order.
|
// Ensure all methods are present in order.
|
||||||
// Some payment methods may not be present in PAYMENT_GATEWAY_SORT_ORDER if they
|
// 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.
|
// depend on state, e.g. COD can depend on shipping method.
|
||||||
|
@ -168,7 +166,8 @@ export const usePaymentMethods = ( dispatcher ) => {
|
||||||
return usePaymentMethodRegistration(
|
return usePaymentMethodRegistration(
|
||||||
dispatcher,
|
dispatcher,
|
||||||
standardMethods,
|
standardMethods,
|
||||||
Array.from( displayOrder )
|
Array.from( displayOrder ),
|
||||||
|
noticeContexts.PAYMENTS
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,9 +180,11 @@ export const usePaymentMethods = ( dispatcher ) => {
|
||||||
*/
|
*/
|
||||||
export const useExpressPaymentMethods = ( dispatcher ) => {
|
export const useExpressPaymentMethods = ( dispatcher ) => {
|
||||||
const expressMethods = getExpressPaymentMethods();
|
const expressMethods = getExpressPaymentMethods();
|
||||||
|
const { noticeContexts } = useEmitResponse();
|
||||||
return usePaymentMethodRegistration(
|
return usePaymentMethodRegistration(
|
||||||
dispatcher,
|
dispatcher,
|
||||||
expressMethods,
|
expressMethods,
|
||||||
Object.keys( expressMethods )
|
Object.keys( expressMethods ),
|
||||||
|
noticeContexts.EXPRESS_PAYMENTS
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,11 @@ import {
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
import { __experimentalCreateInterpolateElement } from 'wordpress-element';
|
import { __experimentalCreateInterpolateElement } from 'wordpress-element';
|
||||||
import { useRef } from '@wordpress/element';
|
import { useRef } from '@wordpress/element';
|
||||||
import { EditorProvider, useEditorContext } from '@woocommerce/base-context';
|
import {
|
||||||
|
EditorProvider,
|
||||||
|
useEditorContext,
|
||||||
|
StoreNoticesProvider,
|
||||||
|
} from '@woocommerce/base-context';
|
||||||
import PageSelector from '@woocommerce/editor-components/page-selector';
|
import PageSelector from '@woocommerce/editor-components/page-selector';
|
||||||
import {
|
import {
|
||||||
previewCart,
|
previewCart,
|
||||||
|
@ -320,9 +324,11 @@ const CheckoutEditor = ( { attributes, setAttributes } ) => {
|
||||||
'woo-gutenberg-products-block'
|
'woo-gutenberg-products-block'
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
<Disabled>
|
<StoreNoticesProvider context="wc/checkout">
|
||||||
<Block attributes={ attributes } />
|
<Disabled>
|
||||||
</Disabled>
|
<Block attributes={ attributes } />
|
||||||
|
</Disabled>
|
||||||
|
</StoreNoticesProvider>
|
||||||
</BlockErrorBoundary>
|
</BlockErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
|
|
|
@ -32,6 +32,9 @@ function paymentRequestAvailable( currencyCode ) {
|
||||||
isStripeInitialized = true;
|
isStripeInitialized = true;
|
||||||
return canPay;
|
return canPay;
|
||||||
}
|
}
|
||||||
|
if ( stripe.error && stripe.error instanceof Error ) {
|
||||||
|
throw stripe.error;
|
||||||
|
}
|
||||||
// Do a test payment to confirm if payment method is available.
|
// Do a test payment to confirm if payment method is available.
|
||||||
const paymentRequest = stripe.paymentRequest( {
|
const paymentRequest = stripe.paymentRequest( {
|
||||||
total: {
|
total: {
|
||||||
|
|
Loading…
Reference in New Issue