Polar

Polar 是一个以开发者为先的支付基础设施。开箱即用,它为支付、结账等提供了许多以开发者为中心的集成。这个插件可以帮助你将 Polar 与 Better Auth 集成,使你的身份验证和支付流程无缝衔接。

此插件由 Polar 团队维护。如有错误、问题或功能请求,请访问 Polar GitHub 仓库

特性

【Features】

  • 结账集成
  • 客户门户
  • 注册时自动创建客户
  • 事件采集与客户计量器,用于灵活的基于使用量的计费
  • 通过签名验证安全处理 Polar 网钩
  • 参考系统用于将购买与组织关联

安装

【Installation】

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

准备

【Preparation】

进入你的 Polar 组织设置,并创建一个组织访问令牌。将其添加到你的环境中。

【Go to your Polar Organization Settings, and create an Organization Access Token. Add it to your environment.】

# .env
POLAR_ACCESS_TOKEN=...

配置 BetterAuth 服务器

【Configuring BetterAuth Server】

Polar 插件附带一些额外插件,这些插件可以为你的技术栈增加功能。

【The Polar plugin comes with a handful additional plugins which adds functionality to your stack.】

  • 结账 - 实现无缝结账集成
  • 门户 - 让你的客户能够管理他们的订单、订阅和已授予的权益
  • 用法 - 一个简单的扩展,用于列出客户计量表和获取基于使用量计费的事件
  • Webhook - 监听相关的 Polar Webhook
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,
    // Use 'sandbox' if you're using the Polar Sandbox environment
    // Remember that access tokens, products, etc. are completely separated between environments.
    // Access tokens obtained in Production are for instance not usable in the Sandbox environment.
    server: 'sandbox'
});

const auth = betterAuth({
    // ... Better Auth config
    plugins: [
        polar({
            client: polarClient,
            createCustomerOnSignUp: true,
            use: [
                checkout({
                    products: [
                        {
                            productId: "123-456-789", // ID of Product from Polar Dashboard
                            slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
                        }
                    ],
                    successUrl: "/success?checkout_id={CHECKOUT_ID}",
                    authenticatedUsersOnly: true
                }),
                portal(),
                usage(),
                webhooks({
                    secret: process.env.POLAR_WEBHOOK_SECRET,
                    onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
                    onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
                    ...  // Over 25 granular webhook handlers
                    onPayload: (payload) => // Catch-all for all events
                })
            ],
        })
    ]
});

配置 BetterAuth 客户端

【Configuring BetterAuth Client】

你将使用 BetterAuth 客户端来操作 Polar 功能。

【You will be using the BetterAuth Client to interact with the Polar functionalities.】

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

// This is all that is needed
// All Polar plugins, etc. should be attached to the server-side BetterAuth config
export const authClient = createAuthClient({
  plugins: [polarClient()],
});

配置选项

【Configuration Options】

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,
  // Use 'sandbox' if you're using the Polar Sandbox environment
  // Remember that access tokens, products, etc. are completely separated between environments.
  // Access tokens obtained in Production are for instance not usable in the Sandbox environment.
  server: "sandbox",
});

const auth = betterAuth({
  // ... Better Auth config
  plugins: [
    polar({
      client: polarClient,
      createCustomerOnSignUp: true,
      getCustomerCreateParams: ({ user }, request) => ({
        metadata: {
          myCustomProperty: 123,
        },
      }),
      use: [
        // This is where you add Polar plugins
      ],
    }),
  ],
});

必需选项

【Required Options】

  • client:Polar SDK 客户端实例

可选项

【Optional Options】

  • createCustomerOnSignUp:当用户注册时自动创建一个 Polar 客户
  • getCustomerCreateParams:用于提供额外客户创建元数据的自定义函数

客户

【Customers】

当启用 createCustomerOnSignUp 时,在 Better-Auth 数据库中添加新用户时,会自动创建一个新的 Polar 客户。

【When createCustomerOnSignUp is enabled, a new Polar Customer is automatically created when a new User is added in the Better-Auth Database.】

所有新客户都会被创建并关联一个 externalId,这是你在数据库中用户的 ID。这使我们能够跳过数据库中任何 Polar 与用户的映射。

【All new customers are created with an associated externalId, which is the ID of your User in the Database. This allows us to skip any Polar to User mapping in your Database.】

结账插件

【Checkout Plugin】

要在你的应用中支持结账,只需将 Checkout 插件传递给 use-property 即可。

【To support checkouts in your app, simply pass the Checkout plugin to the use-property.】

import { polar, checkout } from "@polar-sh/better-auth";

const auth = betterAuth({
    // ... Better Auth config
    plugins: [
        polar({
            ...
            use: [
                checkout({
                    // Optional field - will make it possible to pass a slug to checkout instead of Product ID
                    products: [ { productId: "123-456-789", slug: "pro" } ],
                    // Relative URL to return to when checkout is successfully completed
                    successUrl: "/success?checkout_id={CHECKOUT_ID}",
                    // Whether you want to allow unauthenticated checkout sessions or not
                    authenticatedUsersOnly: true
                })
            ],
        })
    ]
});

启用结账功能后,你可以使用 BetterAuth 客户端上的 checkout-method 初始化结账会话。这将会将用户重定向到产品结账页面。

【When checkouts are enabled, you're able to initialize Checkout Sessions using the checkout-method on the BetterAuth Client. This will redirect the user to the Product Checkout.】

await authClient.checkout({
  // Any Polar Product ID can be passed here
  products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
  // Or, if you setup "products" in the Checkout Config, you can pass the slug
  slug: "pro",
});

结账时将自动使用经过身份验证的用户作为结账客户。电子邮件地址将被“锁定”。

【Checkouts will automatically carry the authenticated User as the customer to the checkout. Email-address will be "locked-in".】

如果 authenticatedUsersOnlyfalse,则可以在没有任何关联客户的情况下触发结账会话。

【If authenticatedUsersOnly is false - then it will be possible to trigger checkout sessions without any associated customer.】

组织支持

【Organization Support】

此插件支持组织插件。如果你在结账的 referenceId 中传递组织 ID,就可以跟踪组织成员所进行的购买。

【This plugin supports the Organization plugin. If you pass the organization ID to the Checkout referenceId, you will be able to keep track of purchases made from organization members.】

const organizationId = (await authClient.organization.list())?.data?.[0]?.id,

await authClient.checkout({
    // Any Polar Product ID can be passed here
    products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
    // Or, if you setup "products" in the Checkout Config, you can pass the slug
    slug: 'pro',
    // Reference ID will be saved as `referenceId` in the metadata of the checkout, order & subscription object
    referenceId: organizationId
});

门户插件

【Portal Plugin】

一个允许客户管理其购买、订单和订阅的插件。

【A plugin which enables customer management of their purchases, orders and subscriptions.】

import { polar, checkout, portal } from "@polar-sh/better-auth";

const auth = betterAuth({
    // ... Better Auth config
    plugins: [
        polar({
            ...
            use: [
                checkout(...),
                portal()
            ],
        })
    ]
});

portal 插件为 BetterAuth 客户端提供了一套客户管理方法,这些方法被归类在 authClient.customer 下。

【The portal-plugin gives the BetterAuth Client a set of customer management methods, scoped under authClient.customer.】

客户门户管理

【Customer Portal Management】

以下方法将把用户重定向到 Polar 客户门户,用户可以在其中查看订单、购买、订阅、权益等。

【The following method will redirect the user to the Polar Customer Portal, where they can see orders, purchases, subscriptions, benefits, etc.】

await authClient.customer.portal();

客户状态

【Customer State】

该门户插件还添加了一个便捷的状态方法,用于获取一般客户状态。

【The portal plugin also adds a convenient state-method for retrieving the general Customer State.】

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

客户状态对象包含:

【The customer state object contains:】

  • 有关客户的所有数据。
  • 他们的有效订阅列表
    • 注意:这不包括由母组织进行的订阅。有关更多信息,请参阅下面的订阅列表方法。
  • 他们享有的福利清单。
  • 他们所有活动表的清单及其当前余额。

因此,通过那一个对象,你就掌握了检查是否应为你的服务提供访问权限所需的所有信息。

【Thus, with that single object, you have all the required information to check if you should provision access to your service or not.】

你可以在 Polar 文档中了解更多关于 Polar 客户状态的信息

福利、订单与订阅

【Benefits, Orders & Subscriptions】

该门户插件为列出与经过身份验证的用户/客户相关的福利、订单和订阅提供了三种便捷的方法。

【The portal plugin adds 3 convenient methods for listing benefits, orders & subscriptions relevant to the authenticated user/customer.】

所有这些方法都使用 Polar CustomerPortal API

好处

【Benefits】

此方法仅列出经过身份验证的用户/客户所获得的权益。

【This method only lists granted benefits for the authenticated user/customer.】

const { data: benefits } = await authClient.customer.benefits.list({
  query: {
    page: 1,
    limit: 10,
  },
});

订单

【Orders】

此方法会列出经过身份验证的用户/客户的订单,如购买和订阅续订。

【This method lists orders like purchases and subscription renewals for the authenticated user/customer.】

const { data: orders } = await authClient.customer.orders.list({
  query: {
    page: 1,
    limit: 10,
    productBillingType: "one_time", // or 'recurring'
  },
});

订阅

【Subscriptions】

此方法列出与经过身份验证的用户/客户相关的订阅。

【This method lists the subscriptions associated with authenticated user/customer.】

const { data: subscriptions } = await authClient.customer.subscriptions.list({
  query: {
    page: 1,
    limit: 10,
    active: true,
  },
});

重要 - 组织支持

这不会返回由母组织为经过身份验证的用户所做的订阅。

【This will not return subscriptions made by a parent organization to the authenticated user.】

但是,你可以向此方法传递 referenceId。这将返回与该 referenceId 相关的所有订阅,而不是与用户相关的订阅。

【However, you can pass a referenceId to this method. This will return all subscriptions associated with that referenceId instead of subscriptions associated with the user.】

所以为了确定用户是否应该有访问权限,需要传递用户的组织 ID,以查看该组织是否有有效的订阅。

【So in order to figure out if a user should have access, pass the user's organization ID to see if there is an active subscription for that organization.】

const organizationId = (await authClient.organization.list())?.data?.[0]?.id,

const { data: subscriptions } = await authClient.customer.orders.list({
    query: {
	    page: 1,
		limit: 10,
		active: true,
        referenceId: organizationId
    },
});

const userShouldHaveAccess = subscriptions.some(
    sub => // Your logic to check subscription product or whatever.
)

使用插件

【Usage Plugin】

一个用于按使用量计费的简单插件。

【A simple plugin for Usage Based Billing.】

import { polar, checkout, portal, usage } from "@polar-sh/better-auth";

const auth = betterAuth({
    // ... Better Auth config
    plugins: [
        polar({
            ...
            use: [
                checkout(...),
                portal(),
                usage()
            ],
        })
    ]
});

事件摄取

【Event Ingestion】

Polar 的按使用量计费完全基于事件接收。从你的应用中接收事件,创建计量器来表示使用情况,并将计量价格添加到产品中以进行收费。

【Polar's Usage Based Billing builds entirely on event ingestion. Ingest events from your application, create Meters to represent that usage, and add metered prices to Products to charge for it.】

在 Polar 文档中了解更多关于基于使用量计费的信息。

const { data: ingested } = await authClient.usage.ingest({
  event: "file-uploads",
  metadata: {
    uploadedFiles: 12,
  },
});

经过验证的用户会自动与接收的事件关联。

【The authenticated user is automatically associated with the ingested event.】

客户表计

【Customer Meters】

列出经过身份验证的用户使用计量器的简单方法,或者我们称之为客户计量器。

【A simple method for listing the authenticated user's Usage Meters, or as we call them, Customer Meters.】

客户表包含有关其在你定义的电表上的所有消耗信息。

【Customer Meter's contains all information about their consumption on your defined meters.】

  • 客户信息
  • 仪表信息
  • 客户表信息
    • 已消耗单元
    • 学分
    • 平衡
const { data: customerMeters } = await authClient.usage.meters.list({
  query: {
    page: 1,
    limit: 10,
  },
});

Webhooks 插件

【Webhooks Plugin】

Webhooks 插件可用于捕获来自你的 Polar 组织的传入事件。

【The Webhooks plugin can be used to capture incoming events from your Polar organization.】

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

const auth = betterAuth({
    // ... Better Auth config
    plugins: [
        polar({
            ...
            use: [
                webhooks({
                    secret: process.env.POLAR_WEBHOOK_SECRET,
                    onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
                    onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
                    ...  // Over 25 granular webhook handlers
                    onPayload: (payload) => // Catch-all for all events
                })
            ],
        })
    ]
});

在你的 Polar 组织设置页面中配置 Webhook 端点。Webhook 端点配置在 /polar/webhooks。

【Configure a Webhook endpoint in your Polar Organization Settings page. Webhook endpoint is configured at /polar/webhooks.】

将密钥添加到你的环境中。

【Add the secret to your environment.】

# .env
POLAR_WEBHOOK_SECRET=...

该插件支持所有 Polar webhook 事件的处理程序:

【The plugin supports handlers for all Polar webhook events:】

  • onPayload - 用于捕获任何传入 Webhook 事件的通用处理器
  • onCheckoutCreated - 当创建结账时触发
  • onCheckoutUpdated - 当结账信息更新时触发
  • onOrderCreated - 当订单创建时触发
  • onOrderPaid - 当订单付款时触发
  • onOrderRefunded - 当订单被退款时触发
  • onRefundCreated - 当退款创建时触发
  • onRefundUpdated - 当退款更新时触发
  • onSubscriptionCreated - 当订阅被创建时触发
  • onSubscriptionUpdated - 当订阅被更新时触发
  • onSubscriptionActive - 当订阅变为活跃时触发
  • onSubscriptionCanceled - 当订阅被取消时触发
  • onSubscriptionRevoked - 当订阅被撤销时触发
  • onSubscriptionUncanceled - 当订阅取消被撤销时触发
  • onProductCreated - 当产品被创建时触发
  • onProductUpdated - 当产品更新时触发
  • onOrganizationUpdated - 当组织更新时触发
  • onBenefitCreated - 当福利被创建时触发
  • onBenefitUpdated - 当福利更新时触发
  • onBenefitGrantCreated - 在福利授予创建时触发
  • onBenefitGrantUpdated - 当福利授予被更新时触发
  • onBenefitGrantRevoked - 当福利授予被撤销时触发
  • onCustomerCreated - 当客户被创建时触发
  • onCustomerUpdated - 当客户信息被更新时触发
  • onCustomerDeleted - 当客户被删除时触发
  • onCustomerStateChanged - 当客户被创建时触发

On this page