### Client Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/quickstart.mdx Upgrade to Pro button example. ```typescript import { paykitClient } from "@/lib/paykit-client"; ``` -------------------------------- ### Local Setup Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md Commands to fork, clone, install dependencies, and build the project locally. ```bash git clone https://github.comcom//paykit.git cd paykit pnpm install pnpm build ``` -------------------------------- ### src/routes/paykit.$.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Mount the PayKit request handler for Tanstack Start. ```typescript 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, }, }, }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Initializes PayKit with defined products. ```typescript import { free, pro } from "./products"; export const paykit = createPayKit({ // ... products: [free, pro], }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/plugins.mdx Example of installing and using a plugin with createPayKit. ```typescript import { dash } from "@paykitjs/dash"; export const paykit = createPayKit({ // ... plugins: [dash()], }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Create a PayKit instance. ```typescript import { createPayKit } from "paykitjs"; export const paykit = createPayKit({ // ... }); ``` -------------------------------- ### Lemon Squeezy Provider Setup Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/lemonsqueezy.mdx Example of how to set up the Lemon Squeezy provider with your API key and webhook secret. ```typescript import { lemonSqueezy } from "paykitjs/providers/lemonsqueezy"; const provider = lemonSqueezy({ apiKey: process.env.LEMONSQUEEZY_API_KEY!, webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET!, }); ``` -------------------------------- ### Server Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/quickstart.mdx Call upsertCustomer when the user is created, or before the purchase. Then, pass customerId to subscribe. ```typescript await paykit.upsertCustomer({ id: "user_123", // user or organization id email: "jane@example.com", name: "Jane Doe", }); // then, pass `customerId` to purchase: await paykit.subscribe({ customerId: "user_123", planId: "pro" }) ``` -------------------------------- ### paykitjs init Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/cli.mdx Initializes a new PayKit project with an interactive setup wizard. ```bash paykitjs init ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Configure PayKit with a database connection. ```typescript import { Pool } from "pg"; export const paykit = createPayKit({ // ... database: new Pool({ connectionString: process.env.DATABASE_URL!, }), }); ``` -------------------------------- ### Paddle Provider Setup Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/paddle.mdx Example of how to set up the Paddle provider with your API key and webhook secret. ```typescript import { paddle } from "paykitjs/providers/paddle"; const provider = paddle({ apiKey: process.env.PADDLE_API_KEY!, webhookSecret: process.env.PADDLE_WEBHOOK_SECRET!, }); ``` -------------------------------- ### products.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Defines products and features for PayKit billing. ```typescript 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() ], }); ``` -------------------------------- ### Testing with Vitest Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md Example of how to run specific tests or patterns using Vitest. ```bash vitest /path/to/test-file -t "pattern" ``` -------------------------------- ### Planned setup Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/creem.mdx This code snippet shows the planned setup for the Creem provider using paykitjs. ```typescript import { creem } from "paykitjs/providers/creem"; const provider = creem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, }); ``` -------------------------------- ### Server Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/quickstart.mdx To subscribe a customer to a plan, call subscribe with the plan ID. For paid plans without a saved payment method, it returns a paymentUrl pointing to the provider checkout. ```typescript const result = await paykit.subscribe({ customerId: "user_123", planId: "pro", successUrl: "https://myapp.com/billing/success", cancelUrl: "https://myapp.com/billing", }); if (result.paymentUrl) { // redirect user to provider checkout } ``` -------------------------------- ### Client Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/quickstart.mdx The client's customer is automatically identified. You don't need to pass customerId to purchase on the client. ```typescript // server.ts const paykit = createPayKit({ // ... // client's customer is automatically identified here: identify: async ({ headers }) => { const session = await auth.api.getSession({ headers }); if (!session) return null; return { customerId: session.user.id, // user or organization id email: session.user.email, name: session.user.name, }; }, }); // client.ts const paykitClient = createPayKitClient(); // so you don't need to pass `customerId` to purchase on client: await paykitClient.subscribe({ planId: "pro" }) ``` -------------------------------- ### terminal Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/guides/skills.mdx Install the PayKit skills using the skills CLI. ```bash npx skills add getpaykit/skills ``` -------------------------------- ### server.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Mount the PayKit request handler for other frameworks. ```typescript import { paykit } from "./paykit"; // paykit.handler: (request: Request) => Promise // Mount it on any path that catches /paykit/* app.all("/paykit/*", (req) => paykit.handler(req)); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Configure server-side authentication for client requests. ```typescript 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! }; }, }); ``` -------------------------------- ### Client-side subscription Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/subscriptions.mdx Example of subscribing to a plan on the client. ```tsx const { paymentUrl } = await paykitClient.subscribe({ planId: "pro", successUrl: "/billing/success", cancelUrl: "/billing", }); if (paymentUrl) { window.location.href = paymentUrl; } ``` -------------------------------- ### src/app/paykit/[[...slug]]/route.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/installation.mdx Mount the PayKit request handler for Next.js. ```typescript import { paykitHandler } from "paykitjs/handlers/next"; import { paykit } from "@/lib/paykit"; export const { GET, POST } = paykitHandler(paykit); ``` -------------------------------- ### Server-side subscription Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/subscriptions.mdx Example of subscribing to a plan on the server. ```typescript const result = await paykit.subscribe({ customerId: "user_123", planId: "pro", successUrl: "https://myapp.com/billing/success", cancelUrl: "https://myapp.com/billing", }); if (result.paymentUrl) { // Redirect user to provider checkout } ``` -------------------------------- ### Conventional Commits Example Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md Examples of commit messages following the Conventional Commits specification. ```bash feat(stripe): add webhook handler fix(paykit): correct subscription renewal logic docs: update README setup steps chore: bump pnpm to 11.1.1 ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/get-started/quickstart.mdx Listen to events: To react to billing changes, add the on option to your PayKit instance. The customer.updated event fires after any subscription or entitlement change. ```typescript export const paykit = createPayKit({ // ... on: { // [!code highlight] "customer.updated": ({ payload }) => { // [!code highlight] console.log("billing changed for", payload.customerId); // [!code highlight] }, // [!code highlight] }, // [!code highlight] }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/plugins.mdx Example of configuring authorization for the dashboard plugin. ```typescript import { dash } from "@paykitjs/dash"; export const paykit = createPayKit({ // ... plugins: [ dash({ authorize: async (request) => { const session = await auth.api.getSession({ headers: request.headers, }); if (!session) throw new Error("Not authenticated"); }, }), ], }); ``` -------------------------------- ### products.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/plans-and-features.mdx Example of a default plan within a group. ```typescript export const free = plan({ id: "free", group: "base", default: true, // ... }); ``` -------------------------------- ### Register multiple adapters Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/multi-provider-setup.mdx Configure PayKit with multiple payment providers like Stripe and PayPal. ```typescript import { createPayKit } from "paykitjs"; import { postgresStorage } from "paykitjs/storage/postgres"; import { paypal } from "paykitjs/providers/paypal"; import { stripe } from "paykitjs/providers/stripe"; export const paykit = createPayKit({ storage: postgresStorage({ connectionString: process.env.DATABASE_URL!, }), providers: [ stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, }), paypal({ clientId: process.env.PAYPAL_CLIENT_ID!, clientSecret: process.env.PAYPAL_CLIENT_SECRET!, webhookId: process.env.PAYPAL_WEBHOOK_ID!, }), ], }); ``` -------------------------------- ### Product arrays Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/typescript.mdx Example of passing products to `createPayKit` as an array of values returned by `plan(...)`. ```typescript import { free, pro, ultra } from "./products"; export const paykit = createPayKit({ products: [free, pro, ultra], // ... }); ``` -------------------------------- ### PayKit Configuration Source: https://github.com/getpaykit/paykit/blob/main/README.md Example of how to configure PayKit with Stripe, defining features, plans, and products. ```typescript import { stripe } from "@paykitjs/stripe"; import { createPayKit, feature, plan } from "paykitjs"; const messages = feature({ id: "messages", type: "metered" }); const free = plan({ id: "free", group: "base", default: true, includes: [messages({ limit: 100, reset: "month" })], }); const pro = plan({ id: "pro", group: "base", price: { amount: 19, interval: "month" }, includes: [messages({ limit: 2_000, reset: "month" })], }); export const paykit = createPayKit({ provider: stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, }), database: process.env.DATABASE_URL!, products: [free, pro], }); ``` -------------------------------- ### Choose the provider at the call site Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/multi-provider-setup.mdx Specify the provider to use when creating a checkout session. ```typescript await paykit.checkout.create({ providerId: "paypal", customerId: "cust_abc", amount: 2900, description: "Starter plan", successURL: "https://app.example.com/billing/success", }); ``` -------------------------------- ### PayPal Provider Initialization Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/paypal.mdx Example of how to initialize the PayPal provider with necessary credentials. ```typescript import { paypal } from "paykitjs/providers/paypal"; const provider = paypal({ clientId: process.env.PAYPAL_CLIENT_ID!, clientSecret: process.env.PAYPAL_CLIENT_SECRET!, webhookId: process.env.PAYPAL_WEBHOOK_ID!, }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/payment-providers.mdx Example of initializing PayKit with the Stripe provider. ```typescript import { stripe } from "@paykitjs/stripe"; export const paykit = createPayKit({ // ... provider: stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, }), }); ``` -------------------------------- ### paykitjs status Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/cli.mdx Validates the PayKit setup without making changes. ```bash paykitjs status ``` -------------------------------- ### src/lib/paykit-client.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/client.mdx Install the client and create an instance typed to your server paykit export. It uses `import type` to carry server types into the browser without bundling any server code. ```typescript import { createPayKitClient } from "paykitjs/client"; import type { paykit } from "@/server/paykit"; export const paykitClient = createPayKitClient(); ``` -------------------------------- ### Resume subscription Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/subscriptions.mdx Example of resuming a subscription by subscribing to the current active plan when a downgrade is pending. ```typescript // Customer is on "pro" with a pending downgrade to "free" await paykit.subscribe({ customerId: "user_123", planId: "pro" }); // Scheduled downgrade is cleared. Customer stays on "pro". ``` -------------------------------- ### paykitjs status with throw Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/cli.mdx Validates the PayKit setup and exits with code 1 on failures, useful for CI pipelines. ```bash paykitjs status --throw ``` -------------------------------- ### Getting a customer Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/customers.mdx The `getCustomer` function returns the customer along with their active subscriptions and entitlements. `subscriptions` is an array of `{ planId, status, cancelAtPeriodEnd, currentPeriodStart, currentPeriodEnd }`. `entitlements` is a record keyed by feature ID with `{ balance, limit, usage, unlimited, nextResetAt }`. ```typescript const customer = await paykit.getCustomer({ id: "user_123" }); // customer.subscriptions: active subscriptions with planId, status, period dates // customer.entitlements: feature balances keyed by feature ID ``` -------------------------------- ### Compile-time error example Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/typescript.mdx Illustrates a compile-time error when an invalid plan ID is used. ```typescript // Type error: "typo" is not assignable to "free" | "pro" | "ultra" await paykit.subscribe({ customerId: "user_123", planId: "typo" }); ``` -------------------------------- ### Start the attach flow Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/save-a-payment-method.mdx This code snippet initiates the process of attaching a payment method for a customer. ```typescript const result = await paykit.paymentMethod.attach({ providerId: "stripe", customerId: customer.id, returnURL: "https://app.example.com/settings/billing", }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/stripe.mdx Example of configuring PayKit with Stripe, enabling Managed Payments and specifying a preview API version. ```typescript export const paykit = createPayKit({ // ... provider: stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, apiVersion: "2026-03-04.preview", managedPayments: true, }), }); ``` -------------------------------- ### Regression Test Example Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md JSDoc comment format for referencing issue URLs in regression tests. ```typescript /** @see https://github.com/getpaykit/paykit/issues/123 */ it("does not throw when ...", () => { ``` -------------------------------- ### Using internal IDs Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/payment-providers.mdx Example of how the application uses its own IDs for customer and plan, while PayKit handles the mapping to provider-native IDs internally. ```typescript // Your app always uses its own IDs await paykit.subscribe({ customerId: "user_123", planId: "pro" }); // PayKit resolves the Stripe customer and price internally ``` -------------------------------- ### Sync the app customer Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/create-your-first-checkout.mdx Syncs a customer with PayKit, returning the PayKit customer ID. ```typescript const customer = await paykit.customer.sync({ referenceId: "user_123", email: "jane@example.com", }); ``` -------------------------------- ### Architecture Diagram Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-index.mdx Illustrates the flow of information and control between your application, PayKit Core, Provider Adapter, and the External Provider. ```txt Your App -> PayKit Core -> Provider Adapter -> External Provider ``` -------------------------------- ### Create checkout Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/create-your-first-checkout.mdx Creates a checkout session with the specified provider and customer. ```typescript const checkout = await paykit.checkout.create({ providerId: "stripe", customerId: customer.id, amount: 2900, description: "Starter plan", successURL: "https://app.example.com/billing/success", cancelURL: "https://app.example.com/billing/cancel", attachMethod: true, }); ``` -------------------------------- ### React to completion Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/create-your-first-checkout.mdx Handles the 'checkout.completed' event to grant access after a successful checkout. ```typescript const paykit = createPayKit({ // ... on: { "checkout.completed": async ({ customer }) => { await grantStarterAccess(customer.referenceId); }, }, }); ``` -------------------------------- ### paykitjs push with next build Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/cli.mdx Runs paykitjs push and then builds the Next.js application, skipping confirmation prompts. ```bash paykitjs push -y && next build ``` -------------------------------- ### Change scheduled downgrade target Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/subscriptions.mdx Example of changing the scheduled downgrade target when one is already pending. ```typescript // Customer is on "ultra" with a pending downgrade to "free" await paykit.subscribe({ customerId: "user_123", planId: "pro" }); // Scheduled target changes from "free" to "pro". ``` -------------------------------- ### src/app/paykit/[[...slug]]/route.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/handle-webhooks.mdx Mount the PayKit route. ```typescript import { paykit } from "@/lib/paykit"; import { paykitHandler } from "paykitjs/handlers/next"; export const { GET, POST } = paykitHandler(paykit); ``` -------------------------------- ### src/lib/paykit-client.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/typescript.mdx Demonstrates creating a PayKit client SDK that inherits types from the server instance for client-side type safety. ```typescript // Client inherits server types import type { paykit } from "@/server/paykit"; const client = createPayKitClient(); await client.subscribe({ planId: "pro" }); // ✓ type-safe ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/plans-and-features.mdx Pass your products array to createPayKit for type-safe plan and feature IDs. ```typescript import { free, pro, ultra } from "./products"; export const paykit = createPayKit({ // ... products: [free, pro, ultra], }); // Plan and feature IDs are now type-safe: await paykit.subscribe({ customerId: "user_123", planId: "pro" }); // ✓ await paykit.subscribe({ customerId: "user_123", planId: "typo" }); // ✗ type error ``` -------------------------------- ### paykitjs push Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/cli.mdx Applies pending database migrations and syncs plan definitions to the database and payment provider. ```bash paykitjs push ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/typescript.mdx Demonstrates how PayKit infers plan and feature IDs from the products configuration, providing compile-time safety for method calls. ```typescript // Types are inferred from your products export const paykit = createPayKit({ products: [free, pro, ultra], // ... }); // planId only accepts "free" | "pro" | "ultra" await paykit.subscribe({ customerId: "user_123", planId: "pro" }); // featureId only accepts "messages" | "pro_models" | "priority_support" await paykit.check({ customerId: "user_123", featureId: "messages" }); ``` -------------------------------- ### Listing customers Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/customers.mdx The `listCustomers` function returns a paginated list. You can filter by plan to find everyone on a specific tier. ```typescript const { data, total, hasMore } = await paykit.listCustomers({ limit: 50, offset: 0, planIds: ["pro", "ultra"], }); ``` -------------------------------- ### Subscribe to a paid plan (Server) Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Initiates a subscription to a paid plan. If a payment URL is returned, the user is redirected to complete the checkout process. ```typescript const result = await paykit.subscribe({ customerId: "user_123", planId: "pro", successUrl: "https://myapp.com/billing/success", cancelUrl: "https://myapp.com/billing", }); if (result.paymentUrl) { return Response.redirect(result.paymentUrl); } ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/database.mdx Or pass a connection string directly to createPayKit. ```typescript // Or pass a connection string directly export const paykit = createPayKit({ database: process.env.DATABASE_URL, // ... }); ``` -------------------------------- ### Creating customers Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/customers.mdx The `upsertCustomer` function creates a customer if they don't exist, or updates their details if they do. If a default plan is configured, newly created customers are automatically subscribed to it. ```typescript await paykit.upsertCustomer({ id: "user_123", email: "jane@example.com", name: "Jane Doe", }); ``` -------------------------------- ### Create a customer (Client) Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Sets up the `identify` function on the PayKit instance for automatic customer creation on the client-side. It retrieves user session information to identify the customer. ```typescript 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, }; }, }); ``` -------------------------------- ### Development Commands Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md Common commands for development, including watch mode, type checking, linting, and formatting. ```bash pnpm dev # Watch mode (Turbo) pnpm typecheck # tsc --build pnpm lint # oxlint --deny-warnings pnpm lint:fix # Auto-fix lint issues pnpm format # oxfmt pnpm format:check # Check formatting without writing ``` -------------------------------- ### Custom base URL Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/client.mdx If you changed `basePath` on your server instance, pass the PayKit root URL to the client. ```ts export const paykitClient = createPayKitClient({ baseURL: "/custom", }); ``` -------------------------------- ### Define plans Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Defines features and plans for subscription tiers, including a free tier and a paid 'Pro' tier with metered features and boolean features. ```typescript 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: 2_000, reset: "month" }), proModels(), ], }); ``` ```typescript import { free, pro } from "./products"; export const paykit = createPayKit({ // ... products: [free, pro], }); ``` -------------------------------- ### products.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/plans-and-features.mdx Define plans with IDs, names, groups, pricing, and included features. ```typescript 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: 2_000, reset: "month" }), proModels(), ], }); export const ultra = plan({ id: "ultra", name: "Ultra", group: "base", price: { amount: 49, interval: "month" }, includes: [ messages({ limit: 10_000, reset: "month" }), proModels(), prioritySupport(), ], }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/database.mdx Pass a pg.Pool instance to createPayKit. ```typescript import { Pool } from "pg"; export const paykit = createPayKit({ database: new Pool({ connectionString: process.env.DATABASE_URL, }), // ... }); ``` -------------------------------- ### Pick a method Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/charge-a-saved-payment-method.mdx Selects the default payment method or the first available one. ```typescript const method = methods.find((item) => item.isDefault) ?? methods[0]; if (!method) { throw new Error("No saved payment method"); } ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/customers.mdx The `identify` option on `createPayKit` resolves the authenticated customer from the incoming request. `identify` is required if you're using the client SDK or the built-in HTTP router. If it returns `null`, the request is treated as unauthenticated and rejected. ```typescript export const paykit = createPayKit({ // ... identify: async (request) => { const session = await auth.api.getSession({ headers: request.headers }); if (!session) return null; return { customerId: session.user.id, email: session.user.email, name: session.user.name, }; }, }); ``` -------------------------------- ### Subscribe from a React component Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/client.mdx Subscribe from a React component ```tsx // Subscribe from a React component ``` -------------------------------- ### Upgrade Subscription Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Upgrades a customer to a higher-priced plan immediately. ```typescript await paykit.subscribe({ customerId: "user_123", planId: "pro", // moving up from free }); // subscription is active immediately ``` -------------------------------- ### Server Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/polar.mdx Generate a customer portal URL on the server. ```typescript const { url } = await paykit.customerPortal({ customerId: "user_123", returnUrl: "https://myapp.com/billing", }); // redirect the user to `url` ``` -------------------------------- ### Resume Subscription Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Resumes a subscription after cancellation by subscribing the customer back to their current paid plan. ```typescript await paykit.subscribe({ customerId: "user_123", planId: "pro", // resume: clears the scheduled downgrade }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/stripe.mdx Configure Stripe for PayKit, set up webhooks, sync products, and use the customer portal. ```typescript import { stripe } from "@paykitjs/stripe"; import { createPayKit } from "paykitjs"; export const paykit = createPayKit({ // ... provider: stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, }), }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/stripe.mdx Configure Stripe for PayKit, set up webhooks, sync products, and use the customer portal. ```typescript export const paykit = createPayKit({ // ... testing: { enabled: true, }, }); ``` -------------------------------- ### paykit.ts Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/stripe.mdx Configure Stripe for PayKit, set up webhooks, sync products, and use the customer portal. ```typescript export const paykit = createPayKit({ // ... provider: stripe({ secretKey: process.env.STRIPE_SECRET_KEY!, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, apiVersion: "2026-03-04.preview", }), }); ``` -------------------------------- ### Practical pattern for checking and reporting usage Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/entitlements.mdx A pattern to check entitlements before acting and report usage only after the action succeeds. ```typescript export async function POST(request: Request) { const { allowed } = await paykit.check({ customerId: userId, featureId: "messages", }); if (!allowed) { return Response.json({ error: "Usage limit reached" }, { status: 403 }); } const response = await generateChatResponse(input); await paykit.report({ customerId: userId, featureId: "messages", amount: 1, }); return Response.json(response); } ``` -------------------------------- ### Client Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/polar.mdx Redirect the user to the customer portal from the client. ```typescript const { url } = await paykitClient.customerPortal({ returnUrl: window.location.href, }); window.location.href = url; ``` -------------------------------- ### Inferring plan and feature IDs Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/concepts/typescript.mdx Shows how to use the `$infer` helper to expose inferred union types for plan and feature IDs. ```typescript type PlanId = typeof paykit.$infer.planId; // => "free" | "pro" | "ultra" type FeatureId = typeof paykit.$infer.featureId; // => "messages" | "pro_models" | "priority_support" ``` -------------------------------- ### Listen to Billing Changes Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Adds event handlers to the PayKit instance to react to billing changes, such as customer subscription updates. ```typescript export const paykit = createPayKit({ // ... on: { "customer.updated": ({ payload }) => { console.log("billing changed for", payload.customerId); console.log("subscriptions:", payload.subscriptions); // sync to your own data layer, invalidate caches, etc. }, }, }); ``` -------------------------------- ### Register event handlers Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/handle-webhooks.mdx Register event handlers for PayKit events. ```typescript const paykit = createPayKit({ // ... on: { "payment_method.attached": async ({ customer }) => { await markBillingReady(customer.referenceId); }, "charge.failed": async ({ customer, error }) => { await notifyBillingFailure(customer.referenceId, error); }, }, }); ``` -------------------------------- ### Changesets Command Source: https://github.com/getpaykit/paykit/blob/main/CONTRIBUTING.md Command to generate a changeset file after making changes to packages. ```bash pnpm changeset ``` -------------------------------- ### Sandbox mode Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/providers/polar.mdx Configure the Polar adapter to use the sandbox environment. ```typescript provider: polar({ accessToken: process.env.POLAR_ACCESS_TOKEN!, webhookSecret: process.env.POLAR_WEBHOOK_SECRET!, server: "sandbox", }), ``` -------------------------------- ### Load the customer's methods Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/charge-a-saved-payment-method.mdx Retrieves a list of saved payment methods for a given customer and provider. ```typescript const methods = await paykit.paymentMethod.list({ providerId: "stripe", customerId: "cust_abc", }); ``` -------------------------------- ### PayKitProvider Interface Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/drafts/docs-guides/create-a-custom-provider.mdx The interface that custom providers must implement to adhere to the PayKit contract. ```typescript interface PayKitProvider { id: string; upsertCustomer(...): Promise<{ providerCustomerId: string }>; checkout(...): Promise<{ url: string }>; attachPaymentMethod(...): Promise<{ url: string }>; detachPaymentMethod(...): Promise; charge(...): Promise; refund(...): Promise; handleWebhook(...): Promise; } ``` -------------------------------- ### Check Feature Usage Source: https://github.com/getpaykit/paykit/blob/main/apps/web/content/docs/flows/subscription-billing.mdx Checks if a customer can use a specific feature, like pro models. Also demonstrates checking boolean features. ```typescript return Response.json({ error: "Usage limit reached" }, { status: 403 }); } // also check boolean features: const { allowed: canUseProModels } = await paykit.check({ customerId: userId, featureId: "pro_models", }); ```