跳转到主要内容

Documentation Index

Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

On-demand subscriptions let you authorize a customer’s payment method once and then charge variable amounts whenever you need, instead of on a fixed schedule. This feature is available for all accounts—no approval required. Use this guide to:
  • Create an on-demand subscription (authorize a mandate with optional initial price)
  • Trigger subsequent charges with custom amounts
  • Track outcomes using webhooks
For a general subscription setup, see the Subscription Integration Guide.

Prerequisites

  • Dodo Payments merchant account and API key
  • Webhook secret configured and an endpoint to receive events
  • A subscription product in your catalog
If you want the customer to approve the mandate via hosted checkout, set payment_link: true and provide a return_url.

How on-demand works

  1. You create a subscription with the on_demand object to authorize a payment method and optionally collect an initial charge.
  2. Later, you create charges against that subscription with custom amounts using the dedicated charge endpoint.
  3. You listen to webhooks (e.g., payment.succeeded, payment.failed) to update your system.

Create an on-demand subscription

Endpoint: POST /checkouts Key request fields (body):
Please find them in Create Checkout Session

Create an on-demand subscription

import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: 'test_mode', // defaults to 'live_mode'
});

async function main() {
  const subscription = await client.checkoutSessions.create({
    product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
    billing_address:  { city: 'SF', country: 'US', state: 'CA', street: '1 Market St', zipcode: '94105' },
    customer: { customer_id: 'cus_123' },
    return_url: 'https://example.com/billing/success',
    subscription_data: {
        on_demand: {
            mandate_only: true // set false to collect an initial charge
            // product_price: 1000, // optional: charge $10.00 now if mandate_only is false
            // product_currency: 'USD',
            // product_description: 'Custom initial charge',
            // adaptive_currency_fees_inclusive: false,
        }
    }
  });

  console.log(subscription.checkout_url);
}

main().catch(console.error);
Success
{
  "session_id": "cks_123",
  "checkout_url": "https://test.checkout.dodopayments.com/session/cks123"
}

Charge an on-demand subscription

After the mandate is authorized, create charges as needed. Endpoint: POST /subscriptions/{subscription_id}/charge Key request fields (body):
product_price
integer
必填
Amount to charge (in the smallest currency unit). Example: to charge $25.00, pass 2500.
product_currency
string
Optional currency override for the charge.
product_description
string
Optional description override for this charge.
adaptive_currency_fees_inclusive
boolean
If true, includes adaptive currency fees within product_price. If false, fees are added on top.
metadata
object
Additional metadata for the payment. If omitted, the subscription metadata is used.
import DodoPayments from 'dodopayments';

const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY });

async function chargeNow(subscriptionId) {
  const res = await client.subscriptions.charge(subscriptionId, { product_price: 2500 });
  console.log(res.payment_id);
}

chargeNow('sub_123').catch(console.error);
Success
{ "payment_id": "pay_abc123" }
Charging a subscription that is not on-demand may fail. Ensure the subscription has on_demand: true in its details before charging.

处理失败的扣费

当一次按需订阅的扣费失败时,您可以决定接下来的操作。不像定期订阅,失败的续订会终止后续的自动扣费,按需订阅在失败后仍可扣费。您可以再次调用扣费端点,作为您自己的重试逻辑的一部分。

失败时会发生什么

请求要么返回错误响应,要么异步完成并发送包含拒绝原因的 webhook。 订阅可能会进入待处理状态并发送一个 webhook(请参见订阅状态 → 待处理)。这是一种信号,而不是锁定。对于按需订阅,这不会阻止您再次扣费。 对于按需流程,Dodo 不会自动重试。您可以随时再次调用来重试。应用下面的安全重试策略 — 使用指数退避,跳过硬拒绝,避免突发模式 — 以防重试被我们的欺诈和风险系统标记。 如果重试不断失败,因为支付方式本身已失效(如卡过期、账户关闭等),请使用更新支付方式从顾客处收集新的支付方式。成功后,订阅返回到已激活的状态,并发送相应的 webhook。 按需 vs 定期:对于定期订阅,Dodo 会进行其自身的续订重试和催缴。对于按需订阅,重试策略由您掌控,因为只有您知道下一次扣费的准确时间(这是由您的使用事件驱动的,而不是日历)。

失败的按需扣费的 Webhook 顺序

序号事件含义
1尝试按需扣费未成功(包含拒绝原因)
2订阅被置于待处理状态(信息性;不阻止进一步扣费)
3*后续扣费—重试或支付方式更新后—成功
4*成功扣费后,订阅恢复正常状态
事件 3 和 4 仅在后续扣费成功后触发。

重试责任

Dodo Payments 不会自动重试失败的按需扣费。重试策略由您掌控。遵循以下安全重试指南,以避免被我们的欺诈检测系统标记为测试卡。 订阅催缴 — 内置的邮件恢复序列 — 针对定期订阅的失败续订付款和用户发起的取消。它不适用于按需扣费失败。当您决定需要更新支付方式时,请直接与客户沟通(例如,通过交易邮件或应用内提示)。

付款重试

我们的欺诈检测系统可能会阻止激进的重试模式(并可能将其标记为潜在的测试卡)。请遵循安全重试政策。 突发重试模式可能会被我们的风险系统和处理器标记为欺诈或疑似测试卡。请避免集中的重试;遵循下面的退避计划和时间对齐指导。

安全重试策略原则

  • 退避机制:在重试之间使用指数退避。
  • 重试限制:限制总重试次数(最多3–4次)。
  • 智能过滤:仅在可重试的失败上重试(例如,网络/发行方错误,资金不足);切勿重试硬拒绝。
  • 防止测试卡:不要重试以下失败。
  • 可变元数据(可选):如果您维护自己的重试系统,通过元数据区分重试。

建议的重试计划(订阅)

  • 第1次尝试:创建扣费时立即进行
  • 第2次尝试:3天后
  • 第3次尝试:再过7天(共10天)
  • 第4次尝试(最终):再过7天(共17天)
最后一步:如果仍未付款,根据您的政策将订阅标记为未支付或取消。在期间通知客户更新其支付方式。

避免突发重试;与授权时间对齐

  • 将重试锚定在原始授权时间戳,以避免在您的平台上出现“突发”行为。
  • 示例:如果客户今天下午1:10开始试用或授权,按退避计划在后续天的下午1:10安排重试(例如,+3天 → 下午1:10,+7天 → 下午1:10)。
  • 或者,如果您存储了上次成功付款时间,安排下次尝试,以保持时间对齐。
时区和DST:使用一致的时间标准进行调度,显示时转换以保持间隔。

不应重试的拒绝代码

有关详细的拒绝原因及其是否可由用户纠正,请参见交易失败文档。 仅在软/临时问题(如,网络超时)上重试。如果同样的拒绝再次出现,则暂停进一步重试。

实施指南(无代码)

  • 使用调度器/队列持久化精确时间戳;在相同的HH:MM偏移量计算下次尝试。
  • 维护并参考上次成功支付时间戳以计算下次尝试;避免在同一时刻集中多个订阅。
  • 始终评估上次拒绝原因;停止对上述跳过列表中的硬拒绝重试。
  • 限制每位客户和每个账户的并发重试,以防止意外激增。
  • 积极沟通:通过电子邮件/短信通知客户在下次计划尝试前更新其支付方式。
  • 仅将元数据用于可观察性;切勿试图通过旋转无关字段来“规避”欺诈/风险系统。

通过 Webhook 跟踪结果

实现 Webhook 处理以跟踪客户旅程。请参见实现 Webhooks
  • subscription.active: 授权成功且订阅已激活
  • subscription.failed: 创建失败(例如,授权失败)
  • subscription.on_hold: 订阅被置于待处理状态(例如,未支付状态)
  • payment.succeeded: 扣费成功
  • payment.failed: 扣费失败
对于按需流程,关注基于使用的扣费。当后随时,请参见处理失败的扣费以恢复订阅。

测试和下一步

使用您的测试 API 密钥创建订阅,然后打开链接完成授权。调用带有小金额的扣费端点(例如),并验证您收到。

故障排除

  • 422 无效请求:确保在创建时提供,在扣费时提供。
  • 货币错误:如果您覆盖,请确认它是否支持您的账户和客户。
  • 未收到 Webhooks:验证您的 Webhook URL 和签名密钥配置。
Last modified on May 14, 2026