Getting Started
Install and configure BillSDK in 5 minutes
Installation
npm install billsdkDatabase Schema
Create the billing tables in your schema:
// db/schema.ts
import { jsonb, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const customer = pgTable("customer", {
id: uuid("id").primaryKey().defaultRandom(),
externalId: text("external_id").unique(), // Your user ID
email: text("email").notNull(),
name: text("name"),
providerCustomerId: text("provider_customer_id"), // Stripe customer ID
metadata: jsonb("metadata"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
export const subscription = pgTable("subscription", {
id: uuid("id").primaryKey().defaultRandom(),
customerId: uuid("customer_id")
.references(() => customer.id, { onDelete: "cascade" })
.notNull(),
planCode: text("plan_code").notNull(),
interval: text("interval").default("monthly").notNull(),
status: text("status").default("active").notNull(),
providerSubscriptionId: text("provider_subscription_id"),
providerCheckoutSessionId: text("provider_checkout_session_id"),
currentPeriodStart: timestamp("current_period_start").defaultNow().notNull(),
currentPeriodEnd: timestamp("current_period_end").notNull(),
canceledAt: timestamp("canceled_at"),
cancelAt: timestamp("cancel_at"),
trialStart: timestamp("trial_start"),
trialEnd: timestamp("trial_end"),
metadata: jsonb("metadata"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});Run migrations:
npx drizzle-kit pushConfiguration
// lib/billing.ts
import { billsdk, drizzleAdapter } from "billsdk";
import { stripePayment } from "@billsdk/stripe";
import { db } from "./db";
import * as schema from "./db/schema";
export const billing = billsdk({
database: drizzleAdapter(db, {
schema,
provider: "pg", // or "mysql", "sqlite"
}),
payment: stripePayment({
secretKey: process.env.STRIPE_SECRET_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
}),
features: [
{ code: "export", name: "Export Data" },
{ code: "api_access", name: "API Access" },
],
plans: [
{
code: "free",
name: "Free",
prices: [{ amount: 0, interval: "monthly" }],
features: ["export"],
},
{
code: "pro",
name: "Pro",
prices: [
{ amount: 2000, interval: "monthly" }, // $20/mo (cents)
{ amount: 20000, interval: "yearly" }, // $200/yr
],
features: ["export", "api_access"],
},
],
});Mount the API
// app/api/billing/[...all]/route.ts
import { billingHandler } from "billsdk/next";
import { billing } from "@/lib/billing";
export const { GET, POST } = billingHandler(billing);Stripe Webhook
Configure the webhook in Stripe Dashboard:
- Go to Developers → Webhooks
- Add endpoint:
https://your-domain.com/api/billing/webhook - Select events:
checkout.session.completed,invoice.payment_failed - Copy the signing secret to
STRIPE_WEBHOOK_SECRET
Basic Usage
// Create customer when user signs up
await billing.api.createCustomer({
externalId: user.id,
email: user.email,
name: user.name,
});
// Start subscription (redirects to Stripe Checkout)
const { redirectUrl } = await billing.api.createSubscription({
customerId: user.id, // externalId
planCode: "pro",
interval: "monthly",
successUrl: "https://your-app.com/success",
cancelUrl: "https://your-app.com/pricing",
});
// Redirect user to checkout
redirect(redirectUrl);After payment, Stripe sends a webhook. BillSDK automatically activates the subscription.
Check Feature Access
const { allowed } = await billing.api.checkFeature({
customerId: user.id,
feature: "api_access", // Type-safe
});
if (!allowed) {
return "Upgrade to Pro for API access";
}Environment Variables
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...For local development, use Stripe CLI to forward webhooks:
stripe listen --forward-to localhost:3000/api/billing/webhook