billSDK

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:

.env
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:

db/schema.ts
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 push

See Drizzle Adapter for MySQL and SQLite schemas.

Create Billing Instance

Create a file named billing.ts in your lib/ folder:

lib/billing.ts
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:

app/api/billing/[...all]/route.ts
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:

lib/billing-client.ts
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/webhook

Select these events:

  • checkout.session.completed
  • invoice.paid
  • invoice.payment_failed
  • customer.subscription.deleted

For local development, use Stripe CLI to forward webhooks:

stripe listen --forward-to localhost:3000/api/billing/webhook

Done

You're ready to use BillSDK. Continue to Basic Usage to create customers and subscriptions.

On this page