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
Cara Kerja On-Demand
- 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.
Membuat Subscription On-Demand
Endpoint: POST /checkouts Key request fields (body):Please find them in Create Checkout Session
Membuat Subscription On-Demand
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
Menagih Subscription On-Demand
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
integer
wajib
Jumlah yang akan ditagih (dalam unit terkecil mata uang). Contoh: untuk menagih $25.00, teruskan
2500.string
Optional currency override for the charge.
string
Optional description override for this charge.
boolean
If true, includes adaptive currency fees within
product_price. If false, fees are added on top.object
Tentukan bagaimana saldo wallet pelanggan digunakan untuk menyelesaikan charge ini.
object
Metadata tambahan untuk payment. Jika dihilangkan, metadata subscription akan digunakan.
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
Menangani Charge yang Gagal
Saat charge terhadap subscription on-demand gagal, Anda menentukan tindakan berikutnya. Tidak seperti subscription terjadwal — ketika renewal yang gagal menghentikan billing otomatis berikutnya — subscription on-demand tetap dapat di-charge setelah kegagalan. Anda dapat memanggil endpoint charge lagi sebagai bagian dari logika retry Anda sendiri.Yang Terjadi Saat Gagal
1
Charge attempt fails
Request
POST /subscriptions/{subscription_id}/charge akan mengembalikan error response atau selesai secara asynchronous dan mengirimkan webhook payment.failed dengan alasan penolakan.2
Subscription may transition to on_hold
Subscription dapat berpindah ke status
on_hold dan mengirim webhook subscription.on_hold (lihat Status Subscription → On Hold). Ini adalah sinyal—bukan penguncian. Untuk subscription on-demand, on_hold tidak mencegah Anda menagih lagi. Charge baru ditolak dengan 409 saat pembayaran sebelumnya masih tertunda, dan dengan 429 setelah lebih dari empat pembayaran gagal sejak pembayaran terakhir yang berhasil.3
Retry the charge (your call)
Untuk flow on-demand, Dodo tidak melakukan auto-retry. Anda dapat memanggil
POST /subscriptions/{subscription_id}/charge lagi kapan saja untuk retry. Terapkan safe retry policy di bawah — gunakan exponential backoff, lewati hard decline, dan hindari pola burst — agar retry tidak ditandai oleh sistem fraud dan risk kami.4
Optionally, ask the customer for a new payment method
Jika retry terus gagal karena payment method itu sendiri bermasalah (kartu kedaluwarsa, akun ditutup, dan sebagainya), gunakan
POST /subscriptions/{subscription_id}/update-payment-method untuk mengumpulkan payment method baru dari pelanggan. Setelah berhasil, subscription kembali ke active dan webhook payment.succeeded diikuti oleh subscription.active akan dikirimkan.On-demand vs terjadwal: Untuk subscription terjadwal, Dodo menjalankan retry renewal dan dunning-nya sendiri. Untuk subscription on-demand, Anda bertanggung jawab atas retry policy karena hanya Anda yang mengetahui kapan charge berikutnya harus dilakukan (hal ini didorong oleh event penggunaan Anda, bukan kalender).
Urutan Webhook pada Charge On-Demand yang Gagal
Event 3 dan 4 hanya dipicu setelah charge lanjutan berhasil.
Tanggung Jawab Retry
Subscription Dunning — rangkaian recovery melalui email bawaan — ditujukan untuk payment renewal yang gagal pada subscription terjadwal dan pembatalan yang dilakukan pelanggan. Fitur ini tidak dirancang untuk kegagalan charge on-demand. Berkomunikasilah langsung dengan pelanggan (misalnya melalui email transaksional atau prompt dalam aplikasi) saat Anda memutuskan bahwa payment method perlu diperbarui.Retry Pembayaran
Sistem deteksi fraud kami dapat memblokir pola retry yang agresif (dan menandainya sebagai kemungkinan card testing). Ikuti safe retry policy.Prinsip Kebijakan Retry yang Aman
- Mekanisme backoff: Gunakan exponential backoff di antara retry.
- Batas retry: Batasi jumlah retry (maksimal 3–4 upaya).
- Pemfilteran cerdas: Lakukan retry hanya pada kegagalan yang dapat di-retry (misalnya error network/issuer, saldo tidak mencukupi); jangan pernah melakukan retry pada hard decline.
- Pencegahan card testing: Jangan melakukan retry pada kegagalan seperti
DO_NOT_HONOR,STOLEN_CARD,LOST_CARD,PICKUP_CARD,FRAUDULENT,AUTHENTICATION_FAILURE. - Variasikan metadata (opsional): Jika Anda memelihara sistem retry sendiri, bedakan retry melalui metadata (misalnya
retry_attempt).
Jadwal Retry yang Disarankan (Subscription)
- Upaya ke-1: Segera saat Anda membuat charge
- Upaya ke-2: Setelah 3 hari
- Upaya ke-3: Setelah 7 hari berikutnya (total 10 hari)
- Upaya ke-4 (terakhir): Setelah 7 hari berikutnya (total 17 hari)
Hindari Retry Bertubi-tubi; Selaraskan dengan Waktu Otorisasi
- Jadikan timestamp authorization awal sebagai acuan retry untuk menghindari perilaku “burst” di seluruh portofolio Anda.
- Contoh: Jika pelanggan memulai trial atau mandate pada pukul 13.10 hari ini, jadwalkan retry lanjutan pada pukul 13.10 di hari-hari berikutnya sesuai backoff Anda (misalnya, +3 hari → 13.10, +7 hari → 13.10).
- Atau, jika Anda menyimpan waktu payment berhasil terakhir
T, jadwalkan upaya berikutnya padaT + X daysuntuk mempertahankan penyelarasan waktu dalam sehari.
Time zone dan DST: gunakan standar waktu yang konsisten untuk penjadwalan dan lakukan konversi hanya untuk tampilan agar interval tetap terjaga.
Kode Penolakan yang Tidak Boleh Di-retry
STOLEN_CARDDO_NOT_HONORFRAUDULENTPICKUP_CARDAUTHENTICATION_FAILURELOST_CARD
Untuk daftar lengkap alasan penolakan dan apakah alasan tersebut dapat diperbaiki oleh pengguna, lihat dokumentasi
Transaction Failures.
Panduan Implementasi (Tanpa Kode)
- Gunakan scheduler/queue yang menyimpan timestamp secara presisi; hitung upaya berikutnya pada offset waktu yang sama persis (misalnya
T + 3 dayspada HH:MM yang sama). - Simpan dan gunakan timestamp payment berhasil terakhir
Tuntuk menghitung upaya berikutnya; jangan mengelompokkan beberapa subscription pada waktu yang sama persis. - Selalu evaluasi alasan penolakan terakhir; hentikan retry untuk hard decline dalam daftar pengecualian di atas.
- Batasi retry bersamaan per pelanggan dan per akun untuk mencegah lonjakan yang tidak disengaja.
- Berkomunikasilah secara proaktif: kirim email/SMS kepada pelanggan agar memperbarui payment method mereka sebelum upaya terjadwal berikutnya.
- Gunakan metadata hanya untuk observability (misalnya
retry_attempt); jangan pernah mencoba “menghindari” sistem fraud/risk dengan memutar field yang tidak penting.
Pembatalan
Subscription on-demand mengikuti flow pembatalan yang berbeda dari subscription terjadwal karena tidak ada siklus billing tetap yang dapat menjadi acuan tanggal berakhir segera.Perilaku Customer Portal
Saat pelanggan membatalkan subscription on-demand dari Customer Portal, pembatalan secara default dijadwalkan untuk tanggal billing berikutnya. Opsi Cancel Now sengaja tidak ditampilkan untuk subscription on-demand. Alasannya: subscription on-demand tidak memiliki tanggal renewal berulang yang dapat diprediksi — waktu charge berikutnya sepenuhnya ditentukan oleh event penggunaan Anda. Menjadwalkan pembatalan pada tanggal billing berikutnya membuat mandate tetap aktif hingga batas periode sehingga penggunaan yang sedang berjalan tetap dapat di-charge, lalu mengakhiri subscription dengan benar. Setelah pelanggan mengonfirmasi pembatalan:- Subscription tetap dalam state
activedan tetap dapat di-charge melaluiPOST /subscriptions/{id}/chargehingga tanggal pembatalan terjadwal. cancel_at_next_billing_datediatur ketruepada subscription.- Webhook
subscription.cancelleddikirimkan saat pembatalan mulai berlaku.
Jika Anda perlu mengakhiri subscription segera (misalnya sebagai respons terhadap refund atau permintaan dukungan), batalkan secara programmatic melalui API, bukan dengan mengandalkan flow Customer Portal.
Membatalkan Secara Programatis
Anda dapat membatalkan subscription on-demand melalui API kapan saja. Anda mengontrol apakah pembatalan dilakukan segera atau dijadwalkan. Endpoint: PATCH /subscriptions/{subscription_id}- Cancel immediately
- Cancel at next billing date
Atur
status subscription ke cancelled untuk segera mengakhirinya. Mandate dicabut dan tidak ada charge berikutnya yang dapat dibuat.cURL
Webhook saat Pembatalan
Menetapkan atau menghapus
cancel_at_next_billing_date tidak mengirim webhook khusus. Untuk melacak pembatalan terjadwal, baca cancel_at_next_billing_date dari respons API atau dari payload subscription.updated berikutnya.
Lacak Hasil dengan Webhook
Implementasikan penanganan webhook untuk melacak perjalanan customer. Lihat Webhooks.- subscription.active: Mandate diotorisasi dan subscription diaktifkan
- subscription.failed: Pembuatan gagal (misalnya, kegagalan mandate)
- subscription.on_hold: Subscription ditangguhkan (misalnya, status belum dibayar)
- subscription.cancelled: Subscription dibatalkan sepenuhnya (lihat Pembatalan)
- payment.succeeded: Charge berhasil
- payment.failed: Charge gagal
Pengujian dan Langkah Berikutnya
1
Create in test mode
Gunakan API key pengujian Anda untuk membuat subscription, lalu buka
checkout_url yang dikembalikan dan selesaikan mandate.2
Trigger a charge
Panggil endpoint charge dengan
product_price kecil (misalnya, 100) dan verifikasi bahwa Anda menerima payment.succeeded.3
Go live
Beralih ke API key live setelah Anda memvalidasi event dan pembaruan state internal.
Pemecahan Masalah
- 422 Invalid Request: Pastikan
on_demand.mandate_onlydiberikan saat pembuatan danproduct_pricediberikan untuk charge. - Currency errors: Jika Anda mengganti
product_currency, pastikan mata uang tersebut didukung untuk akun dan customer Anda. - No webhooks received: Verifikasi URL webhook dan konfigurasi secret tanda tangan Anda.