Stackmesa SaaS Starter

Auth · Teams · Stripe billing · Email — wired end to end, built to be deleted.

A Next.js 14 starter where the boring hard parts are finished and tested. Below: the running product, the actual code, and the billing lifecycle replayed exactly as it ran.

79 files · 5,556 lines 72 unit tests 9 email templates 6 signed webhook fixtures TypeScript strict
localhost:3000/settings/billing
Stackmesa billing page — live per-seat subscription

src/app — App Router pages + API routes (17)
page.tsx130 loc
layout.tsx68 loc
page.tsx152 loc
layout.tsx15 loc
page.tsx102 loc
tabs.tsx28 loc
page.tsx215 loc
layout.tsx16 loc
page.tsx100 loc
page.tsx78 loc
page.tsx23 loc
route.ts9 loc
route.ts272 loc
globals.css256 loc
page.tsx133 loc
layout.tsx22 loc
page.tsx144 loc
src/lib/billing — Billing: plans, access, ledger, seats, sync (6)
plans.ts89 loc
seats.ts70 loc
stripe.ts27 loc
subscription.ts72 loc
sync.ts62 loc
webhook-ledger.ts46 loc
src/lib/actions — Server actions: auth, teams, billing (3)
auth.ts159 loc
billing.ts98 loc
teams.ts259 loc
src/lib/teams — Team plumbing (2)
current.ts62 loc
service.ts27 loc
src/lib/email — Email transport (1)
send.ts116 loc
src/emails — React Email templates (10)
_components.tsx73 loc
invite-accepted.tsx25 loc
invite.tsx28 loc
magic-link.tsx19 loc
payment-failed.tsx40 loc
payment-recovered.tsx18 loc
plan-changed.tsx26 loc
subscription-canceled.tsx34 loc
subscription-started.tsx26 loc
welcome.tsx21 loc
src/lib — Core lib: env, auth, guards, validators (7)
auth.config.ts76 loc
auth.ts86 loc
db.ts21 loc
env.ts126 loc
guards.ts53 loc
passwords.ts24 loc
validators.ts84 loc
src/components — Shared components (2)
auto-form.tsx38 loc
flash.tsx20 loc
prisma — Schema + dev seed (2)
schema.prisma199 loc
seed.mjs55 loc
scripts — Webhook harness, fixtures, Stripe bootstrap (8)
invoice-paid.json19 loc
invoice-payment-failed.json20 loc
subscription-canceled-at-period-end.json32 loc
subscription-created.json32 loc
subscription-deleted.json32 loc
subscription-updated-plan-change.json32 loc
stripe-bootstrap.mjs70 loc
webhook-harness.mjs139 loc
tests — Unit suite (vitest) (8)
env.test.ts101 loc
passwords.test.ts27 loc
plans.test.ts57 loc
seats.test.ts41 loc
setup.ts14 loc
subscription.test.ts98 loc
validators.test.ts120 loc
webhook-ledger.test.ts47 loc
docs — Module guides with deletion guides (4)
module-auth.md89 loc
module-billing.md129 loc
module-email.md69 loc
module-teams.md92 loc
./ — Project root (9)
.env.example49 loc
.gitignore14 loc
docker-compose.yml25 loc
middleware.ts24 loc
next.config.mjs12 loc
package.json49 loc
README-setup.md127 loc
tsconfig.json36 loc
vitest.config.ts18 loc


      
the exact commands and responses from the recorded run
Press replay — three deliveries, including a deliberate duplicate.

What you're watching

Failure starts the clock. The first failed invoice stamps a 7-day grace deadline and sends this email to the team's owners — the real one from the run:

The duplicate is refused. The same event id delivered twice returns {"duplicate":true} — the WebhookEvent ledger at work. The download includes the harness and all 6 fixtures, so you can replay this against your own machine with no Stripe account.

In the download

  • Full source — 79 files, 5,556 lines (4,710 of code) — no build-time magic, no private packages
  • Auth three ways: credentials, magic links, Google/GitHub OAuth
  • Team workspaces: invitations, roles, ownership transfer, last-owner protection
  • Stripe subscriptions: idempotent webhooks, per-seat + flat plans, proration, grace + dunning
  • 9 React Email templates with a console transport for keyless dev
  • Webhook harness + 6 signed fixtures, Stripe bootstrap script, dev seed
  • README + 4 module guides, each ending in a deletion guide

Honestly not included: an admin panel, analytics, a component library, or mobile apps. It's a starter — the product on top is yours.

The test suite (72 passing)

SuiteCovers
envenv schema + feature flags15
passwordsbcrypt contract4
plansprice↔plan mapping8
seatsseat quantity + proration6
subscriptionaccess + grace matrix15
validatorsevery input schema18
webhook-ledgeridempotency decisions6

Run them yourself: npm test — plus npm run typecheck (strict, noUncheckedIndexedAccess) both green.