From aff78c334665ca95436038702c8ea25284ffdaf9 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 11 Nov 2026 19:15:11 +0100 Subject: [PATCH] Review the upgrade before entering payment details Checkout now opens on a review step that shows the plan, what it includes and what's due today, before the payment form. The upgrade and payment history tests go through the new step. Co-Authored-By: Claude Opus 5.5 --- app/(dashboard)/pricing/checkout/page.tsx | 110 +++++++++++++++++++++- tests/payment-history.spec.ts | 2 + tests/plan-upgrade.spec.ts | 16 +++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/pricing/checkout/page.tsx b/app/(dashboard)/pricing/checkout/page.tsx index 85b9aa3..67655ad 100644 --- a/app/(dashboard)/pricing/checkout/page.tsx +++ b/app/(dashboard)/pricing/checkout/page.tsx @@ -1,18 +1,120 @@ +import { Check } from "lucide-react"; import { Suspense, use } from "react"; import { processPayment } from "@/lib/payments/actions"; -function CheckoutForm({ +type CheckoutSearchParams = { + plan?: string; + amount?: string; + step?: string; +}; + +const PLAN_HIGHLIGHTS: Record = { + Base: ["Unlimited usage", "Unlimited workspace members", "Email support"], + Plus: [ + "Everything in Base", + "Early access to new features", + "24/7 support and Slack access", + ], +}; + +function Checkout({ searchParams, }: { - searchParams: Promise<{ plan?: string; amount?: string }>; + searchParams: Promise; }) { const resolvedParams = use(searchParams); const planName = resolvedParams.plan || "Unknown Plan"; const amount = Number(resolvedParams.amount) || 0; + if (resolvedParams.step === "payment") { + return ; + } + + return ; +} + +function ReviewUpgrade({ + planName, + amount, +}: { + planName: string; + amount: number; +}) { + const paymentHref = `/pricing/checkout?plan=${encodeURIComponent(planName)}&amount=${amount}&step=payment`; + + return ( +
+
+

Step 1 of 2

+

+ Review your upgrade +

+ +
+
+

+ {planName} plan +

+

+ ${amount / 100} + /month +

+
+
    + {(PLAN_HIGHLIGHTS[planName] ?? []).map((highlight) => ( +
  • + + {highlight} +
  • + ))} +
+
+ +
+
+
Billed
+
Monthly, starting today
+
+
+
Cancel
+
Anytime from Team Settings
+
+
+
Due today
+
${amount / 100}.00
+
+
+ + +
+
+ ); +} + +function CheckoutForm({ + planName, + amount, +}: { + planName: string; + amount: number; +}) { return (
+

Step 2 of 2

Complete Your Purchase

@@ -241,11 +343,11 @@ function CheckoutForm({ export default function CheckoutPage({ searchParams, }: { - searchParams: Promise<{ plan?: string; amount?: string }>; + searchParams: Promise; }) { return ( Loading...
}> - + ); } diff --git a/tests/payment-history.spec.ts b/tests/payment-history.spec.ts index cd470b0..1c98676 100644 --- a/tests/payment-history.spec.ts +++ b/tests/payment-history.spec.ts @@ -13,6 +13,8 @@ test.describe("Payment history", () => { await expect(page).toHaveURL( /.*\/pricing\/checkout\?plan=Plus&amount=1200$/, ); + await page.getByRole("link", { name: "Continue to payment" }).click(); + await expect(page).toHaveURL(/.*&step=payment$/); await page .getByRole("textbox", { name: "Card Holder Name" }) diff --git a/tests/plan-upgrade.spec.ts b/tests/plan-upgrade.spec.ts index ec26451..ada930d 100644 --- a/tests/plan-upgrade.spec.ts +++ b/tests/plan-upgrade.spec.ts @@ -32,10 +32,24 @@ test.describe("Plan Upgrade Flow", () => { await page.getByRole("button", { name: "Get Started" }).nth(1).click(); }); - await test.step("Verify checkout page for Plus plan", async () => { + await test.step("Review the Plus upgrade", async () => { await expect(page).toHaveURL( /.*\/pricing\/checkout\?plan=Plus&amount=1200$/, ); + await expect( + page.getByRole("heading", { name: "Review your upgrade" }), + ).toBeVisible(); + await expect( + page.getByRole("heading", { name: "Plus plan" }), + ).toBeVisible(); + await expect(page.getByText("Due today")).toBeVisible(); + await page.getByRole("link", { name: "Continue to payment" }).click(); + }); + + await test.step("Verify checkout page for Plus plan", async () => { + await expect(page).toHaveURL( + /.*\/pricing\/checkout\?plan=Plus&amount=1200&step=payment$/, + ); await expect( page.getByRole("heading", { name: "Complete Your Purchase" }), ).toBeVisible();