Polar
Polar 是一个面向开发者的支付基础设施。它开箱即用地提供了许多面向开发者的支付、结账等集成功能。本插件帮助你将 Polar 与 Better Auth 集成,使你的身份验证与支付流程无缝衔接。
此插件由 Polar 团队维护。如需报告错误、问题或功能请求, 请访问 Polar GitHub 仓库。
功能特性
- 结账集成
- 客户门户
- 注册时自动创建客户
- 事件摄取和客户计量器,用于灵活的基于使用量的计费
- 通过签名验证安全处理 Polar Webhooks
- 关联购买与组织的参考系统
安装
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
准备工作
前往你的 Polar 组织设置,创建一个组织访问令牌。将其添加到你的环境变量中。
# .env
POLAR_ACCESS_TOKEN=...
配置 BetterAuth 服务器
Polar 插件附带了一些额外的插件,可为您的技术栈添加功能。
- 结账(Checkout)- 启用无缝结账集成
- 门户(Portal)- 让您的客户能够管理他们的订单、订阅和授予的权益
- 用量(Usage)- 用于列出客户计量器和为基于用量的计费(Usage Based Billing)摄取事件的简单扩展
- 网络钩子(Webhooks)- 监听相关的 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,
// 如果您使用的是 Polar 沙盒环境,请使用 'sandbox'
// 请记住,访问令牌、产品等在环境之间是完全隔离的。
// 例如,在生产环境中获取的访问令牌不能在沙盒环境中使用。
server: 'sandbox'
});
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true, // 注册时创建客户
use: [
checkout({
products: [
{
productId: "123-456-789", // 来自 Polar 仪表板的产品的 ID
slug: "pro" // 用于在结账 URL 中方便引用的自定义标识符,例如 /checkout/pro
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}", // 成功 URL
authenticatedUsersOnly: true // 仅限认证用户
}),
portal(), // 门户插件
usage(), // 用量插件
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET, // Webhook 密钥
onCustomerStateChanged: (payload) => // 当与客户相关的任何内容发生变化时触发
onOrderPaid: (payload) => // 当订单支付完成时触发(购买、订阅续费等)
... // 超过 25 个细粒度的 webhook 处理程序
onPayload: (payload) => // 捕获所有事件的通用处理程序
})
],
})
]
});
配置 BetterAuth 客户端
您将使用 BetterAuth 客户端与 Polar 功能进行交互。
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth";
import { organizationClient } from "better-auth/client/plugins";
// 只需这些配置即可
// 所有 Polar 插件等都应附加到服务器端的 BetterAuth 配置中
export const authClient = createAuthClient({
plugins: [polarClient()],
});
配置选项
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,
// 如果使用 Polar 沙盒环境,请使用 'sandbox'
// 请注意,访问令牌、产品等在环境之间是完全隔离的
// 例如,在生产环境中获取的访问令牌不能在沙盒环境中使用
server: "sandbox",
});
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }, request) => ({
metadata: {
myCustomProperty: 123,
},
}),
use: [
// 在此处添加 Polar 插件
],
}),
],
});
必需选项
client
: Polar SDK 客户端实例
可选选项
createCustomerOnSignUp
: 用户注册时自动创建 Polar 客户getCustomerCreateParams
: 自定义函数,用于提供额外的客户创建元数据
客户
当启用 createCustomerOnSignUp
时,每当 Better-Auth 数据库中添加新用户时,系统会自动创建一个新的 Polar 客户。
所有新创建的客户都会关联一个 externalId
,该 ID 对应您在数据库中的用户 ID。这样我们就能避免在您的数据库中进行任何 Polar 到用户的映射操作。
结账插件
要在您的应用中支持结账功能,只需将结账插件传递给 use 属性即可。
import { polar, checkout } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
...
use: [
checkout({
// 可选字段 - 允许使用产品 slug 而非产品 ID 进行结账
products: [ { productId: "123-456-789", slug: "pro" } ],
// 结账成功后的返回 URL
successUrl: "/success?checkout_id={CHECKOUT_ID}",
// 是否仅允许已认证用户进行结账
authenticatedUsersOnly: true
})
],
})
]
});
启用结账功能后,您可以通过 BetterAuth 客户端上的 checkout 方法初始化结账会话。这将把用户重定向到产品结账页面。
await authClient.checkout({
// 可以传入任何 Polar 产品 ID
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// 或者,如果您在结账配置中设置了 "products",可以传入 slug
slug: "pro",
});
结账过程会自动将已认证用户作为客户信息带入结账流程。邮箱地址将被"锁定"。
如果 authenticatedUsersOnly
设置为 false
,则可以在没有任何关联客户的情况下触发结账会话。
组织支持
此插件支持组织插件。如果您将组织 ID 传递给结账 referenceId,您将能够跟踪组织成员进行的购买。
const organizationId = (await authClient.organization.list())?.data?.[0]?.id,
await authClient.checkout({
// 此处可传递任何 Polar 产品 ID
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// 或者,如果您在结账配置中设置了 "products",可以传递 slug
slug: 'pro',
// 参考 ID 将作为 `referenceId` 保存在结账、订单和订阅对象的元数据中
referenceId: organizationId
});
门户插件
一个允许客户管理其购买、订单和订阅的插件。
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
门户插件为 BetterAuth 客户端提供了一组客户管理方法,这些方法位于 authClient.customer
作用域下。
客户门户管理
以下方法将重定向用户到 Polar 客户门户,在那里他们可以查看订单、购买记录、订阅、权益等。
await authClient.customer.portal();
客户状态
门户插件还提供了一个便捷的状态方法,用于检索通用的客户状态。
const { data: customerState } = await authClient.customer.state();
客户状态对象包含:
- 关于该客户的所有数据
- 他们的活跃订阅列表
- 注意:这不包括由父组织创建的订阅。更多信息请参见下面的订阅列表方法
- 他们被授予的权益列表
- 他们的活跃计量器列表及其当前余额
因此,通过这一个对象,您就拥有了检查是否应该提供服务访问权限所需的所有信息。
您可以在 Polar 文档中了解更多关于 Polar 客户状态的信息。
权益、订单和订阅
门户插件提供了3个便捷的方法,用于列出与认证用户/客户相关的权益、订单和订阅。
所有这些方法都使用 Polar CustomerPortal API
权益
此方法仅列出认证用户/客户被授予的权益。
const { data: benefits } = await authClient.customer.benefits.list({
query: {
page: 1,
limit: 10,
},
});
订单
此方法列出认证用户/客户的订单,如购买和订阅续订。
const { data: orders } = await authClient.customer.orders.list({
query: {
page: 1,
limit: 10,
productBillingType: "one_time", // 或 'recurring'
},
});
订阅
此方法列出与认证用户/客户关联的订阅。
const { data: subscriptions } = await authClient.customer.subscriptions.list({
query: {
page: 1,
limit: 10,
active: true,
},
});
重要提示 - 组织支持
此方法不会返回父组织为认证用户创建的订阅。
但是,您可以向此方法传递一个 referenceId
参数。这将返回与该 referenceId 关联的所有订阅,而不是与用户关联的订阅。
因此,要确定用户是否应该拥有访问权限,请传递用户的组织 ID 来检查该组织是否有活跃的订阅。
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 => // 您检查订阅产品或其它逻辑的代码
)
使用量插件
一个用于基于使用量计费的简单插件。
import { polar, checkout, portal, usage } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
...
use: [
checkout(...),
portal(),
usage()
],
})
]
});
事件摄取
Polar 的基于使用量计费完全建立在事件摄取之上。从您的应用程序中摄取事件,创建计量器来表示该使用量,并向产品添加计量价格以进行收费。
const { data: ingested } = await authClient.usage.ingest({
event: "file-uploads",
metadata: {
uploadedFiles: 12,
},
});
认证用户会自动与摄取的事件关联。
客户计量单元
一种简单的方法,用于列出认证用户的使用计量单元(我们称之为客户计量单元)。
客户计量单元包含用户在您定义的计量单元上消费的所有信息。
- 客户信息
- 计量单元信息
- 客户计量单元信息
- 已消费单元数
- 已抵扣单元数
- 余额
const { data: customerMeters } = await authClient.usage.meters.list({
query: {
page: 1,
limit: 10,
},
});
Webhooks 插件
Webhooks 插件可用于捕获来自您 Polar 组织的传入事件。
import { polar, webhooks } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth 配置
plugins: [
polar({
...
use: [
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => // 当客户相关状态发生任何变化时触发
onOrderPaid: (payload) => // 当订单支付完成时触发(购买、订阅续费等)
... // 超过 25 个细粒度的 webhook 处理器
onPayload: (payload) => // 所有事件的通用捕获器
})
],
})
]
});
在您的 Polar 组织设置页面配置 Webhook 端点。Webhook 端点配置在 /polar/webhooks。
将密钥添加到您的环境变量中。
# .env
POLAR_WEBHOOK_SECRET=...
该插件支持所有 Polar webhook 事件的处理程序:
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
- 当创建客户时触发