> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usepaykit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Polar

> Polar provider for PayKit.

```bash theme={null}
npm install @paykit-sdk/polar
```

## Setup

<Tabs>
  <Tab title="Environment variables">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { polar } from '@paykit-sdk/polar';

    export const paykit = new PayKit(polar());
    ```

    Required env vars:

    ```bash theme={null}
    POLAR_ACCESS_TOKEN=polar_oat_...
    POLAR_WEBHOOK_SECRET=your-webhook-secret
    ```
  </Tab>

  <Tab title="Direct config">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { createPolar } from '@paykit-sdk/polar';

    export const paykit = new PayKit(
      createPolar({ accessToken: 'polar_oat_...', isSandbox: true }),
    );
    ```
  </Tab>
</Tabs>

## How it works

```typescript theme={null}
const checkout = await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'product_abc123',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
});

// Redirect the customer to checkout.payment_url
```

Subscriptions are created through checkouts, not `paykit.subscriptions.create` directly — Polar fires `subscription.created` once the customer completes a subscription checkout.

## Webhooks

Enable these events in your Polar dashboard:

* `order.paid`
* `order.created`
* `customer.created`
* `customer.updated`
* `customer.deleted`
* `subscription.created`
* `subscription.updated`
* `subscription.revoked`
* `refund.created`

```typescript theme={null}
const webhook = paykit.webhooks
  .setup({ webhookSecret: process.env.POLAR_WEBHOOK_SECRET! })
  .on('payment.created', async event => {
    /* order.created */
  })
  .on('payment.succeeded', async event => {
    /* order.paid */
  })
  .on('subscription.created', async event => {
    /* subscription.created */
  })
  .on('subscription.updated', async event => {
    /* subscription.updated */
  })
  .on('subscription.canceled', async event => {
    /* subscription.revoked */
  })
  .on('customer.created', async event => {
    /* customer.created */
  })
  .on('customer.updated', async event => {
    /* customer.updated */
  })
  .on('customer.deleted', async event => {
    /* customer.deleted */
  })
  .on('invoice.generated', async event => {
    /* emitted alongside payment.succeeded on order.paid */
  })
  .on('refund.created', async event => {
    /* refund.created */
  });

await webhook.handle({
  body: rawBody,
  headersAsObject: Object.fromEntries(request.headers),
  fullUrl: request.url,
});
```

### Raw Polar events

Every incoming Polar event is also emitted as a `polar.<event.type>` raw event — typed via the `@polar-sh/sdk` webhook types:

```typescript theme={null}
paykit.webhooks
  .setup({ webhookSecret: process.env.POLAR_WEBHOOK_SECRET! })
  .on('polar.order.paid', async event => {
    // event.data is typed as Polar Order
  })
  .on('polar.subscription.revoked', async event => {
    // event.data is typed as Polar Subscription
  })
  .on('polar.refund.created', async event => {
    // event.data is typed as Polar Refund
  });
```

Polar event to PayKit event mappings:

| Polar event | PayKit events emitted |
| - | - |
| `polar.order.created` | `payment.created` |
| `polar.order.paid` | `payment.succeeded` + `invoice.generated` |
| `polar.customer.created` | `customer.created` |
| `polar.customer.updated` | `customer.updated` |
| `polar.customer.deleted` | `customer.deleted` |
| `polar.subscription.created` | `subscription.created` |
| `polar.subscription.updated` | `subscription.updated` |
| `polar.subscription.revoked` | `subscription.canceled` |
| `polar.refund.created` | `refund.created` |

## provider\_metadata

`provider_metadata` for Polar is passed through directly to the underlying Polar SDK call, giving you full access to any field the Polar API accepts:

```typescript theme={null}
// checkout.provider_metadata is spread into Polar's CheckoutCreate
await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'product_abc123',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  provider_metadata: {
    allowDiscountCodes: true,
    discountId: 'discount_xyz',
  },
});

// customer.provider_metadata is spread into Polar's CustomerCreate / CustomerUpdate
await paykit.customers.create({
  email: 'user@example.com',
  provider_metadata: {
    externalId: 'usr_123',
  },
});

// updateSubscription.provider_metadata is REQUIRED and must be one of these shapes:
await paykit.subscriptions.update('sub_123', {
  provider_metadata: { productId: 'product_456' }, // change product
});
await paykit.subscriptions.update('sub_123', {
  provider_metadata: { discountId: 'discount_789' }, // apply discount
});
await paykit.subscriptions.update('sub_123', {
  provider_metadata: { trialEnd: new Date('2025-12-31') }, // extend trial
});

// refund.provider_metadata is spread into Polar's refund create
await paykit.refunds.create({
  payment_id: 'order_abc123',
  amount: 1000,
  reason: 'customer_request',
});
```

## Escape hatch

`paykit._native` returns the raw Polar SDK client, so you can reach anything PayKit does not map yet without leaving PayKit. Providers without an official SDK return [our HTTP client](/concepts/http-client) instead.

```typescript theme={null}
const checkout = await paykit._native.checkouts.get({
  id: 'chk_123',
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.