Configure Stripe

GuideBilling setupCheckout and webhooks

Fast Unicorn supports both one-time payments and subscriptions through Stripe.

This guide shows where the Stripe integration lives, which environment variables it needs, and how checkout, customer linking, and webhook synchronization work together.

What this guide covers

Products and prices

How Stripe price IDs map to the billing tiers expected by the template.

Checkout flows

How one-time payments, subscriptions, and upgrades are created and linked to users.

Webhook synchronization

How long-lived billing state is updated from Stripe events instead of only from the browser.

Main files and folders

FilePropósito
src/app/api/stripe/checkout/route.tsCheckout entrypoint.
src/app/api/stripe/webhook/route.tsWebhook endpoint.
src/server/api/routers/stripe.tsBilling-related API contracts.
src/server/services/billing/checkout.tsStripe checkout session creation.
src/server/services/billing/customers.tsCustomer lookup and linking.
src/server/services/billing/subscription.tsTier and price mapping.
src/server/services/billing/webhooks.tsEvent synchronization logic.
Environment requirements

Required environment variables

VariablePropósito
STRIPE_SECRET_KEYStripe server API key.
STRIPE_PUBLISHABLE_KEYStripe publishable key.
STRIPE_WEBHOOK_SECRETStripe webhook secret.
STRIPE_PRICE_BASIC_IDStripe price ID for the BASIC tier.
STRIPE_PRICE_PRO_IDStripe price ID for the PRO tier.
STRIPE_PRICE_ENTERPRISE_IDStripe price ID for the ENTERPRISE tier.
STRIPE_PRICE_ONE_TIME_IDStripe price ID for the ONE_TIME tier.
NEXT_PUBLIC_APP_URLBase URL used for app links and checkout redirects.

Use this order when enabling or changing billing behavior.

1

Create products and prices in Stripe

Define the products you want to sell and copy the generated price IDs into the environment variables expected by the app.

2

Review the billing services

Confirm the checkout and subscription services map those price IDs to the tiers and checkout modes you want to support.

3

Configure webhook delivery

Point Stripe or Stripe CLI to the local webhook endpoint so subscription and payment events can sync correctly.

4

Test the full billing lifecycle

Verify checkout creation, webhook processing, customer linking, and billing state updates before shipping changes.

Create your Stripe products

Before using checkout, create the prices you need in Stripe and store their IDs in the environment variables expected by src/env.js.

The billing service maps those IDs to template tiers through src/server/services/billing/webhook-sync.ts.

This keeps price selection centralized instead of scattering Stripe identifiers across routes or UI components.

Checkout flow

The template supports:

  • one-time checkout
  • subscription checkout
  • upgrade checkout

Checkout sessions are created through createStripeCheckoutSession in src/server/services/billing/checkout.ts.

Customer linking

Stripe customers are linked back to the authenticated app user through metadata and the local stripeCustomerId field.

This allows the app to preserve ownership even after checkout redirects or asynchronous webhook delivery.

This lets the app:

  • verify ownership
  • find active subscriptions
  • sync invoices and payments

Why customer linking matters

Billing state should stay connected to a real application user, not only to a Stripe customer record. That makes account recovery, upgrades, and access checks much more reliable.

Webhook route

The webhook endpoint lives at /api/stripe/webhook.

It verifies the Stripe signature and delegates event handling to src/server/services/billing/webhook-sync.ts.

This webhook flow should remain the source of truth for long-lived billing state such as subscription updates and completed payments.

Events currently handled

  • checkout.session.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • payment_intent.succeeded

Local testing

Use Stripe CLI to forward webhook events:

Forward Stripe webhooks locallybash
stripe listen --forward-to http://localhost:3000/api/stripe/webhook

Then copy the generated signing secret into STRIPE_WEBHOOK_SECRET.

Extension guidance

When changing billing behavior:

  • keep entrypoint validation thin in route handlers
  • keep Stripe orchestration in src/server/services/billing
  • keep tier logic centralized in webhook-sync.ts
  • make webhook sync the source of truth for long-lived billing state

Project structure reminder

If billing logic starts growing, keep route handlers thin and preserve the service boundary. That follows the same separation described in Project Structure.