关于 testing-and-go-live
面向接入 Dodo Payments 的开发者,提供支付流程测试与上线准备指南。涵盖测试和生产环境隔离、支付及 Webhook 验证,以及产品迁移和上线检查。
主要功能
- 01区分测试与生产环境
- 02测试卡片及区域支付方式
- 03测试订阅续费
- 04本地监听和触发 Webhook
- 05测试产品迁移至生产
- 06生产上线检查清单
适用场景
- 01验证支付成功、拒绝及续费失败
- 02在本地或暂存环境测试 Webhook
- 03准备从测试模式切换至生产
- 04将测试环境产品配置迁移至生产
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dodopayments/ skills --skill testing-and-go-live安装后在 Claude Code 中输入/testing-and-go-live确认已加载。
详细介绍
测试与正式上线
此技能涵盖测试模式、测试付款方式、Webhook 测试和正式上线检查清单。构建需要在正式上线前验证的付款流程,或准备从测试环境切换到生产环境时,请使用此技能。
何时使用此技能
- 你需要测试银行卡付款成功/拒绝场景、订阅续费或区域付款方式。
- 你正在本地或预发布环境中设置 Webhook 测试。
- 你正准备上线到生产环境,需要一份正式上线检查清单。
- 你想确认测试环境和正式环境已正确隔离。
- 你正在将产品从测试环境复制到正式环境。
核心概念
测试模式与正式模式: Dodo Payments 维护着两个完全独立的环境。每个环境都有自己的基础 URL、API 密钥、Webhook、产品、客户和交易。数据不会在两个环境之间转移。
- 测试模式:
https://test.dodopayments.com - 正式模式:
https://live.dodopayments.com
每个环境都需要使用从控制面板签发的独立 API 密钥。如果未指定 environment,SDK 默认使用 live_mode,这在开发过程中很容易造成严重问题。
测试付款方式: Dodo 记录了用于测试的特定银行卡号、VPA 和付款场景。这些方式仅在测试模式下有效。
Webhook 测试: CLI 提供用于在本地监听 Webhook,以及触发测试事件(无需实际付款)的命令。
设置测试模式
开发期间始终显式设置 environment: 'test_mode'。默认值为 live_mode。
import DodoPayments from 'dodopayments';
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: 'test_mode', // REQUIRED — defaults to 'live_mode'
});
import os
from dodopayments import DodoPayments
client = DodoPayments(
bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
environment="test_mode", # defaults to "live_mode"
)
client := dodopayments.NewClient(
option.WithBearerToken("dodo_test_..."),
option.WithEnvironmentTestMode(), // defaults to WithEnvironmentLiveMode()
)
从 Dodo 控制面板获取测试 API 密钥。测试密钥的前缀为 dodo_test_。
测试付款方式
银行卡付款成功与拒绝场景
在测试模式下使用以下银行卡号,并设置有效期为 06/32(或 12/34),CVV 为 123。
| 场景 | 银行卡号 | 结果 |
|---|---|---|
| 成功 | 4242 4242 4242 4242 | 付款成功 |
| 拒绝 | 4000 0000 0000 0002 | 付款被拒绝 |
| 拒绝(余额不足) | 4000 0000 0000 9995 | 余额不足错误 |
仅使用 Testing Process 页面列出的银行卡;其他 Stripe 风格的测试卡号(例如“银行卡遗失”变体)并未记录为适用于 Dodo。
订阅续费失败
要测试订阅续费失败,请使用以下银行卡:
| 银行卡号 | 有效期 | CVV | 行为 |
|---|---|---|---|
4000 0000 0000 0341 | 12/34 | 123 | 在订阅下次扣款时被拒绝(续费、升级或降级) |
先使用付款成功的银行卡创建订阅,然后在客户门户中将其付款方式切换为此卡。
UPI(印度)
使用以下 VPA 测试 UPI 付款成功和失败。账单国家/地区必须为 IN,货币必须为 INR(非印度商家还需要启用 Adaptive Currency):
| VPA | 结果 |
|---|---|
success@upi | 付款成功 |
failure@upi | 付款失败 |
BNPL、电子钱包和区域付款方式
测试模式支持 BNPL、Apple Pay、Google Pay 和欧洲付款方式(Giropay、iDEAL、Bancontact)。具体测试场景因付款方式而异。请参阅 Testing Process 文档,了解最新的测试矩阵。
测试订阅续费
若要指定日期触发订阅续费,而无需等待,请更新 next_billing_date:
await client.subscriptions.update('sub_123', {
next_billing_date: '2026-05-03T00:00:00Z',
});
client.subscriptions.update(
'sub_123',
next_billing_date='2026-05-03T00:00:00Z'
)
// NextBillingDate is param.Field[time.Time], not a string. Passing a string
// literal compiles to param.Field[string] and will not type-check.
client.Subscriptions.Update(ctx, "sub_123", dodopayments.SubscriptionUpdateParams{
NextBillingDate: dodopayments.F(time.Date(2026, time.May, 3, 0, 0, 0, 0, time.UTC)),
})
这会立即安排续费。如果你使用了订阅续费失败卡,扣款失败时会触发 payment.failed,受影响的订阅会进入 on_hold 状态,并触发 subscription.on_hold。
Webhook 测试
CLI 与 SDK 分开提供:
npm install -g dodopayments-cli
dodo login
dodo login 会打开 API Keys 页面,接受粘贴的密钥,并询问这是测试模式还是正式模式。测试密钥和正式密钥可以各自保持一个已认证状态。
在本地监听 Webhook
dodo wh listen http://localhost:3000/webhook
此命令通过出站中继,将真实的测试模式事件转发到你的本地 URL。它不是隧道:没有任何服务会公开监听,也不会打印供你粘贴到控制面板的 URL。如果你需要可注册的 HTTPS URL,请改用 ngrok——具体流程见 webhook-integration 技能。
运行前请了解以下两项限制:
- 必须使用测试模式密钥。 监听流程不支持实时模式密钥。
- 直接模式下必须提供 URL 参数。 仅输入
dodo wh listen只能在交互式 TUI 中作为/wh listen使用,它会打开向导。
通过这种方式接收的事件带有有效签名,因此请照常使用 webhooks.unwrap() 对其进行验证。
触发模拟 Webhook 事件
dodo wh trigger payment.success http://localhost:3000/webhook
为所选事件发送逼真的模拟载荷。此操作离线运行,即使在未登录时也能使用——这正是关键所在:这些载荷没有签名。 由于没有有效签名可供验证,unwrap() 会拒绝它们。
触发的事件只能使用 unsafeUnwrap(),切勿在也会接收真实流量的端点上使用。触发器名称的格式为 <category>.<event>,例如 payment.success、subscription.active 或 dispute.opened。
触发器名称不一定与载荷的 type 相同。 payment.success 会发送 type: "payment.succeeded",refund.success 会发送 refund.succeeded,而 licence.created 会发送 license_key.created。请在处理程序中根据载荷类型进行分支;case 'payment.success' 永远不会匹配。
与 listen 一样,直接模式下必须提供两个参数;在 TUI 中使用 /wh trigger 则会打开向导。
Webhook 签名验证
详见 webhook-integration 技能。请验证所有通过网络接收的 Webhook,无论是测试模式还是实时模式。唯一的例外是上文由 dodo wh trigger 生成的无签名载荷。
将产品从测试模式复制到实时模式
产品因环境而异。你不能在生产环境中重复使用测试产品 ID。请改为在实时模式下重新创建产品,或使用控制面板导出并重新导入。
手动方法:
- 记下测试模式下的产品详情(名称、价格、税务类别等)。
- 在实时模式下创建配置相同的新产品。
- 更新代码,使用新的实时产品 ID。
编程方法:
从测试模式获取产品,提取其配置,然后在实时模式下创建产品。API 密钥按模式区分——测试密钥无法通过实时模式的身份验证,反之亦然——因此需要两个不同的密钥:
// In test mode
const testClient = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_TEST_API_KEY,
environment: 'test_mode',
});
const testProduct = await testClient.products.retrieve('pdt_test_123');
// In live mode
const liveClient = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_LIVE_API_KEY,
environment: 'live_mode',
});
const liveProduct = await liveClient.products.create({
name: testProduct.name,
price: testProduct.price,
tax_category: testProduct.tax_category,
// ... other fields
});
上线检查清单
切换到生产环境前,请逐项确认:
企业与合规
- 已在 Dodo 控制面板完成 KYC(了解你的客户)验证。
- 企业验证已获批准。
- 控制面板中的企业信息正确无误。
API 与密钥
- 已从控制面板获取实时 API 密钥(前缀为
dodo_live_)。 - 已轮换或撤销意外提交到代码仓库的所有测试密钥。
- 代码使用
environment: 'live_mode'(或省略此项,因为实时模式是默认设置)。
Webhook
- Webhook 端点可通过 HTTPS 公开访问。
- 已在控制面板中将 Webhook URL 更新为生产域名。
- 已更新环境变量中的 Webhook 密钥。
- Webhook 处理程序使用
client.webhooks.unwrap()或standardwebhooks包验证签名。 - 已在生产环境(或使用实时密钥的预发布环境)中端到端测试 Webhook 流程。
产品与结账
- 所有产品均在实时模式下创建(而非从测试模式复制)。
- 结账代码使用实时产品 ID。
- 返回 URL 指向生产域名。
- 产品的税务类别正确无误。
监控与支持
- 已设置对支付失败、Webhook 投递和 API 错误的监控。
- 知道如何访问 Dodo 仪表板查看交易和争议。
- 已制定处理退款和拒付的计划。
常见错误
未设置 environment
SDK 默认使用 live_mode。如果在开发期间忘记设置 environment: 'test_mode',测试代码就会访问生产环境并产生真实扣款。
// WRONG — this hits live mode
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
});
// CORRECT
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: 'test_mode',
});
在生产环境中重复使用测试产品 ID
测试环境和生产环境完全隔离。测试模式中的产品 ID 在生产模式中不存在。请务必在生产模式中创建新产品,或使用仪表板迁移产品。
// WRONG — pdt_test_123 does not exist in live mode
const session = await liveClient.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_test_123', quantity: 1 }],
});
// CORRECT — use a live product ID
const session = await liveClient.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_live_456', quantity: 1 }],
});
忘记为生产环境重新配置 Webhook
测试模式和生产模式使用不同的 Webhook 端点和密钥。如果将测试 Webhook 配置复制到生产环境,却未更新 URL 和密钥,Webhook 将投递失败或发送到错误的位置。
- 在生产环境变量中更新
DODO_PAYMENTS_WEBHOOK_KEY。 - 在仪表板中将 Webhook URL 更新为生产域名。
- 在生产环境中测试 Webhook 投递。
在生产环境中未验证 Webhook 签名
请务必使用 client.webhooks.unwrap() 或 standardwebhooks 包验证 Webhook 签名。切勿信任未经验证的 Webhook,即使它来自 Dodo。
// WRONG — no verification
app.post('/webhook', (req, res) => {
const event = req.body;
handleEvent(event);
res.json({ received: true });
});
// CORRECT
app.post('/webhook', async (req, res) => {
try {
const unwrapped = client.webhooks.unwrap(req.body.toString(), {
headers: {
'webhook-id': req.headers['webhook-id'] as string,
'webhook-signature': req.headers['webhook-signature'] as string,
'webhook-timestamp': req.headers['webhook-timestamp'] as string,
},
});
handleEvent(unwrapped);
res.json({ received: true });
} catch (error) {
res.status(401).json({ error: 'Invalid signature' });
}
});
混用测试密钥和生产密钥
测试密钥(前缀为 dodo_test_)只能与 https://test.dodopayments.com 配合使用。生产密钥需从仪表板按模式签发。在生产模式中使用测试密钥会导致身份验证错误。