Internal documentation for developers. This page covers Stripe integration patterns, webhook handling, checkout flows, and plan-gating logic. Not intended for end users.
Route Map
Stripe Client Setup
The Stripe client is initialized in@zappway/lib/stripe (or similar). The API key is loaded from STRIPE_SECRET_KEY environment variable.
Checkout Flow
Webhook Handler
The webhook is the source of truth for subscription state. All plan changes must go through the webhook, not the checkout session response.Plan Gating
Plans gate access to features via the session’s subscription metadata. The pattern used across the app:assertXxxPlan helpers exist for other premium features. They read from req.session.organization.subscription (loaded from DB via NextAuth callbacks).
Proration Preview
Before upgrading, the UI calls proration preview to show the user the exact charge:Usage Credits
Theusage-credits endpoint manages prepaid credits for usage-based billing (e.g., message volume). Credits are stored in the DB and decremented on each billable event.
Validate Downgrade
Checks if the org’s current resource usage is compatible with a lower plan:Environment Variables Required
Known Issues / Gotchas
- Webhook idempotency: Stripe can deliver the same event multiple times. All webhook handlers must be idempotent — use
stripe_event_idas a dedup key stored in the DB. - Checkout session expiration: Sessions expire after 24 hours. If the user abandons checkout and returns later, they need a new session.
validate-downgrademust check ALL resource types that have plan limits (agents, skills, datastores, team members, etc.). If a new resource type gets a plan limit, this endpoint must be updated.- Customer Portal URL expires after 5 minutes — generate it on-demand, never cache it.

