pealdevDOCS
Back to peal.dev

STRIPE STARTER · SUCCESS & CANCEL PAGES

Success & cancel pages

After checkout, Stripe redirects to /checkout/success?session_id=cs_xxx or /checkout/cancel. The success page fetches the session server-side and renders the order summary. The cancel page reassures.

Stripe doesn't trust the URL it redirects to with anything sensitive. The success URL just gets a session_id query param — your server fetches the full session from Stripe and renders from that. Treat the redirect as a hint, not a source of truth.

How the success page works

  • Parses searchParams through successPageParamsSchema (cs_test_xxx or cs_live_xxx, regex-validated).
  • Falls back to <InvalidSessionView /> on any parse error.
  • Calls stripe.checkout.sessions.retrieve() with expand: ["line_items.data.price", "subscription", "payment_intent"].
  • Renders an order confirmation card: line items, total, payment type badge, billing interval (if subscription), confirmation email.
  • If subscription + a Customer ID exists, shows a CustomerPortalButton — return path back to /checkout/success.

The fallback view

TSX
// Invalid session_id, expired link, or session not in "complete" state
// → <InvalidSessionView /> renders a friendly card with a back-to-home link
// Don't 404 — users sometimes refresh the page or share the link.

The cancel page

/checkout/cancel renders a calm "no payment was taken" message, a Safe badge, and links back to /pricing and /. It's deliberately not scary — users cancel for benign reasons ("wait, let me check my card balance").

Don't fulfill on the success page

It's tempting: the page knows the session is complete, just call your DB. Don't. The user might never reach the page (closed the tab, lost network). The webhook fires regardless. Fulfill in the webhook handler — the success page is for UX, not state changes.

session.status === "complete" is the truth

Even a complete session can fail later (3DS challenge that didn't resolve, ACH that returns). The webhook will tell you. The success page checks status === "complete" to render the confirmation — but if you need a permanent record, write it from the webhook.

Customizing the success card

Edit src/app/checkout/success/page.tsx. The card uses shadcn/ui primitives (Card, Separator, Badge, Button). Drop in your own copy, add a "What's next" section, link to your dashboard. Keep the session fetch and the fallback — they're load-bearing.