Skip to content
Documentation
Billing

Checkout, subscriptions and webhooks

Follow a subscription from purchase to synchronized access, cancellation and safe deletion.

View Markdown

Start from Settings → Billing

Owners/admins select the workspace, choose monthly or yearly pricing and start Checkout. Members see a permission explanation. The server targets the explicit workspace from the form and verifies current access, preventing a stale tab from purchasing for a newly selected workspace silently.

Checkout purchases one recurring price with quantity one. An existing subscriber uses Manage subscription to open Stripe Customer Portal for payment methods, invoices, plan changes and cancellation. Do not create a second subscription to implement a plan change.

Subscription lifecycle

State/transitionApplication behavior
No subscriptionFree/unsubscribed; core auth/settings remain available
Checkout pendingRequest identity prevents duplicate checkout creation; deletion is guarded
Recognized active/trialingWorkspace receives paid entitlement
Scheduled cancellationPaid while still active through the paid period
Past due/unpaid/incomplete/pausedNo app-added paid grace period
Canceled or expired incompleteNo entitlement; deletion may proceed after reconciliation

Upgrades use Stripe's immediate prorated invoice and confirmation. Decreases in amount or shorter intervals are scheduled for period end through the configured portal behavior. Review the real portal confirmation before submitting a change; the app does not implement a separate customer credit-adjustment panel.

How webhooks reach local development

Billing webhook handling lives in hosted Supabase Edge Functions. The endpoint is reachable before Next.js is deployed, so local test billing does not require ngrok or a manually exposed localhost webhook. Kickstart installs the mode-specific endpoint, function configuration, portal and reconciliation resources.

lib/billing/engine.mjs and lib/billing/server.ts coordinate provider state and database synchronization. Incoming events must pass signature verification. Events can be duplicated or arrive out of order, so the engine uses durable account mapping, synchronization leases and idempotent state handling rather than trusting arrival order.

The hosted source includes supabase/functions/stripe-billing. Keep test and live function/secrets separate according to installer configuration. Never send the setup environment wholesale into a function.

Verify a test purchase

Use a dedicated authorized test account/workspace and the configured Stripe test environment:

  1. Record the selected workspace and its initial unsubscribed state without exposing keys.
  2. Start a plan purchase from Billing, confirm the hosted Stripe screen is in test mode, and use Stripe's documented test payment method.
  3. Complete Checkout and return to the app. Wait for synchronization or use Refresh status.
  4. Confirm the correct workspace shows the recognized subscription and that another unpaid workspace remains unpaid.
  5. Open Manage subscription and inspect the corresponding portal subscription.

Use Stripe's current testing guide for test payment details and failure scenarios. Do not enter real payment information in test documentation or represent a mocked Checkout URL as a completed payment.

Exercise failure, changes and cancellation

Run a plan change through Portal and verify the effective timing and invoice result. Test payment failure using the documented sandbox flow and inspect the resulting subscription state. The absence of paid entitlement must match synchronized state, not the last optimistic UI selection.

Schedule cancellation and confirm access remains while the subscription is still active. Once the paid period ends and the terminal state is synchronized, verify the tag and paid operation change. Use provider test tools where appropriate, but record whether the event originated from Stripe or from a fixture.

Replay and reconcile

Webhook replay must not duplicate customers, subscriptions or entitlement effects. Test an older event after a newer one and confirm the final state agrees with Stripe. If local subscription cache data is unavailable, durable customer/subscription lookup information supports recovery; do not erase that mapping while cleaning UI rows.

Scheduled reconciliation and Refresh status help recover missed events. Inspect the configured mode and account before diagnosing a stale cache. A valid signature probe proves rejection/acceptance logic, not actual Stripe event delivery. A Checkout success query parameter is never sufficient proof of payment.

Deletion guards

Pending Checkout and nonterminal subscriptions block workspace deletion. Account deletion also checks any workspaces it would delete. Cancel first, wait until the paid period ends, refresh the subscription and retry the explicit deletion flow. Transferred/shared workspaces and their subscriptions remain intact.

Stripe financial records are not deleted as part of ordinary workspace removal. Durable billing records can survive without their organization link. Decide your actual retention policy for launch; do not promise a total financial-record purge or bypass the guard by deleting private rows.

Diagnose unavailable billing

If the catalog shows disabled controls, inspect whether it is using display-only seed entries or an installed provider catalog. If Portal is unavailable, confirm the selected role, runtime key/mode and installed portal configuration. If the wrong price appears, inspect the owned catalog for multiple active eligible variants.

If webhook creation succeeded but its signing secret was not saved, recover the matching secret from Stripe Workbench; the installer does not create another endpoint blindly. Keep receipts and resource IDs. The troubleshooting guide covers uncertain operations and origin/source guards.

Evidence required before launch

Record actual purchase, plan change, failed payment, cancellation, duplicate/out-of-order events, cache recovery, workspace isolation and test-to-live promotion. A documentation build, fixture suite or provider configuration read does not prove these flows. No new purchase or Stripe-originated event was executed for this manual. Continue with production launch.

Template 0.0.1 · September 2026 source snapshot