Contents

The Complete Stripe Payments Guide: One-Time Charges and Subscriptions (Next.js Practice)

Stripe is the default choice for SaaS and subscription products, but it comes with a lot of moving parts. Teams often mis-model subscriptions, pick the wrong integration, or rely on front-end success pages—then end up refactoring after launch.

This guide explains the Stripe essentials with a clear engineering lens: one-time payments vs subscriptions, key objects, webhook strategy, and a minimal database schema that scales.

1. Two layers to clarify: Integration vs Billing Model

Integration options

IntegrationWhat it isRecommendation
CheckoutStripe-hosted payment page⭐⭐⭐⭐⭐
Payment Element / ElementsEmbedded custom UI⭐⭐⭐
Payment LinksNo-code shareable links⭐⭐
InvoicingB2B invoice flow⭐⭐

Takeaway: For MVPs, Stripe Checkout is the fastest and safest path.

Billing models

ModelCore Stripe objects
One-time paymentPaymentIntent
Monthly subscriptionSubscription + Invoice
Annual subscriptionSubscription + Invoice

Monthly and annual subscriptions are the same system—just different Price.recurring.interval values.

2. One-time payments: simple, but traceable

Use cases: one-off purchases, credit packs, single-service fees.

Recommended Checkout flow:

  1. Create a Checkout Session (mode=payment) on the server
  2. Redirect to session.url
  3. Confirm payment via Webhook
  4. Update order status

Key objects:

  • Checkout Session (cs_...)
  • PaymentIntent (pi_...)
  • Charge (ch_...)

Rule: Webhooks are the source of truth. Do not rely on the front-end success page.

3. Subscriptions: separate price from subscription

Correct modeling in Stripe

  • Product: your plan (e.g., Pro)
  • Price: monthly or yearly pricing
  • Subscription: a customer’s subscription record

Example:

  • pro_monthlyrecurring.interval = month
  • pro_yearlyrecurring.interval = year

Subscription flow (Checkout)

  1. Create Checkout Session (mode=subscription)
  2. Pass line_items.price
  3. User completes payment
  4. Stripe creates Subscription
  5. Invoice generated each billing period
  6. Webhooks drive state changes

4. One-time vs subscription: quick comparison

DimensionOne-timeSubscription
Checkout modepaymentsubscription
Core objectsPaymentIntentSubscription + Invoice
Auto-renewNoYes
Subscription table neededNoYes
Webhook complexityLowMedium

5. Keys and environment variables

Secret Key (server)

STRIPE_SECRET_KEY=sk_...

Used to create sessions, subscriptions, refunds, and handle webhooks.

Publishable Key (client)

NEXT_PUBLIC_STRIPE_PUBLIC_KEY=pk_...

Only needed when using Payment Element or Stripe.js directly. For Checkout redirects, it is usually unnecessary.

Webhook Secret (production)

STRIPE_WEBHOOK_SECRET=whsec_...

Validates webhook signatures and prevents spoofed requests.

6. Minimal, scalable database design

Recommended tables:

TablePurpose
billing_ordersBusiness order entry point
billing_paymentsPayment ledger and refunds
billing_subscriptionsSubscription state tracking
stripe_eventsWebhook idempotency and audit

Why split them?

  • Orders, payments, and subscriptions are different lifecycles
  • Subscriptions recur and can fail
  • Webhooks are retried and must be idempotent

7. Webhooks are the source of truth

Front-end success ≠ payment success. Validate via webhook events:

One-time payment events:

  • checkout.session.completed
  • payment_intent.succeeded

Subscription events:

  • customer.subscription.created
  • customer.subscription.updated
  • invoice.paid
  • invoice.payment_failed

Best practices:

  • Store event.id in stripe_events for idempotency
  • Always retry on failures
  • Keep logs for audit

8. Next.js implementation tips

  • Use API Routes or Route Handlers to create Checkout Sessions
  • Verify webhooks in a dedicated server endpoint
  • Add a billing service layer to isolate Stripe logic
  • Drive order/subscription state with a clear state machine

9. Launch checklist

  • Billing model clearly separated (one-time vs subscription)
  • Stripe Checkout used for MVP
  • Orders, payments, subscriptions modeled separately
  • Webhooks are treated as truth
  • Idempotency and retries implemented

If you later add trials, coupons, multi-currency, or invoicing, extend this model rather than replacing it.