关于 framework-adapters
面向在 Web 框架中集成 Dodo Payments 的开发者,介绍官方适配器如何提供结账、客户门户和 Webhook 路由处理器。覆盖多种框架,并说明环境变量、请求处理和安全配置要点。
主要功能
- 01提供多框架官方适配器
- 02创建结账路由处理器
- 03生成客户门户会话链接
- 04验证 Webhook 签名并分发事件
- 05映射环境变量和请求响应格式
适用场景
- 01在 Web 框架中添加支付结账路由
- 02创建需身份验证的客户门户入口
- 03接收并验证支付 Webhook
- 04按框架选择适配器和处理器模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dodopayments/ skills --skill framework-adapters安装后在 Claude Code 中输入/framework-adapters确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/astro.mdbun.mdconvex.mdexpress.mdfastify.mdhono.mdnuxt.mdremix.mdsveltekit.mdtanstack-start.md详细介绍
框架适配器
当你需要使用官方适配器包将 Dodo Payments 集成到 Web 框架时,请使用此技能。框架适配器负责路由配置、环境配置和处理程序设置,因此你无需从头构建结账、门户和 Webhook 路由。
何时使用此技能
- 你正在使用 Next.js、Express、Fastify、Hono、Astro、Remix、SvelteKit、Nuxt、TanStack、Bun 或 Convex 构建结账流程。
- 你需要设置客户门户会话端点。
- 你要添加用于验证签名并分发事件的 Webhook 处理程序。
- 你希望采用符合框架惯例的路由位置和环境变量处理方式。
- 你需要在静态、动态或会话结账模式之间进行选择。
框架适配器的功能
Dodo 发布了 @dodopayments/* 包,用框架专用的路由处理程序封装核心 SDK。你无需编写自己的 HTTP 处理程序,只需导入适配器的处理程序并直接挂载到路由中。
每个适配器都提供三类处理程序:
- Checkout: 静态(仅 GET)、动态(POST,通过已弃用的
POST /payments/POST /subscriptions端点创建付款或订阅——仅适用于现有集成),或会话(POST,附带结账会话负载——新集成请使用此模式)。 - CustomerPortal: 生成有时效限制的门户会话链接。
- Webhooks: 验证 Webhook 签名并分发类型化事件。
安全性——CustomerPortal 不进行身份验证。 每个
CustomerPortal处理程序都会为其接收到的任何?customer_id=打开门户。若直接挂载,任何访客都可以通过猜测或枚举 ID 打开任意客户的门户。请将此路由置于你自己的身份验证机制之后,并在服务端根据已登录会话解析客户 ID;切勿从客户端接收客户 ID。下方的门户示例采用了这种做法。
各适配器的导出名称并不统一。 大多数适配器导出 Checkout / CustomerPortal / Webhooks,但有两个适配器不同,返回值的形状也各不相同。编写导入语句前请查看此表:
| 适配器 | Checkout 导出项 | Portal 导出项 | 处理程序形状 |
|---|---|---|---|
nextjs、hono、astro、bun、remix、tanstack | Checkout | CustomerPortal | 返回一个请求处理程序 |
express | checkoutHandler(小写) | CustomerPortal | 返回 (req, res) |
fastify | Checkout | CustomerPortal | 返回 { getHandler, postHandler } |
sveltekit | Checkout | CustomerPortal | 返回 { GET, POST } / { GET } |
nuxt | checkoutHandler(自动导入) | customerPortalHandler | 无需导入语句 |
convex | checkout(来自 dodo.api()) | customerPortal(来自 dodo.api(),使用 identify) | createDodoWebhookHandler |
适配器会处理 Webhook 验证所需的原始请求体保留、环境变量映射,以及框架专用的请求/响应形状。结账负载的设计属于 checkout-integration 技能;Webhook 业务逻辑属于 webhook-integration;门户行为属于 customer-management。
框架选择与安装
| 框架 | 包 | 安装 |
|---|---|---|
| Next.js | @dodopayments/nextjs | npm install @dodopayments/nextjs |
| Express | @dodopayments/express | npm install @dodopayments/express |
| Fastify | @dodopayments/fastify | npm install @dodopayments/fastify |
| Hono | @dodopayments/hono | npm install @dodopayments/hono |
| Astro | @dodopayments/astro | npm install @dodopayments/astro |
| Remix | @dodopayments/remix | npm install @dodopayments/remix |
| SvelteKit | @dodopayments/sveltekit | npm install @dodopayments/sveltekit |
| Nuxt | @dodopayments/nuxt | npm install @dodopayments/nuxt |
| TanStack Start | @dodopayments/tanstack | npm install @dodopayments/tanstack |
| Bun | @dodopayments/bun | bun add @dodopayments/bun |
| Convex | @dodopayments/convex | npm install @dodopayments/convex |
环境变量
大多数适配器使用以下标准名称:
DODO_PAYMENTS_API_KEY=dodo_test_...
DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
DODO_PAYMENTS_ENVIRONMENT=test_mode
DODO_PAYMENTS_RETURN_URL=https://yourdomain.com/checkout/success
Nuxt 使用私有运行时配置前缀:
NUXT_PRIVATE_BEARER_TOKEN=dodo_test_...
NUXT_PRIVATE_WEBHOOK_KEY=your-webhook-secret
NUXT_PRIVATE_ENVIRONMENT=test_mode
NUXT_PRIVATE_RETURN_URL=https://yourdomain.com/checkout/success
Convex 使用控制面板环境变量(而非本地 .env):
DODO_PAYMENTS_API_KEY=dodo_test_...
DODO_PAYMENTS_ENVIRONMENT=test_mode
DODO_PAYMENTS_WEBHOOK_SECRET=your-webhook-secret
**Webhook 密钥命名:**有些适配器引用 DODO_PAYMENTS_WEBHOOK_KEY,另一些使用 DODO_PAYMENTS_WEBHOOK_SECRET。请查看框架的适配器文档和控制面板配置,确认正确的变量名称。
缩窄 environment 类型
适配器配置将 environment 的类型定义为 Pick<ClientOptions, "environment">,即字面量联合类型 "test_mode" | "live_mode"。process.env.X 的类型是 string | undefined,不能赋值给它——直接传入会在所有适配器中导致类型错误。
定义一次此辅助函数,并在构造适配器配置的地方导入:
// lib/dodo-env.ts
export const dodoEnvironment =
process.env.DODO_PAYMENTS_ENVIRONMENT === "live_mode" ? "live_mode" : "test_mode";
默认使用 test_mode 是有意为之:缺失或拼写错误的变量绝不能静默地解析为实时模式。以下示例均使用 dodoEnvironment。
Next.js
包:@dodopayments/nextjs
路由位置:app/api/checkout/route.ts、app/api/customer-portal/route.ts、app/api/webhook/route.ts
结账(每个路由选择一种模式)
// app/api/checkout/route.ts
import { Checkout } from "@dodopayments/nextjs";
import { dodoEnvironment } from "@/lib/dodo-env";
export const GET = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: dodoEnvironment,
type: "static",
});
export const POST = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: dodoEnvironment,
type: "session",
});
客户门户
// app/api/customer-portal/route.ts
import { NextRequest } from "next/server";
import { CustomerPortal } from "@dodopayments/nextjs";
import { dodoEnvironment } from "@/lib/dodo-env";
// your auth helper
import { getSessionUser } from "@/lib/auth";
const portal = CustomerPortal({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: dodoEnvironment,
});
export async function GET(req: NextRequest) {
const user = await getSessionUser(req);
if (!user?.dodoCustomerId) return new Response("Unauthorized", { status: 401 });
// Overwrite any client-supplied ?customer_id= with the signed-in user's ID.
const url = new URL(req.url);
url.searchParams.set("customer_id", user.dodoCustomerId);
return portal(new NextRequest(url, req));
}
Webhook
// app/api/webhook/route.ts
import { Webhooks } from "@dodopayments/nextjs";
export const POST = Webhooks({
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
onPayload: async (payload) => {
console.log("Webhook received:", payload.type);
},
});
Express
完整指南:references/express.md。
涵盖:
- 结账
- 客户门户
- Webhook
Fastify
完整指南:references/fastify.md。
涵盖:
- 结账
- Webhook
Hono
完整指南:references/hono.md。
涵盖:
- 结账
- 客户门户
- Webhook
Astro
完整指南:references/astro.md。
涵盖:
- 结账
- Webhook
Remix
完整指南:references/remix.md。
涵盖:
- 结账
- 客户门户
- Webhook
SvelteKit
完整指南:references/sveltekit.md。
涵盖:
- 结账
- Webhook
Nuxt
完整指南:references/nuxt.md。
涵盖:
- 结账
- Webhook
TanStack Start
完整指南:references/tanstack-start.md。
涵盖:
- 结账
Bun
完整指南:references/bun.md。
涵盖:
- 结账和门户
Convex
完整指南:references/convex.md。
常见错误
-
重复的 POST 处理程序: Next.js 和 Astro 文档中展示了两个
export const POST声明。每条路由选择一种结账模式,或添加路由逻辑以在两者之间进行分发。 -
Webhook 变量名称错误: 检查你的适配器使用的是
DODO_PAYMENTS_WEBHOOK_KEY还是DODO_PAYMENTS_WEBHOOK_SECRET。文档中的名称不一致;请使用你的适配器实际引用的名称。 -
未保留原始请求正文: Webhook 处理程序必须接收原始请求正文,而不是重新解析后的 JSON 对象。Fastify 需要显式的字符串正文解析器;其他框架会自动处理。**Express 是例外:**它的
Webhooks处理程序会根据已解析的req.body进行验证,因此请在路由之前注册express.json(),不要使用express.raw(),否则每次验证都会失败。webhook-integration技能涵盖了 Webhook 签名验证。 -
混用框架约定: 每个框架都有自己的请求/响应格式。不要尝试在 Express 中使用 Next.js 处理程序,反之亦然。请使用适用于你所用框架的适配器。
-
硬编码密钥: 始终从环境变量读取 API 密钥和 Webhook 密钥,绝不要将它们写入代码或配置文件。
-
跳过环境设置: 缺少
DODO_PAYMENTS_API_KEY和DODO_PAYMENTS_ENVIRONMENT时,适配器将无法工作。请在测试前设置这些变量。 -
未验证身份便开放 CustomerPortal: 门户处理程序会信任请求中的
?customer_id=。请像上面的示例一样,在服务端从已登录会话中解析 ID,否则任何人都能打开任意客户的门户。 -
直接使用
@dodopayments/core: 核心包是内部依赖,并非有文档说明的公共入口点。请使用适用于你所用技术栈的框架适配器。