Skip to main content
Vibestrap’s payment system is designed for template users first: configure a product, pass a productId, and let the starter handle checkout, webhooks, orders, credits, licenses, and the billing portal.

The quick path

For most products you only touch three places:
  1. Create Products + Prices in Stripe.
  2. Paste the price IDs into .env.local.
  3. Adjust plans and packs in src/config/site.ts.
The checkout call stays small:
await createCheckoutAction({ productId: 'pro' });
await createCheckoutAction({ productId: 'credits_standard' });
For links that should work before login, send buyers to:
/checkout?productId=pro
Unauthenticated buyers are redirected to login, then returned to /checkout and sent to the hosted checkout page automatically.

Config layers

siteConfig.billing is the beginner-facing surface:
billing: {
  enabled: true,
  defaultProductId: 'pro',
  successPath: '/settings/billing?status=success',
  cancelPath: '/pricing?status=canceled',
}
siteConfig.demoPlans defines the four demo SaaS tiers your app sells. siteConfig.payment is the advanced provider layer: Stripe is enabled by default; Creem and NOWPayments can be added when you need alternate rails.

Provider facade

Provider code still lives behind one contract:
interface PaymentProvider {
  createCheckout(opts): Promise<{ id: string; url: string }>;
  createPortalLink(opts): Promise<{ url: string }>;
}
Stripe, Creem, and NOWPayments each normalize webhooks to the same NormalizedEvent shape. The shared handler in src/payment/handlers/core.ts inserts the payment row, grants credits, issues licenses, and records affiliate commission.

Idempotency

Provider retries are expected. The payment table has unique indexes on both invoiceId and sessionId; the handler inserts with conflict protection and then grants entitlements idempotently by paymentId. That means replaying a webhook should not create duplicate payments, credits, licenses, or affiliate commissions.

Provider notes

ProviderUse it forNotes
StripeDefault SaaS checkoutSubscriptions, one-time payments, credit packs, portal.
CreemAlternate card/local railsUseful when Stripe is not ideal for your market.
NOWPaymentsCrypto one-time paymentsNo subscriptions; invoice flow only.

Verify it works

  1. Set Stripe keys and price IDs in .env.local.
  2. Run pnpm dev.
  3. Open /checkout?productId=pro.
  4. Complete a test payment.
  5. Confirm a row appears in payment and the expected credits/license are granted.
  6. Replay the webhook; the database should stay unchanged.