跳转到主要内容

Prerequisites

To integrate the Dodo Payments API, you’ll need:
  • A Dodo Payments merchant account
  • API credentials (API key and webhook secret key) from the dashboard
For a more detailed guide on the prerequisites, check this section.

API Integration

Checkout Sessions

Use Checkout Sessions to sell subscription products with a secure, hosted checkout. Pass your subscription product in product_cart and redirect customers to the returned checkout_url.
Mixed Checkout: You can combine subscription products with one-time products in the same checkout session. This enables use cases like setup fees with subscriptions, hardware bundles with SaaS, and more. See the Checkout Sessions guide for examples.
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 session = await client.checkoutSessions.create({
    product_cart: [
      { product_id: 'prod_subscription_monthly', quantity: 1 }
    ],
    // Optional: configure trials for subscription products
    subscription_data: { trial_period_days: 14 },
    customer: {
      email: 'subscriber@example.com',
      name: 'Jane Doe',
    },
    return_url: 'https://example.com/success',
  });

  console.log(session.checkout_url);
}

main();

API Response

The following is an example of the response:
{
  "session_id": "cks_Gi6KGJ2zFJo9rq9Ukifwa",
  "checkout_url": "https://test.checkout.dodopayments.com/session/cks_Gi6KGJ2zFJo9rq9Ukifwa"
}
Redirect the customer to checkout_url.

Webhooks

When integrating subscriptions, you’ll receive webhooks to track the subscription lifecycle. These webhooks help you manage subscription states and payment scenarios effectively. To set up your webhook endpoint, please follow our Detailed Integration Guide.

Subscription Event Types

The following webhook events track subscription status changes:
  1. subscription.active - Subscription is successfully activated.
  2. subscription.updated - Subscription object was updated (fires on any field change).
  3. subscription.on_hold - Subscription is put on hold due to failed renewal.
  4. subscription.failed - Subscription creation failed during mandate creation.
  5. subscription.renewed - Subscription is renewed for the next billing period.
For reliable subscription lifecycle management, we recommend tracking these subscription events.
Use subscription.updated to get real-time notifications about any subscription changes, keeping your application state in sync without polling the API.

Payment Scenarios

Successful Payment Flow When a payment succeeds, you’ll receive the following webhooks:
  1. subscription.active - Indicates subscription activation
  2. payment.succeeded - Confirms the initial payment:
    • For immediate billing (0 trial days): Expect within 2-10 minutes
    • For trial days: whenever that ends
  3. subscription.renewed - Indicates payment deduction and renewal for next cycle. (Basically, whenever payment gets deducted for subscription products, you will get subscription.renewed webhook along with payment.succeeded)
Payment Failure Scenarios
  1. Subscription Failure
  • subscription.failed - Subscription creation failed due to failure to create a mandate.
  • payment.failed - Indicates failed payment.
  1. Subscription On Hold
  • subscription.on_hold - Subscription is put on hold due to failed renewal payment or failed plan change charge.
  • When a subscription goes on hold, it will not renew automatically until the payment method is updated.
Best Practice: To simplify implementation, we recommend primarily tracking subscription events for managing the subscription lifecycle.
有关阅读 error_code/error_message、决定何时重试以及向客户报告失败的完整步骤,请参阅处理付款失败

subscription.failedsubscription.on_hold

这两个事件容易混淆,但需要非常不同的处理方式:
事件触发时间状态可恢复?处理方法
subscription.failed初始授权未能在订阅创建时创建failed不可恢复 — 终端状态不授予访问权限。请客户使用不同的付款方式开始一个订阅。
subscription.on_hold已激活订阅的续费付款(或计划更改费用)失败on_hold通过更新付款方式进行恢复;请参阅下面的处理暂停订阅
subscription.failed 是终端状态— 该订阅无法重新激活。客户必须创建新的订阅。此事件触发时绝不要授予权利。

处理暂停订阅

当订阅进入 on_hold 状态时,您需要更新付款方式以重新激活它。本节解释了订阅何时暂停以及如何处理它们。

何时订阅会暂停

订阅暂停的情况:
  • 续费付款失败:自动续费因资金不足、卡片过期或银行拒绝而失败
  • 计划更改费用失败:计划升级/降级期间的即时费用失败
  • 付款方式授权失败:该付款方式无法授权进行重复收费
处于 on_hold 状态的订阅不会自动续订。您必须更新付款方式以重新激活订阅。

重新激活暂停的订阅

要从 on_hold 状态重新激活订阅,请使用更新付款方式 API。这自动:
  1. 为剩余欠款生成收款
  2. 为费用生成发票
  3. 使用新的付款方式处理付款
  4. 付款成功后,将订阅重新激活为 active 状态
1

Handle subscription.on_hold webhook

接收到 subscription.on_hold webhook 时,更新您的应用程序状态并通知客户:
// Webhook handler
app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;
  
  if (event.type === 'subscription.on_hold') {
    const subscription = event.data;
    
    // Update subscription status in your database
    await updateSubscriptionStatus(subscription.subscription_id, 'on_hold');
    
    // Notify customer to update payment method
    await sendEmailToCustomer(subscription.customer_id, {
      subject: 'Payment Required - Subscription On Hold',
      message: 'Your subscription is on hold. Please update your payment method to continue service.'
    });
  }
  
  res.json({ received: true });
});
2

Update payment method

客户准备好更新付款方式时,请调用更新付款方式 API:
// Update with new payment method
const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
  type: 'new',
  return_url: 'https://example.com/return'
});

// For on_hold subscriptions, a charge is automatically created
if (response.payment_id) {
  console.log('Charge created for remaining dues:', response.payment_id);
  // Redirect customer to response.payment_link to complete payment
}
如果客户已保存付款方式,可以使用现有的付款方式 ID:
await client.subscriptions.updatePaymentMethod(subscriptionId, {
  type: 'existing',
  payment_method_id: 'pm_abc123'
});
3

Monitor webhook events

更新付款方式后,监控这些 webhook 事件:
  1. payment.succeeded - 剩余欠款的收费成功
  2. subscription.active - 订阅已重新激活
if (event.type === 'payment.succeeded') {
  const payment = event.data;
  
  // Check if this payment is for an on_hold subscription
  if (payment.subscription_id) {
    // Wait for subscription.active webhook to confirm reactivation
  }
}

if (event.type === 'subscription.active') {
  const subscription = event.data;
  
  // Update subscription status in your database
  await updateSubscriptionStatus(subscription.subscription_id, 'active');
  
  // Restore customer access
  await restoreCustomerAccess(subscription.customer_id);
  
  // Notify customer of successful reactivation
  await sendEmailToCustomer(subscription.customer_id, {
    subject: 'Subscription Reactivated',
    message: 'Your subscription has been reactivated successfully.'
  });
}

示例订阅事件负载


属性类型必需描述
business_idstring业务的唯一标识符
timestampstring事件发生的时间戳(不一定与交付时间相同)
typestring事件类型。请参阅事件类型
dataobject主要数据负载。请参阅数据对象

更改订阅计划

您可以使用更改计划 API 端点升级或降级订阅计划。这使您可以修改订阅的产品、数量以及处理分摊。

Change Plan API Reference

有关更改订阅计划的详细信息,请参阅我们的计划更改 API 文档。

分摊选项

更改订阅计划时,您有两种方式来处理即时收费:

1. prorated_immediately

  • 根据当前计费周期的剩余时间计算分摊金额
  • 仅对新旧计划之间的差额向客户收费
  • 在试用期内,这会立即将用户切换到新计划,并立即向客户收费

2. full_immediately

  • 对新计划的全部订阅金额向客户收费
  • 忽略之前计划的剩余时间或积分
  • 适用于您希望重置计费周期或无论分摊与否都要收取全部金额的情况

3. difference_immediately

  • 升级时,客户会立即支付两个计划金额差额。
  • 例如,如果当前计划为 30 美元并且客户升级到 80 美元,则他们会立即被收取 50 美元。
  • 降级时,当前计划的未使用金额会以内部积分形式添加,并自动应用于未来的订阅续期。
  • 例如,如果当前计划为 50 美元,而客户切换到 20 美元计划,则剩余的 30 美元将被计入,并用于下一计费周期。

行为

  • 调用此 API 时,Dodo Payments 会立即根据您选择的分摊选项发起收费
  • 如果计划更改是降级并且您使用 prorated_immediately,积分将被自动计算并添加到订阅的积分余额中。这些积分特定于该订阅,仅用于抵消相同订阅的未来重复付款
  • full_immediately 选项绕过积分计算并收取完整的新计划金额
谨慎选择您的分摊选项:使用 prorated_immediately 进行公平的计费,考虑到未使用的时间,或当您希望无论当前计费周期如何都收取完整的新计划金额时使用 full_immediately

收费处理

  • 更改计划时发起的即时收费通常在不到2分钟内完成处理
  • 如果此即时费用因任何原因失败,订阅会被自动暂停,直至问题解决

按需订阅

按需订阅允许您灵活地向客户收费,而不仅仅是按固定时间表。这项功能适用于所有帐户。
创建按需订阅: 要创建按需订阅,请使用POST /subscriptions API 端点,并在请求体中包含 on_demand 字段。这使您可以授权不立即收费的付款方式,或设置自定义初始价格。 对按需订阅收费: 对于后续收费,请使用POST /subscriptions//charge端点并指定向客户收费的金额。
有关完整的分步指南,包括请求/响应示例、安全重试策略和 webhook 处理,请参阅按需订阅指南

相关 API 参考

Create Subscription

创建订阅产品和管理订阅生命周期的 API 参考

Change Subscription Plan

升级、降级或更改订阅计划的 API 参考以及分摊选项

Update Payment Method

更新付款方式和重新激活暂停订阅的 API 参考

Patch Subscription

更新订阅详情和配置的 API 参考
最后修改于 2026年6月18日