PayKitv0.1 beta

Polar

Configure Polar for PayKit, set up webhooks, sync products, and use the customer portal.

Polar is a developer-first payment platform. The @paykitjs/polar adapter handles all Polar API interactions, webhook processing, and product syncing.

Installation

pnpm add @paykitjs/polar
bun add @paykitjs/polar
npm install @paykitjs/polar

Configuration

Pass the polar() adapter to createPayKit with your access token and webhook secret.

paykit.ts
import { polar } from "@paykitjs/polar";
import { createPayKit } from "paykitjs";

export const paykit = createPayKit({
  // ...
  provider: polar({
    accessToken: process.env.POLAR_ACCESS_TOKEN!,
    webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
    server: "sandbox", // or "production"
  }),
});

Environment variables

Add these variables to your .env file:

.env
POLAR_ACCESS_TOKEN=polar_oat_...
POLAR_WEBHOOK_SECRET=...
  • POLAR_ACCESS_TOKEN: create one in Polar Settings under Access Tokens. The token needs the following scopes: products:read, products:write, customers:read, customers:write, customer_sessions:write, subscriptions:read, subscriptions:write, checkouts:write, organizations:read, organizations:write.
  • POLAR_WEBHOOK_SECRET: generated when you create a webhook endpoint. See the section below.

Webhook setup

In the Polar Dashboard, go to Settings > Webhooks and create a new endpoint pointing to:

https://your-app.com/paykit/api/webhook/polar

Enable the following events:

  • checkout.created, checkout.updated
  • subscription.created, subscription.updated, subscription.active, subscription.canceled, subscription.uncanceled, subscription.revoked

You can also select all events. PayKit silently ignores any events it doesn't need. After saving, Polar displays the signing secret. Copy it as your POLAR_WEBHOOK_SECRET.

Polar uses the Standard Webhooks specification for signature verification and event deduplication.

Local development

Use the Polar CLI to forward webhook events to your local server:

polar listen http://localhost:3000/paykit/api/webhook/polar

The CLI prints a webhook signing secret at startup. Use that as POLAR_WEBHOOK_SECRET in your local .env.

Install the Polar CLI with curl -fsSL https://polar.sh/install.sh | bash. You'll need to run polar login once to authenticate.

Product syncing

paykitjs push creates and updates Polar products to match your plan definitions. You don't need to touch the Polar Dashboard for product management.

pnpm dlx paykitjs push
bunx paykitjs push
npx paykitjs push

On every push, PayKit automatically:

  • Creates or updates products to match your plans
  • Sets all products to private visibility (only purchasable via PayKit checkout, not the customer portal)
  • Archives orphan products not managed by PayKit
  • Configures your Polar organization settings (multiple subscriptions enabled, portal plan changes disabled)

Run this once on setup, and again every time you change your plans or pricing.

Customer portal

PayKit can open Polar's customer portal so users can view their subscriptions and invoices.

const { url } = await paykit.customerPortal({
  customerId: "user_123",
  returnUrl: "https://myapp.com/billing",
});

// redirect the user to `url`
const { url } = await paykitClient.customerPortal({
  returnUrl: window.location.href,
});

window.location.href = url;

Plan changes are disabled in the portal automatically by PayKit. Subscriptions should be managed through PayKit's API to keep state in sync.

Dedicated account

PayKit requires full ownership of your Polar account. The push command will block if it detects customers on Polar that are not managed by PayKit. Use a dedicated Polar organization for your PayKit integration.

Sandbox mode

Pass server: "sandbox" to test with Polar's sandbox environment. This uses separate sandbox API endpoints and test data.

provider: polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
  webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
  server: "sandbox",
}),

Use a sandbox access token when testing. Sandbox and production are completely isolated in Polar.

On this page