Installation
Learn how to configure BillSDK in your project.
Install the Packages
npm install billsdk @billsdk/stripe@billsdk/stripe is optional if you only need free plans. The Drizzle adapter is included in billsdk.
Set Environment Variables
Create a .env file in the root of your project:
BILLSDK_SECRET=your-secret-key-at-least-32-characters
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...Generate a secret with openssl rand -base64 32. Get Stripe keys from Stripe Dashboard → API Keys and Webhooks.
BILLSDK_SECRET is required. BillSDK will not start without it.
Create Database Schema
Add the billing tables to your Drizzle schema:
import { integer, 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(),
email: text("email").notNull(),
name: text("name"),
providerCustomerId: text("provider_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"),
scheduledPlanCode: text("scheduled_plan_code"),
scheduledInterval: text("scheduled_interval"),
metadata: jsonb("metadata"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
export const payment = pgTable("payment", {
id: uuid("id").primaryKey().defaultRandom(),
customerId: uuid("customer_id").references(() => customer.id, { onDelete: "cascade" }).notNull(),
subscriptionId: uuid("subscription_id"),
type: text("type").notNull(),
status: text("status").default("pending").notNull(),
amount: integer("amount").notNull(),
currency: text("currency").default("usd").notNull(),
providerPaymentId: text("provider_payment_id"),
refundedAmount: integer("refunded_amount"),
metadata: jsonb("metadata"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});Then push to your database:
npx drizzle-kit pushSee Drizzle Adapter for MySQL and SQLite schemas.
Create Billing Instance
Create a file named billing.ts in your lib/ folder:
import { billsdk } from "billsdk";
import { drizzleAdapter } from "billsdk/adapters/drizzle";
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"
}),
secret: process.env.BILLSDK_SECRET!,
trustedOrigins: [process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"],
payment: stripePayment({
secretKey: process.env.STRIPE_SECRET_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
}),
features: [
{ code: "api_access", name: "API Access" },
{ code: "priority_support", name: "Priority Support" },
],
plans: [
{
code: "free",
name: "Free",
prices: [{ amount: 0, interval: "monthly" }],
features: [],
},
{
code: "pro",
name: "Pro",
prices: [
{ amount: 2000, interval: "monthly" },
{ amount: 20000, interval: "yearly" },
],
features: ["api_access", "priority_support"],
},
],
});Prices are in cents. 2000 = $20.00.
Mount Route Handler
Create a catch-all route to handle billing API requests:
import { billingHandler } from "billsdk/next";
import { billing } from "@/lib/billing";
export const { GET, POST } = billingHandler(billing);Create Client Instance
Create a client to interact with the billing API from your frontend:
import { createBillingClient } from "billsdk/react";
export const billingClient = createBillingClient();
export const { useCustomer, useSubscription, usePlans } = billingClient;Configure Stripe Webhook
Add a webhook endpoint in Stripe Dashboard pointing to:
https://your-domain.com/api/billing/webhookSelect these events:
checkout.session.completedinvoice.paidinvoice.payment_failedcustomer.subscription.deleted
For local development, use Stripe CLI to forward webhooks:
stripe listen --forward-to localhost:3000/api/billing/webhookDone
You're ready to use BillSDK. Continue to Basic Usage to create customers and subscriptions.