home / skills / tuzzy08 / skills / polar-better-auth

polar-better-auth skill

/polar-better-auth

This skill integrates Polar payments with Better Auth to automate customer syncing, billing usage, and secure access via checkout, portal, and webhooks.

npx playbooks add skill tuzzy08/skills --skill polar-better-auth

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
7.8 KB
---
name: polar-better-auth
description: Deep integration of Polar payments with Better Auth. Use for zero-config billing, automatic customer syncing, usage-based billing, and customer portals when using Better Auth.
---

# Polar + Better Auth Integration

A [Better Auth](https://github.com/better-auth/better-auth) plugin for integrating [Polar](https://polar.sh) payments and subscriptions into your authentication flow.

> **Note:** Fetch complete documentation index at: `https://polar.sh/docs/llms.txt`

## Features

- **Automatic Customer Creation**: Syncs signup users to Polar customers.
- **Sync Deletion**: Deletes Polar customer when user is deleted.
- **Reference System**: Associates purchases with organizations/users.
- **Plugins**: Checkout, Usage (Billing), Webhooks, and Customer Portal.

## Installation

```bash
npm install better-auth @polar-sh/better-auth @polar-sh/sdk
# or
yarn add better-auth @polar-sh/better-auth @polar-sh/sdk
# or
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
```

## Server Configuration

Initialize the Polar client and add the plugin to your Better Auth configuration.

### Environment Variables

```bash
POLAR_ACCESS_TOKEN=polar_oat_...
POLAR_WEBHOOK_SECRET=...
```

### Full Server Setup

```typescript
import { betterAuth } from "better-auth";
import {
  polar,
  checkout,
  portal,
  usage,
  webhooks,
} from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";

const polarClient = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN,
  server: "sandbox", // Use 'sandbox' for testing, defaults to 'production'
});

const auth = betterAuth({
  plugins: [
    polar({
      client: polarClient,
      createCustomerOnSignUp: true, // Auto-create Polar customer
      // Optional: Custom metadata for new customers
      getCustomerCreateParams: ({ user }, request) => ({
        metadata: { source: "better-auth" },
      }),
      use: [
        // 1. Checkout Plugin
        checkout({
          products: [{ productId: "prod_123", slug: "pro" }],
          successUrl: "/success?checkout_id={CHECKOUT_ID}",
          authenticatedUsersOnly: true,
          returnUrl: "https://myapp.com",
          theme: "dark", // 'light' or 'dark'
        }),

        // 2. Customer Portal Plugin
        portal({
          returnUrl: "https://myapp.com",
        }),

        // 3. Usage Billing Plugin
        usage(),

        // 4. Webhooks Plugin
        webhooks({
          secret: process.env.POLAR_WEBHOOK_SECRET,
          onOrderPaid: (payload) => console.log("💸 Order Paid:", payload),
          onCustomerStateChanged: (payload) =>
            console.log("👤 State Changed:", payload),
          onPayload: (payload) => console.log("📨 Other Event:", payload),
        }),
      ],
    }),
  ],
});
```

### Server Options Dictionary

| Option                    | Type       | Description                                               |
| ------------------------- | ---------- | --------------------------------------------------------- |
| `client`                  | `Polar`    | **Required.** The Polar SDK instance.                     |
| `createCustomerOnSignUp`  | `boolean`  | Auto-create Polar customer on signup.                     |
| `use`                     | `Plugin[]` | Array of sub-plugins (checkout, portal, usage, webhooks). |
| `getCustomerCreateParams` | `Function` | Returns metadata/params for interaction.                  |

---

## Client Configuration

```typescript
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth";

export const authClient = createAuthClient({
  plugins: [polarClient()],
});
```

---

## Plugins Summary

### 1. Checkout Plugin

Enables creating checkout sessions directly from the client.

**Configuration:**

- `products`: Array of `{ productId, slug }`. Allows referencing products by slug.
- `successUrl`: Redirect URL after payment. Supports `{CHECKOUT_ID}`.
- `authenticatedUsersOnly`: `true` forces user login before checkout.
- `returnUrl`: Back button URL in checkout.
- `theme`: `light` or `dark`.

**Client Usage:**

```typescript
await authClient.checkout({
  // Option A: Use slug defined in config
  slug: "pro",
  // Option B: Use direct Product ID
  products: ["prod_123"],
  // Optional: Link to an Organization (B2B)
  referenceId: "org_123",
});
```

### 2. Usage Plugin (Billing)

Handles Event Ingestion and Meter retrieval for Usage-Based Billing.

**Client Usage:**

**A. Ingest Events:**

```typescript
await authClient.usage.ingestion({
  event: "ai_generation", // Must match Meter definition in Dashboard
  metadata: {
    tokens: 156,
    model: "gpt-4",
  },
});
```

_Note: Automatically links event to the authenticated user._

**B. List Meters:**

```typescript
const { data: meters } = await authClient.usage.meters.list({
  query: { page: 1, limit: 10 },
});
// Returns: consumed units, credited units, current balance
```

### 3. Portal Plugin

Manages customer access to the hosted Customer Portal.

**Client Usage:**

**A. Open Portal:**

```typescript
// Redirects user to Polar Customer Portal
await authClient.customer.portal();
```

**B. Get Customer State:**
Returns active subscriptions, granted benefits, and meter balances.

```typescript
const { data: state } = await authClient.customer.state();
```

**C. List Resources:**

```typescript
// List Active Subscriptions
const { data: subs } = await authClient.customer.subscriptions.list({
  query: { active: true },
});

// List Orders (Purchases)
const { data: orders } = await authClient.customer.orders.list();

// List Benefits
const { data: benefits } = await authClient.customer.benefits.list();
```

### 4. Webhooks Plugin

The `webhooks` plugin captures incoming events from your Polar organization.

**Setup Steps:**

1.  **Configure Endpoint:** Go to [Polar Dashboard > Webhooks](https://polar.sh/docs/integrate/webhooks/endpoints) and set endpoint to `/api/auth/polar/webhooks`.
2.  **Set Secret:** Add `POLAR_WEBHOOK_SECRET` to your environment variables.
3.  **Add Plugin:**

```typescript
import { polar, webhooks } from "@polar-sh/better-auth";

const auth = betterAuth({
  plugins: [
    polar({
      client: polarClient,
      use: [
        webhooks({
          secret: process.env.POLAR_WEBHOOK_SECRET,
          // Handlers
          onCustomerStateChanged: (payload) => {
            console.log("Customer state changed:", payload);
          },
          onOrderPaid: (payload) => {
            console.log("Order paid:", payload);
          },
          // ... over 25+ handlers available
          onPayload: (payload) => {
            console.log("Catch-all event:", payload);
          },
        }),
      ],
    }),
  ],
});
```

**Supported Events:**

- `onPayload` (Catch-all)
- `onCheckoutCreated`, `onCheckoutUpdated`
- `onOrderCreated`, `onOrderPaid`, `onOrderRefunded`
- `onRefundCreated`, `onRefundUpdated`
- `onSubscriptionCreated`, `onSubscriptionUpdated`
- `onSubscriptionActive`, `onSubscriptionCanceled`, `onSubscriptionRevoked`
- `onProductCreated`, `onProductUpdated`
- `onCustomerCreated`, `onCustomerUpdated`, `onCustomerDeleted`, `onCustomerStateChanged`
- `onBenefitCreated`, `onBenefitGrantCreated`, `onBenefitGrantRevoked`

---

## Common Workflows

### Sync Customer Deletion

To delete the Polar customer when a user is deleted in your database:

```typescript
const auth = betterAuth({
  user: {
    deleteUser: {
      enabled: true,
      afterDelete: async (user) => {
        await polarClient.customers.deleteExternal({
          externalId: user.id,
        });
      },
    },
  },
});
```

### Check Organization Access

Check if a user has access via an Organization subscription (B2B):

```typescript
const orgId = (await authClient.organization.list())?.data?.[0]?.id;

const { data: orders } = await authClient.customer.orders.list({
  query: {
    active: true,
    referenceId: orgId, // Filter by Org ID
  },
});

const hasAccess = orders.length > 0;
```

Overview

This skill integrates Polar payments and subscriptions deeply into Better Auth to provide zero-config billing, automatic customer syncing, usage-based billing, and a hosted customer portal. It wires Polar client, checkout, usage, portal, and webhook plugins into your auth flow so signups map to Polar customers and billing events flow automatically. Setup is minimal and designed for production and sandbox workflows.

How this skill works

The plugin initializes a Polar SDK client inside your Better Auth server configuration and registers sub-plugins for checkout, portal, usage ingestion, and webhooks. It automatically creates and deletes Polar customers when users sign up or are removed, associates purchases with user or organization references, and exposes client methods for creating checkouts, ingesting usage events, and opening the customer portal. Webhook handlers surface Polar events back into your app for custom business logic.

When to use it

  • You need automatic billing and customer syncing with minimal server code.
  • You want usage-based metering and ingestion tied to authenticated users.
  • You need a hosted customer portal for subscription and order management.
  • You want to capture Polar webhooks and respond to order/subscription events.
  • You need to associate purchases with organizations for B2B access control.

Best practices

  • Use sandbox Polar server for development and switch to production in env for deploys.
  • Enable createCustomerOnSignUp to avoid manual customer creation and ensure reliable linkage.
  • Set POLAR_WEBHOOK_SECRET and validate webhooks to secure event handling.
  • Map user or organization IDs to Polar referenceId for clear ownership and billing queries.
  • Keep meters and event names consistent with dashboard definitions for accurate usage billing.

Example use cases

  • Auto-create Polar customers on user signup and sync deletions when users are removed.
  • Trigger a checkout flow from the client for one-off purchases or subscription upgrades.
  • Ingest AI usage events (tokens, requests) to bill usage-based features monthly.
  • Redirect users to the hosted customer portal to manage subscriptions and payment methods.
  • Listen to webhooks to unlock features when an order is paid or a subscription activates.

FAQ

Do I need a Polar account and access token?

Yes. Configure POLAR_ACCESS_TOKEN and choose sandbox or production in the Polar client.

Can usage events be linked to the authenticated user automatically?

Yes. Events ingested through the usage plugin are automatically tied to the authenticated user.