diff --git a/examples/hooks/11-Custom-Checkout.js b/examples/hooks/11-Custom-Checkout.js
index 9f99533..2b3d027 100644
--- a/examples/hooks/11-Custom-Checkout.js
+++ b/examples/hooks/11-Custom-Checkout.js
@@ -3,27 +3,20 @@ import {loadStripe} from '@stripe/stripe-js';
import {
PaymentElement,
useStripe,
- CustomCheckoutProvider,
- useCustomCheckout,
+ CheckoutProvider,
+ useCheckout,
AddressElement,
} from '../../src';
import '../styles/common.css';
-const CustomerDetails = () => {
- const {
- phoneNumber,
- updatePhoneNumber,
- email,
- updateEmail,
- } = useCustomCheckout();
-
+const CustomerDetails = ({phoneNumber, setPhoneNumber, email, setEmail}) => {
const handlePhoneNumberChange = (event) => {
- updatePhoneNumber(event.target.value);
+ setPhoneNumber(event.target.value);
};
const handleEmailChange = (event) => {
- updateEmail(event.target.value);
+ setEmail(event.target.value);
};
return (
@@ -52,35 +45,36 @@ const CustomerDetails = () => {
};
const CheckoutForm = () => {
- const customCheckout = useCustomCheckout();
+ const checkout = useCheckout();
const [status, setStatus] = React.useState();
const [loading, setLoading] = React.useState(false);
const stripe = useStripe();
+ const [phoneNumber, setPhoneNumber] = React.useState('');
+ const [email, setEmail] = React.useState('');
React.useEffect(() => {
- const {confirmationRequirements} = customCheckout || {};
+ const {confirmationRequirements} = checkout || {};
setStatus(
confirmationRequirements && confirmationRequirements.length > 0
? `Missing: ${confirmationRequirements.join(', ')}`
: ''
);
- }, [customCheckout, setStatus]);
+ }, [checkout, setStatus]);
const handleSubmit = async (event) => {
event.preventDefault();
- if (!stripe || !customCheckout) {
- return;
- }
-
- const {canConfirm, confirm} = customCheckout;
- if (!canConfirm) {
+ if (!stripe || !checkout) {
return;
}
try {
setLoading(true);
- await confirm({return_url: window.location.href});
+ await checkout.confirm({
+ email,
+ phoneNumber,
+ returnUrl: window.location.href,
+ });
setLoading(false);
} catch (err) {
console.error(err);
@@ -88,12 +82,16 @@ const CheckoutForm = () => {
}
};
- const buttonDisabled =
- !stripe || !customCheckout || !customCheckout.canConfirm || loading;
+ const buttonDisabled = !stripe || !checkout || loading;
return (
{stripePromise && clientSecret && (
-
-
+
)}
>
);
diff --git a/package.json b/package.json
index 01c9cbb..7b3de29 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"@storybook/react": "^6.5.0-beta.8",
- "@stripe/stripe-js": "^4.9.0",
+ "@stripe/stripe-js": "^5.0.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/react-hooks": "^8.0.0",
@@ -108,7 +108,7 @@
"@types/react": "18.0.5"
},
"peerDependencies": {
- "@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0",
+ "@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
diff --git a/src/components/CustomCheckout.test.tsx b/src/components/CheckoutProvider.test.tsx
similarity index 62%
rename from src/components/CustomCheckout.test.tsx
rename to src/components/CheckoutProvider.test.tsx
index 7be6dbb..57b5fb6 100644
--- a/src/components/CustomCheckout.test.tsx
+++ b/src/components/CheckoutProvider.test.tsx
@@ -2,31 +2,31 @@ import React, {StrictMode} from 'react';
import {render, act, waitFor} from '@testing-library/react';
import {renderHook} from '@testing-library/react-hooks';
-import {CustomCheckoutProvider, useCustomCheckout} from './CustomCheckout';
+import {CheckoutProvider, useCheckout} from './CheckoutProvider';
import {Elements} from './Elements';
import {useStripe} from './useStripe';
import * as mocks from '../../test/mocks';
-describe('CustomCheckoutProvider', () => {
+describe('CheckoutProvider', () => {
let mockStripe: any;
let mockStripePromise: any;
- let mockCustomCheckoutSdk: any;
+ let mockCheckoutSdk: any;
let mockSession: any;
let consoleError: any;
let consoleWarn: any;
- let mockCustomCheckout: any;
+ let mockCheckout: any;
beforeEach(() => {
mockStripe = mocks.mockStripe();
mockStripePromise = Promise.resolve(mockStripe);
- mockCustomCheckoutSdk = mocks.mockCustomCheckoutSdk();
- mockStripe.initCustomCheckout.mockResolvedValue(mockCustomCheckoutSdk);
- mockSession = mocks.mockCustomCheckoutSession();
- mockCustomCheckoutSdk.session.mockReturnValue(mockSession);
+ mockCheckoutSdk = mocks.mockCheckoutSdk();
+ mockStripe.initCheckout.mockResolvedValue(mockCheckoutSdk);
+ mockSession = mocks.mockCheckoutSession();
+ mockCheckoutSdk.session.mockReturnValue(mockSession);
- const {on: _on, session: _session, ...actions} = mockCustomCheckoutSdk;
+ const {on: _on, session: _session, ...actions} = mockCheckoutSdk;
- mockCustomCheckout = {...actions, ...mockSession};
+ mockCheckout = {...actions, ...mockSession};
jest.spyOn(console, 'error');
jest.spyOn(console, 'warn');
@@ -38,17 +38,14 @@ describe('CustomCheckoutProvider', () => {
jest.restoreAllMocks();
});
- test('injects CustomCheckoutProvider with the useCustomCheckout hook', async () => {
+ test('injects CheckoutProvider with the useCheckout hook', async () => {
const wrapper = ({children}: any) => (
-
+
{children}
-
+
);
- const {result, waitForNextUpdate} = renderHook(() => useCustomCheckout(), {
+ const {result, waitForNextUpdate} = renderHook(() => useCheckout(), {
wrapper,
});
@@ -56,17 +53,14 @@ describe('CustomCheckoutProvider', () => {
await waitForNextUpdate();
// wait for all (potentially multiple) updates to finish
- await waitFor(() => expect(result.current).toEqual(mockCustomCheckout));
+ await waitFor(() => expect(result.current).toEqual(mockCheckout));
});
- test('injects CustomCheckoutProvider with the useStripe hook', async () => {
+ test('injects CheckoutProvider with the useStripe hook', async () => {
const wrapper = ({children}: any) => (
-
+
{children}
-
+
);
const {result, waitForNextUpdate} = renderHook(() => useStripe(), {
@@ -83,33 +77,30 @@ describe('CustomCheckoutProvider', () => {
test('allows a transition from null to a valid Stripe object', async () => {
let stripeProp: any = null;
const wrapper = ({children}: any) => (
-
+
{children}
-
+
);
- const {result, rerender} = renderHook(() => useCustomCheckout(), {wrapper});
+ const {result, rerender} = renderHook(() => useCheckout(), {wrapper});
expect(result.current).toBe(undefined);
stripeProp = mockStripe;
act(() => rerender());
- await waitFor(() => expect(result.current).toEqual(mockCustomCheckout));
+ await waitFor(() => expect(result.current).toEqual(mockCheckout));
});
test('works with a Promise resolving to a valid Stripe object', async () => {
const wrapper = ({children}: any) => (
-
{children}
-
+
);
- const {result, waitForNextUpdate} = renderHook(() => useCustomCheckout(), {
+ const {result, waitForNextUpdate} = renderHook(() => useCheckout(), {
wrapper,
});
@@ -117,22 +108,19 @@ describe('CustomCheckoutProvider', () => {
await waitForNextUpdate();
- await waitFor(() => expect(result.current).toEqual(mockCustomCheckout));
+ await waitFor(() => expect(result.current).toEqual(mockCheckout));
});
test('allows a transition from null to a valid Promise', async () => {
let stripeProp: any = null;
const wrapper = ({children}: any) => (
-
+
{children}
-
+
);
const {result, rerender, waitForNextUpdate} = renderHook(
- () => useCustomCheckout(),
+ () => useCheckout(),
{wrapper}
);
expect(result.current).toBe(undefined);
@@ -144,22 +132,22 @@ describe('CustomCheckoutProvider', () => {
await waitForNextUpdate();
- await waitFor(() => expect(result.current).toEqual(mockCustomCheckout));
+ await waitFor(() => expect(result.current).toEqual(mockCheckout));
});
- test('does not set context if Promise resolves after Elements is unmounted', async () => {
+ test('does not set context if Promise resolves after CheckoutProvider is unmounted', async () => {
// Silence console output so test output is less noisy
consoleError.mockImplementation(() => {});
let result: any;
act(() => {
result = render(
-
{null}
-
+
);
});
@@ -172,19 +160,19 @@ describe('CustomCheckoutProvider', () => {
test('works with a Promise resolving to null for SSR safety', async () => {
const nullPromise = Promise.resolve(null);
const TestComponent = () => {
- const customCheckout = useCustomCheckout();
+ const customCheckout = useCheckout();
return customCheckout ? not empty
: null;
};
let result: any;
act(() => {
result = render(
-
-
+
);
});
@@ -206,14 +194,14 @@ describe('CustomCheckoutProvider', () => {
expect(() =>
render(
-
-
+
)
- ).toThrow('Invalid prop `stripe` supplied to `CustomCheckoutProvider`.');
+ ).toThrow('Invalid prop `stripe` supplied to `CheckoutProvider`.');
});
});
@@ -223,7 +211,7 @@ describe('CustomCheckoutProvider', () => {
let result: any;
act(() => {
result = render(
-
@@ -233,7 +221,7 @@ describe('CustomCheckoutProvider', () => {
const mockStripe2: any = mocks.mockStripe();
act(() => {
result.rerender(
-
@@ -241,19 +229,19 @@ describe('CustomCheckoutProvider', () => {
});
await waitFor(() => {
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1);
- expect(mockStripe2.initCustomCheckout).toHaveBeenCalledTimes(0);
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1);
+ expect(mockStripe2.initCheckout).toHaveBeenCalledTimes(0);
expect(consoleWarn).toHaveBeenCalledWith(
- 'Unsupported prop change on CustomCheckoutProvider: You cannot change the `stripe` prop after setting it.'
+ 'Unsupported prop change on CheckoutProvider: You cannot change the `stripe` prop after setting it.'
);
});
});
- test('initCustomCheckout only called once and allows changes to elementsOptions appearance after setting the Stripe object', async () => {
+ test('initCheckout only called once and allows changes to elementsOptions appearance after setting the Stripe object', async () => {
let result: any;
act(() => {
result = render(
- {
});
await waitFor(() =>
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledWith({
+ expect(mockStripe.initCheckout).toHaveBeenCalledWith({
clientSecret: 'cs_123',
elementsOptions: {
appearance: {theme: 'stripe'},
@@ -276,7 +264,7 @@ describe('CustomCheckoutProvider', () => {
act(() => {
result.rerender(
- {
});
await waitFor(() => {
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1);
- expect(mockCustomCheckoutSdk.changeAppearance).toHaveBeenCalledTimes(1);
- expect(mockCustomCheckoutSdk.changeAppearance).toHaveBeenCalledWith({
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1);
+ expect(mockCheckoutSdk.changeAppearance).toHaveBeenCalledTimes(1);
+ expect(mockCheckoutSdk.changeAppearance).toHaveBeenCalledWith({
theme: 'night',
});
});
@@ -299,7 +287,7 @@ describe('CustomCheckoutProvider', () => {
let result: any;
act(() => {
result = render(
- {
});
await waitFor(() =>
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(0)
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(0)
);
act(() => {
result.rerender(
- {
await waitFor(() => {
expect(console.warn).not.toHaveBeenCalled();
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1);
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledWith({
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1);
+ expect(mockStripe.initCheckout).toHaveBeenCalledWith({
clientSecret: 'cs_123',
elementsOptions: {
appearance: {theme: 'stripe'},
@@ -339,15 +327,15 @@ describe('CustomCheckoutProvider', () => {
});
});
- test('throws when trying to call useCustomCheckout outside of CustomCheckoutProvider context', () => {
- const {result} = renderHook(() => useCustomCheckout());
+ test('throws when trying to call useCheckout outside of CheckoutProvider context', () => {
+ const {result} = renderHook(() => useCheckout());
expect(result.error && result.error.message).toBe(
- 'Could not find CustomCheckoutProvider context; You need to wrap the part of your app that calls useCustomCheckout() in an provider.'
+ 'Could not find CheckoutProvider context; You need to wrap the part of your app that calls useCheckout() in an provider.'
);
});
- test('throws when trying to call useStripe outside of CustomCheckoutProvider context', () => {
+ test('throws when trying to call useStripe outside of CheckoutProvider context', () => {
const {result} = renderHook(() => useStripe());
expect(result.error && result.error.message).toBe(
@@ -355,15 +343,15 @@ describe('CustomCheckoutProvider', () => {
);
});
- test('throws when trying to call useStripe in Elements -> CustomCheckoutProvider nested context', async () => {
+ test('throws when trying to call useStripe in Elements -> CheckoutProvider nested context', async () => {
const wrapper = ({children}: any) => (
-
{children}
-
+
);
@@ -374,18 +362,15 @@ describe('CustomCheckoutProvider', () => {
await waitForNextUpdate();
expect(result.error && result.error.message).toBe(
- 'You cannot wrap the part of your app that calls useStripe() in both and providers.'
+ 'You cannot wrap the part of your app that calls useStripe() in both and providers.'
);
});
- test('throws when trying to call useStripe in CustomCheckoutProvider -> Elements nested context', async () => {
+ test('throws when trying to call useStripe in CheckoutProvider -> Elements nested context', async () => {
const wrapper = ({children}: any) => (
-
+
{children}
-
+
);
const {result, waitForNextUpdate} = renderHook(() => useStripe(), {
@@ -395,65 +380,65 @@ describe('CustomCheckoutProvider', () => {
await waitForNextUpdate();
expect(result.error && result.error.message).toBe(
- 'You cannot wrap the part of your app that calls useStripe() in both and providers.'
+ 'You cannot wrap the part of your app that calls useStripe() in both and providers.'
);
});
describe('React.StrictMode', () => {
- test('initCustomCheckout once in StrictMode', async () => {
+ test('initCheckout once in StrictMode', async () => {
const TestComponent = () => {
- const _ = useCustomCheckout();
+ const _ = useCheckout();
return ;
};
act(() => {
render(
-
-
+
);
});
await waitFor(() =>
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1)
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1)
);
});
- test('initCustomCheckout once with stripePromise in StrictMode', async () => {
+ test('initCheckout once with stripePromise in StrictMode', async () => {
const TestComponent = () => {
- const _ = useCustomCheckout();
+ const _ = useCheckout();
return ;
};
act(() => {
render(
-
-
+
);
});
await waitFor(() =>
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1)
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1)
);
});
- test('allows changes to options via (mockCustomCheckoutSdk.changeAppearance after setting the Stripe object in StrictMode', async () => {
+ test('allows changes to options via (mockCheckoutSdk.changeAppearance after setting the Stripe object in StrictMode', async () => {
let result: any;
act(() => {
result = render(
- {
});
await waitFor(() => {
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledTimes(1);
- expect(mockStripe.initCustomCheckout).toHaveBeenCalledWith({
+ expect(mockStripe.initCheckout).toHaveBeenCalledTimes(1);
+ expect(mockStripe.initCheckout).toHaveBeenCalledWith({
clientSecret: 'cs_123',
elementsOptions: {
appearance: {theme: 'stripe'},
@@ -479,7 +464,7 @@ describe('CustomCheckoutProvider', () => {
act(() => {
result.rerender(
- {
});
await waitFor(() => {
- expect(mockCustomCheckoutSdk.changeAppearance).toHaveBeenCalledTimes(1);
- expect(mockCustomCheckoutSdk.changeAppearance).toHaveBeenCalledWith({
+ expect(mockCheckoutSdk.changeAppearance).toHaveBeenCalledTimes(1);
+ expect(mockCheckoutSdk.changeAppearance).toHaveBeenCalledWith({
theme: 'night',
});
});
diff --git a/src/components/CheckoutProvider.tsx b/src/components/CheckoutProvider.tsx
new file mode 100644
index 0000000..0041a63
--- /dev/null
+++ b/src/components/CheckoutProvider.tsx
@@ -0,0 +1,265 @@
+import {FunctionComponent, PropsWithChildren, ReactNode} from 'react';
+import * as stripeJs from '@stripe/stripe-js';
+
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import {parseStripeProp} from '../utils/parseStripeProp';
+import {usePrevious} from '../utils/usePrevious';
+import {isUnknownObject} from '../utils/guards';
+import {isEqual} from '../utils/isEqual';
+import {
+ ElementsContext,
+ ElementsContextValue,
+ parseElementsContext,
+} from './Elements';
+import {registerWithStripeJs} from '../utils/registerWithStripeJs';
+
+interface CheckoutSdkContextValue {
+ checkoutSdk: stripeJs.StripeCheckout | null;
+ stripe: stripeJs.Stripe | null;
+}
+
+const CheckoutSdkContext = React.createContext(
+ null
+);
+CheckoutSdkContext.displayName = 'CheckoutSdkContext';
+
+export const parseCheckoutSdkContext = (
+ ctx: CheckoutSdkContextValue | null,
+ useCase: string
+): CheckoutSdkContextValue => {
+ if (!ctx) {
+ throw new Error(
+ `Could not find CheckoutProvider context; You need to wrap the part of your app that ${useCase} in an provider.`
+ );
+ }
+
+ return ctx;
+};
+
+type StripeCheckoutActions = Omit<
+ Omit,
+ 'on'
+>;
+
+interface CheckoutContextValue
+ extends StripeCheckoutActions,
+ stripeJs.StripeCheckoutSession {}
+const CheckoutContext = React.createContext(null);
+CheckoutContext.displayName = 'CheckoutContext';
+
+export const extractCheckoutContextValue = (
+ checkoutSdk: stripeJs.StripeCheckout | null,
+ sessionState: stripeJs.StripeCheckoutSession | null
+): CheckoutContextValue | null => {
+ if (!checkoutSdk) {
+ return null;
+ }
+
+ const {on: _on, session: _session, ...actions} = checkoutSdk;
+ if (!sessionState) {
+ return {...actions, ...checkoutSdk.session()};
+ }
+
+ return {...actions, ...sessionState};
+};
+
+interface CheckoutProviderProps {
+ /**
+ * A [Stripe object](https://stripe.com/docs/js/initializing) or a `Promise` resolving to a `Stripe` object.
+ * The easiest way to initialize a `Stripe` object is with the the [Stripe.js wrapper module](https://github.com/stripe/stripe-js/blob/master/README.md#readme).
+ * Once this prop has been set, it can not be changed.
+ *
+ * You can also pass in `null` or a `Promise` resolving to `null` if you are performing an initial server-side render or when generating a static site.
+ */
+ stripe: PromiseLike | stripeJs.Stripe | null;
+ options: stripeJs.StripeCheckoutOptions;
+}
+
+interface PrivateCheckoutProviderProps {
+ stripe: unknown;
+ options: stripeJs.StripeCheckoutOptions;
+ children?: ReactNode;
+}
+const INVALID_STRIPE_ERROR =
+ 'Invalid prop `stripe` supplied to `CheckoutProvider`. We recommend using the `loadStripe` utility from `@stripe/stripe-js`. See https://stripe.com/docs/stripe-js/react#elements-props-stripe for details.';
+
+export const CheckoutProvider: FunctionComponent> = (({
+ stripe: rawStripeProp,
+ options,
+ children,
+}: PrivateCheckoutProviderProps) => {
+ const parsed = React.useMemo(
+ () => parseStripeProp(rawStripeProp, INVALID_STRIPE_ERROR),
+ [rawStripeProp]
+ );
+
+ // State used to trigger a re-render when sdk.session is updated
+ const [
+ session,
+ setSession,
+ ] = React.useState(null);
+
+ const [ctx, setContext] = React.useState(() => ({
+ stripe: parsed.tag === 'sync' ? parsed.stripe : null,
+ checkoutSdk: null,
+ }));
+
+ const safeSetContext = (
+ stripe: stripeJs.Stripe,
+ checkoutSdk: stripeJs.StripeCheckout
+ ) => {
+ setContext((ctx) => {
+ if (ctx.stripe && ctx.checkoutSdk) {
+ return ctx;
+ }
+
+ return {stripe, checkoutSdk};
+ });
+ };
+
+ // Ref used to avoid calling initCheckout multiple times when options changes
+ const initCheckoutCalledRef = React.useRef(false);
+
+ React.useEffect(() => {
+ let isMounted = true;
+
+ if (parsed.tag === 'async' && !ctx.stripe) {
+ parsed.stripePromise.then((stripe) => {
+ if (stripe && isMounted && !initCheckoutCalledRef.current) {
+ // Only update context if the component is still mounted
+ // and stripe is not null. We allow stripe to be null to make
+ // handling SSR easier.
+ initCheckoutCalledRef.current = true;
+ stripe.initCheckout(options).then((checkoutSdk) => {
+ if (checkoutSdk) {
+ safeSetContext(stripe, checkoutSdk);
+ checkoutSdk.on('change', setSession);
+ }
+ });
+ }
+ });
+ } else if (
+ parsed.tag === 'sync' &&
+ parsed.stripe &&
+ !initCheckoutCalledRef.current
+ ) {
+ initCheckoutCalledRef.current = true;
+ parsed.stripe.initCheckout(options).then((checkoutSdk) => {
+ if (checkoutSdk) {
+ safeSetContext(parsed.stripe, checkoutSdk);
+ checkoutSdk.on('change', setSession);
+ }
+ });
+ }
+
+ return () => {
+ isMounted = false;
+ };
+ }, [parsed, ctx, options, setSession]);
+
+ // Warn on changes to stripe prop
+ const prevStripe = usePrevious(rawStripeProp);
+ React.useEffect(() => {
+ if (prevStripe !== null && prevStripe !== rawStripeProp) {
+ console.warn(
+ 'Unsupported prop change on CheckoutProvider: You cannot change the `stripe` prop after setting it.'
+ );
+ }
+ }, [prevStripe, rawStripeProp]);
+
+ // Apply updates to elements when options prop has relevant changes
+ const prevOptions = usePrevious(options);
+ React.useEffect(() => {
+ if (!ctx.checkoutSdk) {
+ return;
+ }
+
+ if (
+ options.clientSecret &&
+ !isUnknownObject(prevOptions) &&
+ !isEqual(options.clientSecret, prevOptions.clientSecret)
+ ) {
+ console.warn(
+ 'Unsupported prop change: options.clientSecret is not a mutable property.'
+ );
+ }
+
+ const previousAppearance = prevOptions?.elementsOptions?.appearance;
+ const currentAppearance = options?.elementsOptions?.appearance;
+ if (currentAppearance && !isEqual(currentAppearance, previousAppearance)) {
+ ctx.checkoutSdk.changeAppearance(currentAppearance);
+ }
+ }, [options, prevOptions, ctx.checkoutSdk]);
+
+ // Attach react-stripe-js version to stripe.js instance
+ React.useEffect(() => {
+ registerWithStripeJs(ctx.stripe);
+ }, [ctx.stripe]);
+
+ const checkoutContextValue = React.useMemo(
+ () => extractCheckoutContextValue(ctx.checkoutSdk, session),
+ [ctx.checkoutSdk, session]
+ );
+
+ if (!ctx.checkoutSdk) {
+ return null;
+ }
+
+ return (
+
+
+ {children}
+
+
+ );
+}) as FunctionComponent>;
+
+CheckoutProvider.propTypes = {
+ stripe: PropTypes.any,
+ options: PropTypes.shape({
+ clientSecret: PropTypes.string.isRequired,
+ elementsOptions: PropTypes.object as any,
+ }).isRequired,
+};
+
+export const useCheckoutSdkContextWithUseCase = (
+ useCaseString: string
+): CheckoutSdkContextValue => {
+ const ctx = React.useContext(CheckoutSdkContext);
+ return parseCheckoutSdkContext(ctx, useCaseString);
+};
+
+export const useElementsOrCheckoutSdkContextWithUseCase = (
+ useCaseString: string
+): CheckoutSdkContextValue | ElementsContextValue => {
+ const checkoutSdkContext = React.useContext(CheckoutSdkContext);
+ const elementsContext = React.useContext(ElementsContext);
+
+ if (checkoutSdkContext && elementsContext) {
+ throw new Error(
+ `You cannot wrap the part of your app that ${useCaseString} in both and providers.`
+ );
+ }
+
+ if (checkoutSdkContext) {
+ return parseCheckoutSdkContext(checkoutSdkContext, useCaseString);
+ }
+
+ return parseElementsContext(elementsContext, useCaseString);
+};
+
+export const useCheckout = (): CheckoutContextValue => {
+ // ensure it's in CheckoutProvider
+ useCheckoutSdkContextWithUseCase('calls useCheckout()');
+ const ctx = React.useContext(CheckoutContext);
+ if (!ctx) {
+ throw new Error(
+ 'Could not find Checkout Context; You need to wrap the part of your app that calls useCheckout() in an provider.'
+ );
+ }
+ return ctx;
+};
diff --git a/src/components/CustomCheckout.tsx b/src/components/CustomCheckout.tsx
deleted file mode 100644
index f593236..0000000
--- a/src/components/CustomCheckout.tsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import {FunctionComponent, PropsWithChildren, ReactNode} from 'react';
-import * as stripeJs from '@stripe/stripe-js';
-
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import {parseStripeProp} from '../utils/parseStripeProp';
-import {usePrevious} from '../utils/usePrevious';
-import {isUnknownObject} from '../utils/guards';
-import {isEqual} from '../utils/isEqual';
-import {
- ElementsContext,
- ElementsContextValue,
- parseElementsContext,
-} from './Elements';
-import {registerWithStripeJs} from '../utils/registerWithStripeJs';
-
-interface CustomCheckoutSdkContextValue {
- customCheckoutSdk: stripeJs.StripeCustomCheckout | null;
- stripe: stripeJs.Stripe | null;
-}
-
-const CustomCheckoutSdkContext = React.createContext(
- null
-);
-CustomCheckoutSdkContext.displayName = 'CustomCheckoutSdkContext';
-
-export const parseCustomCheckoutSdkContext = (
- ctx: CustomCheckoutSdkContextValue | null,
- useCase: string
-): CustomCheckoutSdkContextValue => {
- if (!ctx) {
- throw new Error(
- `Could not find CustomCheckoutProvider context; You need to wrap the part of your app that ${useCase} in an provider.`
- );
- }
-
- return ctx;
-};
-
-type StripeCustomCheckoutActions = Omit<
- Omit,
- 'on'
->;
-
-interface CustomCheckoutContextValue
- extends StripeCustomCheckoutActions,
- stripeJs.StripeCustomCheckoutSession {}
-const CustomCheckoutContext = React.createContext(
- null
-);
-CustomCheckoutContext.displayName = 'CustomCheckoutContext';
-
-export const extractCustomCheckoutContextValue = (
- customCheckoutSdk: stripeJs.StripeCustomCheckout | null,
- sessionState: stripeJs.StripeCustomCheckoutSession | null
-): CustomCheckoutContextValue | null => {
- if (!customCheckoutSdk) {
- return null;
- }
-
- const {on: _on, session: _session, ...actions} = customCheckoutSdk;
- if (!sessionState) {
- return {...actions, ...customCheckoutSdk.session()};
- }
-
- return {...actions, ...sessionState};
-};
-
-interface CustomCheckoutProviderProps {
- /**
- * A [Stripe object](https://stripe.com/docs/js/initializing) or a `Promise` resolving to a `Stripe` object.
- * The easiest way to initialize a `Stripe` object is with the the [Stripe.js wrapper module](https://github.com/stripe/stripe-js/blob/master/README.md#readme).
- * Once this prop has been set, it can not be changed.
- *
- * You can also pass in `null` or a `Promise` resolving to `null` if you are performing an initial server-side render or when generating a static site.
- */
- stripe: PromiseLike | stripeJs.Stripe | null;
- options: stripeJs.StripeCustomCheckoutOptions;
-}
-
-interface PrivateCustomCheckoutProviderProps {
- stripe: unknown;
- options: stripeJs.StripeCustomCheckoutOptions;
- children?: ReactNode;
-}
-const INVALID_STRIPE_ERROR =
- 'Invalid prop `stripe` supplied to `CustomCheckoutProvider`. We recommend using the `loadStripe` utility from `@stripe/stripe-js`. See https://stripe.com/docs/stripe-js/react#elements-props-stripe for details.';
-
-export const CustomCheckoutProvider: FunctionComponent> = (({
- stripe: rawStripeProp,
- options,
- children,
-}: PrivateCustomCheckoutProviderProps) => {
- const parsed = React.useMemo(
- () => parseStripeProp(rawStripeProp, INVALID_STRIPE_ERROR),
- [rawStripeProp]
- );
-
- // State used to trigger a re-render when sdk.session is updated
- const [
- session,
- setSession,
- ] = React.useState(null);
-
- const [ctx, setContext] = React.useState(
- () => ({
- stripe: parsed.tag === 'sync' ? parsed.stripe : null,
- customCheckoutSdk: null,
- })
- );
-
- const safeSetContext = (
- stripe: stripeJs.Stripe,
- customCheckoutSdk: stripeJs.StripeCustomCheckout
- ) => {
- setContext((ctx) => {
- if (ctx.stripe && ctx.customCheckoutSdk) {
- return ctx;
- }
-
- return {stripe, customCheckoutSdk};
- });
- };
-
- // Ref used to avoid calling initCustomCheckout multiple times when options changes
- const initCustomCheckoutCalledRef = React.useRef(false);
-
- React.useEffect(() => {
- let isMounted = true;
-
- if (parsed.tag === 'async' && !ctx.stripe) {
- parsed.stripePromise.then((stripe) => {
- if (stripe && isMounted && !initCustomCheckoutCalledRef.current) {
- // Only update context if the component is still mounted
- // and stripe is not null. We allow stripe to be null to make
- // handling SSR easier.
- initCustomCheckoutCalledRef.current = true;
- stripe.initCustomCheckout(options).then((customCheckoutSdk) => {
- if (customCheckoutSdk) {
- safeSetContext(stripe, customCheckoutSdk);
- customCheckoutSdk.on('change', setSession);
- }
- });
- }
- });
- } else if (
- parsed.tag === 'sync' &&
- parsed.stripe &&
- !initCustomCheckoutCalledRef.current
- ) {
- initCustomCheckoutCalledRef.current = true;
- parsed.stripe.initCustomCheckout(options).then((customCheckoutSdk) => {
- if (customCheckoutSdk) {
- safeSetContext(parsed.stripe, customCheckoutSdk);
- customCheckoutSdk.on('change', setSession);
- }
- });
- }
-
- return () => {
- isMounted = false;
- };
- }, [parsed, ctx, options, setSession]);
-
- // Warn on changes to stripe prop
- const prevStripe = usePrevious(rawStripeProp);
- React.useEffect(() => {
- if (prevStripe !== null && prevStripe !== rawStripeProp) {
- console.warn(
- 'Unsupported prop change on CustomCheckoutProvider: You cannot change the `stripe` prop after setting it.'
- );
- }
- }, [prevStripe, rawStripeProp]);
-
- // Apply updates to elements when options prop has relevant changes
- const prevOptions = usePrevious(options);
- React.useEffect(() => {
- if (!ctx.customCheckoutSdk) {
- return;
- }
-
- if (
- options.clientSecret &&
- !isUnknownObject(prevOptions) &&
- !isEqual(options.clientSecret, prevOptions.clientSecret)
- ) {
- console.warn(
- 'Unsupported prop change: options.client_secret is not a mutable property.'
- );
- }
-
- const previousAppearance = prevOptions?.elementsOptions?.appearance;
- const currentAppearance = options?.elementsOptions?.appearance;
- if (currentAppearance && !isEqual(currentAppearance, previousAppearance)) {
- ctx.customCheckoutSdk.changeAppearance(currentAppearance);
- }
- }, [options, prevOptions, ctx.customCheckoutSdk]);
-
- // Attach react-stripe-js version to stripe.js instance
- React.useEffect(() => {
- registerWithStripeJs(ctx.stripe);
- }, [ctx.stripe]);
-
- const customCheckoutContextValue = React.useMemo(
- () => extractCustomCheckoutContextValue(ctx.customCheckoutSdk, session),
- [ctx.customCheckoutSdk, session]
- );
-
- if (!ctx.customCheckoutSdk) {
- return null;
- }
-
- return (
-
-
- {children}
-
-
- );
-}) as FunctionComponent>;
-
-CustomCheckoutProvider.propTypes = {
- stripe: PropTypes.any,
- options: PropTypes.shape({
- clientSecret: PropTypes.string.isRequired,
- elementsOptions: PropTypes.object as any,
- }).isRequired,
-};
-
-export const useCustomCheckoutSdkContextWithUseCase = (
- useCaseString: string
-): CustomCheckoutSdkContextValue => {
- const ctx = React.useContext(CustomCheckoutSdkContext);
- return parseCustomCheckoutSdkContext(ctx, useCaseString);
-};
-
-export const useElementsOrCustomCheckoutSdkContextWithUseCase = (
- useCaseString: string
-): CustomCheckoutSdkContextValue | ElementsContextValue => {
- const customCheckoutSdkContext = React.useContext(CustomCheckoutSdkContext);
- const elementsContext = React.useContext(ElementsContext);
-
- if (customCheckoutSdkContext && elementsContext) {
- throw new Error(
- `You cannot wrap the part of your app that ${useCaseString} in both and providers.`
- );
- }
-
- if (customCheckoutSdkContext) {
- return parseCustomCheckoutSdkContext(
- customCheckoutSdkContext,
- useCaseString
- );
- }
-
- return parseElementsContext(elementsContext, useCaseString);
-};
-
-export const useCustomCheckout = (): CustomCheckoutContextValue => {
- // ensure it's in CustomCheckoutProvider
- useCustomCheckoutSdkContextWithUseCase('calls useCustomCheckout()');
- const ctx = React.useContext(CustomCheckoutContext);
- if (!ctx) {
- throw new Error(
- 'Could not find CustomCheckout Context; You need to wrap the part of your app that calls useCustomCheckout() in an provider.'
- );
- }
- return ctx;
-};
diff --git a/src/components/createElementComponent.test.tsx b/src/components/createElementComponent.test.tsx
index 6e831f7..546d84e 100644
--- a/src/components/createElementComponent.test.tsx
+++ b/src/components/createElementComponent.test.tsx
@@ -2,7 +2,7 @@ import React, {StrictMode} from 'react';
import {render, act, waitFor} from '@testing-library/react';
import * as ElementsModule from './Elements';
-import * as CustomCheckoutModule from './CustomCheckout';
+import * as CheckoutModule from './CheckoutProvider';
import createElementComponent from './createElementComponent';
import * as mocks from '../../test/mocks';
import {
@@ -13,13 +13,13 @@ import {
} from '../types';
const {Elements} = ElementsModule;
-const {CustomCheckoutProvider} = CustomCheckoutModule;
+const {CheckoutProvider} = CheckoutModule;
describe('createElementComponent', () => {
let mockStripe: any;
let mockElements: any;
let mockElement: any;
- let mockCustomCheckoutSdk: any;
+ let mockCheckoutSdk: any;
let simulateElementsEvents: Record;
let simulateOn: any;
@@ -31,12 +31,12 @@ describe('createElementComponent', () => {
beforeEach(() => {
mockStripe = mocks.mockStripe();
mockElements = mocks.mockElements();
- mockCustomCheckoutSdk = mocks.mockCustomCheckoutSdk();
+ mockCheckoutSdk = mocks.mockCheckoutSdk();
mockElement = mocks.mockElement();
mockStripe.elements.mockReturnValue(mockElements);
mockElements.create.mockReturnValue(mockElement);
- mockStripe.initCustomCheckout.mockResolvedValue(mockCustomCheckoutSdk);
- mockCustomCheckoutSdk.createElement.mockReturnValue(mockElement);
+ mockStripe.initCheckout.mockResolvedValue(mockCheckoutSdk);
+ mockCheckoutSdk.createElement.mockReturnValue(mockElement);
jest.spyOn(React, 'useLayoutEffect');
simulateElementsEvents = {};
@@ -85,14 +85,14 @@ describe('createElementComponent', () => {
});
});
- describe('on the server - only for CustomCheckoutProvider', () => {
+ describe('on the server - only for CheckoutProvider', () => {
const CardElement = createElementComponent('card', true);
it('does not render anything', () => {
const {container} = render(
-
+
-
+
);
expect(container.firstChild).toBe(null);
@@ -101,11 +101,7 @@ describe('createElementComponent', () => {
describe.each([
['Elements', Elements, {clientSecret: 'pi_123'}],
- [
- 'CustomCheckoutProvider',
- CustomCheckoutProvider,
- {clientSecret: 'cs_123'},
- ],
+ ['CheckoutProvider', CheckoutProvider, {clientSecret: 'cs_123'}],
])(
'on the server with Provider - %s',
(_providerName, Provider, providerOptions) => {
@@ -317,10 +313,7 @@ describe('createElementComponent', () => {
it('attaches event listeners once the element is created', () => {
jest
- .spyOn(
- CustomCheckoutModule,
- 'useElementsOrCustomCheckoutSdkContextWithUseCase'
- )
+ .spyOn(CheckoutModule, 'useElementsOrCheckoutSdkContextWithUseCase')
.mockReturnValueOnce({elements: null, stripe: null})
.mockReturnValue({elements: mockElements, stripe: mockStripe});
@@ -842,7 +835,7 @@ describe('createElementComponent', () => {
});
});
- describe('Within a CustomCheckoutProvider', () => {
+ describe('Within a CheckoutProvider', () => {
let peMounted = false;
let result: any;
beforeEach(() => {
@@ -862,12 +855,12 @@ describe('createElementComponent', () => {
it('Can remove and add PaymentElement at the same time', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -876,12 +869,12 @@ describe('createElementComponent', () => {
const rerender = result.rerender;
act(() => {
rerender(
-
-
+
);
});
@@ -892,12 +885,12 @@ describe('createElementComponent', () => {
it('passes id to the wrapping DOM element', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -910,12 +903,12 @@ describe('createElementComponent', () => {
it('passes className to the wrapping DOM element', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -929,16 +922,16 @@ describe('createElementComponent', () => {
const options: any = {foo: 'foo'};
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
- expect(mockCustomCheckoutSdk.createElement).toHaveBeenCalledWith(
+ expect(mockCheckoutSdk.createElement).toHaveBeenCalledWith(
'payment',
options
);
@@ -950,7 +943,7 @@ describe('createElementComponent', () => {
let elementCreated = false;
let elementMounted = false;
- mockCustomCheckoutSdk.createElement.mockImplementation(() => {
+ mockCheckoutSdk.createElement.mockImplementation(() => {
expect(elementCreated).toBe(false);
elementCreated = true;
@@ -969,18 +962,18 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(elementMounted).toBeTruthy());
- expect(mockCustomCheckoutSdk.createElement).toHaveBeenCalledTimes(2);
+ expect(mockCheckoutSdk.createElement).toHaveBeenCalledTimes(2);
expect(mockElement.mount).toHaveBeenCalledTimes(2);
expect(mockElement.destroy).toHaveBeenCalledTimes(1);
});
@@ -988,12 +981,12 @@ describe('createElementComponent', () => {
it('mounts the element', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1007,15 +1000,12 @@ describe('createElementComponent', () => {
expect(simulateOff).not.toBeCalled();
});
- it('does not create and mount until CustomCheckoutSdk has been instantiated', async () => {
+ it('does not create and mount until CheckoutSdk has been instantiated', async () => {
act(() => {
result = render(
-
+
-
+
);
});
@@ -1026,18 +1016,18 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
expect(mockElement.mount).toHaveBeenCalled();
- expect(mockCustomCheckoutSdk.createElement).toHaveBeenCalled();
+ expect(mockCheckoutSdk.createElement).toHaveBeenCalled();
});
it('adds an event handlers to an Element', async () => {
@@ -1045,12 +1035,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1063,35 +1053,32 @@ describe('createElementComponent', () => {
it('attaches event listeners once the element is created', async () => {
const mockHandler = jest.fn();
- // This won't create the element, since customCheckoutSdk is undefined on this render
+ // This won't create the element, since checkoutSdk is undefined on this render
act(() => {
result = render(
-
+
-
+
);
});
- expect(mockCustomCheckoutSdk.createElement).not.toBeCalled();
+ expect(mockCheckoutSdk.createElement).not.toBeCalled();
expect(simulateOn).not.toBeCalled();
- // This creates the element now that customCheckoutSdk is defined
+ // This creates the element now that checkoutSdk is defined
act(() => {
result.rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
- expect(mockCustomCheckoutSdk.createElement).toBeCalled();
+ expect(mockCheckoutSdk.createElement).toBeCalled();
expect(simulateOn).toBeCalledWith('change', expect.any(Function));
expect(simulateOff).not.toBeCalled();
@@ -1105,12 +1092,12 @@ describe('createElementComponent', () => {
const mockHandler = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1121,12 +1108,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1138,12 +1125,12 @@ describe('createElementComponent', () => {
const mockHandler = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1154,12 +1141,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1173,12 +1160,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1188,12 +1175,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1208,24 +1195,24 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
const {rerender} = result;
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1242,12 +1229,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1255,12 +1242,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1276,12 +1263,12 @@ describe('createElementComponent', () => {
const mockHandler2 = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1289,12 +1276,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1309,12 +1296,12 @@ describe('createElementComponent', () => {
const mockHandler2 = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1322,12 +1309,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1342,12 +1329,12 @@ describe('createElementComponent', () => {
const mockHandler2 = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1355,12 +1342,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1375,12 +1362,12 @@ describe('createElementComponent', () => {
const mockHandler2 = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1388,12 +1375,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1408,12 +1395,12 @@ describe('createElementComponent', () => {
const mockHandler2 = jest.fn();
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1421,12 +1408,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1439,12 +1426,12 @@ describe('createElementComponent', () => {
it('updates the Element when options change', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1452,12 +1439,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1470,12 +1457,12 @@ describe('createElementComponent', () => {
it('does not trigger unnecessary updates', async () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1483,12 +1470,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1499,13 +1486,13 @@ describe('createElementComponent', () => {
it('updates the Element when options change from null to non-null value', async () => {
act(() => {
result = render(
-
{/* @ts-expect-error */}
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1513,12 +1500,12 @@ describe('createElementComponent', () => {
act(() => {
rerender(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1531,12 +1518,9 @@ describe('createElementComponent', () => {
it('destroys an existing Element when the component unmounts', async () => {
act(() => {
result = render(
-
+
-
+
);
});
const {unmount} = result;
@@ -1547,12 +1531,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1567,12 +1551,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
await waitFor(() => expect(peMounted).toBeTruthy());
@@ -1587,12 +1571,12 @@ describe('createElementComponent', () => {
act(() => {
result = render(
-
-
+
);
});
diff --git a/src/components/createElementComponent.tsx b/src/components/createElementComponent.tsx
index bf1e3a4..3996dc9 100644
--- a/src/components/createElementComponent.tsx
+++ b/src/components/createElementComponent.tsx
@@ -13,7 +13,7 @@ import {
extractAllowedOptionsUpdates,
UnknownOptions,
} from '../utils/extractAllowedOptionsUpdates';
-import {useElementsOrCustomCheckoutSdkContextWithUseCase} from './CustomCheckout';
+import {useElementsOrCheckoutSdkContextWithUseCase} from './CheckoutProvider';
type UnknownCallback = (...args: unknown[]) => any;
@@ -62,12 +62,11 @@ const createElementComponent = (
onShippingAddressChange,
onShippingRateChange,
}) => {
- const ctx = useElementsOrCustomCheckoutSdkContextWithUseCase(
+ const ctx = useElementsOrCheckoutSdkContextWithUseCase(
`mounts <${displayName}>`
);
const elements = 'elements' in ctx ? ctx.elements : null;
- const customCheckoutSdk =
- 'customCheckoutSdk' in ctx ? ctx.customCheckoutSdk : null;
+ const checkoutSdk = 'checkoutSdk' in ctx ? ctx.checkoutSdk : null;
const [element, setElement] = React.useState(
null
);
@@ -109,11 +108,11 @@ const createElementComponent = (
if (
elementRef.current === null &&
domNode.current !== null &&
- (elements || customCheckoutSdk)
+ (elements || checkoutSdk)
) {
let newElement: stripeJs.StripeElement | null = null;
- if (customCheckoutSdk) {
- newElement = customCheckoutSdk.createElement(type as any, options);
+ if (checkoutSdk) {
+ newElement = checkoutSdk.createElement(type as any, options);
} else if (elements) {
newElement = elements.create(type as any, options);
}
@@ -127,7 +126,7 @@ const createElementComponent = (
newElement.mount(domNode.current);
}
}
- }, [elements, customCheckoutSdk, options]);
+ }, [elements, checkoutSdk, options]);
const prevOptions = usePrevious(options);
React.useEffect(() => {
@@ -165,7 +164,7 @@ const createElementComponent = (
// Only render the Element wrapper in a server environment.
const ServerElement: FunctionComponent = (props) => {
- useElementsOrCustomCheckoutSdkContextWithUseCase(`mounts <${displayName}>`);
+ useElementsOrCheckoutSdkContextWithUseCase(`mounts <${displayName}>`);
const {id, className} = props;
return ;
};
diff --git a/src/components/useStripe.tsx b/src/components/useStripe.tsx
index 6cf3566..e246722 100644
--- a/src/components/useStripe.tsx
+++ b/src/components/useStripe.tsx
@@ -1,11 +1,11 @@
import * as stripeJs from '@stripe/stripe-js';
-import {useElementsOrCustomCheckoutSdkContextWithUseCase} from './CustomCheckout';
+import {useElementsOrCheckoutSdkContextWithUseCase} from './CheckoutProvider';
/**
* @docs https://stripe.com/docs/stripe-js/react#usestripe-hook
*/
export const useStripe = (): stripeJs.Stripe | null => {
- const {stripe} = useElementsOrCustomCheckoutSdkContextWithUseCase(
+ const {stripe} = useElementsOrCheckoutSdkContextWithUseCase(
'calls useStripe()'
);
return stripe;
diff --git a/src/index.ts b/src/index.ts
index 4b93f32..5c855e0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -27,10 +27,7 @@ export * from './types';
export {useElements, Elements, ElementsConsumer} from './components/Elements';
-export {
- useCustomCheckout,
- CustomCheckoutProvider,
-} from './components/CustomCheckout';
+export {useCheckout, CheckoutProvider} from './components/CheckoutProvider';
export {EmbeddedCheckout} from './components/EmbeddedCheckout';
export {EmbeddedCheckoutProvider} from './components/EmbeddedCheckoutProvider';
export {useStripe} from './components/useStripe';
diff --git a/test/mocks.js b/test/mocks.js
index 016aea7..95784fc 100644
--- a/test/mocks.js
+++ b/test/mocks.js
@@ -19,7 +19,7 @@ export const mockElements = () => {
};
};
-export const mockCustomCheckoutSession = () => {
+export const mockCheckoutSession = () => {
return {
lineItems: [],
currency: 'usd',
@@ -37,7 +37,7 @@ export const mockCustomCheckoutSession = () => {
};
};
-export const mockCustomCheckoutSdk = () => {
+export const mockCheckoutSdk = () => {
const elements = {};
return {
@@ -49,7 +49,7 @@ export const mockCustomCheckoutSdk = () => {
getElement: jest.fn((type) => {
return elements[type] || null;
}),
- session: jest.fn(() => mockCustomCheckoutSession()),
+ session: jest.fn(() => mockCheckoutSession()),
applyPromotionCode: jest.fn(),
removePromotionCode: jest.fn(),
updateShippingAddress: jest.fn(),
@@ -70,7 +70,7 @@ export const mockEmbeddedCheckout = () => ({
});
export const mockStripe = () => {
- const customCheckoutSdk = mockCustomCheckoutSdk();
+ const checkoutSdk = mockCheckoutSdk();
return {
elements: jest.fn(() => mockElements()),
createToken: jest.fn(),
@@ -81,7 +81,7 @@ export const mockStripe = () => {
paymentRequest: jest.fn(),
registerAppInfo: jest.fn(),
_registerWrapper: jest.fn(),
- initCustomCheckout: jest.fn().mockResolvedValue(customCheckoutSdk),
+ initCheckout: jest.fn().mockResolvedValue(checkoutSdk),
initEmbeddedCheckout: jest.fn(() =>
Promise.resolve(mockEmbeddedCheckout())
),
diff --git a/yarn.lock b/yarn.lock
index 1ba4a0e..57dfff6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2350,10 +2350,10 @@
regenerator-runtime "^0.13.7"
resolve-from "^5.0.0"
-"@stripe/stripe-js@^4.9.0":
- version "4.9.0"
- resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-4.9.0.tgz#95dc000b2f90aeb4db2f2bab82a2fc574f26d1fd"
- integrity sha512-tMPZQZZXGWyNX7hbgenq+1xEj2oigJ54XddbtSX36VedoKsPBq7dxwRXu4Xd5FdpT3JDyyDtnmvYkaSnH1yHTQ==
+"@stripe/stripe-js@^5.0.0":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-5.0.0.tgz#c5fe8e8fbd94080cfd7d55e35e51939ec5c57346"
+ integrity sha512-u/f/uq7oCgHGgnSxC/I68pHpMf1XQMNBeb8mbhbxFSCji86uz4HvC50vbUS1FBTkx+b4gkTIo/UCnYjwn1cseg==
"@testing-library/dom@^8.5.0":
version "8.13.0"