Database
How PayKit stores and syncs billing state locally.
PayKit uses your app's PostgreSQL database to store billing state. Entitlement checks are fast local queries, not provider API calls. No round-trips to Stripe on every check().
Configuration
Pass a pg.Pool instance or a connection string to createPayKit.
import { Pool } from "pg";
export const paykit = createPayKit({
database: new Pool({
connectionString: process.env.DATABASE_URL,
}),
// ...
});// Or pass a connection string directly
export const paykit = createPayKit({
database: process.env.DATABASE_URL,
// ...
});Tables
PayKit creates tables prefixed with paykit_. The key ones are:
| Table | What it stores |
|---|---|
paykit_customer | Customer records and provider ID mappings |
paykit_subscription | Active and past subscriptions |
paykit_entitlement | Current feature access and metered balances |
paykit_product | Products synced from your config |
paykit_feature | Features defined in your plan config |
paykit_invoice | Invoice records synced from the provider |
paykit_payment_method | Saved payment methods |
paykit_webhook_event | Processed webhook events for deduplication |
PayKit owns these tables. Don't write to them directly. Use the PayKit API.
Migrations
paykitjs push applies any pending migrations. Run it on initial setup and whenever you update your plan configuration.
pnpm dlx paykitjs pushbunx paykitjs pushnpx paykitjs pushWhat's synced
PayKit keeps two sources of truth in sync:
From webhooks (provider to database): Subscriptions, invoices, payment methods, and customer-provider ID mappings are synced automatically when provider events arrive.
From your config (code to database): Plans, products, and features are synced from your createPayKit configuration when you run paykitjs push. Each time you change a plan and push, a new version is created. Previous versions are kept so running instances can continue serving existing subscriptions.