Skip to main content

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
For a general subscription setup, see the Subscription Integration Guide.

Prerequisites

  • Dodo Payments merchant account and API key
  • Webhook secret configured and an endpoint to receive events
  • A subscription product in your catalog
This guide creates the on-demand subscription through a checkout session (POST /checkouts), which always returns a hosted checkout_url. Redirect the customer there to approve the mandate, and set return_url to where they should land afterward.

How on-demand works

  1. You create a subscription with the on_demand object to authorize a payment method and optionally collect an initial charge.
  2. Later, you create charges against that subscription with custom amounts using the dedicated charge endpoint.
  3. 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

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):
integer
erforderlich
Amount to charge (in the smallest currency unit). Example: to charge $25.00, pass 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
Legt fest, wie das Wallet-Guthaben des Kunden verwendet wird, um diese Belastung zu begleichen.
object
Zusätzliche Metadaten für die Zahlung. Wenn nicht angegeben, werden die Metadaten des Abonnements verwendet.
Success
Die Belastung eines Abonnements, das nicht auf Abruf erfolgt, kann fehlschlagen. Stelle sicher, dass das Abonnement in seinen Details über on_demand: true verfügt, bevor du es belastest.

Umgang mit fehlgeschlagenen Belastungen

Wenn eine Belastung eines On-Demand-Abonnements fehlschlägt, entscheidest du, was als Nächstes passiert. Anders als bei geplanten Abonnements – bei denen eine fehlgeschlagene Verlängerung die weitere automatische Abrechnung beendet – können On-Demand-Abonnements nach einem Fehler weiterhin belastet werden. Du kannst den Charge-Endpoint im Rahmen deiner eigenen Retry-Logik erneut aufrufen.

Was bei einem Fehler passiert

1

Charge attempt fails

Die Anfrage POST /subscriptions/{subscription_id}/charge gibt entweder eine Fehlerantwort zurück oder wird asynchron abgeschlossen und löst einen payment.failed-Webhook mit dem Ablehnungsgrund aus.
2

Subscription may transition to on_hold

Das Abonnement kann in den Status on_hold wechseln und einen subscription.on_hold-Webhook auslösen (siehe Abonnementstatus → On Hold). Dies ist ein Signal – keine Sperre. Bei On-Demand-Abonnements verhindert on_hold nicht, dass du erneut eine Belastung vornimmst.
3

Retry the charge (your call)

Bei On-Demand-Abläufen führt Dodo keine automatischen Retries durch. Du kannst POST /subscriptions/{subscription_id}/charge jederzeit erneut aufrufen. Wende die unten beschriebene Richtlinie für sichere Retries an – verwende exponentielles Backoff, überspringe endgültige Ablehnungen und vermeide Burst-Muster –, damit Retries nicht von unseren Betrugs- und Risikosystemen markiert werden.
4

Optionally, ask the customer for a new payment method

Wenn Retries weiterhin fehlschlagen, weil die Zahlungsmethode selbst nicht funktioniert (abgelaufene Karte, geschlossenes Konto usw.), verwende POST /subscriptions/{subscription_id}/update-payment-method, um eine neue Zahlungsmethode vom Kunden zu erfassen. Bei Erfolg kehrt das Abonnement zu active zurück und anschließend werden payment.succeeded- und subscription.active-Webhooks ausgelöst.
On-Demand vs. geplant: Bei geplanten Abonnements führt Dodo eigene Retries für Verlängerungen und Dunning durch. Bei On-Demand-Abonnements legst du die Retry-Richtlinie selbst fest, da nur du weißt, wann die nächste Belastung erfolgen soll (sie wird durch deine Nutzungsereignisse und nicht durch einen Kalender ausgelöst).

Webhook-Sequenz bei einer fehlgeschlagenen On-Demand-Belastung

Die Ereignisse 3 und 4 werden erst ausgelöst, nachdem eine Folgebelastung erfolgreich war.

Verantwortung für Retries

Dodo Payments führt keine automatischen Retries für fehlgeschlagene On-Demand-Belastungen durch. Du bist für die Retry-Richtlinie verantwortlich. Befolge die unten aufgeführten Richtlinien für sichere Retries, damit unsere Systeme zur Betrugserkennung dich nicht als Card Testing einstufen.
Subscription Dunning – die integrierte E-Mail-Wiederherstellungssequenz – ist auf fehlgeschlagene Verlängerungszahlungen bei geplanten Abonnements und vom Kunden veranlasste Kündigungen beschränkt. Sie ist nicht für fehlgeschlagene On-Demand-Belastungen vorgesehen. Kommuniziere direkt mit dem Kunden (z. B. per Transaktions-E-Mail oder In-App-Aufforderung), wenn du entscheidest, dass die Zahlungsmethode aktualisiert werden muss.

Zahlungs-Retries

Unser System zur Betrugserkennung kann aggressive Retry-Muster blockieren (und sie als potenzielles Card Testing markieren). Befolge eine sichere Retry-Richtlinie.
Burst-Retry-Muster können von unseren Risikosystemen und Zahlungsabwicklern als betrügerisch oder als vermutetes Card Testing markiert werden. Vermeide gebündelte Retries und befolge den unten beschriebenen Backoff-Zeitplan sowie die Hinweise zur zeitlichen Abstimmung.

Grundsätze für sichere Retry-Richtlinien

  • Backoff-Mechanismus: Verwende exponentielles Backoff zwischen Retries.
  • Retry-Limits: Begrenze die Gesamtzahl der Retries (maximal 3–4 Versuche).
  • Intelligente Filterung: Wiederhole nur bei wiederholbaren Fehlern (z. B. Netzwerk-/Issuer-Fehlern, unzureichendem Guthaben); wiederhole niemals endgültige Ablehnungen.
  • Verhinderung von Card Testing: Wiederhole keine Fehler wie DO_NOT_HONOR, STOLEN_CARD, LOST_CARD, PICKUP_CARD, FRAUDULENT, AUTHENTICATION_FAILURE.
  • Metadaten variieren (optional): Wenn du ein eigenes Retry-System unterhältst, unterscheide Retries über Metadaten (z. B. retry_attempt).

Vorgeschlagener Retry-Zeitplan (Abonnements)

  • 1. Versuch: Sofort beim Erstellen der Belastung
  • 2. Versuch: Nach 3 Tagen
  • 3. Versuch: Nach weiteren 7 Tagen (insgesamt 10 Tage)
  • 4. Versuch (final): Nach weiteren 7 Tagen (insgesamt 17 Tage)
Letzter Schritt: Wenn die Zahlung weiterhin aussteht, markiere das Abonnement abhängig von deiner Richtlinie als unbezahlt oder kündige es. Informiere den Kunden innerhalb dieses Zeitfensters darüber, seine Zahlungsmethode zu aktualisieren.

Burst-Retries vermeiden und an der Autorisierungszeit ausrichten

  • Verankere Retries am ursprünglichen Zeitstempel der Autorisierung, um ein „Burst“-Verhalten über dein gesamtes Portfolio hinweg zu vermeiden.
  • Beispiel: Wenn der Kunde heute um 13:10 Uhr einen Testzeitraum oder ein Mandat startet, plane Folge-Retries an den darauffolgenden Tagen gemäß deinem Backoff um 13:10 Uhr (z. B. +3 Tage → 13:10 Uhr, +7 Tage → 13:10 Uhr).
  • Wenn du alternativ die Zeit der letzten erfolgreichen Zahlung T speicherst, plane den nächsten Versuch für T + X days, um die zeitliche Ausrichtung beizubehalten.
Zeitzone und DST: Verwende für die Planung einen einheitlichen Zeitstandard und rechne nur für die Anzeige um, damit die Intervalle erhalten bleiben.

Ablehnungscodes, bei denen du keinen Retry durchführen solltest

  • STOLEN_CARD
  • DO_NOT_HONOR
  • FRAUDULENT
  • PICKUP_CARD
  • AUTHENTICATION_FAILURE
  • LOST_CARD
Eine umfassende Liste der Ablehnungsgründe und Informationen dazu, ob sie vom Benutzer korrigiert werden können, findest du in der Dokumentation zu Transaktionsfehlern.
Wiederhole nur bei vorübergehenden Problemen (z. B. insufficient_funds, issuer_unavailable, processing_error, Netzwerk-Timeouts). Wenn dieselbe Ablehnung erneut auftritt, pausiere weitere Retries.

Implementierungsrichtlinien (ohne Code)

  • Verwende einen Scheduler/eine Queue, die präzise Zeitstempel speichert; berechne den nächsten Versuch zum exakten Tageszeitpunkt des Offsets (z. B. T + 3 days zur gleichen HH:MM-Zeit).
  • Speichere den Zeitstempel der letzten erfolgreichen Zahlung T und verwende ihn zur Berechnung des nächsten Versuchs; bündele nicht mehrere Abonnements zum selben Zeitpunkt.
  • Werte immer den letzten Ablehnungsgrund aus und stoppe Retries bei endgültigen Ablehnungen aus der obigen Ausschlussliste.
  • Begrenze gleichzeitige Retries pro Kunde und Konto, um versehentliche Lastspitzen zu verhindern.
  • Kommuniziere proaktiv: Sende dem Kunden eine E-Mail/SMS, damit er seine Zahlungsmethode vor dem nächsten geplanten Versuch aktualisiert.
  • Verwende Metadaten ausschließlich zur Beobachtung (z. B. retry_attempt); versuche niemals, Betrugs-/Risikosysteme durch das Rotieren unwesentlicher Felder zu „umgehen“.

Kündigung

On-Demand-Abonnements folgen einem anderen Kündigungsablauf als geplante Abonnements, da es keinen festen Abrechnungszyklus gibt, an dem ein sofortiges Enddatum ausgerichtet werden kann.

Verhalten des Customer Portal

Wenn ein Kunde ein On-Demand-Abonnement über das Customer Portal kündigt, wird die Kündigung standardmäßig für das nächste Abrechnungsdatum geplant. Die Option Jetzt kündigen wird bei On-Demand-Abonnements absichtlich nicht angezeigt. Der Grund: On-Demand-Abonnements haben keine vorhersehbaren wiederkehrenden Verlängerungsdaten – der Zeitpunkt der nächsten Belastung wird vollständig durch deine Nutzungsereignisse bestimmt. Durch die Planung der Kündigung zum nächsten Abrechnungsdatum bleibt das Mandat bis zum Ende des Zeitraums aktiv, sodass laufende Nutzung weiterhin belastet werden kann. Anschließend wird das Abonnement ordnungsgemäß beendet. Nachdem der Kunde die Kündigung bestätigt hat:
  • Das Abonnement bleibt active und kann bis zum geplanten Kündigungsdatum weiterhin über POST /subscriptions/{id}/charge belastet werden.
  • cancel_at_next_billing_date wird im Abonnement auf true gesetzt.
  • Wenn die Kündigung wirksam wird, wird ein subscription.cancelled-Webhook ausgelöst.
Wenn du das Abonnement sofort beenden musst (zum Beispiel als Reaktion auf eine Rückerstattung oder eine Supportanfrage), kündige es programmgesteuert über die API, anstatt dich auf den Ablauf im Customer Portal zu verlassen.

Programmgesteuert kündigen

Du kannst ein On-Demand-Abonnement jederzeit über die API kündigen. Du entscheidest, ob die Kündigung sofort oder geplant erfolgen soll. Endpoint: PATCH /subscriptions/{subscription_id}
Setze status des Abonnements auf cancelled, um es sofort zu beenden. Das Mandat wird widerrufen und es können keine weiteren Belastungen erstellt werden.
cURL

Webhooks bei der Kündigung

Um in deinem Handler On-Demand-Kündigungen von Kündigungen geplanter Abonnements zu unterscheiden, prüfe beim Verarbeiten des Webhooks das on_demand-Flag des Abonnements.

Ergebnisse mit Webhooks verfolgen

Implementiere die Verarbeitung von Webhooks, um die Customer Journey zu verfolgen. Siehe Webhooks implementieren.
  • subscription.active: Mandat autorisiert und Abonnement aktiviert
  • subscription.failed: Erstellung fehlgeschlagen (z. B. Mandatsfehler)
  • subscription.on_hold: Abonnement pausiert (z. B. Status „unbezahlt“)
  • subscription.cancelled: Abonnement vollständig gekündigt (siehe Kündigung)
  • payment.succeeded: Belastung erfolgreich
  • payment.failed: Belastung fehlgeschlagen
Konzentriere dich bei On-Demand-Abläufen auf payment.succeeded und payment.failed, um nutzungsbasierte Belastungen abzugleichen. Wenn auf payment.failed subscription.on_hold folgt, siehe Umgang mit fehlgeschlagenen Belastungen, um das Abonnement wiederherzustellen.

Testen und nächste Schritte

1

Create in test mode

Verwende deinen Test-API-Key, um das Abonnement zu erstellen. Öffne anschließend den zurückgegebenen checkout_url und schließe das Mandat ab.
2

Trigger a charge

Rufe den Charge-Endpoint mit einem kleinen product_price (z. B. 100) auf und überprüfe, dass du payment.succeeded erhältst.
3

Go live

Wechsle zu deinem Live-API-Key, sobald du die Ereignisse und internen Statusaktualisierungen validiert hast.

Fehlerbehebung

  • 422 Invalid Request: Stelle sicher, dass on_demand.mandate_only bei der Erstellung und product_price für Belastungen angegeben wird.
  • Währungsfehler: Wenn du product_currency überschreibst, bestätige, dass die Währung für dein Konto und deinen Kunden unterstützt wird.
  • Keine Webhooks empfangen: Überprüfe die Konfiguration deiner Webhook-URL und des Signaturgeheimnisses.
Zuletzt geändert am 6. August 2026