billSDK

Getting Started

Install and configure BillSDK in 5 minutes

Installation

npm install billsdk

Database 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 push

Configuration

// 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:

  1. Go to Developers → Webhooks
  2. Add endpoint: https://your-domain.com/api/billing/webhook
  3. Select events: checkout.session.completed, invoice.payment_failed
  4. 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

On this page