BillingSDK Integration
Reference: docs.dodopayments.com/developer-resources/billingsdk | billingsdk.com
BillingSDK provides open-source, customizable React components for billing interfaces - pricing tables, subscription management, usage meters, and more.
Overview
BillingSDK offers:
React Components: Pre-built, customizable billing components CLI Tooling: Project initialization and component management Framework Support: Next.js, Express.js, Hono, Fastify, React Payment Provider: Full integration with Dodo Payments Quick Start Options Option 1: New Project (Recommended)
Complete project setup with framework configuration and API routes:
npx @billingsdk/cli init
The CLI will:
Configure your framework (Next.js App Router) Set up Dodo Payments integration Generate API routes for checkout, customers, webhooks Install dependencies Create configuration files Option 2: Add to Existing Project
Add individual components using the CLI:
npx @billingsdk/cli add pricing-table-one npx @billingsdk/cli add subscription-management npx @billingsdk/cli add usage-meter-circle
Option 3: Manual via shadcn/ui
Install directly using shadcn registry:
npx shadcn@latest add @billingsdk/pricing-table-one
CLI Reference Initialize Project npx @billingsdk/cli init
Interactive setup prompts:
Select framework (Next.js, Express.js, Hono, Fastify, React)
Select payment provider (Dodo Payments)
Configure project settings
Add Components
npx @billingsdk/cli add
Available components:
pricing-table-one - Simple pricing table pricing-table-two - Feature-rich pricing table subscription-management - Manage active subscriptions usage-meter-circle - Circular usage visualization More components available... What happens when adding: Downloads component from registry Installs files to components/billingsdk/ Updates project configuration Installs additional dependencies Components Pricing Table One
Simple, clean pricing table for displaying plans.
Installation:
npx @billingsdk/cli add pricing-table-one
or
npx shadcn@latest add @billingsdk/pricing-table-one
Usage:
import { PricingTableOne } from "@/components/billingsdk/pricing-table-one";
const plans = [ { id: 'prod_free', name: 'Free', price: 0, interval: 'month', features: ['5 projects', 'Basic support'], }, { id: 'prod_pro', name: 'Pro', price: 29, interval: 'month', features: ['Unlimited projects', 'Priority support', 'API access'], popular: true, }, { id: 'prod_enterprise', name: 'Enterprise', price: 99, interval: 'month', features: ['Everything in Pro', 'Custom integrations', 'Dedicated support'], }, ];
export function PricingPage() { const handleSelectPlan = async (planId: string) => { // Create checkout session const response = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productId: planId }), });
const { checkoutUrl } = await response.json();
window.location.href = checkoutUrl;
};
return (
Pricing Table Two
Feature-comparison pricing table with toggle for monthly/yearly.
Installation:
npx @billingsdk/cli add pricing-table-two
Usage:
import { PricingTableTwo } from "@/components/billingsdk/pricing-table-two";
const plans = [ { id: 'prod_starter_monthly', yearlyId: 'prod_starter_yearly', name: 'Starter', monthlyPrice: 19, yearlyPrice: 190, features: [ { name: 'Projects', value: '10' }, { name: 'Storage', value: '5 GB' }, { name: 'Support', value: 'Email' }, ], }, { id: 'prod_pro_monthly', yearlyId: 'prod_pro_yearly', name: 'Pro', monthlyPrice: 49, yearlyPrice: 490, popular: true, features: [ { name: 'Projects', value: 'Unlimited' }, { name: 'Storage', value: '50 GB' }, { name: 'Support', value: 'Priority' }, ], }, ];
export function PricingPage() {
return (
Selected: ${planId}, Interval: ${billingInterval});
}}
/>
);
}
Subscription Management
Allow users to view and manage their subscription.
Installation:
npx @billingsdk/cli add subscription-management
Usage:
import { SubscriptionManagement } from "@/components/billingsdk/subscription-management";
export function AccountPage() { const subscription = { plan: 'Pro', status: 'active', currentPeriodEnd: '2025-02-21', amount: 49, interval: 'month', };
return (
Usage Meter
Display usage-based billing metrics.
Installation:
npx @billingsdk/cli add usage-meter-circle
Usage:
import { UsageMeterCircle } from "@/components/billingsdk/usage-meter-circle";
export function UsageDashboard() { return (
Next.js Integration Project Structure (after init) your-project/ ├── app/ │ ├── api/ │ │ ├── checkout/ │ │ │ └── route.ts │ │ ├── portal/ │ │ │ └── route.ts │ │ └── webhooks/ │ │ └── dodo/ │ │ └── route.ts │ └── pricing/ │ └── page.tsx ├── components/ │ └── billingsdk/ │ ├── pricing-table-one.tsx │ └── subscription-management.tsx ├── lib/ │ ├── dodo.ts │ └── billingsdk-config.ts └── .env.local
Generated API Routes
Checkout Route (app/api/checkout/route.ts):
import { NextRequest, NextResponse } from 'next/server'; import { dodo } from '@/lib/dodo';
export async function POST(req: NextRequest) { const { productId, email } = await req.json();
const session = await dodo.checkoutSessions.create({
product_cart: [{ product_id: productId, quantity: 1 }],
customer: { email },
return_url: ${process.env.NEXT_PUBLIC_APP_URL}/success,
});
return NextResponse.json({ checkoutUrl: session.checkout_url }); }
Portal Route (app/api/portal/route.ts):
import { NextRequest, NextResponse } from 'next/server'; import { dodo } from '@/lib/dodo'; import { getSession } from '@/lib/auth';
export async function POST(req: NextRequest) { const session = await getSession();
const portal = await dodo.customers.createPortalSession({
customer_id: session.user.customerId,
return_url: ${process.env.NEXT_PUBLIC_APP_URL}/account,
});
return NextResponse.json({ url: portal.url }); }
Configuration File
lib/billingsdk-config.ts:
export const plans = [ { id: process.env.NEXT_PUBLIC_PLAN_FREE_ID!, name: 'Free', description: 'Perfect for trying out', price: 0, interval: 'month' as const, features: [ '5 projects', '1 GB storage', 'Community support', ], }, { id: process.env.NEXT_PUBLIC_PLAN_PRO_ID!, name: 'Pro', description: 'For professionals', price: 29, interval: 'month' as const, popular: true, features: [ 'Unlimited projects', '50 GB storage', 'Priority support', 'API access', ], }, ];
export const config = { returnUrl: process.env.NEXT_PUBLIC_APP_URL + '/success', portalReturnUrl: process.env.NEXT_PUBLIC_APP_URL + '/account', };
Customization Styling with Tailwind
Components use Tailwind CSS and shadcn/ui patterns. Customize via:
Theme variables in globals.css Direct class overrides on components Component source modification (files are local)
Example - Custom colors:
/ globals.css / @layer base { :root { --primary: 220 90% 56%; --primary-foreground: 0 0% 100%; } }
Component Props
Most components accept standard styling props:
Environment Variables
.env.local
Dodo Payments
DODO_PAYMENTS_API_KEY=sk_live_xxxxx DODO_PAYMENTS_WEBHOOK_SECRET=whsec_xxxxx
Product IDs (from dashboard)
NEXT_PUBLIC_PLAN_FREE_ID=prod_xxxxx NEXT_PUBLIC_PLAN_PRO_ID=prod_xxxxx NEXT_PUBLIC_PLAN_ENTERPRISE_ID=prod_xxxxx
App
NEXT_PUBLIC_APP_URL=https://yoursite.com
Best Practices 1. Use Product IDs from Environment
Keep product IDs in environment variables for easy staging/production switching.
- Handle Loading States
Components should show loading states during checkout:
const [loading, setLoading] = useState(false);
const handleSelect = async (planId: string) => { setLoading(true); try { const response = await fetch('/api/checkout', {...}); const { checkoutUrl } = await response.json(); window.location.href = checkoutUrl; } finally { setLoading(false); } };
- Server-Side Data Fetching
Fetch subscription data server-side when possible:
// app/account/page.tsx import { getSubscription } from '@/lib/subscription';
export default async function AccountPage() { const subscription = await getSubscription();
return
- Implement Webhooks
Always use webhooks as source of truth for subscription status, not client-side data.
Resources BillingSDK Documentation Dodo Payments Integration Component Gallery GitHub Repository