Getting Started
Install OpenBilling packages and wire up a shared billing provider interface for Stripe or Dodo.
Getting Started
OpenBilling is split into a provider-neutral core package plus provider adapters.
Install packages
Stripe only:
pnpm add @openbilling/core @openbilling/stripeDodo only:
pnpm add @openbilling/core @openbilling/dodoIf your app switches providers at runtime:
pnpm add @openbilling/core @openbilling/stripe @openbilling/dodoRequired environment variables
Stripe setup:
STRIPE_API_KEY=...
STRIPE_WEBHOOK_SECRET=...Dodo setup:
DODO_API_KEY=...
DODO_WEBHOOK_SECRET=...These are the only environment variables required for package setup itself. Provider-specific checkout catalog IDs can come from config, your database, or app-specific environment variables.
Stripe setup
import { createStripeProvider } from '@openbilling/stripe';
export const billing = createStripeProvider({
apiKey: process.env.STRIPE_API_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
});Stripe checkout currently requires priceId:
const checkout = await billing.createCheckout({
customerEmail: 'demo@example.com',
priceId: 'price_123',
successUrl: 'https://example.com/success',
cancelUrl: 'https://example.com/cancel',
mode: 'subscription',
});Dodo setup
import { createDodoProvider } from '@openbilling/dodo';
export const billing = createDodoProvider({
apiKey: process.env.DODO_API_KEY!,
webhookSecret: process.env.DODO_WEBHOOK_SECRET!,
});Dodo checkout currently requires productId:
const checkout = await billing.createCheckout({
customerEmail: 'demo@example.com',
productId: 'prod_123',
successUrl: 'https://example.com/success',
cancelUrl: 'https://example.com/cancel',
mode: 'subscription',
});Shared provider selection
If your app switches between providers, keep the selection in one place and return the shared BillingProvider interface.
import { Provider, type BillingProvider, type BillingProviderName } from '@openbilling/core';
import { createDodoProvider } from '@openbilling/dodo';
import { createStripeProvider } from '@openbilling/stripe';
export function getBillingProvider(providerName: BillingProviderName): BillingProvider {
switch (providerName) {
case Provider.Stripe:
return createStripeProvider({
apiKey: process.env.STRIPE_API_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
});
case Provider.Dodo:
return createDodoProvider({
apiKey: process.env.DODO_API_KEY!,
webhookSecret: process.env.DODO_WEBHOOK_SECRET!,
});
default:
throw new Error('Unsupported billing provider.');
}
}Common workflow
Once the provider is selected, the app-level workflow stays the same:
const billing = getBillingProvider(process.env.BILLING_PROVIDER!);
const portal = await billing.createPortalLink({
customerId: 'cus_123',
returnUrl: 'https://example.com/account',
});
const event = await billing.verifyWebhook({
payload: rawBody,
headers: Object.fromEntries(request.headers.entries()),
});Next steps
- Read Concepts for the abstraction boundary
- Use Stripe or Dodo Payments for provider-specific setup
- Review Webhooks before wiring production handlers