关于 checkout-integration
面向开发者的 Dodo Payments 结账集成技能,介绍通过托管结账、嵌入式结账或静态付款链接收款。还涵盖订阅试用、结账定制以及通过 Webhook 验证付款。
主要功能
- 01创建托管 Checkout Sessions
- 02使用静态付款链接
- 03嵌入式或弹窗结账
- 04收集客户信息与自定义字段
- 05配置折扣码和货币选项
- 06通过 Webhook 验证付款
适用场景
- 01构建一次性付款流程
- 02创建含试用期的订阅结账
- 03在站点内显示结账弹窗
- 04为订单配置折扣码和客户字段
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dodopayments/ skills --skill checkout-integration安装后在 Claude Code 中输入/checkout-integration确认已加载。
附带文件references/overlay-checkout.mdreferences/server-examples.md
详细介绍
Dodo Payments 结账集成
使用 client.checkoutSessions.create(...) 构建托管结账页面或叠加式结账模态框。这是所有新付款集成的推荐方式。
何时使用此技能
- 构建一次性付款结账流程
- 创建可选试用期的订阅结账
- 在叠加层或内嵌模态框中嵌入结账
- 在结账时收集客户账单信息和自定义字段
- 应用折扣码或处理货币选择
- 在付款完成后重定向客户
结账方式
Dodo Payments 提供三种收款方式:
| 方式 | 适用场景 | 设置 |
|---|---|---|
| 结账会话(推荐) | 大多数集成;完全可控 | 服务端 SDK 调用 |
| 静态付款链接 | 无代码分享;可重复使用的 URL | 控制面板或直接 URL |
| 叠加式/内嵌结账 | 结账过程留在你的网站上 | 客户端 SDK |
旧版: 通过 POST /payments 或 POST /subscriptions 创建的动态付款链接已弃用。请改用结账会话。
核心概念
金额以最小货币单位表示: 所有价格均以分(或等值单位)表示。10 美元的 USD 扣款金额为 1000。
结账会话: 一次性使用的会话,可生成托管结账 URL。24 小时后过期(若 confirm=true,则 15 分钟后过期)。
返回 URL: 客户付款后跳转到的页面。Dodo 会附加 payment_id(一次性付款)或 subscription_id(订阅)、status,以及可用时的 license_key 和 email。它仅用于界面提示,绝不能作为付款凭证。
通过 webhook 授予权益: 浏览器重定向不是事实来源。授予访问权限前,务必通过 webhook 验证付款。验证方法请参阅 webhook-integration 技能。
创建结账会话
基本的一次性付款
import DodoPayments from 'dodopayments';
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: 'test_mode', // or 'live_mode'
});
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: {
email: '[email protected]',
name: 'Jane Doe'
},
return_url: 'https://yoursite.com/checkout/success'
});
console.log('Redirect to:', session.checkout_url);
响应字段:
session_id:唯一的结账会话 IDcheckout_url:托管结账 URL(将客户重定向到此处)。当confirm=true时可为空client_secret:仅在confirm=true时提供publishable_key:仅在confirm=true时提供。将它与client_secret配对,用于内嵌 SDK 流程payment_id:仅在confirm=true时提供
publishable_key 是 confirm 模式内嵌结账返回的每个会话专属值。它不是 Stripe 风格的可发布 API 密钥——Dodo 不会签发此类凭证,所有 API 密钥都是机密信息,仅限服务端使用。
包含多个产品
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_item_1', quantity: 2 },
{ product_id: 'pdt_item_2', quantity: 1 }
],
customer: { email: '[email protected]' },
return_url: 'https://yoursite.com/success'
});
使用现有客户
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { customer_id: 'cus_existing_id' },
return_url: 'https://yoursite.com/success'
});
包含账单地址和税号
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: {
email: '[email protected]',
name: 'Jane Doe'
},
billing_address: {
country: 'US',
street: '123 Main St',
city: 'San Francisco',
state: 'CA',
zipcode: '94105'
},
tax_id: 'VAT123456789',
customer_business_name: 'Acme Corp',
return_url: 'https://yoursite.com/success'
});
使用折扣码
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
discount_codes: ['PROMO10', 'WELCOME5'], // max 20, ordered
feature_flags: {
allow_discount_code: true
},
return_url: 'https://yoursite.com/success'
});
包含订阅和试用期
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_monthly_subscription', quantity: 1 }
],
subscription_data: {
trial_period_days: 14
},
customer: { email: '[email protected]' },
return_url: 'https://yoursite.com/success'
});
包含自定义字段
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
custom_fields: [
{
key: 'company_size',
label: 'Company Size',
field_type: 'dropdown',
options: ['1-10', '11-50', '51-200', '200+'],
required: true
},
{
key: 'use_case',
label: 'Primary Use Case',
field_type: 'text',
placeholder: 'e.g., analytics, reporting',
required: false
}
],
return_url: 'https://yoursite.com/success'
});
包含元数据
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
metadata: {
order_id: 'order_12345',
referral_code: 'FRIEND20',
campaign: 'summer_sale'
},
return_url: 'https://yoursite.com/success'
});
Next.js App Router
完整指南:references/server-examples.md。
涵盖:
- API 路由
- 客户端组件
- 成功页面
Express.js
完整指南:references/server-examples.md。
Python (FastAPI)
完整指南:references/server-examples.md。
叠加式结账
完整指南:references/overlay-checkout.md。
涵盖:
- 安装
- 服务端路由
- 浏览器叠加模式和内嵌模式
静态付款链接
可分享的无代码链接。无需服务端 API 调用。
基本格式
https://checkout.dodopayments.com/buy/{productid}
在测试模式下,主机为 test.checkout.dodopayments.com(例如 https://test.checkout.dodopayments.com/buy/{productid});测试模式下的产品不会存在于正式环境主机上。
示例:
https://checkout.dodopayments.com/buy/pdt_example
使用查询参数
https://checkout.dodopayments.com/buy/pdt_example?quantity=2&[email protected]&redirect_url=https%3A%2F%2Fyoursite.com%2Fsuccess
支持的参数:
quantity:商品数量redirect_url:成功后重定向 URLemail:预填客户邮箱fullName、firstName、lastName:预填姓名country、city、state、zipCode、addressLine:预填地址paymentCurrency:强制指定货币metadata_*:自定义元数据(例如,metadata_orderId=123)
检索和预览会话
检索会话状态
const status = await client.checkoutSessions.retrieve('cks_session_id');
console.log(status.id);
console.log(status.payment_status);
预览会话(不创建)
const preview = await client.checkoutSessions.preview({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
billing_currency: 'EUR'
});
console.log('Preview total:', preview.current_breakup.total_amount);
自定义
主题和外观
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
customer: { email: '[email protected]' },
customization: {
theme: 'dark', // 'light', 'dark', or 'system'
force_language: 'en',
show_order_details: true,
theme_config: {
font_size: 'md',
font_weight: 'normal',
radius: '8px',
pay_button_text: 'Complete Purchase',
light: {
bg_primary: '#ffffff',
text_primary: '#000000',
button_primary: '#0066ff'
}
}
},
return_url: 'https://yoursite.com/success'
});
功能标志
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
customer: { email: '[email protected]' },
feature_flags: {
allow_discount_code: true,
allow_currency_selection: true,
allow_customer_editing_email: true,
allow_phone_number_collection: true,
require_phone_number: false,
allow_tax_id: true
},
return_url: 'https://yoursite.com/success'
});
付款后流程
处理返回 URL
付款后,客户会被重定向到带有查询参数的 return_url:
# One-time payment
https://yoursite.com/success?payment_id=pay_xxx&status=succeeded&email=customer%40example.com
# Subscription (with license keys)
https://yoursite.com/success?subscription_id=sub_xxx&status=active&license_key=LK-001,LK-002&email=customer%40example.com
查询参数:
payment_id(一次性付款)或subscription_id(订阅)status:成功时为succeeded(一次性付款)或active(订阅);拒付时为failed;稍后结算时为processing(或某个requires_*值);会话过期时为expiredlicense_key:产品会发放许可证密钥时提供(多个密钥以逗号分隔)email:客户有已记录的电子邮件时提供
没有 session_id 参数。将 processing/缺失状态视为“未知”,并等待 webhook。
在服务端验证付款
不要信任浏览器重定向。使用
references/server-examples.md 中的 Express webhook 路由:该路由会在
express.json() 之前接收经过签名的原始请求正文,根据经过验证的 payment.succeeded 事件中的
event.data.customer.customer_id 完成一次性购买,并且仅在经过验证的 subscription.active 事件中授予订阅访问权限。
webhook-integration 技能中介绍了 webhook 签名验证。
常见错误
1. 根据返回 URL 授予访问权限 返回 URL 重定向不能证明付款已完成。切勿根据其查询参数授予访问权限或履行订单;应等待经过验证的 webhook 事件。
2. 使用已弃用的 API
新集成中不要使用 client.payments.create() 或 client.subscriptions.create()。这两个 API 均已弃用。请使用 client.checkoutSessions.create()。
3. 忘记 environment 标志
默认值为 live_mode。开发期间务必设置 environment: 'test_mode',以免对真实银行卡收费。
4. 使用已弃用的 discount_code
请使用 discount_codes(数组)。单数形式的 discount_code 字符串已弃用(为向后兼容仍可接受),且不能在同一请求中与 discount_codes 一起使用。
5. 金额单位错误
所有金额均以货币的最小单位表示(美元为美分)。10 美元应写为 1000,而不是 10。
6. 重复使用结账 URL 结账 URL 只能使用一次。每次尝试结账都要创建一个新会话。
7. 忽略 confirm=true 的行为
当 confirm=true 时,会话会立即完成,结账 URL 的有效期也会从 24 小时缩短为 15 分钟。仅在已掌握所有必需的客户数据时使用。
8. 忘记为 webhook 保留原始请求正文
Webhook 签名验证需要原始请求正文,而非重新序列化的 JSON 对象。应在 express.json() 之前使用 express.raw({ type: 'application/json' }) 挂载 webhook 路由。
9. 信任客户端提供的产品 ID
不要接受浏览器传来的任意 pdt_ ID。对用户进行身份验证,接受公开的方案 slug,在服务端将其映射到允许列表中的产品 ID,并拒绝非正整数的数量。