From 94ce8b0d58690433940506c2ca8a68425a342544 Mon Sep 17 00:00:00 2001 From: Juan Paolo Mariano <65009749+jp-mariano@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:26:09 +0800 Subject: [PATCH] Fix to accept setup intent (#3334) * Fix to accept setup intent * Added definition for 'confirmSetup' * Added frequency checks for 'StripeCheckout' tests * minor:react router v6: get params alongside component lazy loading --------- Co-authored-by: ap-justin <89639563+ap-justin@users.noreply.github.com> --- .../Steps/Submit/StripeCheckout/Checkout.tsx | 9 +++++-- .../StripeCheckout/StripeCheckout.test.tsx | 12 ++++++++-- src/pages/StripePaymentStatus.tsx | 24 +++++++++++++++---- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/components/donation/Steps/Submit/StripeCheckout/Checkout.tsx b/src/components/donation/Steps/Submit/StripeCheckout/Checkout.tsx index 87d7108ae1..c5636ab968 100644 --- a/src/components/donation/Steps/Submit/StripeCheckout/Checkout.tsx +++ b/src/components/donation/Steps/Submit/StripeCheckout/Checkout.tsx @@ -43,12 +43,17 @@ export default function Checkout(props: StripeCheckoutStep) { ? `${window.location.origin}${appRoutes.donate_widget}/${donateWidgetRoutes.stripe_payment_status}` : `${window.location.origin}${appRoutes.stripe_payment_status}`; - const { error } = await stripe.confirmPayment({ + const stripeConfirmParams = { elements, confirmParams: { return_url, }, - }); + }; + + const { error } = + props.details.frequency === "subscription" + ? await stripe.confirmSetup(stripeConfirmParams) + : await stripe.confirmPayment(stripeConfirmParams); // This point will only be reached if there is an immediate error when // confirming the payment. Otherwise, your customer will be redirected to diff --git a/src/components/donation/Steps/Submit/StripeCheckout/StripeCheckout.test.tsx b/src/components/donation/Steps/Submit/StripeCheckout/StripeCheckout.test.tsx index f10a359a63..eaf2285701 100644 --- a/src/components/donation/Steps/Submit/StripeCheckout/StripeCheckout.test.tsx +++ b/src/components/donation/Steps/Submit/StripeCheckout/StripeCheckout.test.tsx @@ -20,6 +20,7 @@ vi.mock("../../Context", () => ({ })); const confirmPaymentMock = vi.hoisted(() => vi.fn()); +const confirmSetupMock = vi.hoisted(() => vi.fn()); vi.mock("@stripe/react-stripe-js", () => ({ Elements: vi.fn(({ children }) => children), @@ -35,6 +36,7 @@ vi.mock("@stripe/react-stripe-js", () => ({ useStripe: vi.fn(() => { const stripe: Stripe = { confirmPayment: confirmPaymentMock, + confirmSetup: confirmSetupMock, } as any; return stripe; }), @@ -130,7 +132,10 @@ describe("stripe checkout", () => { type: "card_error", message: "invalid card", }; - confirmPaymentMock.mockResolvedValueOnce({ error: err }); + + if (state.details.frequency === "one-time") + confirmPaymentMock.mockResolvedValueOnce({ error: err }); + else confirmSetupMock.mockResolvedValueOnce({ error: err }); //user sees modal on card error await userEvent.click(donateBtn); @@ -149,7 +154,10 @@ describe("stripe checkout", () => { message: "unhelpful error message that won't be shown", }; - confirmPaymentMock.mockResolvedValueOnce({ error: err }); + if (state.details.frequency === "one-time") + confirmPaymentMock.mockResolvedValueOnce({ error: err }); + else confirmSetupMock.mockResolvedValueOnce({ error: err }); + await userEvent.click(donateBtn); const errorModal = screen.getByRole("dialog"); diff --git a/src/pages/StripePaymentStatus.tsx b/src/pages/StripePaymentStatus.tsx index b4491dffac..e9a7995ade 100644 --- a/src/pages/StripePaymentStatus.tsx +++ b/src/pages/StripePaymentStatus.tsx @@ -1,3 +1,4 @@ +import { skipToken } from "@reduxjs/toolkit/query"; import type { PaymentIntent } from "@stripe/stripe-js"; import Icon from "components/Icon"; import LoadText from "components/LoadText"; @@ -6,19 +7,32 @@ import Seo from "components/Seo"; import { EMAIL_SUPPORT } from "constants/env"; import { appRoutes, donateWidgetRoutes } from "constants/routes"; import { useCallback, useEffect } from "react"; -import { Link, Navigate, useOutletContext } from "react-router-dom"; +import { + Link, + type LoaderFunction, + Navigate, + useLoaderData, + useOutletContext, +} from "react-router-dom"; import { useStripePaymentStatusQuery } from "services/apes"; import type { GuestDonor } from "types/aws"; import type { DonateThanksState } from "types/pages"; +export const loader: LoaderFunction = ({ request }) => { + const url = new URL(request.url); + return ( + url.searchParams.get("payment_intent") ?? + url.searchParams.get("setup_intent") ?? + "" + ); +}; + export function Component() { const isInWidget = useOutletContext(); - const paymentIntentId = - new URLSearchParams(window.location.search).get("payment_intent") ?? ""; + const paymentIntentId = useLoaderData() as string; const queryState = useStripePaymentStatusQuery( - { paymentIntentId }, - { skip: !paymentIntentId } + paymentIntentId ? { paymentIntentId } : skipToken ); const { refetch } = queryState;