polar-better-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Polar + Better Auth Integration

Polar + Better Auth 集成

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
一款Better Auth插件,用于将Polar支付和订阅功能集成到你的认证流程中。
注意: 完整文档索引可通过以下地址获取:
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.
  • 自动创建客户:将注册用户同步为Polar客户。
  • 同步删除:当用户被删除时,同步删除Polar客户。
  • 关联系统:将购买记录与组织/用户关联。
  • 子插件:包含结账、使用量(计费)、Webhook和客户门户插件。

Installation

安装

bash
npm install better-auth @polar-sh/better-auth @polar-sh/sdk
bash
npm install better-auth @polar-sh/better-auth @polar-sh/sdk

or

or

yarn add better-auth @polar-sh/better-auth @polar-sh/sdk
yarn add better-auth @polar-sh/better-auth @polar-sh/sdk

or

or

pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
undefined
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
undefined

Server Configuration

服务器配置

Initialize the Polar client and add the plugin to your Better Auth configuration.
初始化Polar客户端,并将该插件添加到你的Better Auth配置中。

Environment Variables

环境变量

bash
POLAR_ACCESS_TOKEN=polar_oat_...
POLAR_WEBHOOK_SECRET=...
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),
        }),
      ],
    }),
  ],
});
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", // 测试环境使用'sandbox',默认是'production'
});

const auth = betterAuth({
  plugins: [
    polar({
      client: polarClient,
      createCustomerOnSignUp: true, // 注册时自动创建Polar客户
      // 可选:为新客户添加自定义元数据
      getCustomerCreateParams: ({ user }, request) => ({
        metadata: { source: "better-auth" },
      }),
      use: [
        // 1. 结账插件
        checkout({
          products: [{ productId: "prod_123", slug: "pro" }],
          successUrl: "/success?checkout_id={CHECKOUT_ID}",
          authenticatedUsersOnly: true,
          returnUrl: "https://myapp.com",
          theme: "dark", // 'light' 或 'dark'
        }),

        // 2. 客户门户插件
        portal({
          returnUrl: "https://myapp.com",
        }),

        // 3. 使用量计费插件
        usage(),

        // 4. Webhook插件
        webhooks({
          secret: process.env.POLAR_WEBHOOK_SECRET,
          onOrderPaid: (payload) => console.log("💸 订单已支付:", payload),
          onCustomerStateChanged: (payload) =>
            console.log("👤 状态已更改:", payload),
          onPayload: (payload) => console.log("📨 其他事件:", payload),
        }),
      ],
    }),
  ],
});

Server Options Dictionary

服务器选项说明

OptionTypeDescription
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
Polar
必填项。 Polar SDK实例。
createCustomerOnSignUp
boolean
注册时自动创建Polar客户。
use
Plugin[]
子插件数组(结账、门户、使用量、Webhook)。
getCustomerCreateParams
Function
返回交互时使用的元数据/参数。

Client Configuration

客户端配置

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

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

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

1. 结账插件

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",
});
支持直接从客户端创建结账会话。
配置项:
  • products
    :
    { productId, slug }
    数组。允许通过slug引用产品。
  • successUrl
    : 支付成功后的重定向URL。支持
    {CHECKOUT_ID}
    变量。
  • authenticatedUsersOnly
    : 设置为
    true
    时,强制用户先登录再结账。
  • returnUrl
    : 结账页面中返回按钮的跳转URL。
  • theme
    : 主题,可选
    light
    dark
客户端使用示例:
typescript
await authClient.checkout({
  // 方式A:使用配置中定义的slug
  slug: "pro",
  // 方式B:直接使用产品ID
  products: ["prod_123"],
  // 可选:关联到某个组织(B2B场景)
  referenceId: "org_123",
});

2. Usage Plugin (Billing)

2. 使用量计费插件

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
处理事件上报和计量数据查询,用于基于使用量的计费。
客户端使用示例:
A. 上报事件:
typescript
await authClient.usage.ingestion({
  event: "ai_generation", // 必须与Polar后台中定义的计量项一致
  metadata: {
    tokens: 156,
    model: "gpt-4",
  },
});
注意:会自动将事件与当前认证用户关联。
B. 查询计量项:
typescript
const { data: meters } = await authClient.usage.meters.list({
  query: { page: 1, limit: 10 },
});
// 返回结果:已使用量、已充值量、当前余额

3. Portal Plugin

3. 客户门户插件

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();
管理客户对Polar托管的客户门户的访问权限。
客户端使用示例:
A. 打开客户门户:
typescript
// 重定向用户到Polar客户门户
await authClient.customer.portal();
B. 获取客户状态: 返回活跃订阅、已授予的权益以及计量项余额。
typescript
const { data: state } = await authClient.customer.state();
C. 查询资源列表:
typescript
// 查询活跃订阅
const { data: subs } = await authClient.customer.subscriptions.list({
  query: { active: true },
});

// 查询订单(购买记录)
const { data: orders } = await authClient.customer.orders.list();

// 查询权益
const { data: benefits } = await authClient.customer.benefits.list();

4. Webhooks Plugin

4. Webhook插件

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

webhooks
插件用于捕获来自你的Polar组织的入站事件。
设置步骤:
  1. 配置端点: 前往Polar后台 > Webhooks,将端点设置为
    /api/auth/polar/webhooks
  2. 设置密钥:
    POLAR_WEBHOOK_SECRET
    添加到你的环境变量中。
  3. 添加插件:
typescript
import { polar, webhooks } from "@polar-sh/better-auth";

const auth = betterAuth({
  plugins: [
    polar({
      client: polarClient,
      use: [
        webhooks({
          secret: process.env.POLAR_WEBHOOK_SECRET,
          // 事件处理器
          onCustomerStateChanged: (payload) => {
            console.log("客户状态已更改:", payload);
          },
          onOrderPaid: (payload) => {
            console.log("订单已支付:", payload);
          },
          // ... 支持25+种处理器
          onPayload: (payload) => {
            console.log("通用事件捕获:", payload);
          },
        }),
      ],
    }),
  ],
});
支持的事件:
  • onPayload
    (通用捕获)
  • 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,
        });
      },
    },
  },
});
当你的数据库中删除用户时,同步删除对应的Polar客户:
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;
检查用户是否通过组织订阅获得访问权限(B2B场景):
typescript
const orgId = (await authClient.organization.list())?.data?.[0]?.id;

const { data: orders } = await authClient.customer.orders.list({
  query: {
    active: true,
    referenceId: orgId, // 按组织ID筛选
  },
});

const hasAccess = orders.length > 0;