Spacerr includes Stripe checkout examples for monthly subscriptions, yearly subscriptions, and one time payments. These are examples, not fixed rules. You can change the products, price IDs, plan names, access rules, and pricing UI to fit your own SaaS model.
What It Includes
The billing layer includes:
- Stripe checkout session creation.
- Server fetched Stripe prices.
- Monthly subscription examples.
- Yearly subscription examples.
- One time payment examples.
- Post checkout dashboard handling.
- Stripe webhook handling through Better Auth.
- Paid access tracking.
- Billing status APIs.
- Public pricing UI.
Price IDs
Create prices in Stripe, then place the IDs in your environment file.
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
STRIPE_PRICE_ID_MONTHLY_SUBSCRIPTION="price_..."
STRIPE_PRICE_ID_YEARLY_SUBSCRIPTION="price_..."
STRIPE_PRICE_ID_ONE_TIME="price_..."The starter uses Stripe Price objects as the source of truth. Use Price IDs that start with price_, not Stripe Product IDs that start with prod_. That means pricing displayed in the UI can come from Stripe instead of being hardcoded in React components.
Important Files
Billing code lives in src/features/billing.
src/features/billing
api
components
constants
hooks
lib
repositories
schemas
types
utilsStart with these files:
src/features/billing/schemas/create-checkout-session.schema.tsfor allowed checkout products.src/features/billing/utils/get-stripe-price-id.utils.tsfor mapping product choices to Stripe price IDs.src/features/billing/repositories/billing-products.repository.tsfor loading Stripe product and price data.src/features/billing/utils/create-checkout-session-url.server.tsfor checkout session creation.src/features/billing/utils/get-kit-access.server.tsfor paid access checks.src/components/product/pricing.section.tsxfor the public pricing section.
Checkout Flow
The default checkout behavior is simple:
- The user chooses a monthly, yearly, or one time product.
- The server checks whether the user already has access.
- If the user already bought that product, the product stays visible as owned and checkout is not created again.
- If the user does not have access yet, the server creates a Stripe checkout session.
- After checkout, Stripe sends the user back to the dashboard.
- Stripe webhooks update the user's access state.
- The dashboard shows what the user owns so they do not rebuy the same product.
Always keep the final access check on the server. The UI can show owned products, but server code decides whether checkout should start.
Webhooks
Local webhook testing uses the Stripe CLI.
stripe login
bun stripe:listenThe listener forwards events to the Better Auth Stripe webhook route.
localhost:3000/api/auth/stripe/webhookFor production, add the production webhook endpoint in Stripe and copy the live webhook signing secret into your production environment.
https://your-domain.com/api/auth/stripe/webhookChanging Pricing
You can build almost any pricing model from the included examples.
For a subscription SaaS:
- Create monthly and yearly recurring prices in Stripe.
- Add their price IDs to env vars.
- Map plan choices to the right Stripe prices.
- Track active subscription access after webhook events.
- Update the pricing section copy and plan cards.
For lifetime access or downloadable products:
- Create one time prices in Stripe.
- Map product choices to one time price IDs.
- Track permanent access after successful checkout.
- Update the pricing section and post purchase experience.
The examples are intentionally small so you can reshape them quickly. You are not locked into the default plan names, access fields, or checkout products.
Access State
The starter stores permanent one time purchase access on the user record and reads active subscription access from Stripe by customer ID. The admin docs show how to present that as one clear access status for operators.
Keep access checks server side. UI state can make the product feel responsive, but route handlers and server utilities decide whether a user can access paid features.
Launch Checklist
Before charging real users:
- Switch Stripe from test mode to live mode.
- Use the live Stripe secret key.
- Create live monthly, yearly, and one time prices.
- Add the live webhook endpoint.
- Copy the live webhook signing secret.
- Run a real checkout with a small internal test product or Stripe test tools.
- Confirm the dashboard return, dashboard access, admin status, and webhook logs.