PayKitv0.1 beta

Quickstart

Getting started with PayKit.

This page assumes you have completed the installation steps and have a running PayKit instance with plans defined.

Sync a customer

Before a customer can subscribe, they need to exist in PayKit. Client side syncs them automatically, but on the server side you create them manually.

// call upsertCustomer when the user is created, or before the purchase:

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" })
// 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<typeof paykit>();

// so you don't need to pass `customerId` to purchase on client:
await paykitClient.subscribe({ planId: "pro" })

The customer ID can be your app's own user ID, or an organization ID.

Subscribe to a plan

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.

server.ts
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
}
component.tsx
import { paykitClient } from "@/lib/paykit-client";

<Button
  onClick={async () => {
    const { paymentUrl } = await paykitClient.subscribe({
      planId: "pro",
      successUrl: "/billing/success",
      cancelUrl: "/billing",
    });
    if (paymentUrl) {
      window.location.href = paymentUrl;
    }
  }}
>
  Upgrade to Pro
</Button>

For free plans, the subscription activates immediately with no redirect.

Usage billing

Use check to verify the customer has remaining balance, perform the action, then report to decrement usage. Both are server-side operations.

app/api/chat/route.ts
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);
}

For boolean features like pro_models, check returns allowed without any balance tracking.

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.

paykit.ts
export const paykit = createPayKit({
  // ...
  on: {  
    "customer.updated": ({ payload }) => {  
      console.log("billing changed for", payload.customerId);  
    },  
  },  
});

Next steps

On this page