Installation
Install PayKit, configure your billing instance, and mount the route handler in your app.
Install the package
Let's start by adding PayKit to your project:
pnpm add paykitjsbun add paykitjsnpm install paykitjsIf you're using a separate client and server setup, make sure to install the package in both apps.
Create the PayKit instance
Create a file named paykit.ts anywhere in your app.
Usually you would put it somewhere in src/, src/lib/.
Import createPayKit and export your paykit instance. This is the main server entry point
for all billing.
import { createPayKit } from "paykitjs";
export const paykit = createPayKit({
// ...
});Configure provider
PayKit can operate with different payment providers. Here's an example with Stripe, but you can easily configure another one, like Polar, Creem, and custom!
Install the provider adapter and plug it into your instance.
pnpm add @paykitjs/stripebun add @paykitjs/stripenpm install @paykitjs/stripeimport { stripe } from "@paykitjs/stripe";
export const paykit = createPayKit({
// ...
provider: stripe({
secretKey: process.env.STRIPE_SECRET_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
}),
});Configure database
PayKit needs a database to store billing state, such as subscriptions. You can create a separate database, or simply plug it into the app's own db.
Pass a connection string directly, or pg.Pool.
import { Pool } from "pg";
export const paykit = createPayKit({
// ...
database: new Pool({
connectionString: process.env.DATABASE_URL!,
}),
});It works by creating a few tables prefixed with paykit_. You can learn more here.
Mount request handler
To handle webhooks and client API requests, you need to set up a request handler on your server.
Create a new file or route in your framework's designated catch-all route handler. This route should handle requests for the path /paykit/* (unless you've configured a different base path).
import { paykitHandler } from "paykitjs/handlers/next";
import { paykit } from "@/lib/paykit";
export const { GET, POST } = paykitHandler(paykit);import { createFileRoute } from "@tanstack/react-router";
import { paykit } from "@/lib/paykit";
async function handle({ request }: { request: Request }) {
return paykit.handler(request)
}
export const Route = createFileRoute("/paykit/$")({
server: {
handlers: {
GET: handle,
POST: handle,
},
},
});The core handler is a standard Web API function that takes a Request and
returns a Response. You can use it with any framework that supports the
Web API standard.
import { paykit } from "./paykit";
// paykit.handler: (request: Request) => Promise<Response>
// Mount it on any path that catches /paykit/*
app.all("/paykit/*", (req) => paykit.handler(req));Create client instance
The client-side library helps you interact with the server. PayKit client sdk suitable for almost all modern frameworks, including React.
import { createPayKitClient } from "paykitjs/client";
import type { paykit } from "@/server/paykit";
export const paykitClient = createPayKitClient<typeof paykit>();To use client we also need to authenticate incoming requests on the server, to do this, add the identify option to your server instance.
export const paykit = createPayKit({
// ...
identify: async ({ headers }) => {
const session = await auth.api.getSession({ headers });
if (!session) return null;
return {
customerId: session.user.id,
email: session.user.email,
name: session.user.name,
// Just pass user's data, and Stripe customer gets synced automatically!
};
},
});Define your products
Optionally. PayKit provides a code-first way to create your plans, and a very useful usage billing with track() and report() out of the box.
By defining you products in code don't have to touch Stripe's dashboard at all. It automatically syncs your pricing with your provider, and you unlock a full type-safety with inferred ID types!
import { feature, plan } from "paykitjs";
const messages = feature({ id: "messages", type: "metered" });
const proModels = feature({ id: "pro_models", type: "boolean" });
export const free = plan({
id: "free",
name: "Free",
group: "base",
default: true,
includes: [
messages({ limit: 100, reset: "month" })
],
});
export const pro = plan({
id: "pro",
name: "Pro",
group: "base",
price: { amount: 19, interval: "month" },
includes: [
messages({ limit: 2000, reset: "month" }),
proModels()
],
});Then pass your plans to the main options:
import { free, pro } from "./plans";
export const paykit = createPayKit({
// ...
plans: [free, pro],
});This is an example setup for AI chat app. Read further on how to build your own billing config.
Push changes to DB
PayKit includes a CLI tool to keep your database in sync with your configuration.
pnpm dlx paykitjs pushbunx paykitjs pushnpx paykitjs pushThis applies database migrations and syncs your plan definitions to provider's
products.
Run it once on setup, and every time after you change your products configuration.
For production deployments, see the CLI reference.
You're now ready to use PayKit in your app! 🚀