Change Plan API
Plan Change Preview
Integration Guide
Apa Itu Upgrade atau Downgrade Subscription?
Ubah plan subscription pelanggan untuk memindahkan mereka antar-tier, menyesuaikan quantity untuk produk berbasis seat, atau memigrasikan mereka ke produk baru. API secara otomatis menghitung prorasi dan biaya berdasarkan billing mode yang Anda pilih.Kapan Menggunakan Perubahan Plan
- Upgrade when a customer needs more features, usage, or seats
- Downgrade when usage decreases
- Migrate users to a new product or price without cancelling their subscription
Plan Change Flow
Prerequisites
Before implementing subscription plan changes, ensure you have:- A Dodo Payments merchant account with active subscription products
- API credentials (API key and webhook secret key) from the dashboard
- An existing active subscription to modify
- Webhook endpoint configured to handle subscription events
Step-by-Step Implementation Guide
Follow this comprehensive guide to implement subscription plan changes in your application:Understand Plan Change Requirements
- Which subscription products can be changed to which others
- What proration mode fits your business model
- How to handle failed plan changes gracefully
- Which webhook events to track for state management
Choose Your Proration Strategy
- prorated_immediately
- difference_immediately
- full_immediately
- do_not_bill
- Mengkreditkan bagian yang tidak terpakai dari siklus saat ini, diprorata berdasarkan waktu yang tersisa
- Kemudian menagih satu siklus penuh pada plan baru — harga plan baru tidak pernah diprorata
- Biaya bersih = siklus baru penuh − (fraksi yang tersisa × siklus lama penuh)
Implement the Change Plan API
prorated_immediately, full_immediately, difference_immediately, or do_not_bill.null, atau mengirim array kosong akan menghapus add-on yang sudah ada, jadi sertakan add-on saat ini untuk mempertahankannya.prevent_change: Keep subscription on current plan until payment succeedsapply_change(default): Apply plan change immediately regardless of payment outcome
allow_plan_change_via_payment_link milik bisnis (Settings → Subscriptions → Collect Plan Change Payments by Payment Link), effective_at: immediately, dan on_payment_failure: prevent_change. Lihat Collecting Payment via a Checkout Link. Diabaikan oleh preview route.- Tidak diberikan /
null— diskon yang ada denganpreserve_on_plan_change=truedipertahankan jika berlaku untuk produk baru. [](array kosong) — menghapus semua diskon yang ada dari subscription.["CODE_A", "CODE_B", ...]— mengganti semua diskon yang ada dengan kumpulan stacked ini.
discount_codes untuk integration baru. Field ini masih berfungsi untuk backward compatibility, tetapi tidak dapat digabungkan dengan discount_codes dalam request yang sama.immediately(default): Terapkan perubahan plan segeranext_billing_date: Jadwalkan perubahan pada tanggal billing berikutnya. Pelanggan tetap menggunakan plan saat ini hingga periode billing berakhir. Gunakan opsi ini untuk downgrade agar pelanggan tetap memperoleh manfaat plan saat ini hingga akhir periode billing.
Handle Webhook Events
subscription.active: Perubahan plan berhasil, subscription diperbaruisubscription.plan_changed: Plan subscription berubah (upgrade/downgrade/update addon)subscription.on_hold: Biaya perubahan plan gagal, renewal dihentikanpayment.succeeded: Biaya langsung untuk perubahan plan berhasilpayment.failed: Biaya langsung gagal
Update Your Application State
- Berikan/cabut fitur berdasarkan plan baru
- Perbarui dashboard pelanggan dengan detail plan baru
- Kirim email konfirmasi tentang perubahan plan
- Catat perubahan billing untuk keperluan audit
Test and Monitor
- Uji semua mode prorasi dengan berbagai skenario
- Pastikan penanganan webhook berfungsi dengan benar
- Pantau tingkat keberhasilan perubahan plan
- Siapkan alert untuk perubahan plan yang gagal
Preview Perubahan Plan
Sebelum menerapkan perubahan plan, gunakan Preview API untuk menunjukkan kepada pelanggan jumlah pasti yang akan ditagihkan:- Node.js SDK
- Python SDK
Change Plan API
Gunakan Change Plan API untuk mengubah product, quantity, dan perilaku prorasi pada subscription yang aktif.Contoh Quick Start
- Node.js SDK
- Python SDK
- Go SDK
- HTTP
200 OK segera — sebelum biaya apa pun benar-benar diselesaikan. Isi body (ChangePlanResponse) bergantung pada cara perubahan tersebut ditagihkan:
collect_via_payment_link, subscription tetap menggunakan plan saat ini hingga pelanggan menyelesaikan pembayaran.Konfirmasikan hasilnya melalui webhook (payment.succeeded, payment.failed, subscription.plan_changed) atau dengan membaca ulang subscription menggunakan GET /subscriptions/{subscription_id} — lihat What Happens While the Link Is Unpaid untuk kasus payment-link.Mengumpulkan Pembayaran melalui Checkout Link
Secara default, perubahan plan langsung menagih payment method yang tersimpan pada subscription. Aturcollect_via_payment_link: true untuk mengarahkan pelanggan ke halaman checkout yang di-host — berguna jika tidak ada payment method tersimpan atau Anda ingin pelanggan secara aktif mengonfirmasi harga baru.
Requirements
collect_via_payment_link: true hanya berhasil jika semua kondisi berikut terpenuhi — jika tidak, request gagal dengan 422:
- Bisnis memiliki capability
allow_plan_change_via_payment_linkyang diaktifkan (Settings → Subscriptions → Collect Plan Change Payments by Payment Link). effective_atadalahimmediately(default). Perubahan terjadwal (next_billing_date) tidak pernah memerlukan halaman checkout.on_payment_failureefektif menghasilkanprevent_change. Anda tidak perlu mengirimkannya secara eksplisit — jika default tingkat bisnis Anda sudah berupaprevent_change, menghilangkan field ini sudah memenuhi kondisi.apply_changeeksplisit akan gagal dengan422.
collect_via_payment_link berlaku untuk setiap perubahan langsung yang menghasilkan biaya, termasuk downgrade, selama requirements di atas terpenuhi.payment_link dan field checkout lainnya dikembalikan sebagai null, dan perubahan langsung diterapkan. Ini bukan 422. Panggil Preview Plan Change terlebih dahulu untuk memeriksa jumlahnya sebelum meminta link.
- Node.js SDK
- Python SDK
- HTTP
Apa yang Terjadi Saat Link Belum Dibayar
- Subscription tetap menggunakan plan saat ini —
product_id,recurring_pre_tax_amount, dannext_billing_datesemuanya tidak berubah hingga link dibayar. - Request
change-planberikutnya ditolak dengan409 PendingPlanChangeExistssaat link masih tertunda. Batalkan perubahan terjadwal denganDELETE /subscriptions/{subscription_id}/change-plan/scheduledjika diperlukan, tetapi endpoint tersebut tidak membatalkan perubahan payment-link yang tertunda — hanya pembayaran berhasil atau kedaluwarsa yang dapat melakukannya. - Pelanggan dapat mencoba kembali kartu pada checkout session yang sama setelah penolakan; pemanggilan
change-planbaru bukan cara untuk mencoba kembali. - Jika link tidak pernah dibayar, link berhenti berfungsi setelah
expires_on— subscription secara otomatis dapat menerima request perubahan plan baru tidak lama kemudian. - Jika perubahan terjadwal sudah ada dan Anda menggantinya dengan
cancel_scheduled_change_plan: true, jadwal asli tetap berlaku saat link belum dibayar dan hanya dibatalkan setelah link dibayar — dalam transaksi yang sama ketika plan baru diterapkan.
Mengelola Addon
Saat mengubah plan subscription, Anda juga dapat mengubah addon:Menerapkan Kode Diskon
Terapkan satu atau beberapa kode diskon stacked saat mengubah plan subscription (maks. 20, diterapkan sesuai urutan dalam array):- Node.js SDK
- Python SDK
- HTTP
Perilaku Diskon saat Perubahan Plan
discount_code pada endpoint ini deprecated, tetapi masih berfungsi untuk backward compatibility — integration yang sudah ada tidak perlu segera diubah. Field ini tidak dapat digabungkan dengan discount_codes dalam request yang sama. Migrasikan ke bentuk array saat memungkinkan.Mode Prorasi
Pilih cara menagih pelanggan saat mengubah plan:prorated_immediately
- Mengkreditkan bagian yang tidak terpakai dari siklus saat ini — base plan, quantity, dan addon — diprorata berdasarkan waktu yang tersisa
- Kemudian menagih satu siklus penuh pada plan, quantity, dan addon baru. Biaya itu sendiri tidak pernah diprorata
- Biaya langsung bersih = (siklus baru penuh) − (fraksi yang tersisa × siklus lama penuh)
- Jika kredit melebihi biaya siklus baru (umum terjadi pada downgrade), selisihnya disimpan sebagai kredit pada subscription untuk renewal mendatang
- Jika sedang dalam trial, langsung menagih dan beralih ke plan baru sekarang
full_immediately
- Menagih jumlah penuh plan baru secara langsung
- Mengabaikan waktu yang tersisa dari plan lama — tidak ada kredit untuk siklus saat ini
prorated_immediately dan oleh downgrade menggunakan difference_immediately memiliki cakupan subscription dan berbeda dari entitlement Credit-Based Billing. Kredit tersebut otomatis diterapkan pada renewal mendatang dari subscription yang sama dan tidak dapat dipindahkan antar-subscription.difference_immediately
- Upgrade: langsung menagih selisih harga antara plan lama dan baru
- Downgrade: menambahkan nilai yang tersisa sebagai kredit internal ke subscription dan menerapkannya secara otomatis pada renewal
do_not_bill
- Tidak ada biaya atau kredit yang dihitung
- Pelanggan langsung beralih ke plan baru tanpa penyesuaian billing
- Siklus billing tetap tidak berubah
- Cocok untuk migrasi sebagai bentuk penghargaan, perpindahan ke plan gratis, atau menyerap perbedaan biaya
prorated_immediately dan oleh downgrade menggunakan difference_immediately memiliki cakupan subscription dan berbeda dari entitlement Credit-Based Billing. Kredit tersebut otomatis diterapkan pada renewal mendatang untuk subscription yang sama dan tidak dapat ditransfer antar-subscription.difference_immediately
- Upgrade: segera menagih selisih harga antara paket lama dan baru
- Downgrade: menambahkan nilai tersisa sebagai kredit internal ke subscription dan menerapkannya otomatis pada renewal
do_not_bill
- Tidak ada penagihan atau kredit yang dihitung
- Customer segera beralih ke paket baru tanpa penyesuaian billing
- Siklus billing tetap tidak berubah
- Cocok untuk migrasi sebagai bentuk goodwill, perpindahan ke paket gratis, atau penyerapan perbedaan biaya
Skenario contoh
Gunakan angka kanonis berikut secara konsisten:- Paket saat ini: Basic seharga $30/bulan
- Target upgrade: Pro seharga $80/bulan
- Target downgrade (dari Pro): Starter seharga $20/bulan
- Siklus billing: 30 hari, dimulai pada January 1
- Perubahan paket terjadi pada January 16 (tersisa 15 hari, 15 hari telah digunakan)
Upgrade: Basic ($30) → Pro ($80) with prorated_immediately
Upgrade: Basic ($30) → Pro ($80) with prorated_immediately
Downgrade: Pro ($80) → Starter ($20) with prorated_immediately
Downgrade: Pro ($80) → Starter ($20) with prorated_immediately
Upgrade: Basic ($30) → Pro ($80) with difference_immediately
Upgrade: Basic ($30) → Pro ($80) with difference_immediately
Downgrade: Pro ($80) → Starter ($20) with difference_immediately
Downgrade: Pro ($80) → Starter ($20) with difference_immediately
Upgrade: Basic ($30) → Pro ($80) with full_immediately
Upgrade: Basic ($30) → Pro ($80) with full_immediately
Mid-cycle upgrade with add-ons using prorated_immediately
Mid-cycle upgrade with add-ons using prorated_immediately
Cara setiap mode memproses billing
Menangani Kegagalan Pembayaran
Kontrol apa yang terjadi ketika pembayaran perubahan paket gagal menggunakan parameteron_payment_failure.
Mode Kegagalan Pembayaran
- prevent_change (Recommended for critical upgrades)
- apply_change (Default)
- Perubahan paket ditandai sebagai “pending”
- Customer tetap memiliki akses ke paket saat ini
- Subscription berpindah ke status
activehanya setelah pembayaran berhasil - Berguna ketika Anda ingin memastikan pembayaran sebelum memberikan fitur yang ditingkatkan
on_payment_failure menggunakan pengaturan default tingkat business yang dikonfigurasi di dashboard.Kapan Menggunakan Setiap Mode
Default Business & Collection
Daripada mengirim parameter proration pada setiap perubahan paket, Anda dapat menetapkan perilaku default upgrade & downgrade sekali di tingkat business. Default ini berlaku untuk semua perubahan paket melalui customer portal dan dapat ditimpa pada setiap product collection. Terdapat default terpisah untuk upgrade dan downgrade:Urutan resolusi
Untuk perubahan paket apa pun, setiap pengaturan di-resolve dalam urutan berikut:Menangani webhook
Lacak status subscription melalui webhook untuk mengonfirmasi perubahan paket dan pembayaran.Jenis event yang perlu ditangani
subscription.active: subscription diaktifkansubscription.plan_changed: paket subscription diubah (upgrade/downgrade/perubahan addon)subscription.on_hold: penagihan gagal, renewal dihentikansubscription.renewed: renewal berhasilpayment.succeeded: pembayaran untuk perubahan paket atau renewal berhasilpayment.failed: pembayaran gagal
Memverifikasi signature dan menangani intent
- Next.js Route Handler
- Express.js
Praktik Terbaik
Ikuti rekomendasi berikut untuk perubahan paket subscription yang andal:Strategi Perubahan Paket
- Uji secara menyeluruh: Selalu uji perubahan paket dalam test mode sebelum production
- Pilih proration dengan cermat: Pilih mode proration yang sesuai dengan model bisnis Anda
- Tangani kegagalan dengan baik: Terapkan penanganan error dan logika retry yang tepat
- Pantau tingkat keberhasilan: Lacak tingkat keberhasilan/kegagalan perubahan paket dan selidiki masalah
Implementasi Webhook
- Verifikasi signature: Selalu validasi signature webhook untuk memastikan keaslian
- Terapkan idempotensi: Tangani event webhook duplikat dengan baik
- Proses secara asynchronous: Jangan menghambat response webhook dengan operasi berat
- Catat semuanya: Simpan log terperinci untuk debugging dan audit
User Experience
- Berkomunikasi dengan jelas: Beri tahu customer tentang perubahan dan waktu billing
- Berikan konfirmasi: Kirim konfirmasi email untuk perubahan paket yang berhasil
- Tangani kasus khusus: Pertimbangkan periode trial, proration, dan pembayaran yang gagal
- Perbarui UI segera: Tampilkan perubahan paket pada antarmuka aplikasi Anda
Masalah Umum dan Solusi
Atasi masalah umum yang ditemui selama perubahan paket subscription:Charge created but subscription not updated
Charge created but subscription not updated
- Pemrosesan webhook gagal atau tertunda
- State aplikasi tidak diperbarui setelah menerima webhook
- Masalah transaksi database saat memperbarui state
- Terapkan penanganan webhook yang kuat dengan logika retry
- Gunakan operasi idempoten untuk pembaruan state
- Tambahkan monitoring untuk mendeteksi dan memberi alert atas event webhook yang terlewat
- Pastikan endpoint webhook dapat diakses dan merespons dengan benar
Credits not applied after downgrade
Credits not applied after downgrade
- Ekspektasi mode proration: downgrade mengkreditkan selisih harga penuh paket dengan
difference_immediately, sedangkanprorated_immediatelymengkreditkan waktu yang tidak terpakai pada siklus lama lalu menagihkan siklus penuh pada paket baru — sehingga saldo kredit hanya tersisa ketika kredit tersebut melebihi harga paket baru - Kredit bersifat spesifik untuk subscription dan tidak dapat ditransfer antar-subscription
- Saldo kredit tidak terlihat di dashboard pelanggan
- Gunakan
difference_immediatelyuntuk downgrade jika Anda menginginkan kredit otomatis - Jelaskan kepada customer bahwa kredit berlaku untuk renewal mendatang pada subscription yang sama
- Implementasikan customer portal untuk menampilkan saldo kredit
- Periksa preview invoice berikutnya untuk melihat kredit yang diterapkan
Webhook signature verification fails
Webhook signature verification fails
- Secret key webhook salah
- Raw request body diubah sebelum verifikasi signature
- Algoritma verifikasi signature salah
- Pastikan Anda menggunakan
DODO_WEBHOOK_SECRETyang benar dari dashboard - Baca raw request body sebelum middleware parsing JSON apa pun
- Gunakan library verifikasi webhook standar untuk platform Anda
- Uji verifikasi signature webhook di environment development
Plan change fails with 422 error
Plan change fails with 422 error
- ID subscription atau ID produk tidak valid
- Subscription tidak dalam state aktif
- Parameter wajib tidak ada
- Produk tidak tersedia untuk perubahan paket
- Pastikan subscription ada dan aktif
- Periksa ID produk valid dan tersedia
- Pastikan semua parameter wajib diberikan
- Tinjau dokumentasi API untuk persyaratan parameter
Immediate charge fails during plan change
Immediate charge fails during plan change
- Dana pada metode pembayaran customer tidak mencukupi
- Metode pembayaran kedaluwarsa atau tidak valid
- Bank menolak transaksi
- Deteksi fraud memblokir penagihan
- Tangani event webhook
payment.faileddengan tepat - Beri tahu customer untuk memperbarui metode pembayaran
- Terapkan logika retry untuk kegagalan sementara
- Pertimbangkan untuk mengizinkan perubahan paket meskipun penagihan segera gagal
Subscription on hold after plan change
Subscription on hold after plan change
on_holdYang terjadi:
Ketika penagihan perubahan paket gagal, subscription secara otomatis ditempatkan dalam state on_hold. Subscription tidak akan melakukan renewal secara otomatis sampai metode pembayaran diperbarui.Solusi: Perbarui metode pembayaran untuk mengaktifkan kembali subscriptionUntuk mengaktifkan kembali subscription dari state on_hold setelah perubahan paket gagal:- Perbarui metode pembayaran menggunakan Update Payment Method API
- Pembuatan penagihan otomatis: API secara otomatis membuat penagihan untuk jumlah terutang yang tersisa
- Pembuatan invoice: Invoice dibuat untuk penagihan tersebut
- Pemrosesan pembayaran: Pembayaran diproses menggunakan metode pembayaran baru
- Pengaktifan kembali: Setelah pembayaran berhasil, subscription diaktifkan kembali ke state
active
subscription.on_hold: Subscription ditahan (diterima saat penagihan perubahan paket gagal)payment.succeeded: Pembayaran untuk jumlah terutang yang tersisa berhasil (setelah metode pembayaran diperbarui)subscription.active: Subscription diaktifkan kembali setelah pembayaran berhasil
- Beri tahu customer segera ketika penagihan perubahan paket gagal
- Berikan instruksi yang jelas tentang cara memperbarui metode pembayaran
- Pantau event webhook untuk melacak status pengaktifan kembali
- Pertimbangkan untuk menerapkan logika retry otomatis bagi kegagalan pembayaran sementara
Update Payment Method API Reference
Menguji Implementasi Anda
Ikuti langkah-langkah berikut untuk menguji implementasi perubahan paket subscription Anda secara menyeluruh:Set up test environment
- Gunakan API key test dan produk test
- Buat subscription test dengan berbagai jenis paket
- Konfigurasikan endpoint webhook test
- Siapkan monitoring dan logging
Test different proration modes
- Uji
prorated_immediatelydengan berbagai posisi dalam siklus billing - Uji
difference_immediatelyuntuk upgrade dan downgrade - Uji
full_immediatelyuntuk mengatur ulang siklus billing - Uji
do_not_billuntuk perpindahan paket tanpa penagihan/kredit - Pastikan perhitungan kredit sudah benar
Test webhook handling
- Pastikan semua event webhook yang relevan diterima
- Uji verifikasi signature webhook
- Tangani event webhook duplikat dengan baik
- Uji skenario kegagalan pemrosesan webhook
Test error scenarios
- Uji dengan ID subscription yang tidak valid
- Uji dengan metode pembayaran yang kedaluwarsa
- Uji kegagalan jaringan dan timeout
- Uji dengan dana yang tidak mencukupi
Monitor in production
- Siapkan alert untuk perubahan paket yang gagal
- Pantau waktu pemrosesan webhook
- Lacak tingkat keberhasilan perubahan paket
- Tinjau tiket dukungan customer untuk masalah perubahan paket
Penanganan Error
Tangani error API umum dengan baik dalam implementasi Anda:HTTP Status Codes
200 OK
200 OK
ChangePlanResponse dengan payment_id, payment_link, client_secret, dan expires_on. Keempatnya nullable, sehingga body diserialisasikan sebagai {} untuk perubahan off-session biasa; keempatnya terisi untuk permintaan collect_via_payment_link yang berhasil, yang mengembalikan handle checkout — lihat Mengumpulkan Pembayaran melalui Checkout Link. Jika on_payment_failure=prevent_change, perubahan paket tetap tertunda hingga pembayaran berhasil.400 Bad Request
400 Bad Request
409 Conflict
409 Conflict
PendingPlanChangeExists). Untuk perubahan yang dijadwalkan, batalkan dengan DELETE /subscriptions/{subscription_id}/change-plan/scheduled sebelum mengirimkan yang baru. Untuk perubahan payment-link yang tertunda, tidak ada endpoint pembatalan — subscription dapat menerima permintaan perubahan paket baru setelah pelanggan membayar atau link kedaluwarsa.422 Unprocessable Entity
422 Unprocessable Entity
collect_via_payment_link — bisnis belum mengaktifkan kapabilitas tersebut, effective_at bukan immediately, atau on_payment_failure bukan prevent_change. Lihat Persyaratan. ID subscription yang tidak ada atau bukan milik akun Anda akan ditampilkan di sini sebagai 422, bukan 404.500 Internal Server Error
500 Internal Server Error