mcpField
P
polar-better-auth

polar-better-auth

github.com/tuzzy08Development089 installsUpdated 40w ago
Repository

About polar-better-auth

A Better Auth plugin for adding Polar payments and subscriptions to authentication flows. It is for developers building applications with billing, and includes checkout, usage, portal, and webhook plugins with user and organization references.

Key features

  1. 01Create Polar customers on signup
  2. 02Delete customers when users are removed
  3. 03Checkout and customer portal plugins
  4. 04Usage event ingestion and meter queries
  5. 05Handle Polar webhook events

Use cases

  1. 01Create Polar customers for new signups
  2. 02Start product checkout from a client
  3. 03Report usage and check meter balances
  4. 04Check access through organization subscriptions

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/tuzzy08/skills --skill polar-better-auth

After installing, type/polar-better-authin Claude Code to confirm it loaded.

Details

From the project README

Polar + Better Auth Integration

A Better Auth plugin for integrating Polar 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

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
POLAR_ACCESS_TOKEN=polar_oat_...
POLAR_WEBHOOK_SECRET=...
Full Server Setup
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
OptionTypeDescription
clientPolarRequired. The Polar SDK instance.
createCustomerOnSignUpbooleanAuto-create Polar customer on signup.
usePlugin[]Array of sub-plugins (checkout, portal, usage, webhooks).
getCustomerCreateParamsFunctionReturns metadata/params for interaction.

Client Configuration

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:

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:

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:

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:

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

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

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

C. List Resources:

// 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 and set endpoint to /api/auth/polar/webhooks.
  2. Set Secret: Add POLAR_WEBHOOK_SECRET to your environment variables.
  3. Add Plugin:
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:

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):

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;

Comments

Markdown supported