by Astra

PayKit is a plugin that streamlines payment gateway integration for applications, providing a unified API for processing transactions, managing subscriptions, and handling webhooks across multiple providers.
PayKit is an embedded TypeScript billing framework that runs inside your application, stores billing state in your own PostgreSQL database, and provides a unified API for products, subscriptions, entitlements, and metered usage. It targets TypeScript server applications that want to own their billing data without relying on hosted billing platforms. The typical buyer is a SaaS developer integrating Stripe-backed billing into a Next.js, Express, or similar Node.js backend.
api/ - Route definition helpers and method descriptors for the HTTP API surfacecli/ - CLI entrypoint (paykitjs) with init, push, and status commandsclient/ - Browser/frontend client factory that proxies calls to the server APIcore/ - PayKit instance creation, error handling, logging, and option validationcustomer/ - Customer CRUD service and API handlersdatabase/ - Drizzle ORM setup, schema, migrations, and migration utilitiesentitlement/ - Feature entitlement checking and balance reportinghandlers/ - Framework-specific HTTP handlers (Next.js included)invoice/ - Invoice retrieval and normalization servicepayment/ - Payment record servicepayment-method/ - Payment method management serviceproduct/ - Product and plan sync servicesproviders/ - Abstract payment provider interface (implement for Stripe, etc.)subscription/ - Subscription lifecycle service and API handlerstesting/ - Test clock and sandbox utilitiestypes/ - Shared TypeScript types: models, options, schema, events, instanceutilities/ - Internal utility helperswebhook/ - Webhook ingestion and event routingindex.ts - Public re-exports for all consumer-facing symbolsversion.ts - Package version constantnpm install drizzle-orm drizzle-orm/node-postgres pg @better-fetch/fetch commander
npm install --save-dev drizzle-kit @types/pg
If you use the Next.js handler (), no additional framework packages are required beyond itself. No native build steps, pod installs, or Expo prebuild are needed — this is a pure Node.js library.
Spin up an isolated sandbox and run it server-side — no local setup.
Tetrees AI Review of this version
This TypeScript cli / script completed archive review. Structure, dependency manifests, documentation, functional source, and common risk patterns were checked by the Tetrees verification pipeline; runtime phases are stated separately.
Deterministic AVCP artifact review
Pipeline avcp-2026-08-04.1 · SHA-256 c8cfdaa1a55d654f…
This version-scoped review deterministically inspects the submitted archive for structure, dependencies, documentation, functional source, and common malicious or high-risk signals. Build and test phases are reported as passed only after an isolated sandbox audition. It is not a guarantee of perfect security.
Reviewed Aug 4, 2026
Push this product straight into your AI IDE, web builder or cloud IDE.
Connect Tetrees to a compatible AI IDE, list products you own, and request the verified ZIP without exposing seller upload controls.
No reviews yet.
Sign in to join the discussion
Loading discussion…
handlers/next.tsnextCopy source: Place the contents of source/ into packages/paykit/src/ (or any path you prefer) inside your project.
TypeScript config: Ensure moduleResolution is "bundler" or "node16", and module is "ESNext" or "NodeNext". The source uses top-level await and ESM dynamic imports.
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"strict": true
}
}
Path alias (optional): If you want import from "paykitjs" to resolve locally, add to tsconfig.json:
{
"compilerOptions": {
"paths": {
"paykitjs": ["./packages/paykit/src/index.ts"]
}
}
}
Environment variables: Set the following before starting your server:
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
Run migrations: Before first start, apply the bundled schema:
import pg from "pg";
import { migrateDatabase } from "./packages/paykit/src/database";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
await migrateDatabase(pool);
Initialize PayKit: See the Working Examples section below.
createPayKitimport { createPayKit } from "./core/create-paykit";
function createPayKit(options: PayKitOptions): PayKitInstance
The primary factory function. Pass your provider, database connection string or Pool, and product definitions. Returns a PayKitInstance with methods for subscriptions, entitlements, customer management, and webhook handling. Call this once at application startup and export the result.
createPayKitClientimport { createPayKitClient } from "./client";
function createPayKitClient<Instance extends RequiresIdentify>(
options?: PayKitClientOptions
): InferClientAPI<Instance>
Creates a type-safe frontend client that proxies method calls to the server API over HTTP POST. The baseURL should match the server's basePath (default "/paykit"). Use this in React, Vue, or any browser context where you need to call PayKit operations from the client side.
createDatabase / migrateDatabaseimport { createDatabase, migrateDatabase } from "./database";
async function createDatabase(database: Pool): Promise<PayKitDatabase>
async function migrateDatabase(database: Pool): Promise<void>
createDatabase wraps a pg.Pool with Drizzle ORM and the PayKit schema. migrateDatabase runs all pending SQL migrations from the bundled migrations/ folder. Call migrateDatabase during deployment or startup before serving traffic.
getPendingMigrationCountasync function getPendingMigrationCount(database: Pool): Promise<number>
Returns the number of unapplied migrations. Useful in health checks or paykitjs status equivalents to detect a schema drift before it causes runtime errors.
Initialize PayKit in an Express application with a Stripe provider, mount the HTTP handler, and handle incoming webhooks.
import express from "express";
import pg from "pg";
import { createPayKit } from "./packages/paykit/src/index";
import { migrateDatabase } from "./packages/paykit/src/database";
// Assume @paykitjs/stripe exports a `stripe` factory matching PaymentProvider
import { stripe } from "@paykitjs/stripe";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL! });
await migrateDatabase(pool);
export const paykit = createPayKit({
provider: stripe({
secretKey: process.env.STRIPE_SECRET_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
}),
database: process.env.DATABASE_URL!,
products: [], // populate with plan() definitions
});
const app = express();
app.use("/paykit", paykit.handler);
app.listen(3000);
Use the plan and feature schema helpers to define a free tier and a paid tier with metered usage, then pass them to createPayKit.
import { createPayKit } from "./packages/paykit/src/index";
import type { PayKitFeatureDefinition, PayKitPlan } from "./packages/paykit/src/types/schema";
// These helpers are exported from the public paykitjs package
// and match the NormalizedSchema/NormalizedPlan types in types/schema.ts
const messages: PayKitFeatureDefinition = { id: "messages", type: "metered" };
const free: PayKitPlan = {
id: "free",
name: "Free",
group: "base",
default: true,
includes: [{ feature: messages, limit: 100, reset: "month" }],
};
const pro: PayKitPlan = {
id: "pro",
name: "Pro",
group: "base",
price: { amount: 19, interval: "month" },
includes: [{ feature: messages, limit: 2000, reset: "month" }],
};
const paykit = createPayKit({
provider: {} as any, // replace with real provider
database: process.env.DATABASE_URL!,
products: [free, pro],
});
Wire up a type-safe browser client that subscribes a customer and checks their feature entitlement without manually constructing fetch requests.
// client-side file (browser / React component)
import { createPayKitClient } from "./packages/paykit/src/client";
import type { PayKitInstance } from "./packages/paykit/src/types/instance";
const client = createPayKitClient<PayKitInstance>({
baseURL: "/paykit",
});
// Subscribe the current user to the pro plan
await client.subscription.subscribe({
customerId: "cus_123",
planId: "pro",
});
// Check if the user can send messages
const result = await client.entitlement.check({
customerId: "cus_123",
featureId: "messages",
});
if (result.granted) {
console.log("Access granted, balance:", result.balance);
}
import pg from "pg";
import express from "express";
import { getPendingMigrationCount } from "./packages/paykit/src/database";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL! });
const app = express();
app.get("/health", async (_req, res) => {
const pending = await getPendingMigrationCount(pool);
if (pending > 0) {
return res.status(503).json({ status: "degraded", pendingMigrations: pending });
}
return res.json({ status: "ok" });
});
index.ts - Barrel export. All public types and createPayKit are re-exported here; import from this file.version.ts - Single constant for the current package version string.api/define-route.ts - Helper for declaring typed API routes used by handlers.api/methods.ts - Centralized map of all server-side API method descriptors.cli/index.ts - CLI entrypoint (#!/usr/bin/env node); lazily loads subcommands for startup speed.cli/commands/init.ts - paykitjs init — scaffolds config file and selects a plan template.cli/commands/push.ts - paykitjs push — syncs local product definitions to the provider.cli/commands/status.ts - paykitjs status — shows pending migrations and sync state.cli/templates/index.ts - Built-in plan templates (saas-starter, usage-based, empty) for init.cli/utils/ - CLI helpers: framework detection, env reading, config loading, migration runner, telemetry.client/index.ts - createPayKitClient factory using @better-fetch/fetch and a recursive Proxy.core/create-paykit.ts - Core factory — wires provider, database, and products into PayKitInstance.core/errors.ts / core/error-codes.ts - Typed error classes and string error code constants.core/logger.ts - Internal structured logger respecting PayKitLoggingOptions.core/validate-options.ts - Runtime validation of PayKitOptions at startup.customer/ - CustomerService and HTTP API handlers for customer lifecycle.database/index.ts - Drizzle setup, createDatabase, migrateDatabase, getPendingMigrationCount.database/schema.ts - Drizzle table definitions for all PayKit entities.database/migrations/ - Raw SQL migration files applied by migrateDatabase.entitlement/ - EntitlementService exposing check (gate access) and report (record usage).handlers/next.ts - Next.js App Router / Pages Router compatible request handler.invoice/ - InvoiceService for fetching and normalizing invoices from the provider.payment/ - PaymentService for recording and querying payments.payment-method/ - PaymentMethodService for attaching and listing payment methods.product/product-sync.service.ts - Syncs local plan definitions to the provider (Stripe Products/Prices).product/product.service.ts - Local product/plan queries against the database.providers/provider.ts - Abstract PaymentProvider interface all provider packages implement.subscription/ - SubscriptionService and API handlers for subscribe, cancel, and portal flows.testing/ - TestingService and API for test clock manipulation in sandbox environments.types/ - Pure TypeScript type definitions: models.ts (DB rows), options.ts, schema.ts (plan DSL), instance.ts (method signatures), events.ts, plugin.ts.utilities/ - Internal utility functions shared across services.webhook/ - Webhook ingestion, signature verification, and event dispatch to services.await and dynamic import(); your package.json must include "type": "module" or you must compile to ESM. Fix: set "type": "module" and "module": "NodeNext" in tsconfig.DATABASE_URL at migration time: migrateDatabase reads the migrations folder relative to the compiled output using import.meta.url; if the file is moved or bundled, the path breaks. Fix: keep the database/migrations/ directory adjacent to the compiled database/index.js and do not inline it into a bundle.pg Pool vs connection string: createDatabase and migrateDatabase both accept a pg.Pool, not a raw string. Fix: construct a new pg.Pool({ connectionString }) and pass that, not the URL directly.createPayKit expects a concrete PaymentProvider implementation. The core source does not ship a provider. Fix: install @paykitjs/stripe (or another provider package) separately.@better-fetch/fetch peer: The client uses createFetch from @better-fetch/fetch. If your bundler or Node version does not resolve it, install it explicitly: npm install @better-fetch/fetch.TestingService and PayKitAdvanceTestClockInput only work when the provider is in test mode. Calling them against a live Stripe key throws. Fix: guard with process.env.NODE_ENV !== "production" or a dedicated PayKitTestingOptions.enabled flag.I have dropped the PayKit billing framework source into `source/` in my project.
I also have `source/USAGE.md` as the integration reference.
The upstream package is `user@example.com`.
Please integrate PayKit into my existing project step by step:
1. Read `source/index.ts` for all public exports and `source/USAGE.md` for setup instructions.
2. Install all required dependencies listed in USAGE.md.
3. Configure `tsconfig.json` for ESM / NodeNext as described in USAGE.md.
4. Create a `paykit.ts` (or `paykit.config.ts`) file at the project root that calls `createPayKit`
with my provider, database pool, and an initial set of plans using the plan/feature types
from `source/types/schema.ts`.
5. Add a `migrateDatabase` call to my server startup script using `source/database/index.ts`.
6. Mount the PayKit HTTP handler at `/paykit` in my existing server framework.
7. Create a `paykitClient.ts` for the frontend using `createPayKitClient` from `source/client/index.ts`.
8. Add a `/health` endpoint that uses `getPendingMigrationCount` to report migration status.
9. Show me where each file was modified and why.
Use only the real exports visible in `source/index.ts` and `source/USAGE.md`. Do not invent APIs.
PayKit is released under the MIT License (see source/LICENSE or the GitHub repository). Upstream package: paykitjs by the PayKit authors. Project home: paykit.sh.
This product’s unique contribution is clean room implementation of valuable code block into AVCP compatible standard.
The full install guide and integration prompts unlock after purchase.
eCommerce, Marketplace & POS Systems
Free