# Better Auth：解决身份验证问题的关键

> 了解 Better Auth 如何与 Dodo Payments 结合，为现代 SaaS 应用统一处理身份验证、结账、订阅、Webhook 和用量计费。
- **Author**: Joshua D'Costa
- **Published**: 2025-12-03
- **Category**: Tools
- **URL**: https://dodopayments.com/blogs/zh/better-auth-dodo-payments

---

如果你曾经需要分别处理身份验证逻辑、计费流程和订阅管理，那你并不孤单。大多数开发者并不会一早醒来就期待着把身份验证流程、支付事件和用量追踪串联起来，尤其是当这些时间本可以用来构建真正的产品时。因此，许多团队会依赖各种库和服务提供商来承担这些繁重工作。

多年来，NextAuth（Auth.js）、Auth0 和 Clerk 等工具已经成为默认选择。它们在许多场景下都运行良好，但每种方案都有自己的取舍，例如复杂性、定价、灵活性受限，或对供应商锁定的担忧。

Better Auth 等现代开源生态采用了全新的方式：为开发者提供原生 TypeScript、完全可自定义的身份验证层，同时避免常见的额外负担。它旨在解决旧方案尚未完全处理的问题，尤其适合构建需要统一、简洁的注册、订阅、用量计费和 Webhook 流程的产品。

本指南将介绍 Better Auth 提供的功能、它在当前身份验证生态中的定位，以及 Dodo Payments 适配器如何将结账、计费门户、[按用量计费](https://dodopayments.com/blogs/usage-based-billing-saas)和[支付通知 Webhook](https://dodopayments.com/blogs/webhooks-payment-notifications)接入这一身份验证层。

## 什么是 Better Auth？

[Better Auth](https://docs.dodopayments.com/developer-resources/better-auth-adaptor#better-auth-adapter) 是一个无头、与框架无关的身份验证和用户管理框架，重点关注开发者体验。实际使用中，它开箱即用地提供核心身份验证流程（注册、登录、会话等），并采用插件架构，让你可以添加额外功能。Better Auth 本身不处理支付----它只是提供一个稳健、类型安全的身份验证层，你可以将其接入任何 JS/TS 技术栈。

> 你在第一个月选择的计费模式，会限制第二年的定价灵活性。从一开始就基于支持订阅、用量、预付额度和混合模式的基础设施进行构建。
>
> \- Ayush Agarwal，Dodo Payments 联合创始人兼 CPTO

安装 Dodo Payments 的 Better Auth 适配器后，你的身份验证系统就具备了"支付感知"能力。例如，每当有人注册时，适配器会在后台自动创建 Dodo Payments 客户；它会使用已定义的产品 slug 处理结账会话，接入自助计费门户，甚至接收用于按用量计费的计量用量事件。它还会为你验证并解析 Dodo Payments Webhook 事件。所有这些功能都通过统一的 TypeScript 类型 API 暴露，因此你无需手动将身份验证数据库与 Dodo Payments SDK 同步，也不用自己编写 Webhook 处理程序。

## Better Auth 功能

使用 Dodo Payments 的 Better Auth 插件后，你可以立即获得以下主要功能：

- **注册时自动创建客户：** 用户在 Better Auth 中完成注册后，适配器会在后台创建对应的客户（无需额外 API 调用）。

- **类型安全的结账流程（产品 slug 映射）：** 你可以通过 slug（例如 `"premium-plan"`）定义产品，而不是使用硬编码 ID。

在结账时，Better Auth 会确保使用正确的产品，而 TypeScript 类型则能尽早捕获不匹配问题。

- **嵌入式计费门户：** 用户登录后，可以直接在你的应用中访问客户门户、更新卡片、查看发票、管理订阅等，接入过程非常简单。

- **内置 Webhook 处理：** 适配器会自动设置验证签名的 Webhook 端点。它会为支付、订阅、退款等事件触发类型化处理程序，因此你无需自己编写签名验证逻辑。

- **接收计量用量：** 如果你采用按用量计费，可以使用相关端点接收用量事件并列出用量记录。这样，你无需额外开发即可针对 API 调用次数、小时数、席位数等进行计费。

- **完整的 TypeScript 支持：** 从前端调用到服务器逻辑，所有内容都具备完整类型。端到端的类型安全意味着更少的运行时错误和更清晰的代码。

- **（额外功能）可观测性与日志：** 适配器会提供来自 Dodo Payments API 的拒付原因、交易详情和遥测数据，让你可以按需记录或展示调试信息。

## 安装与快速设置

### 前置条件

开始之前，请确保你已具备以下条件：

- **Node.js 20+**（用于使用最新的库）。

- **Dodo Payments 账户**，并准备好 API 密钥（你需要密钥以及用于设置 Webhook 的密钥）。

- **一个使用 Better Auth 的现有项目**（或至少已安装 Better Auth 核心库）。

你还需要生成 Better Auth 密钥（通过 Better Auth CLI 或文档），并将其设置到环境中。Better Auth 需要几个环境变量，例如：

`BETTER_AUTH_SECRET=your-better-auth-secret`

`BETTER_AUTH_URL` 应为应用的基础访问地址：本地开发时使用普通 HTTP 的 `localhost:3000`，部署后则使用 HTTPS 下的生产域名。

Better Auth 会自动检测这些变量，因此将它们写入 `.env` 后，无需在代码中再次引用。（一如既往，**切勿提交密钥**----请使用 `.env` 或密钥管理器。）

### 安装软件包

在项目根目录运行以下命令，安装 Dodo Payments 适配器及其依赖：

`npm install @dodopayments/better-auth dodopayments better-auth zod`

该命令会引入：

- `@dodopayments/better-auth`（Better Auth 插件）；

- `dodopayments`（Dodo Payments SDK）；

- `better-auth`（如果尚未安装）；

- `zod`（用于定义模式）。

Dodo Payments 文档确认了这一步的设置方式。

### 环境变量

将以下内容添加到你的 `.env`（或同等配置）文件中：

`DODO_PAYMENTS_API_KEY=your_api_key_here`

`DODO_PAYMENTS_WEBHOOK_SECRET=your_webhook_secret_here`

`BETTER_AUTH_URL=your_app_base_url`

`BETTER_AUTH_SECRET=your_better_auth_secret_here`

前两个变量用于 Dodo Payments：API 密钥（由 SDK 客户端使用）和 Webhook 密钥（用于验证传入的 Webhook）。后两个变量用于 Better Auth 本身（其基础 URL 和密钥）。如前所述，切勿将这些值提交到版本控制系统中。Better Auth 文档也展示了如何在 `.env` 中设置这些变量。

## **服务器端集成（核心连接）**

在服务器端，你需要将 Better Auth 与 Dodo Payments 插件连接起来。在类似 `src/lib/auth.ts` 的文件中（或配置 Better Auth 的其他位置），可以这样做：

```typescript
import { BetterAuth } from "better-auth";
import {
  dodopayments,
  checkout,
  portal,
  webhooks,
  usage,
} from "@dodopayments/better-auth";
import DodoPayments from "dodopayments";

const dodoPayments = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
  environment: "test_mode", // use "live_mode" in production
});

export const { auth, endpoints, client } = BetterAuth({
  plugins: [
    dodopayments({
      client: dodoPayments,
      createCustomerOnSignUp: true,
      use: [
        checkout({
          products: [{ productId: "pdt_xxx", slug: "premium-plan" }],
          successUrl: "/dashboard/success",
          authenticatedUsersOnly: true,
        }),
        portal(),
        webhooks({
          webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!,
          // you can also specify onPaymentSucceeded, onPaymentFailed, etc.
          onPayload: async (payload) => {
            console.log("Received Dodo webhook:", payload.event_type);
          },
        }),
        usage(),
      ],
    }),
  ],
});
```

这段代码完成了以下工作：

- 使用你的 API 密钥创建 `DodoPayments` 客户端。（**注意：**本地使用 `"test_mode"`，生产环境切换为 `"live_mode"`。）

- 在 `BetterAuth({...})` 中加入一个插件：`dodopayments({...})`。

- 传入客户端，启用 `createCustomerOnSignUp`，并在 `use: [...]` 中列出子插件。

- `checkout()` 插件将产品 ID 映射到 slug（这样你就可以在代码中使用易读的 slug），并配置重定向 URL。

- `portal()` 插件启用客户门户。

- `webhooks()` 插件接收你的 Webhook 密钥和处理程序。

- `usage()` 插件启用接收用量的端点。

这是一个最小化的连接示例。你可以根据需要添加更多产品、自定义 Webhook 事件处理程序等。（[官方文档中有类似示例](https://www.better-auth.com/docs/plugins/dodopayments)。）开发期间请保持 `environment` 为 `test_mode`，正式上线时再切换为 `"live_mode"`。

## 客户端设置

在前端（例如 React、Next.js 等）设置 Better Auth 客户端，并引入 Dodo Payments 的客户端插件。例如，在 `src/lib/auth-client.ts` 中：

```typescript
import { createAuthClient } from "better-auth/react";
import { dodopaymentsClient } from "@dodopayments/better-auth";

export const authClient = createAuthClient({
  baseURL: process.env.BETTER_AUTH_URL,
  plugins: [dodopaymentsClient()],
});
```

这样，你就可以在客户端使用 `authClient.dodopayments.*` 方法。这里有意直接读取 `BETTER_AUTH_URL`，而不设置硬编码的备用值：如果缺少该值，CI 应明确失败，而不是悄悄地将生产客户端指向本地开发地址。现在，你可以从界面调用与 Dodo Payments 相关的方法，它们会访问服务器上的 Better Auth 端点。

## 核心使用示例

完成所有连接后，下面是一些常见的使用示例：

**创建结账会话（通过 slug）**：这是启动结账的推荐方式。例如：

```typescript
const { data: session } = await authClient.dodopayments.checkoutSession({
  slug: "premium-plan",
  referenceId: "order_123",
});

if (session) window.location.href = session.checkout_url;
```

这会打开"premium-plan"产品的结账页面。你无需自行收集卡片信息----Dodo Payments 会处理这些步骤。

**注意：** 为保持向后兼容，还提供旧版 `authClient.dodopayments.checkout()` 方法；但对于新的集成，建议使用带 slug 或产品购物车的 `checkoutSession`。

**客户门户**：若要让用户访问计费门户，可以这样做：

```typescript
const { data: portal } = await authClient.dodopayments.customer.portal();

if (portal?.redirect) {
  window.location.href = portal.url;
}
```

这会将用户重定向到 Dodo Payments 的自助服务门户，用户可以在那里管理订阅和支付方式。

**列出订阅 / 支付**：你可以通过以下方式列出客户的订阅或支付记录：

```typescript
const { data: subs } =
  await authClient.dodopayments.customer.subscriptions.list({
    query: { limit: 10, page: 1, status: "active" },
  });

const { data: payments } = await authClient.dodopayments.customer.payments.list(
  {
    query: { limit: 10, page: 1, status: "succeeded" },
  },
);
```

（根据需要调整查询参数。）这样，你就可以在应用中展示计费历史。

**计量用量（Usage 插件）**：对于按用量计费的方案，可以记录如下事件：

```typescript
await authClient.dodopayments.usage.ingest({
  event_id: crypto.randomUUID(),
  event_name: "api_request",
  metadata: { route: "/reports", method: "GET" },
  timestamp: new Date(),
});
```

Dodo 会在后台统计这些用量。你也可以列出某个计量器的近期用量：

```typescript
const { data: usage } = await authClient.dodopayments.usage.meters.list({
  query: { page_size: 20, meter_id: "mtr_yourMeterId" },
});
```

-  （注意：插件会限制 `timestamp` 的时间范围，因此过于久远或过于超前的事件可能会被拒绝。）

## Webhook（安全处理）

Dodo Payments 插件会自动为你处理 Webhook。默认情况下，它会在 `/api/auth/dodopayments/webhooks` 暴露一个端点（在 Next.js 中，这就是你的 API 路由）。使用方法如下：

1. **在 Dodo 的控制面板中设置 Webhook URL。** 使用你的域名加上 `/api/auth/dodopayments/webhooks`。

2. **配置 Webhook 密钥。** 创建 Webhook 时，从 Dodo 获取密钥，并将其作为 `DODO_PAYMENTS_WEBHOOK_SECRET` 保存到 `.env` 中。

3. **在 Better Auth 中启用插件。** 我们已经在上面的服务器设置中加入了 `webhooks({ webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!, ... })`。

完成设置后，传入的 Webhook 事件会根据你的密钥进行验证和解析。你可以在 `webhooks()` 配置中提供处理程序回调。例如：

```typescript
webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!,
  onPaymentSucceeded: async (payload) => {
    /* handle successful payment */
  },
  onPaymentFailed: async (payload) => {
    /* handle payment failure */
  },
  onSubscriptionCancelled: async (payload) => {
    /* ... */
  },
  // ...and so on for disputes, refunds, etc.
});
```

插件提供的签名验证意味着你可以信任收到的 payload。Webhook 插件会通过安全的签名验证，处理来自 Dodo Payments 的实时事件。

## 配置参考

下面是主要插件配置的快速概览：

- `dodopayments({ client, createCustomerOnSignUp, use: [...] })`：顶层插件。传入你的 Dodo Payments `client`，将 `createCustomerOnSignUp: true` 设置为启用自动创建客户，并在 `use` 中列出子插件。

- `checkout({ products, successUrl, authenticatedUsersOnly })`：在 `use` 中使用时，该插件会将 Dodo Payments 产品 ID 映射到 slug。你需要提供一个由 `{ productId, slug }` 组成的数组、用于重定向的 `successUrl`，以及是否必须登录。

- `portal()`：启用客户门户。无需其他选项。

- `usage()`：启用用量计量数据的接收和查询。

- `webhooks({ webhookKey, onPayload, ...handlers })`：启用 Webhook 处理。提供 Dodo Payments 控制面板中的 `webhookKey`，然后指定 `onPaymentSucceeded`、`onSubscriptionCancelled` 等回调。你也可以使用通用的 `onPayload` 处理程序。

## 故障排查与最佳实践

- **API 密钥无效：** 仔细检查 `DODO_PAYMENTS_API_KEY` 是否正确，以及是否具有正确的权限范围。错误的密钥会导致每次调用都出现身份验证错误。

- **Webhook 签名错误：** 确保 `DODO_PAYMENTS_WEBHOOK_SECRET` 与你在 Dodo Payments 控制面板中为该 Webhook URL 设置的值完全一致。

- **未创建客户：** 如果用户注册后在 Dodo Payments 中看不到客户，请确保已设置 `createCustomerOnSignUp: true`。

### 最佳实践：

将所有密钥和秘密值保存在环境变量或密钥管理器中。

正式上线前，先在 `test_mode` 中测试所有功能，并在控制面板中监控 Webhook 和事件。为了便于调试，请在你的系统中记录支付失败和拒付原因。

如果需要重试请求，请使用幂等键。如果你正从其他计费系统迁移，可以先向一部分用户推出新的集成，确认运行顺利后再全面切换；在切换之前，也请阅读我们的[计费系统迁移常见错误](https://dodopayments.com/blogs/billing-system-migration-mistakes)列表。

## 常见问题

### Dodo Payments Better Auth 适配器默认会自动处理哪些工作？

该适配器可以在注册时自动创建客户、通过 slug 启动结账会话、提供计费门户，并使用类型化处理程序处理 Webhook 事件。它还支持接收用量事件，以实现计量计费流程。

### 我仍然需要手动构建 Webhook 验证吗？

不需要。文章说明，配置 Webhook 密钥后，`webhooks()` 插件会处理签名验证和类型化 payload 解析。你只需为关注的事件添加业务逻辑回调。

### 我可以在此设置中同时运行订阅和按用量计费吗？

可以。本指南明确支持在一次集成中结合使用结账、门户和用量插件实现混合计费。这样，你可以在同一个具备身份验证感知能力的流程中，将周期性方案与计量组件结合起来。

### 我应该先在测试模式下开发，再正式上线吗？

应该。本文建议开发期间使用 `test_mode`，生产环境切换到 `live_mode`。这样可以让你在端到端验证结账、Webhook 和用量流程时降低风险。

### 我可以逐步迁移，而不是一次性切换所有用户吗？

可以。故障排查部分建议从其他计费系统迁移时，先向一部分用户推出新集成。分阶段的方式有助于你在全面切换前发现集成问题。

## 总结

如果你希望通过一次统一、可预测的集成，以 TypeScript 优先的开发体验处理身份验证、订阅、用量计费和 Webhook，那么 Dodo Payments 的 Better Auth 适配器是一个高效选择。它将常见的支付基础工作（创建客户、结账、门户、用量报告等）整合到统一流程中。

这意味着你可以少花时间编写自定义连接代码，把更多精力投入产品本身。
---
- [More Tools articles](https://dodopayments.com/blogs/category/tools)
- [All articles](https://dodopayments.com/blogs)