Payments and billing
Configure Stripe, Creem, or Waffo checkout, webhook verification, normalized persistence, entitlements, and customer management.
Stable payment contract
Payment adapters expose checkout creation, customer management where supported, raw-body webhook verification, and a stable event identity and type.
Product pages never parse provider payloads directly.
Select a provider
VITE_PAYMENT_PROVIDER=stripeUse creem or waffo for the other adapters. Leaving the value empty disables paid checkout while preserving the pricing UI.
Price configuration
src/config/website.ts defines free, subscription, and lifetime plans. Provider-specific price IDs come from environment variables and are matched to monthly, yearly, or one-time price entries.
Checkout
POST /api/billing/checkout requires a session, resolves the configured plan and price, then returns the adapter checkout URL.
Success and cancellation URLs return to the product domain.
Webhooks
Provider webhooks must be verified using the raw request body. The billing normalization layer converts vendor data into stable fields:
- provider and event ID;
- customer and subscription ID;
- user identity;
- payment type and status;
- current period boundaries;
- currency and amount.
A unique webhook identity protects persistence from repeated delivery.
Entitlements and history
The billing domain exposes active entitlement lookup and user payment history. The dashboard and billing page consume those operations instead of interpreting vendor states.
Customer portal
Stripe and Creem can return a provider-managed customer portal. Waffo has a different self-service model; unsupported behavior is reported explicitly instead of returning a fake URL.
Production acceptance
- Complete a provider test purchase.
- Confirm webhook verification and idempotency.
- Reload billing state from a second session.
- Open the customer portal where supported.
- Cancel and resume a subscription if the provider supports it.
- Complete a lifetime purchase and verify entitlement behavior.