Vibekit

Payments Overview

Architecture, payment provider abstractions, and billing flows.

packages/payments provides a shared checkout, catalog and billing interface. Built-in providers are stripe, lemonsqueezy, polar, dodopayments, paddle, paypal and mock; Creem is not a bundled adapter.

Select and configure billing

Set PAYMENT_PROVIDER to the selected provider and configure its credentials. Stripe is the default real provider. The built-in payment providers also support saved credentials and provider selection in /app/admin/setup. Environment values take precedence.

ProviderAPI credentialWebhook route
StripeSTRIPE_SECRET_KEY/api/webhooks/stripe
Lemon SqueezyLEMONSQUEEZY_API_KEY/api/webhooks/lemonsqueezy
PolarPOLAR_ACCESS_TOKEN/api/webhooks/polar
Dodo PaymentsDODO_PAYMENTS_API_KEY/api/webhooks/dodopayments
PaddlePADDLE_API_KEY, PADDLE_CLIENT_TOKEN/api/webhooks/paddle
PayPalPAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET/api/webhooks/paypal

Webhook signing secrets and any provider-specific store or product settings are separate from API credentials; inspect the selected adapter and configure its matching endpoint.

MOCK_SERVICES=true explicitly enables simulated checkout, including in a production-mode local test server. Outside production, PAYMENT_PROVIDER=mock also works, and no selected provider or Stripe credentials falls back to mock. Production without credentials fails instead of granting simulated access.

Paddle and PayPal

Both default to sandbox. Set PADDLE_ENVIRONMENT or PAYPAL_ENVIRONMENT to production only with matching live credentials and live catalog mappings. Disable MOCK_SERVICES when testing a real sandbox adapter.

Paddle: create products and prices, generate an API key with the required product/price, transaction, subscription and customer-portal permissions, and generate a browser client token for the same environment. Set PADDLE_WEBHOOK_SECRET from a notification destination pointing to /api/webhooks/paddle. Subscribe to subscription lifecycle events, transaction.completed, adjustment.created and adjustment.updated. Configure and approve your site's /checkout/paddle payment-link page in Paddle; it opens Paddle.js with the transaction ID, then returns to the site's billing page. Use test_ client tokens for sandbox and live_ for production. The adapter supports one-time purchases, recurring plans, customer portal sessions, pause, cancel and resume.

PayPal: create a REST app in the matching sandbox or live account, create an active fixed-price monthly or yearly subscription plan, and link its P-... plan ID as the catalog variant's provider price ID. Set PAYPAL_WEBHOOK_ID to the ID of the app's webhook endpoint at /api/webhooks/paypal; subscribe to BILLING.SUBSCRIPTION.* events. This is a webhook ID, not a shared signing secret: the adapter verifies the exact event JSON through PayPal before fetching current subscription state. PayPal supports recurring checkout, pause, cancel and resume here. One-time payments, setup fees, multi-stage trials and a generated customer portal are not implemented.

Entitlements follow subscription lifecycle state for both providers. Recurring payment/refund events do not create an invoice ledger or revoke subscription access by themselves; cancel or suspend the subscription when your refund policy requires access removal. Paddle additionally provisions completed one-time purchases and revokes them after an approved full refund. Browser return URLs never grant access. Before launch, verify a real sandbox checkout, signed webhook delivery, duplicate delivery and the cancellation/refund policy for the selected provider.

One trusted catalog

config.payments.plans seeds the catalog. /app/admin/pricing stores edits in SystemSetting under pricing, overriding the seed. A plan can contain one-time, monthly and yearly variants, with prices in minor currency units. Real-provider prices must link to matching provider product/price IDs, amounts, currencies and intervals.

Checkout accepts plan and variant IDs, verifies team ownership and catalog availability, then delegates to the selected provider. Return URLs must use the site's origin. Existing customer prices are protected from incompatible edits; add a new variant instead. Changing providers also requires reviewing saved catalog mappings.

Extend a payment provider

Implement PaymentAdapter from payments/types in packages/payments/provider/. Add the provider ID in packages/config/providers.ts and a typed resolver/credential definition in packages/payments/provider/selection.ts. Implement getAllPlans as well as checkout, customer portal and verified webhook handling. The shared real-provider checkout validates linked prices against getAllPlans, so a working catalog implementation is required even without the admin pricing editor; optional subscription operations must fail clearly when unsupported. Add an explicit webhook route following the existing provider routes.

Keep pricing validation, same-origin redirects, team authorization, event deduplication and entitlement updates intact. Test failed checkout, invalid signatures, duplicate events, refunds and subscription changes in the existing contract tests before using a real provider. Adding an adapter does not migrate existing customers or subscriptions automatically.

See Stripe Integration, One-Time Purchases and Subscriptions.

When adding a provider ID, also add its credential-guidance entry in packages/config/providers.ts. This supports product validation and setup guidance; a product manifest does not automatically select a runtime adapter.

On this page