Change Plan API
Plan Change Preview
Integration Guide
What Is a Subscription Upgrade or Downgrade?
Change a customer’s subscription plan to move them between tiers, adjust quantity for seat-based products, or migrate to a new product. The API automatically calculates prorations and charges based on your chosen billing mode.When to Use Plan Changes
- 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
- Credits the unused portion of the current cycle, prorated by time remaining
- Then charges a full cycle at the new plan — the new plan price is never prorated
- Net charge = full new cycle − (remaining fraction × full old cycle)
Implement the Change Plan API
prorated_immediately, full_immediately, difference_immediately ou do_not_bill.null ou envoyer un tableau vide supprime tous les modules complémentaires existants. Incluez donc les modules complémentaires actuels pour les conserver.prevent_change: conserver l’abonnement sur le plan actuel jusqu’à la réussite du paiementapply_change(par défaut) : appliquer immédiatement le changement de plan quel que soit le résultat du paiement
allow_plan_change_via_payment_link capability (Settings → Subscriptions → Collect Plan Change Payments by Payment Link), effective_at: immediately, and on_payment_failure: prevent_change. See Collecting Payment via a Checkout Link. Ignored by the preview route.subscription_id, payment_id et status. Nécessite collect_via_payment_link: true ; sinon la requête échoue avec 422. Consultez Rediriger le client après le paiement.409. Consultez Remplacer un lien de paiement en attente. Ignoré par la route de prévisualisation.- Non fourni /
null— les réductions existantes avecpreserve_on_plan_change=truesont conservées si elles s’appliquent au nouveau produit. [](tableau vide) — supprime toutes les réductions existantes de l’abonnement.["CODE_A", "CODE_B", ...]— remplace toutes les réductions existantes par cet ensemble empilé.
discount_codes pour les nouvelles intégrations. Ce champ reste fonctionnel pour assurer la rétrocompatibilité, mais ne peut pas être combiné avec discount_codes dans la même requête.immediately(par défaut) : appliquer immédiatement le changement de plannext_billing_date: programmer le changement pour la prochaine date de facturation. Le client conserve son plan actuel jusqu’à la fin de la période de facturation. Utilisez cette option pour les déclassements afin que les clients conservent les avantages de leur plan actuel jusqu’à la fin de la période de facturation.
Handle Webhook Events
subscription.active: changement de plan réussi, abonnement mis à joursubscription.plan_changed: plan d’abonnement modifié (montée en gamme/descente en gamme/mise à jour d’un addon)subscription.on_hold: débit du changement de plan échoué, renouvellements arrêtéspayment.succeeded: débit immédiat du changement de plan réussipayment.failed: débit immédiat échoué
Update Your Application State
- Accordez ou révoquez les fonctionnalités selon le nouveau plan
- Mettez à jour le tableau de bord du client avec les détails du nouveau plan
- Envoyez des e-mails de confirmation concernant les changements de plan
- Consignez les changements de facturation à des fins d’audit
Test and Monitor
- Testez tous les modes de proratisation avec différents scénarios
- Vérifiez que la gestion des webhooks fonctionne correctement
- Surveillez les taux de réussite des changements de plan
- Configurez des alertes pour les changements de plan échoués
Prévisualiser les changements de plan
Avant de valider un changement de plan, utilisez la Preview API pour montrer aux clients exactement le montant qui leur sera facturé :- Node.js SDK
- Python SDK
immediate_charge.summary, customer_credits correspond à la variation nette du solde de crédits du client, dans la devise indiquée par customer_credits_currency. Il s’agit de la devise du portefeuille de crédits du client, qui est la devise de l’abonnement. Elle peut différer de currency dans le récapitulatif, qui s’applique à total_amount et tax. Par exemple, un client payant en INR pour un abonnement en USD voit ses crédits en USD.Change Plan API
Utilisez la Change Plan API pour modifier le produit, la quantité et le comportement de la proratisation d’un abonnement actif.Exemples de démarrage rapide
- Node.js SDK
- Python SDK
- Go SDK
- HTTP
200 OK — avant le règlement effectif d’un quelconque débit. Le contenu du corps (ChangePlanResponse) dépend de la manière dont le changement a été collecté :
collect_via_payment_link, l’abonnement reste sur son plan actuel jusqu’à ce que le client effectue le paiement.Confirmez le résultat via webhook (payment.succeeded, payment.failed, subscription.plan_changed) ou relisez l’abonnement avec GET /subscriptions/{subscription_id} — consultez Ce qui se passe tant que le lien est impayé pour le cas du lien de paiement.Collecter un paiement via un lien de checkout
Par défaut, un changement de plan immédiat débite directement le moyen de paiement enregistré de l’abonnement. Définissezcollect_via_payment_link: true pour rediriger le client vers une page de checkout hébergée — utile lorsqu’aucun moyen de paiement n’est enregistré ou lorsque vous souhaitez que le client confirme activement le nouveau prix.
Conditions requises
collect_via_payment_link: true ne réussit que si toutes les conditions suivantes sont remplies — sinon la requête échoue avec 422 :
- La capacité
allow_plan_change_via_payment_linkest activée pour l’entreprise (Settings → Subscriptions → Collect Plan Change Payments by Payment Link). effective_atestimmediately(valeur par défaut). Un changement programmé (next_billing_date) ne nécessite jamais de page de checkout.- La requête définit
on_payment_failure: prevent_change. Une valeurapply_changeexplicite échoue avec422.
collect_via_payment_link s’applique à tout changement immédiat entraînant un débit, y compris les déclassements, tant que les conditions ci-dessus sont remplies.payment_link et les autres champs de checkout reviennent null, et le changement s’applique immédiatement. Il ne s’agit pas d’une 422. Appelez d’abord Preview Plan Change pour vérifier le montant avant de demander un lien.
- Node.js SDK
- Python SDK
- HTTP
Ce qui se passe tant que le lien est impayé
- L’abonnement reste sur son plan actuel —
product_id,recurring_pre_tax_amountetnext_billing_daterestent tous inchangés jusqu’au paiement du lien. - Toute nouvelle requête
change-planest rejetée avec409 PendingPlanChangeExiststant que le lien est en attente. Pour remplacer le changement en attente, envoyez la nouvelle requête aveccancel_older_payment_link: true. Consultez Remplacer un lien de paiement en attente. - Pour réessayer après un paiement refusé, appelez à nouveau
change-planafin d’obtenir un nouveau lien. - Si le lien n’est jamais payé, il cesse de fonctionner après
expires_on— l’abonnement redevient automatiquement disponible pour accepter une nouvelle requête de changement de plan peu après. - Si un changement programmé existait déjà et que vous le remplacez par
cancel_scheduled_change_plan: true, la programmation d’origine reste en place tant que le lien est impayé et n’est annulée qu’une fois le lien payé — dans la même transaction que celle qui applique le nouveau plan.
Rediriger le client après le paiement
Définissezreturn_url pour renvoyer le client vers votre site après le paiement du lien :
return_url avec les paramètres de requête suivants :
return_url nécessite collect_via_payment_link: true. Sans celui-ci, la requête échoue avec 422.
Remplacer un lien de paiement en attente
Si le client quitte le checkout sans payer, définissezcancel_older_payment_link: true dans la requête change-plan suivante. Dodo Payments annule le lien impayé et le nouveau changement de plan remplace celui en attente. Le lien annulé n’accepte plus de paiement, même dans une page de checkout déjà ouverte.
change-plan émet sa propre facture. Le changement de plan remplacé et sa facture sont annulés. Si aucun changement de plan n’est en attente, cancel_older_payment_link n’a aucun effet.
Gérer les addons
Lors de la modification des plans d’abonnement, vous pouvez également modifier les addons :Appliquer des codes de réduction
Appliquez un ou plusieurs codes de réduction empilés lors de la modification des plans d’abonnement (20 maximum, appliqués dans l’ordre du tableau) :- Node.js SDK
- Python SDK
- HTTP
Comportement des réductions lors d’un changement de plan
discount_code de cet endpoint est obsolète, mais reste fonctionnel pour assurer la rétrocompatibilité — les intégrations existantes n’ont pas besoin d’être modifiées immédiatement. Il ne peut pas être combiné avec discount_codes dans la même requête. Migrez vers le format tableau lorsque cela vous convient.Modes de proratisation
Choisissez comment facturer le client lors d’un changement de plan :prorated_immediately
- Crédite la portion inutilisée du cycle actuel — plan de base, quantité et addons — au prorata du temps restant
- Puis facture un cycle complet avec le nouveau plan, la nouvelle quantité et les nouveaux addons. Le débit lui-même n’est jamais proratisé
- Débit immédiat net = (nouveau cycle complet) − (fraction restante × ancien cycle complet)
- Si le crédit dépasse le montant du nouveau cycle (cas fréquent lors des déclassements), la différence est conservée comme crédit associé à l’abonnement pour les renouvellements futurs
- En période d’essai, facture immédiatement et bascule dès maintenant vers le nouveau plan
full_immediately
- Facture immédiatement le montant total du nouveau plan
- Ignore le temps restant de l’ancien plan — aucun crédit pour le cycle actuel
prorated_immediately et par les déclassements utilisant difference_immediately sont associés à l’abonnement et distincts des avantages de Credit-Based Billing. Ils s’appliquent automatiquement aux renouvellements futurs du même abonnement et ne sont pas transférables entre abonnements.difference_immediately
- Montée en gamme : facture immédiatement la différence de prix entre les anciens et les nouveaux plans
- Descente en gamme : ajoute la valeur restante comme crédit interne à l’abonnement et l’applique automatiquement aux renouvellements
do_not_bill
- Aucun débit ni crédit n’est calculé
- Le client passe immédiatement au nouveau plan sans ajustement de facturation
- Le cycle de facturation reste inchangé
- Idéal pour les migrations commerciales, les changements vers un plan gratuit ou l’absorption des différences de coût
Exemples de scénarios
Utilisez systématiquement ces valeurs de référence :- Plan actuel : Basic à $30/mois
- Cible de montée en gamme : Pro à $80/mois
- Cible de descente en gamme (depuis Pro) : Starter à $20/mois
- Cycle de facturation : 30 jours, commencé le 1er janvier
- Le changement de plan intervient le 16 janvier (15 jours restants, 15 jours utilisés)
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
Comment chaque mode traite la facturation
Gérer les échecs de paiement
Contrôlez ce qui se passe lorsqu’un paiement de changement de plan échoue à l’aide du paramètreon_payment_failure.
Modes d’échec de paiement
- prevent_change (Recommended for critical upgrades)
- apply_change (Default)
- Le changement de plan est marqué comme « en attente »
- Le client conserve l’accès à son plan actuel
- L’abonnement passe à l’état
activeuniquement après la réussite du paiement - Utile lorsque vous souhaitez vous assurer du paiement avant d’accorder les fonctionnalités améliorées
on_payment_failure utilise le paramètre par défaut défini au niveau de votre entreprise dans le tableau de bord.Quand utiliser chaque mode
Valeurs par défaut de l’entreprise et de la collecte
Définissez le comportement par défaut des montées et descentes en gamme au niveau de l’entreprise, sous Settings → Subscriptions. Ces valeurs par défaut s’appliquent à tous les changements de plan du Customer Portal et peuvent être remplacées pour chaque collection de produits. Des valeurs par défaut distinctes existent pour les montées et les descentes en gamme :Ordre de résolution
Pour un changement de plan donné, chaque paramètre est résolu dans l’ordre suivant :Gérer les webhooks
Suivez l’état des abonnements via les webhooks pour confirmer les changements de plan et les paiements.Types d’événements à gérer
subscription.active: abonnement activésubscription.plan_changed: plan d’abonnement modifié (montée/descente en gamme, modifications des addons)subscription.on_hold: débit échoué, renouvellements arrêtéssubscription.renewed: renouvellement réussipayment.succeeded: paiement du changement de plan ou du renouvellement réussipayment.failed: paiement échoué
Vérifier les signatures et gérer les intentions
- Next.js Route Handler
- Express.js
Bonnes pratiques
Stratégie de changement de plan
- Testez rigoureusement : testez toujours les changements de plan en mode test avant la production
- Choisissez soigneusement la proratisation : sélectionnez le mode de proratisation adapté à votre modèle économique
- Gérez les échecs avec élégance : implémentez une gestion des erreurs et une logique de nouvelle tentative appropriées
- Surveillez les taux de réussite : suivez les taux de réussite et d’échec des changements de plan et étudiez les problèmes
Implémentation des webhooks
- Vérifiez les signatures : validez toujours les signatures des webhooks pour garantir leur authenticité
- Implémentez l’idempotence : gérez correctement les événements webhook en double
- Traitez les événements de manière asynchrone : ne bloquez pas les réponses aux webhooks avec des opérations lourdes
- Consignez tout : conservez des journaux détaillés à des fins de débogage et d’audit
Expérience utilisateur
- Communiquez clairement : informez les clients des changements de facturation et de leur calendrier
- Fournissez des confirmations : envoyez des e-mails de confirmation pour les changements de plan réussis
- Gérez les cas particuliers : tenez compte des périodes d’essai, des proratisations et des paiements échoués
- Mettez immédiatement l’interface à jour : reflétez les changements de plan dans l’interface de votre application
Problèmes courants et solutions
Résolvez les problèmes habituels rencontrés lors des changements de plan d’abonnement :Charge created but subscription not updated
Charge created but subscription not updated
- Le traitement du webhook a échoué ou a été retardé
- L’état de l’application n’a pas été mis à jour après réception des webhooks
- Problèmes de transaction de base de données lors de la mise à jour de l’état
- Implémentez la gestion des webhooks avec une logique de nouvelle tentative
- Utilisez des opérations idempotentes pour les mises à jour d’état
- Ajoutez une surveillance pour détecter les événements webhook manqués et déclencher des alertes
- Vérifiez que l’endpoint webhook est accessible et répond correctement
Credits not applied after downgrade
Credits not applied after downgrade
- Attentes liées au mode de proratisation : les déclassements créditent la différence de prix totale entre les plans avec
difference_immediately, tandis queprorated_immediatelycrédite le temps inutilisé de l’ancien cycle puis facture un cycle complet au nouveau plan — un solde de crédits ne subsiste donc que lorsque ce crédit dépasse le prix du nouveau plan - Les crédits sont propres à l’abonnement et ne sont pas transférés entre abonnements
- Le solde de crédits n’est pas visible dans le tableau de bord du client
- Utilisez
difference_immediatelypour les déclassements lorsque vous souhaitez des crédits automatiques - Expliquez aux clients que les crédits s’appliquent aux renouvellements futurs du même abonnement
- Implémentez le Customer Portal pour afficher les soldes de crédits
- Consultez la prévisualisation de la prochaine facture pour voir les crédits appliqués
Webhook signature verification fails
Webhook signature verification fails
- Clé secrète webhook incorrecte
- Corps brut de la requête modifié avant la vérification de la signature
- Algorithme de vérification de signature incorrect
- Vérifiez que vous utilisez le bon
DODO_PAYMENTS_WEBHOOK_KEYdepuis le tableau de bord - Lisez le corps brut de la requête avant tout middleware d’analyse JSON
- Utilisez la bibliothèque standard de vérification des webhooks pour votre plateforme
- Testez la vérification des signatures webhook dans l’environnement de développement
Plan change fails with 422 error
Plan change fails with 422 error
- ID d’abonnement ou ID produit non valide
- Abonnement qui n’est pas à l’état actif
- Paramètres obligatoires manquants
- Produit non disponible pour les changements de plan
- Vérifiez que l’abonnement existe et est actif
- Vérifiez que l’ID produit est valide et disponible
- Assurez-vous que tous les paramètres obligatoires sont fournis
- Consultez la documentation de l’API pour connaître les exigences relatives aux paramètres
Immediate charge fails during plan change
Immediate charge fails during plan change
- Fonds insuffisants sur le moyen de paiement du client
- Moyen de paiement expiré ou non valide
- Banque ayant refusé la transaction
- Détection de fraude ayant bloqué le débit
- Gérez correctement les événements webhook
payment.failed - Informez le client qu’il doit mettre à jour son moyen de paiement
- Implémentez une logique de nouvelle tentative pour les échecs temporaires
- Envisagez d’autoriser les changements de plan lorsque les débits immédiats échouent
Subscription on hold after plan change
Subscription on hold after plan change
on_holdCe qui se passe :
Lorsque le débit d’un changement de plan échoue, l’abonnement passe automatiquement à l’état on_hold. L’abonnement ne sera pas renouvelé automatiquement tant que le moyen de paiement n’aura pas été mis à jour.Solution : mettez à jour le moyen de paiement pour réactiver l’abonnementPour réactiver un abonnement à l’état on_hold après l’échec d’un changement de plan :- Mettez à jour le moyen de paiement à l’aide de la Update Payment Method API
- Création automatique du débit : l’API crée automatiquement un débit pour les sommes restantes dues
- Génération de la facture : une facture est générée pour le débit
- Traitement du paiement : le paiement est traité avec le nouveau moyen de paiement
- Réactivation : après la réussite du paiement, l’abonnement est réactivé à l’état
active
subscription.on_hold: abonnement mis en attente (reçu lorsque le débit du changement de plan échoue)payment.succeeded: paiement des sommes restantes dûes réussi (après la mise à jour du moyen de paiement)subscription.active: abonnement réactivé après la réussite du paiement
- Informez immédiatement les clients lorsqu’un débit de changement de plan échoue
- Fournissez des instructions claires pour mettre à jour leur moyen de paiement
- Surveillez les événements webhook pour suivre l’état de la réactivation
- Envisagez d’implémenter une logique de nouvelle tentative automatique pour les échecs de paiement temporaires
Update Payment Method API Reference
Tester votre implémentation
Testez rigoureusement votre implémentation des changements de plan d’abonnement :Set up test environment
- Utilisez des clés API de test et des produits de test
- Créez des abonnements de test avec différents types de plans
- Configurez un endpoint webhook de test
- Configurez la surveillance et la journalisation
Test different proration modes
- Testez
prorated_immediatelyavec différentes positions dans le cycle de facturation - Testez
difference_immediatelypour les montées et descentes en gamme - Testez
full_immediatelypour réinitialiser les cycles de facturation - Testez
do_not_billpour les changements de plan sans débit ni crédit - Vérifiez que les calculs de crédits sont corrects
Test webhook handling
- Vérifiez que tous les événements webhook pertinents sont reçus
- Testez la vérification des signatures webhook
- Gérez correctement les événements webhook en double
- Testez les scénarios d’échec du traitement des webhooks
Test error scenarios
- Testez avec des ID d’abonnement non valides
- Testez avec des moyens de paiement expirés
- Testez les échecs réseau et les délais d’attente
- Testez avec des fonds insuffisants
Monitor in production
- Configurez des alertes pour les changements de plan échoués
- Surveillez les temps de traitement des webhooks
- Suivez les taux de réussite des changements de plan
- Examinez les tickets du support client concernant les problèmes de changement de plan
Gestion des erreurs
Gérez correctement les erreurs API courantes dans votre implémentation :Codes d’état HTTP
200 OK
200 OK
ChangePlanResponse avec payment_id, payment_link, client_secret et expires_on. Les quatre valeurs peuvent être nulles ; le corps est donc sérialisé comme {} pour un changement ordinaire hors session. Elles sont renseignées pour une requête collect_via_payment_link réussie, qui renvoie les références de checkout — consultez Collecter un paiement via un lien de checkout. Si on_payment_failure=prevent_change, le changement de plan reste en attente jusqu’à la réussite du paiement.400 Bad Request
400 Bad Request
409 Conflict
409 Conflict
PendingPlanChangeExists). Pour un changement programmé, annulez-le avec DELETE /subscriptions/{subscription_id}/change-plan/scheduled avant d’en envoyer un nouveau. Pour un changement via lien de paiement en attente, envoyez la nouvelle requête avec cancel_older_payment_link: true pour le remplacer. Consultez Remplacer un lien de paiement en attente.Avec cancel_older_payment_link: true, une 409 signifie que le client paie actuellement le lien précédent (PLAN_CHANGE_PAYMENT_IN_PROGRESS) ou qu’il l’a déjà payé (PLAN_CHANGE_PAYMENT_ALREADY_COMPLETED).422 Unprocessable Entity
422 Unprocessable Entity
collect_via_payment_link — l’entreprise n’a pas activé la capacité, effective_at n’est pas immediately, ou on_payment_failure n’est pas prevent_change. Consultez Conditions requises. Un ID d’abonnement inexistant ou n’appartenant pas à votre compte renvoie 404 avec le code NOT_FOUND.500 Internal Server Error
500 Internal Server Error
Format de réponse des erreurs
Les erreurs renvoient un corps JSON avec uncode et un message compréhensible par l’utilisateur :
Étapes suivantes
- Consultez la Change Plan API
- Découvrez Credit-Based Billing
- Implémentez des alertes pour
subscription.on_hold - Consultez le guide d’intégration des webhooks