Skip to content

Billing

LaunchKit Pro ships with a billing provider switch: MOCK (default), Stripe, or Lemon Squeezy. Mock billing records subscriptions and payments in PostgreSQL without external API calls. Real providers create secure checkout sessions and sync status through webhooks.

See Product Editions for Lite / Pro / Enterprise.

Plan catalog (admin)

Admin → Plans (/admin/plans) — full CRUD on billing plans stored in PostgreSQL:

  • Create / edit plans (name, code, price, edition, features, badges)
  • Archive / restore / safe delete (blocked when subscriptions exist)
  • Optional Stripe Price ID and Lemon Variant ID per plan — used only when BILLING_PROVIDER is STRIPE or LEMON_SQUEEZY
  • Default seed is create-only — admin edits are not overwritten on restart

User billing page reads published plans from the database (not hardcoded JSON).

Mock checkout sends planCode / planId from the selected DB plan.

See Billing Settings for provider status UI.

Provider switch

BILLING_PROVIDERBehavior
MOCKDefault. Instant mock checkout; no payment keys required
STRIPEStripe Checkout Session + Stripe webhooks
LEMON_SQUEEZYLemon Squeezy checkout API + Lemon webhooks
bash
curl -s http://localhost:4000/billing/provider-status | jq

Example response:

json
{
  "provider": "MOCK",
  "configured": true,
  "mode": "mock",
  "message": "Mock billing is active. No real payment will be charged."
}

When BILLING_PROVIDER=STRIPE or LEMON_SQUEEZY but required keys are missing, configured is false and checkout returns a friendly configuration error. The app does not fail startup when inactive provider keys are empty.

No payment secrets in frontend or docs

Stripe secret keys, Lemon Squeezy API keys, and webhook signing secrets belong in backend .env only. Never commit them or expose them in API responses or NEXT_PUBLIC_* variables.

Environment variables

See Environment Variables for the full list. Minimum billing block:

env
BILLING_PROVIDER=MOCK
BILLING_SUCCESS_URL=http://localhost:3000/dashboard/billing/success
BILLING_CANCEL_URL=http://localhost:3000/dashboard/billing/cancel
BILLING_SUPPORT_EMAIL=support@launchkitlabs.com

Stripe (when BILLING_PROVIDER=STRIPE):

  • STRIPE_SECRET_KEY
  • STRIPE_WEBHOOK_SECRET
  • STRIPE_PRICE_LITE, STRIPE_PRICE_PRO, STRIPE_PRICE_ENTERPRISE
  • Optional: STRIPE_CUSTOMER_PORTAL_ENABLED=true

Lemon Squeezy (when BILLING_PROVIDER=LEMON_SQUEEZY):

  • LEMON_SQUEEZY_API_KEY
  • LEMON_SQUEEZY_STORE_ID
  • LEMON_SQUEEZY_WEBHOOK_SECRET
  • LEMON_SQUEEZY_VARIANT_LITE, LEMON_SQUEEZY_VARIANT_PRO, LEMON_SQUEEZY_VARIANT_ENTERPRISE

Provider keys are buyer-owned. Map price/variant IDs to your Stripe or Lemon Squeezy products.

API routes

MethodPathAuthDescription
GET/billing/provider-statusPublicActive provider and configuration state
POST/billing/checkoutJWTCreate checkout (mock, Stripe, or Lemon)
GET/billing/currentJWTCurrent plan / edition for workspace
GET/billing/historyJWTPayment history with receipt links when available
POST/billing/cancelJWTCancel subscription (mock local; real providers foundation)
GET/billing/plansPublicPublished plan catalog (sorted by sort order)
POST/billing/webhooks/stripeSignatureStripe webhook ingestion
POST/billing/webhooks/lemon-squeezySignatureLemon Squeezy webhook ingestion
POST/billing/webhooks/mockDevMock webhook ingestion

Admin plan catalog (admin role required):

MethodPathDescription
GET/admin/billing/plansAll plans including drafts and archived
GET/admin/billing/plans/:planIdSingle plan
POST/admin/billing/plansCreate plan (unique slug, non-negative price)
PATCH/admin/billing/plans/:planIdUpdate plan
POST/admin/billing/plans/:planId/archiveArchive (hidden from user billing)
POST/admin/billing/plans/:planId/restoreRestore archived plan
DELETE/admin/billing/plans/:planIdDelete — only when the plan has no subscriptions; otherwise archive
GET/admin/billing/settingsSecret-safe provider settings status
PATCH/admin/billing/settingsRead-only in env-backed deployments (friendly message)

Legacy workspace routes remain for compatibility:

  • GET /workspaces/:workspaceId/billing
  • POST /workspaces/:workspaceId/billing/mock-checkout
  • POST /workspaces/:workspaceId/billing/cancel
  • GET /workspaces/:workspaceId/billing/payments

Plan catalog

Plans live in the database (Plan model) and are managed from Admin Panel → Plans:

  • Name, slug, description, edition (Lite / Pro / Enterprise / Custom)
  • Price (cents), currency, billing interval (one-time / monthly / yearly)
  • Status: Draft (admin-only), Published (visible on user billing), Archived (hidden, kept for history)
  • Badge text, popular/default flags, sort order, features list, limits
  • Optional provider mapping IDs (stripePriceId, lemonVariantId) stored for future use — no credentials

Default Lite / Pro / Enterprise plans are seeded once on first boot. Seeding is create-only: admin edits to price, features, or status are never overwritten on restart. Plans with subscriptions cannot be deleted — archive them instead.

Checkout

Checkout is driven by the DB plan slug (preferred) or the legacy edition key:

bash
curl -s -X POST http://localhost:4000/billing/checkout \
  -H "Authorization: Bearer <accessToken>" \
  -H "Content-Type: application/json" \
  -d '{"planCode":"pro","workspaceId":"<workspaceId>"}' | jq
  • MOCK — completes locally and updates current edition immediately
  • STRIPE / Lemon Squeezy — returns checkoutUrl; redirect the user; status syncs after webhook confirmation
  • Demo user — can use mock checkout when provider is MOCK; blocked from real provider checkout with a friendly message

Current plan and history

bash
curl -s "http://localhost:4000/billing/current?workspaceId=<workspaceId>" \
  -H "Authorization: Bearer <accessToken>" | jq

curl -s "http://localhost:4000/billing/history?workspaceId=<workspaceId>&page=1&limit=20" \
  -H "Authorization: Bearer <accessToken>" | jq

receiptUrl / invoiceUrl appear only when the provider supplies them.

Webhooks

Real billing requires publicly reachable HTTPS webhook endpoints:

  • https://api.yourdomain.com/billing/webhooks/stripe
  • https://api.yourdomain.com/billing/webhooks/lemon-squeezy

Configure signing secrets in your provider dashboard and backend .env. Webhooks verify raw request bodies, update subscriptions/payments idempotently, and sync workspace edition from checkout metadata.

See Payment Webhooks for event types and local testing notes.

Frontend

Dashboard billing: /dashboard/billing

  • Provider status card (Mock / Stripe / Lemon Squeezy, configured vs missing config)
  • Current plan card — static premium dark design (navy/black gradient); does not use theme accent color so text stays readable in all themes
  • Available Plans — published plans loaded live from GET /billing/plans; admin edits to price/name/features show after refresh
  • Billing history with View receipt links when URLs exist
  • Friendly status messages for mock mode, missing config, demo restrictions, and redirect checkout

Admin panel:

  • Admin → Plans — full plan CRUD with status badges, provider mapping indicators, archive/restore/delete confirmations
  • Admin → Billing Settings — secret-safe provider status (configured booleans, checkout URLs, webhook status). Credentials are env-backed and read-only; secrets are never returned by the API
  • Read-only demo admins can view plans but cannot create/edit/archive/delete

No billing secrets in frontend env vars.

Demo behavior

AccountMOCK providerSTRIPE / Lemon provider
Demo userCan mock checkoutBlocked from real checkout; UI stays visible
Test userMock checkout OKProvider-controlled checkout

LaunchKit Pro by LaunchKit Labs — buyer documentation. No secrets in this site.