.agents/skills/billing-sdk/SKILL.md
Guide for using BillingSDK - open-source React components for pricing tables, subscription management, and billing UI with Dodo Payments.
npx skillsauth add akghosh111/scyra billing-sdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
BillingSDK offers:
Complete project setup with framework configuration and API routes:
npx @billingsdk/cli init
The CLI will:
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
Install directly using shadcn registry:
npx shadcn@latest add @billingsdk/pricing-table-one
npx @billingsdk/cli init
Interactive setup prompts:
npx @billingsdk/cli add <component-name>
Available components:
pricing-table-one - Simple pricing tablepricing-table-two - Feature-rich pricing tablesubscription-management - Manage active subscriptionsusage-meter-circle - Circular usage visualizationcomponents/billingsdk/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 (
<PricingTableOne
plans={plans}
onSelectPlan={handleSelectPlan}
/>
);
}
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 (
<PricingTableTwo
plans={plans}
onSelectPlan={(planId, billingInterval) => {
console.log(`Selected: ${planId}, Interval: ${billingInterval}`);
}}
/>
);
}
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 (
<SubscriptionManagement
subscription={subscription}
onManageBilling={async () => {
// Open customer portal
const response = await fetch('/api/portal', { method: 'POST' });
const { url } = await response.json();
window.location.href = url;
}}
onCancelSubscription={async () => {
if (confirm('Are you sure you want to cancel?')) {
await fetch('/api/subscription/cancel', { method: 'POST' });
}
}}
/>
);
}
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 (
<div className="grid grid-cols-3 gap-4">
<UsageMeterCircle
label="API Calls"
current={8500}
limit={10000}
unit="calls"
/>
<UsageMeterCircle
label="Storage"
current={3.2}
limit={5}
unit="GB"
/>
<UsageMeterCircle
label="Bandwidth"
current={45}
limit={100}
unit="GB"
/>
</div>
);
}
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
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 });
}
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',
};
Components use Tailwind CSS and shadcn/ui patterns. Customize via:
globals.cssExample - Custom colors:
/* globals.css */
@layer base {
:root {
--primary: 220 90% 56%;
--primary-foreground: 0 0% 100%;
}
}
Most components accept standard styling props:
<PricingTableOne
plans={plans}
onSelectPlan={handleSelect}
className="max-w-4xl mx-auto"
containerClassName="gap-8"
cardClassName="border-2"
/>
# .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
Keep product IDs in environment variables for easy staging/production switching.
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);
}
};
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 <SubscriptionManagement subscription={subscription} />;
}
Always use webhooks as source of truth for subscription status, not client-side data.
development
Complete guide for setting up and handling Dodo Payments webhooks for real-time payment event notifications.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
tools
This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.
documentation
Guide for implementing subscription billing with Dodo Payments - trials, upgrades, downgrades, and on-demand billing.