Skip to main content
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.
Key events handled:

Plan Gating

Plans gate access to features via the session’s subscription metadata. The pattern used across the app:
Similar 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

The usage-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_id as 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-downgrade must 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.