/library / template-stripe-checkout-service
templateAPI / Backend

Stripe Checkout + Webhook receiver (drop-in payments)

A minimal service that takes payments via Stripe Checkout and processes webhooks safely. Add to any project that needs one-time or subscription payments without building a full SaaS billing stack.

use whenYou're selling something (a digital good, a license, a tier upgrade). You want Stripe Checkout for the UI, your webhook to update your DB, no third-party billing platform.

April 26, 20262,180 bytesstripepaymentswebhookcheckout

[Service Name] — Stripe Checkout layer

A thin service that takes payments via Stripe Checkout and processes webhooks safely. Stateless except for what you choose to persist.

Source of truth

Stripe is the source of truth for payment state. Your DB stores a denormalized copy for fast reads. The webhook reconciles after every event.

Tech stack

Node 22 + TypeScript + Express or Hono (whatever your existing stack uses). Stripe SDK (stripe npm package, pinned to a specific API version). Postgres for the local mirror. JWT or session auth on the create-checkout endpoint.

Deploy

Anywhere a Node server runs. Webhook endpoint must be reachable from the public internet. For local dev use stripe listen --forward-to localhost:3000/webhooks/stripe.

File map

  • src/routes/checkout.ts POST /api/checkout -> creates a Checkout Session, returns the URL
  • src/routes/billing-portal.ts POST /api/billing-portal -> Customer portal URL for subscription management
  • src/routes/webhook.ts POST /webhooks/stripe -> verifies signature, dispatches by event type
  • src/lib/stripe.ts SDK init, API version pin
  • src/lib/db.ts Postgres writes for customers, subscriptions, orders
  • src/events/ one handler per Stripe event type (checkout.session.completed, customer.subscription.updated, etc)
  • db/schema.sql stripe_customers, subscriptions, orders tables

.env keys

  • STRIPE_SECRET_KEY sk_live_... or sk_test_...
  • STRIPE_WEBHOOK_SECRET whsec_...
  • STRIPE_PRICE_IDS_TIER1, STRIPE_PRICE_IDS_TIER2 your product price IDs
  • STRIPE_PORTAL_RETURN_URL where users land after the portal
  • DATABASE_URL

Hard rules

  • Webhook signature is verified BEFORE parsing the body. Use the raw body, not JSON.parse().
  • Webhook handlers must be idempotent. Same event twice = same result. Store the Stripe event ID, refuse duplicates.
  • Webhook responds 200 within 2 seconds; do the slow work in a background queue if needed.
  • Never store full card numbers, CVCs, or full names on cards. Stripe holds these.
  • API version is PINNED in stripe.ts (apiVersion: '2025-06-30.basil' or whatever your locked version is). Test before bumping.
  • Test mode keys for dev, live mode for prod. Use Stripe's CLI stripe listen to forward events to localhost.

Recent significant changes

  • 2026-04-26: Scaffolded. Locked: Stripe Checkout (hosted) over custom Elements UI, webhooks over polling, Postgres mirror over Stripe-as-source-only.

Next session: start here

  1. Create Stripe account, get test keys.
  2. Create products + prices in Stripe Dashboard, save IDs.
  3. stripe listen --forward-to localhost:3000/webhooks/stripe and copy the whsec_ into .env.
  4. Implement create-checkout endpoint, test with a real test card (4242 4242 4242 4242).
  5. Verify a successful webhook updates your DB before going live.
  6. Activate live mode keys. Test one real payment for $1.

Get the next CLAUDE.md in your inbox.

One new template every week, plus occasional case studies.