关于 better-auth-integration
一个将 Better Auth 与 Dodo Payments 连接的集成包,面向需要在认证流程中加入支付能力的应用开发者。它通过已登录用户的会话提供结账、客户门户、订阅、用量和签名验证的 Webhook 端点。
主要功能
- 01注册时同步 Dodo 客户
- 02提供认证结账与客户门户
- 03列出订阅和支付记录
- 04提交计量用量事件
- 05验证并处理 Webhook 签名
适用场景
- 01用户注册时创建 Dodo 客户
- 02让已登录用户启动结账
- 03允许用户查看订阅和付款
- 04接收并处理 Dodo Webhook
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dodopayments/ skills --skill better-auth-integration安装后在 Claude Code 中输入/better-auth-integration确认已加载。
详细介绍
Better Auth 集成
使用 @dodopayments/better-auth 将 Better Auth 用户与 Dodo Payments 同步,并提供经过身份验证的结账、客户门户、订阅、支付、用量和 Webhook 端点。
适用场景
- Better Auth 用户注册时,自动创建 Dodo 客户。
- 为已登录用户发起结账,而不暴露 Dodo API 密钥。
- 让用户打开客户门户,或列出其订阅和支付记录。
- 为已登录的客户提交计量用量。
- 通过 Better Auth 接收经过签名验证的 Dodo Webhook。
安装
npm install @dodopayments/better-auth dodopayments better-auth zod
需要 Better Auth 1.4 或更高的 1.x 版本。
此软件包导出小写服务端函数 dodopayments、checkout、portal、usage 和 webhooks。其客户端入口点导出 dodopaymentsClient。
服务端设置
每个端点都由 use 中的一个功能注册。请包含应用调用的每项功能;省略任何一项都会导致其路由返回 404。
import { betterAuth } from "better-auth";
import DodoPayments from "dodopayments";
import { checkout, dodopayments, portal, usage, webhooks } from "@dodopayments/better-auth";
const dodoPayments = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: "test_mode",
});
export const auth = betterAuth({
database: {
// your database config
},
plugins: [
dodopayments({
client: dodoPayments,
createCustomerOnSignUp: true,
// `user` here is Better Auth's base user: id, name, email, emailVerified,
// image, createdAt, updatedAt. Anything else (phone number, company, plan)
// must first be declared via `user.additionalFields` in your Better Auth
// config, otherwise it does not exist on the type and will be undefined.
getCustomerParams: (user) => ({
metadata: { better_auth_user_id: user.id },
}),
use: [
checkout({
products: [
{ productId: "pdt_premium", slug: "premium-plan" },
],
successUrl: "https://app.example.com/billing/success",
authenticatedUsersOnly: true,
}),
portal(),
usage(),
webhooks({
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
onPayload: async (payload) => {
console.log("Verified Dodo webhook:", payload.type);
},
}),
],
}),
],
});
products 将应用中的 slug 映射到 Dodo 产品 ID。authenticatedUsersOnly: true 要求用户必须拥有 Better Auth 会话才能结账。getCustomerParams 会在客户同步时添加应用元数据。
此插件会在 Better Auth 的 user 表中添加 dodoCustomerId 字段。添加插件后,请使用 Better Auth CLI 更新数据库架构:
npx auth generate # generate the ORM schema or SQL migration, then apply it yourself
npx auth migrate # or apply it directly (built-in Kysely adapter only)
浏览器客户端设置
import { createAuthClient } from "better-auth/react";
import { dodopaymentsClient } from "@dodopayments/better-auth/client";
export const authClient = createAuthClient({
plugins: [dodopaymentsClient()],
});
这些是浏览器方法。它们会自动发送 Better Auth 会话 Cookie;不要向它们传递 request.headers。
创建结账会话
import { authClient } from "@/lib/auth-client";
async function startCheckout() {
const { data: session, error } = await authClient.dodopayments.checkoutSession({
slug: "premium-plan",
referenceId: "order_123",
});
if (error) {
console.error("Checkout error:", error);
return;
}
if (session) {
window.location.href = session.url;
}
}
slug 必须与 checkout({ products: [...] }) 中配置的产品匹配。referenceId 是可选的对账元数据。
打开客户门户
const { data: portalSession, error } =
await authClient.dodopayments.customer.portal();
if (error) {
console.error("Portal error:", error);
} else if (portalSession?.redirect) {
window.location.href = portalSession.url;
}
门户端点要求用户已登录且已验证,并会解析该用户同步后的 Dodo 客户。
列出订阅和支付记录
const { data: subscriptions, error: subscriptionsError } =
await authClient.dodopayments.customer.subscriptions.list({
query: { page: 1, limit: 10, status: "active" },
});
const { data: payments, error: paymentsError } =
await authClient.dodopayments.customer.payments.list({
query: { page: 1, limit: 10, status: "succeeded" },
});
if (subscriptionsError || paymentsError) {
console.error("Could not load billing history");
} else {
console.log("Subscriptions:", subscriptions?.items);
console.log("Payments:", payments?.items);
}
两个列表响应都使用 items 封装。订阅筛选条件接受 page、limit 和 status;支付筛选条件使用相同的分页字段和支付状态。
提交计量用量
在服务端启用 usage(),然后为已登录的客户提交事件:
const { data, error } = await authClient.dodopayments.usage.ingest({
event_id: "evt_usage_123",
event_name: "api_calls",
metadata: { requests: 100 },
timestamp: new Date(),
});
if (error) {
console.error("Usage ingestion failed:", error);
} else {
console.log("Ingested events:", data?.ingested_count);
}
usage.ingest 和 usage.meters.list 都要求用户已登录且电子邮件地址已验证。插件会从经过身份验证的会话中获取 customer_id。event_id 是事件的幂等键;重试同一事件时请重复使用它。
Webhook 端点
webhooks({ webhookKey, onPayload }) 会自行验证并处理请求。使用 Better Auth 默认的 /api/auth 基础路径时,请在 Dodo 控制面板中配置此 URL:
https://app.example.com/api/auth/dodopayments/webhooks
不要创建第二个路由,也不要调用 auth.api.dodopayments.verifyWebhook();该方法不存在。插件会读取原始请求,使用 webhookKey 验证 Standard Webhooks 签名,并调用 onPayload 以及任何已配置的特定事件回调。
已注册的端点
POST /dodopayments/checkoutPOST /dodopayments/checkout-sessionGET /dodopayments/customer/portalGET /dodopayments/customer/subscriptions/listGET /dodopayments/customer/payments/listPOST /dodopayments/usage/ingestGET /dodopayments/usage/meters/listPOST /dodopayments/webhooks
Better Auth 会在这些插件路径前加上配置的 API 基础路径。
常见错误
仅启用 portal()。 除非 use 中也包含 checkout()、usage() 和 webhooks(),否则结账、用量和 Webhook 路由都不存在。
混用浏览器和服务端 API。 在浏览器代码中调用 authClient.dodopayments... 时,不要设置 headers 选项。处理服务端请求时,请单独使用 Better Auth 的服务端 API。
创建重复客户。 启用 createCustomerOnSignUp 后,不要再为同一用户调用 client.customers.create()。
根据浏览器状态授予访问权限。 应将已验证的 Webhook 事件或服务端 Dodo 查询结果作为权益的真实来源。
添加自定义 Webhook 验证器。 插件负责 /dodopayments/webhooks、签名验证和回调分发。