概览
按需订阅允许您一次性授权客户的支付方式,然后在需要时收取可变金额,而不是固定的日程表。所有账户都可使用此功能,无需审批。 使用本指南:- 创建按需订阅(授权带有可选初始价格的授权)
- 通过自定义金额触发后续收费
- 使用webhook跟踪结果
先决条件
- Dodo Payments商家账户和API密钥
- 配置的webhook密钥和接收事件的端点
- 目录中的订阅产品
按需如何运作
- 您使用
on_demand对象创建订阅以授权支付方式,并可选择收取初始费用。 - 随后,您可以使用专用的收费端点根据该订阅创建自定义金额的收费。
- 您可以监听webhook(例如,
payment.succeeded,payment.failed)来更新您的系统。
创建按需订阅
端点:POST /checkouts 关键请求字段(正文):请查看创建结账会话
创建按需订阅
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
按需订阅的收费
授权授权后,按需创建所需的费用。 端点:POST /subscriptions/{subscription_id}/charge 关键请求字段(正文):- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
处理扣款失败
当对按需 subscription 的扣款失败时,接下来如何处理由你决定。与定期 subscription 不同——定期 subscription 的续期失败后会停止后续自动计费——按需 subscription 在失败后仍可继续扣款。你可以根据自己的重试逻辑再次调用扣款 endpoint。失败时会发生什么
1
Charge attempt fails
POST /subscriptions/{subscription_id}/charge 请求要么返回错误响应,要么异步完成,并发送包含拒付原因的 payment.failed webhook。2
Subscription may transition to on_hold
subscription 可能会进入
on_hold 状态,并发送 subscription.on_hold webhook(参见 Subscription States → On Hold)。这是一种信号,而不是锁定状态。对于按需 subscription,on_hold 不会阻止你再次扣款。3
Retry the charge (your call)
对于按需流程,Dodo 不会自动重试。你可以随时再次调用
POST /subscriptions/{subscription_id}/charge 进行重试。请应用下方的安全重试策略——使用指数退避、跳过硬拒付并避免突发式重试——以免重试被我们的欺诈和风险系统标记。4
Optionally, ask the customer for a new payment method
如果由于 payment method 本身存在问题(卡片过期、账户已关闭等)而导致重试持续失败,请使用
POST /subscriptions/{subscription_id}/update-payment-method 向客户收集新的 payment method。成功后,subscription 会返回 active,并依次发送 payment.succeeded 和 subscription.active webhook。按需与定期的区别:对于定期 subscription,Dodo 会执行自身的续期重试和催收。对于按需 subscription,重试策略由你负责,因为只有你知道下一次扣款应在何时发生(它由使用量事件驱动,而不是日历驱动)。
按需扣款失败时的 webhook 顺序
事件 3 和 4 仅在后续扣款成功后触发。
重试责任
Subscription Dunning——内置的邮件恢复序列——仅适用于定期 subscription 的失败续期付款以及客户发起的取消。它并非为按需扣款失败而设计。当你判断 payment method 需要更新时,请直接与客户沟通(例如通过事务性邮件或应用内提示)。付款重试
我们的欺诈检测系统可能会阻止激进的重试模式(并将其标记为潜在的盗刷测试)。请遵循安全重试策略。安全重试策略的原则
- 退避机制:在重试之间使用指数退避。
- 重试次数限制:限制总重试次数(最多 3–4 次尝试)。
- 智能筛选:仅对可重试的失败进行重试(例如网络错误、发卡行错误、余额不足);绝不重试硬拒付。
- 防止盗刷测试:不要重试诸如
DO_NOT_HONOR、STOLEN_CARD、LOST_CARD、PICKUP_CARD、FRAUDULENT、AUTHENTICATION_FAILURE等失败。 - 改变 metadata(可选):如果你维护自己的重试系统,可通过 metadata 区分重试(例如
retry_attempt)。
建议的重试计划(subscriptions)
- 第 1 次尝试:创建扣款时立即执行
- 第 2 次尝试:3 天后
- 第 3 次尝试:再过 7 天(共 10 天)
- 第 4 次尝试(最后一次):再过 7 天(共 17 天)
避免突发式重试;与授权时间对齐
- 将重试锚定到最初的授权时间戳,以避免整个客户组合出现“突发式”行为。
- 示例:如果客户今天下午 1:10 开始试用或 mandate,请根据退避计划在后续日期的下午 1:10 安排重试(例如 +3 天 → 下午 1:10,+7 天 → 下午 1:10)。
- 或者,如果你存储了上次成功付款的时间
T,请将下一次尝试安排在T + X days,以保持时间对齐。
时区和 DST:使用一致的时间标准进行调度,仅在显示时进行转换,以保持时间间隔。
不应重试的拒付代码
STOLEN_CARDDO_NOT_HONORFRAUDULENTPICKUP_CARDAUTHENTICATION_FAILURELOST_CARD
如需查看完整的拒付原因列表以及这些原因是否可由用户修正,请参阅
Transaction Failures 文档。
实施指南(无代码)
- 使用能够持久化精确时间戳的 scheduler/queue;在准确的时间偏移处计算下一次尝试(例如
T + 3 days,在相同的 HH:MM 执行)。 - 维护并参考上次成功付款的时间戳
T来计算下一次尝试;不要让多个 subscription 在同一时刻集中重试。 - 始终评估上次拒付原因;对于上方跳过列表中的硬拒付,停止重试。
- 限制每位客户和每个账户的并发重试次数,防止意外激增。
- 主动沟通:在下一次计划尝试前,通过电子邮件/SMS 通知客户更新其 payment method。
- 仅将 metadata 用于可观测性(例如
retry_attempt);绝不要通过轮换无关字段来试图“规避”欺诈/风险系统。
取消
按需 subscription 的取消流程与定期 subscription 不同,因为没有固定的计费周期可用于确定立即结束日期。Customer Portal 行为
当客户从 Customer Portal 取消按需 subscription 时,默认情况下,取消操作会安排在下一计费日期执行。按需 subscription 不会显示立即取消选项。 原因在于:按需 subscription 没有可预测的周期性续期日期——下一次扣款时间完全由你的使用量事件驱动。在下一计费日期取消,可以让 mandate 保持有效直到周期边界,从而仍可对正在处理的使用量进行扣款,然后干净地结束 subscription。 客户确认取消后:- subscription 保持
active状态,并可通过POST /subscriptions/{id}/charge继续扣款,直到计划的取消日期。 - subscription 上的
cancel_at_next_billing_date被设置为true。 - 取消生效时会发送
subscription.cancelledwebhook。
如果你需要立即结束 subscription(例如响应退款或支持请求),请通过 API 以编程方式取消,而不要依赖 Customer Portal 流程。
以编程方式取消
你可以随时通过 API 取消按需 subscription。取消是立即执行还是计划执行,由你控制。 Endpoint:PATCH /subscriptions/{subscription_id}- Cancel immediately
- Cancel at next billing date
将 subscription 的
status 设置为 cancelled,即可立即结束 subscription。mandate 将被撤销,之后无法再创建扣款。cURL
取消时的 Webhooks
使用 webhooks 跟踪结果
实施 webhook 处理,以跟踪客户旅程。请参阅 Implementing Webhooks。- subscription.active:mandate 已授权且 subscription 已激活
- subscription.failed:创建失败(例如 mandate 失败)
- subscription.on_hold:subscription 被置于暂停状态(例如未付款状态)
- subscription.cancelled:subscription 已完全取消(参见取消)
- payment.succeeded:扣款成功
- payment.failed:扣款失败
测试和后续步骤
1
Create in test mode
使用 test API key 创建 subscription,然后打开返回的
checkout_url 并完成 mandate。2
Trigger a charge
使用较小的
product_price 调用扣款 endpoint(例如 100),并确认收到 payment.succeeded。3
Go live
验证事件和内部状态更新后,切换到 live API key。
故障排查
- 422 Invalid Request:确保在创建时提供
on_demand.mandate_only,并在扣款时提供product_price。 - 货币错误:如果覆盖
product_currency,请确认该货币受你的账户和客户支持。 - 未收到 webhooks:验证 webhook URL 和签名密钥配置。