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
Prerequisites
- Dodo Payments merchant account and API key
- Webhook secret configured and an endpoint to receive events
- A subscription product in your catalog
How on-demand works
- You create a subscription with the
on_demandobject to authorize a payment method and optionally collect an initial charge. - Later, you create charges against that subscription with custom amounts using the dedicated charge endpoint.
- 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
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
Charge an on-demand subscription
After the mandate is authorized, create charges as needed. Endpoint: POST /subscriptions/{subscription_id}/charge Key request fields (body):Charge request body parameters
Charge request body parameters
Amount to charge (in the smallest currency unit). Example: to charge $25.00, pass
2500.Optional currency override for the charge.
Optional description override for this charge.
If true, includes adaptive currency fees within
product_price. If false, fees are added on top.Additional metadata for the payment. If omitted, the subscription metadata is used.
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
Handling failed charges
When a charge against an on-demand subscription fails, you decide what happens next. Unlike scheduled subscriptions — where a failed renewal stops further automatic billing — on-demand subscriptions remain chargeable after a failure. You can call the charge endpoint again as part of your own retry logic.What happens on failure
Charge attempt fails
The
POST /subscriptions/{subscription_id}/charge request either returns an error response or completes asynchronously and emits a payment.failed webhook with the decline reason.Subscription may transition to on_hold
The subscription may move to the
on_hold state and emit a subscription.on_hold webhook (see Subscription States → On Hold). This is a signal — not a lock. For on-demand subscriptions, on_hold does not prevent you from charging again.Retry the charge (your call)
For on-demand flows, Dodo does not auto-retry. You can call
POST /subscriptions/{subscription_id}/charge again at any time to retry. Apply the safe retry policy below — use exponential backoff, skip hard declines, and avoid burst patterns — so retries are not flagged by our fraud and risk systems.Optionally, ask the customer for a new payment method
Nếu các lần thử lại không thành công vì phương thức thanh toán bị lỗi (thẻ hết hạn, tài khoản đóng, v.v.), sử dụng
POST /subscriptions/{subscription_id}/update-payment-method để thu thập phương thức mới từ khách hàng. Khi thành công, đăng ký sẽ trở về active và payment.succeeded tiếp theo sẽ gửi các webhook subscription.active.On-demand vs scheduled: For scheduled subscriptions, Dodo runs its own renewal retries and dunning. For on-demand subscriptions, you own the retry policy because only you know when the next charge should occur (it’s driven by your usage events, not a calendar).
Webhook sequence on a failed on-demand charge
| Order | Event | Meaning |
|---|---|---|
| 1 | payment.failed | The on-demand charge attempt did not succeed (includes the decline reason) |
| 2 | subscription.on_hold | The subscription was placed on hold (informational; does not block further charges) |
| 3* | payment.succeeded | A subsequent charge — either your retry or after a payment method update — succeeded |
| 4* | subscription.active | The subscription returned to active after a successful charge |
Events 3 and 4 only fire after a follow-up charge succeeds.
Retry responsibility
Subscription Dunning — the built-in email recovery sequence — is scoped to failed renewal payments on scheduled subscriptions and customer-initiated cancellations. It is not designed for on-demand charge failures. Communicate with the customer directly (e.g., transactional email or in-app prompt) when you decide the payment method needs to be updated.Payment retries
Our fraud detection system may block aggressive retry patterns (and can flag them as potential card testing). Follow a safe retry policy.Principles for safe retry policies
- Backoff mechanism: Use exponential backoff between retries.
- Retry limits: Cap total retries (3–4 attempts max).
- Intelligent filtering: Retry only on retryable failures (e.g., network/issuer errors, insufficient funds); never retry hard declines.
- Card testing prevention: Do not retry failures like
DO_NOT_HONOR,STOLEN_CARD,LOST_CARD,PICKUP_CARD,FRAUDULENT,AUTHENTICATION_FAILURE. - Vary metadata (optional): If you maintain your own retry system, differentiate retries via metadata (e.g.,
retry_attempt).
Suggested retry schedule (subscriptions)
- 1st attempt: Immediate when you create the charge
- 2nd attempt: After 3 days
- 3rd attempt: After 7 more days (10 days total)
- 4th attempt (final): After another 7 days (17 days total)
Avoid burst retries; align to authorization time
- Anchor retries to the original authorization timestamp to avoid “burst” behavior across your portfolio.
- Example: If the customer starts a trial or mandate at 1:10 pm today, schedule follow-up retries at 1:10 pm on subsequent days per your backoff (e.g., +3 days → 1:10 pm, +7 days → 1:10 pm).
- Alternatively, if you store the last successful payment time
T, schedule the next attempt atT + X daysto preserve time-of-day alignment.
Time-zone and DST: use a consistent time standard for scheduling and convert for display only to maintain intervals.
Decline codes you should not retry
STOLEN_CARDDO_NOT_HONORFRAUDULENTPICKUP_CARDAUTHENTICATION_FAILURELOST_CARD
For a comprehensive list of decline reasons and whether they are user-correctable, see the
Transaction Failures documentation.
Implementation guidelines (no code)
- Use a scheduler/queue that persists precise timestamps; compute next attempt at the exact time-of-day offset (e.g.,
T + 3 daysat the same HH:MM). - Maintain and reference the last successful payment timestamp
Tto compute the next attempt; do not bunch multiple subscriptions at the same instant. - Always evaluate the last decline reason; stop retries for hard declines in the skip list above.
- Cap concurrent retries per customer and per account to prevent accidental surges.
- Communicate proactively: email/SMS the customer to update their payment method before the next scheduled attempt.
- Use metadata only for observability (e.g.,
retry_attempt); never try to “evade” fraud/risk systems by rotating inconsequential fields.
Track outcomes with webhooks
Implement webhook handling to track the customer journey. See Implementing Webhooks.- subscription.active: Mandate authorized and subscription activated
- subscription.failed: Creation failed (e.g., mandate failure)
- subscription.on_hold: Subscription placed on hold (e.g., unpaid state)
- payment.succeeded: Charge succeeded
- payment.failed: Charge failed
Testing and next steps
Create in test mode
Use your test API key to create the subscription with
payment_link: true, then open the link and complete the mandate.Trigger a charge
Call the charge endpoint with a small
product_price (e.g., 100) and verify you receive payment.succeeded.Troubleshooting
- 422 Invalid Request: Ensure
on_demand.mandate_onlyis provided on creation andproduct_priceis provided for charges. - Currency errors: If you override
product_currency, confirm it’s supported for your account and customer. - No webhooks received: Verify your webhook URL and signature secret configuration.
- Cancel immediately
- Cancel at next billing date
Đặt đăng ký
status thành cancelled để kết thúc ngay lập tức. Ủy quyền bị thu hồi và không thể tạo thêm các khoản phí nào khác.cURL
Webhooks khi hủy bỏ
| Sự kiện | Khi nó kích hoạt |
|---|---|
subscription.cancelled | Đăng ký bị hủy hoàn toàn và không còn có thể tính phí |
subscription.plan_changed | cancel_at_next_billing_date đã được chuyển đổi (hủy theo lịch đã thiết lập hoặc hoàn tác) |
Theo dõi kết quả với webhooks
Thực hiện xử lý webhook để theo dõi hành trình khách hàng. Xem Thực hiện Webhooks.- subscription.active: Ủy quyền được xác nhận và đăng ký được kích hoạt
- subscription.failed: Tạo không thành công (ví dụ, lỗi ủy quyền)
- subscription.on_hold: Đăng ký bị tạm dừng (ví dụ, trạng thái chưa thanh toán)
- subscription.cancelled: Đăng ký bị hủy hoàn toàn (xem Hủy Bỏ)
- payment.succeeded: Tính phí thành công
- payment.failed: Tính phí không thành công
Kiểm tra và bước tiếp theo
Create in test mode
Sử dụng khóa API thử nghiệm của bạn để tạo đăng ký với
payment_link: true, sau đó mở liên kết và hoàn thành yêu cầu.Trigger a charge
Gọi endpoint tính phí với một
product_price nhỏ (ví dụ, 100) và xác nhận bạn nhận được payment.succeeded.Khắc phục sự cố
- 422 Yêu cầu Không hợp lệ: Đảm bảo
on_demand.mandate_onlyđược cung cấp khi tạo vàproduct_priceđược cung cấp cho các khoản phí. - Lỗi tiền tệ: Nếu bạn ghi đè
product_currency, xác nhận nó được hỗ trợ cho tài khoản của bạn và khách hàng. - Không nhận được webhook: Xác minh URL webhook của bạn và cấu hình ký bí mật.