> ## 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.

# Abbonamenti On-Demand

> Integra abbonamenti on-demand autorizzando mandati, creando addebiti variabili, gestendo webhook e implementando politiche di retry sicure.

## Panoramica

Le sottoscrizioni on-demand ti consentono di autorizzare un metodo di pagamento di un cliente una sola volta e poi addebitare importi variabili ogni volta che ne hai bisogno, invece di seguire un programma fisso. Questa funzionalità è disponibile per tutti gli account—non è necessaria alcuna approvazione.

Usa questa guida per:

* Creare una sottoscrizione on-demand (autorizzare un mandato con un prezzo iniziale opzionale)
* Attivare addebiti successivi con importi personalizzati
* Monitorare i risultati utilizzando i webhook

Per una configurazione generale della sottoscrizione, consulta la [Guida all'integrazione delle sottoscrizioni](/developer-resources/subscription-integration-guide).

## Requisiti

* Account commerciante Dodo Payments e chiave API
* Segreto del webhook configurato e un endpoint per ricevere eventi
* Un prodotto in abbonamento nel tuo catalogo

<Tip>
  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.
</Tip>

## Come funziona l'on-demand

1. Crei un abbonamento con l’oggetto `on_demand` per autorizzare un metodo di pagamento e, facoltativamente, addebitare una somma iniziale.
2. Successivamente, crei addebiti contro tale abbonamento con importi personalizzati usando l’endpoint dedicato agli addebiti.
3. Ascolti i webhook (es. `payment.succeeded`, `payment.failed`) per aggiornare il tuo sistema.

## Crea una sottoscrizione on-demand

Endpoint: [POST /checkouts](/api-reference/checkout-sessions/create)

Campi chiave della richiesta (corpo):\
Trovali in [Crea sessione di checkout](/api-reference/checkout-sessions/create)

### Crea una sottoscrizione on-demand

<Tabs>
  <Tab title="Node.js SDK">
    ```javascript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'test_mode', // defaults to 'live_mode'
    });

    async function main() {
      const subscription = await client.checkoutSessions.create({
        product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
        billing_address:  { city: 'SF', country: 'US', state: 'CA', street: '1 Market St', zipcode: '94105' },
        customer: { customer_id: 'cus_123' },
        return_url: 'https://example.com/billing/success',
        subscription_data: {
            on_demand: {
                mandate_only: true // set false to collect an initial charge
                // product_price: 1000, // optional: charge $10.00 now if mandate_only is false
                // product_currency: 'USD',
                // product_description: 'Custom initial charge',
                // adaptive_currency_fees_inclusive: false,
            }
        }
      });

      console.log(subscription.checkout_url);
    }

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get('DODO_PAYMENTS_API_KEY'),
        environment="test_mode",  # defaults to "live_mode"
    )

    checkout = client.checkout_sessions.create(
        product_cart=[
            {"product_id": "pdt_123", "quantity": 1}
        ],
        billing_address={
            "city": "SF",
            "country": "US",
            "state": "CA",
            "street": "1 Market St",
            "zipcode": "94105",
        },
        customer={
            "customer_id": "cus_123",
        },
        return_url="https://example.com/billing/success",
        subscription_data={
          "on_demand":{
          "mandate_only": True,  # set False to collect an initial charge
          # "product_price": 1000,  # optional: charge $10.00 now if mandate_only is false
          # "product_currency": "USD",
          # "product_description": "Custom initial charge",
          # "adaptive_currency_fees_inclusive": False,
          }
        },
    )

    print(checkout.checkout_url)
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    package main

    import (
        "context"
        "fmt"
        "os"

        "github.com/dodopayments/dodopayments-go"
        "github.com/dodopayments/dodopayments-go/option"
    )

    func main() {
        client := dodopayments.NewClient(
            option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
            option.WithEnvironmentTestMode(), // defaults to live mode
        )

        checkout, err := client.CheckoutSessions.New(context.TODO(), dodopayments.CheckoutSessionNewParams{
            CheckoutSessionRequest: dodopayments.CheckoutSessionRequestParam{
                ProductCart: dodopayments.F([]dodopayments.ProductItemReqParam{
                    {
                        ProductID: dodopayments.F("pdt_123"),
                        Quantity:  dodopayments.F(int64(1)),
                    },
                }),
                BillingAddress: dodopayments.F(dodopayments.CheckoutSessionBillingAddressParam{
                    City:    dodopayments.F("SF"),
                    Country: dodopayments.F(dodopayments.CountryCodeUs),
                    State:   dodopayments.F("CA"),
                    Street:  dodopayments.F("1 Market St"),
                    Zipcode: dodopayments.F("94105"),
                }),
                Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](
                    dodopayments.AttachExistingCustomerParam{
                        CustomerID: dodopayments.F("cus_123"),
                    },
                ),
                ReturnURL: dodopayments.F("https://example.com/billing/success"),
                SubscriptionData: dodopayments.F(dodopayments.SubscriptionDataParam{
                    OnDemand: dodopayments.F(dodopayments.OnDemandSubscriptionParam{
                        MandateOnly: dodopayments.F(true), // set false to collect an initial charge
                        // ProductPrice:    dodopayments.F(int64(1000)), // optional: charge $10.00 now if mandate_only is false
                        // ProductCurrency: dodopayments.F(dodopayments.CurrencyUsd),
                        // ProductDescription: dodopayments.F("Custom initial charge"),
                    }),
                }),
            },
        })
        if err != nil {
            panic(err)
        }

        fmt.Println(checkout.CheckoutURL)
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "$DODO_API/checkouts" \
      -H "Authorization: Bearer $DODO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "product_cart": [
          {
            "product_id": "pdt_123",
            "quantity": 1
          }
        ],
        "customer": {
          "customer_id": "cus_123"
        },
        "billing_address": {
          "street": "1 Market St",
          "city": "SF",
          "state": "CA",
          "country": "US",
          "zipcode": "94105"
        },
        "subscription_data": {
          "on_demand": {
            "mandate_only": true
          }
        },
        "return_url": "https://example.com/billing/success"
      }'
    ```
  </Tab>
</Tabs>

```json Success theme={null}
{
  "session_id": "cks_123",
  "checkout_url": "https://test.checkout.dodopayments.com/session/cks123"
}
```

## Addebita una sottoscrizione on-demand

Dopo che il mandato è stato autorizzato, crea addebiti secondo necessità.

Endpoint: [POST /subscriptions/\{subscription\_id}/charge](/api-reference/subscriptions/create-charge)

Campi chiave della richiesta (corpo):

<AccordionGroup>
  <Accordion title="Charge request body parameters">
    <ParamField body="product_price" type="integer" required>
      Importo da addebitare (nell’unità monetaria più piccola). Esempio: per addebitare \$25,00, passa <code>2500</code>.
    </ParamField>

    <ParamField body="product_currency" type="string">
      Sovrascrittura facoltativa della valuta per l’addebito.
    </ParamField>

    <ParamField body="product_description" type="string">
      Sovrascrittura facoltativa della descrizione per questo addebito.
    </ParamField>

    <ParamField body="adaptive_currency_fees_inclusive" type="boolean">
      Se true, include le commissioni in valuta adattiva all’interno di <code>product\_price</code>. Se false, le commissioni vengono aggiunte sopra.
    </ParamField>

    <ParamField body="metadata" type="object">
      Metadata aggiuntivi per il pagamento. Se omessi, vengono usati i metadata dell’abbonamento.
    </ParamField>
  </Accordion>
</AccordionGroup>

<Tabs>
  <Tab title="Node.js SDK">
    ```javascript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY });

    async function chargeNow(subscriptionId) {
      const res = await client.subscriptions.charge(subscriptionId, { product_price: 2500 });
      console.log(res.payment_id);
    }

    chargeNow('sub_123').catch(console.error);
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(bearer_token=os.environ.get('DODO_PAYMENTS_API_KEY'))

    response = client.subscriptions.charge(
        subscription_id="sub_123",
        product_price=2500,
    )

    print(response.payment_id)
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    package main

    import (
      "context"
      "fmt"
      "github.com/dodopayments/dodopayments-go"
      "github.com/dodopayments/dodopayments-go/option"
    )

    func main() {
      client := dodopayments.NewClient(option.WithBearerToken("YOUR_API_KEY"))
      res, err := client.Subscriptions.Charge(context.TODO(), "sub_123", dodopayments.SubscriptionChargeParams{
        ProductPrice: dodopayments.F(int64(2500)),
      })
      if err != nil { panic(err) }
      fmt.Println(res.PaymentID)
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "$DODO_API/subscriptions/sub_123/charge" \
      -H "Authorization: Bearer $DODO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "product_price": 2500,
        "product_description": "Extra usage for March"
      }'
    ```
  </Tab>
</Tabs>

```json Success theme={null}
{ "payment_id": "pay_abc123" }
```

<Warning>
  Addebitare un abbonamento che non è on-demand potrebbe fallire. Assicurati che l’abbonamento abbia `on_demand: true` nei suoi dettagli prima dell’addebito.
</Warning>

## Gestione delle transazioni fallite

Quando un addebito per un abbonamento su richiesta fallisce, decidi tu cosa succede dopo. A differenza degli abbonamenti programmati — dove un rinnovo fallito interrompe ulteriori addebiti automatici — **gli abbonamenti su richiesta rimangono addebitabili dopo un fallimento**. Puoi richiamare l'endpoint di addebito come parte della tua logica di ripetizione.

### Cosa succede in caso di fallimento

<Steps>
  <Step title="Charge attempt fails">
    La richiesta `POST /subscriptions/{subscription_id}/charge` restituisce una risposta di errore oppure si completa in modo asincrono ed emette un webhook `payment.failed` con il motivo del rifiuto.
  </Step>

  <Step title="Subscription may transition to on_hold">
    L'abbonamento può passare allo stato `on_hold` ed emettere un webhook `subscription.on_hold` (vedi [Statuti degli abbonamenti → In sospeso](/features/subscription#on-hold-state)). Questo è un segnale — non un blocco. Per gli abbonamenti su richiesta, `on_hold` **non** impedisce di effettuare nuovamente l'addebito.
  </Step>

  <Step title="Retry the charge (your call)">
    Per i flussi su richiesta, Dodo **non** riprova automaticamente. Puoi richiamare `POST /subscriptions/{subscription_id}/charge` in qualsiasi momento per riprovare. Applica la [politica di ripetizione sicura](#payment-retries) qui sotto — utilizza esponenziale backoff, salta i rifiuti rigidi e evita schemi di burst — in modo che le ripetizioni non vengano segnalate dai nostri sistemi di frode e rischio.
  </Step>

  <Step title="Optionally, ask the customer for a new payment method">
    Se i tentativi di nuovo falliscono perché il metodo di pagamento stesso è malfunzionante (carta scaduta, conto chiuso, ecc.), utilizza [`POST /subscriptions/{subscription_id}/update-payment-method`](/api-reference/subscriptions/update-payment-method) per raccoglierne uno nuovo dal cliente. Al successo, l'abbonamento ritorna a `active` e vengono emessi `payment.succeeded` seguiti da `subscription.active` webhooks.
  </Step>
</Steps>

<Info>
  **Su richiesta vs programmato**: Per gli abbonamenti programmati, Dodo esegue i propri rinnovi e dunning. Per gli abbonamenti su richiesta, tu possiedi la politica di ripetizione perché solo tu sai quando dovrebbe avvenire il prossimo addebito (è guidato dai tuoi eventi di utilizzo, non da un calendario).
</Info>

### Sequenza di webhook su un addebito su richiesta fallito

| Ordine | Evento                 | Significato                                                                                                    |
| ------ | ---------------------- | -------------------------------------------------------------------------------------------------------------- |
| 1      | `payment.failed`       | Il tentativo di addebito su richiesta non è riuscito (include il motivo del rifiuto)                           |
| 2      | `subscription.on_hold` | L'abbonamento è stato sospeso (informativo; non blocca ulteriori addebiti)                                     |
| 3\*    | `payment.succeeded`    | Un addebito successivo — sia la tua ripetizione che dopo un aggiornamento del metodo di pagamento — è riuscito |
| 4\*    | `subscription.active`  | L'abbonamento è tornato a `active` dopo un addebito riuscito                                                   |

<Info>
  Gli eventi 3 e 4 si verificano solo dopo che un addebito di follow-up ha successo.
</Info>

### Responsabilità della ripetizione

<Warning>
  Dodo Payments **non ripete automaticamente gli addebiti su richiesta falliti**. Tu possiedi la politica di ripetizione. Segui le linee guida di ripetizione sicura qui sotto per evitare di essere segnalato dai nostri sistemi di rilevamento delle frodi come test di carte.
</Warning>

[Gestione delle insolvenze degli abbonamenti](/features/recovery/subscription-dunning) — la sequenza di recupero tramite email incorporata — è limitata ai pagamenti di *rinnovo* falliti su abbonamenti programmati e cancellazioni avviate dal cliente. Non è progettata per fallimenti di addebiti su richiesta. Comunica direttamente con il cliente (ad esempio, email transazionale o prompt in-app) quando decidi che il metodo di pagamento deve essere aggiornato.

## Ripetizione dei pagamenti

Il nostro sistema di rilevamento delle frodi può bloccare schemi di ripetizione aggressivi (e può segnalarli come potenziale test di carte). Segui una politica di ripetizione sicura.

<Warning>
  Gli schemi di ripetizione a raffica possono essere segnalati come fraudolenti o sospetti test di carte dai nostri sistemi di rischio e dai processori. Evita ripetizioni raggruppate; segui il programma di backoff e le linee guida di allineamento temporale di seguito.
</Warning>

### Principi per politiche di ripetizione sicure

* **Meccanismo di backoff**: Usa un backoff esponenziale tra le ripetizioni.
* **Limiti di ripetizione**: Limita le ripetizioni totali (max 3–4 tentativi).
* **Filtraggio intelligente**: Ripeti solo su fallimenti ripetibili (ad esempio, errori di rete/emittente, fondi insufficienti); non riprovare mai per rifiuti rigidi.
* **Prevenzione del test di carte**: Non riprovare fallimenti come `DO_NOT_HONOR`, `STOLEN_CARD`, `LOST_CARD`, `PICKUP_CARD`, `FRAUDULENT`, `AUTHENTICATION_FAILURE`.
* **Varietà di metadata (opzionale)**: Se mantieni il tuo sistema di ripetizione, differenzia le ripetizioni tramite metadata (ad esempio, `retry_attempt`).

### Programma di ripetizione suggerito (abbonamenti)

* **1º tentativo**: Immediato quando crei l'addebito
* **2º tentativo**: Dopo 3 giorni
* **3º tentativo**: Dopo altri 7 giorni (10 giorni totali)
* **4º tentativo (finale)**: Dopo altri 7 giorni (17 giorni totali)

Passo finale: se ancora non pagato, segna l'abbonamento come non pagato o annullalo, in base alla tua politica. Notifica il cliente durante la finestra per aggiornare il metodo di pagamento.

### Evitare ripetizioni a raffica; allinea al tempo di autorizzazione

* Ancora le ripetizioni al timestamp di autorizzazione originale per evitare comportamenti a "raffica" su tutto il portafoglio.
* Esempio: Se il cliente avvia una prova o un mandato alle 13:10 di oggi, programma le ripetizioni successive alle 13:10 nei giorni successivi secondo il tuo backoff (ad esempio, +3 giorni → 13:10, +7 giorni → 13:10).
* In alternativa, se memorizzi l'ora dell'ultimo pagamento riuscito `T`, programma il tentativo successivo a `T + X days` per preservare l'allineamento dell'orario.

<Note>
  Fuso orario e DST: usa un standard di tempo coerente per la pianificazione e converti per la visualizzazione solo per mantenere gli intervalli.
</Note>

### Codici di declino che non dovresti riprovare

* `STOLEN_CARD`
* `DO_NOT_HONOR`
* `FRAUDULENT`
* `PICKUP_CARD`
* `AUTHENTICATION_FAILURE`
* `LOST_CARD`

<Note>
  Per un elenco completo delle motivazioni di declino e se sono correggibili dall'utente, vedi la documentazione su
  [Fallimenti delle transazioni](/api-reference/transaction-failures).
</Note>

<Tip>
  Ripeti solo su problemi soft/temporanei (ad esempio, `insufficient_funds`, `issuer_unavailable`, `processing_error`, timeout di rete). Se lo stesso declino si ripete, sospendi ulteriori ripetizioni.
</Tip>

### Linee guida per l'implementazione (senza codice)

* Utilizza un gestore di code che mantiene timestamp precisi; calcola il tentativo successivo all'esatto offset orario (ad esempio, `T + 3 days` alla stessa HH:MM).
* Mantieni e riferisci il timestamp dell'ultimo pagamento riuscito `T` per calcolare il tentativo successivo; non raggruppare più abbonamenti allo stesso istante.
* Valuta sempre il motivo dell'ultimo declino; interrompi le ripetizioni per declini rigidi nella lista di skip sopra.
* Limita le ripetizioni concorrenti per cliente e per account per prevenire picchi accidentali.
* Comunica proattivamente: invia email/SMS al cliente per aggiornare il metodo di pagamento prima del prossimo tentativo programmato.
* Usa metadata solo per osservabilità (ad esempio, `retry_attempt`); non cercare mai di "evadere" i sistemi di frode/rischio ruotando campi irrilevanti.

## Annullamento

Gli abbonamenti on-demand seguono un flusso di annullamento diverso rispetto agli abbonamenti programmati perché non esiste un ciclo di fatturazione fisso per ancorare una data di fine immediata.

### Comportamento del portale clienti

Quando un cliente annulla un abbonamento on-demand dal [Portale Clienti](/features/customer-portal), l'annullamento è **programmato per la prossima data di fatturazione** per impostazione predefinita. L'opzione **Annulla Ora** non viene intenzionalmente mostrata per gli abbonamenti on-demand.

La ragione è che gli abbonamenti on-demand non hanno date di rinnovo ricorrenti prevedibili — il prossimo tempo di addebito è guidato interamente dai tuoi eventi di utilizzo. Programmare l'annullamento alla prossima data di fatturazione mantiene il mandato attivo fino al limite del periodo in modo che qualsiasi utilizzo in corso possa ancora essere addebitato, quindi termina l'abbonamento in modo pulito.

Dopo che il cliente conferma l'annullamento:

* L'abbonamento rimane `active` e rimane addebitabile tramite `POST /subscriptions/{id}/charge` fino alla data di annullamento programmata.
* `cancel_at_next_billing_date` è impostato su `true` sull'abbonamento.
* Viene emesso un webhook `subscription.cancelled` quando l'annullamento ha effetto.

<Info>
  Se hai bisogno di terminare l'abbonamento immediatamente (ad esempio, in risposta a un rimborso o a una richiesta di supporto), annullalo in modo programmatico tramite l'API invece di fare affidamento sul flusso del portale clienti.
</Info>

### Annullare programmaticamente

Puoi annullare un abbonamento on-demand tramite l'API in qualsiasi momento. Controlli se l'annullamento è immediato o programmato.

Endpoint: [PATCH /subscriptions/\{subscription\_id}](/api-reference/subscriptions/patch-subscriptions)

<Tabs>
  <Tab title="Cancel immediately">
    Imposta l'abbonamento `status` su `cancelled` per terminarlo subito. Il mandato è revocato e non possono essere creati ulteriori addebiti.

    ```javascript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY });

    await client.subscriptions.update('sub_123', {
      status: 'cancelled',
    });
    ```

    ```bash cURL theme={null}
    curl -X PATCH "$DODO_API/subscriptions/sub_123" \
      -H "Authorization: Bearer $DODO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "status": "cancelled" }'
    ```
  </Tab>

  <Tab title="Cancel at next billing date">
    Rispecchia il comportamento del portale clienti — mantieni l'abbonamento attivo fino alla data di annullamento programmata, quindi concludilo.

    ```javascript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY });

    await client.subscriptions.update('sub_123', {
      cancel_at_next_billing_date: true,
    });
    ```

    ```bash cURL theme={null}
    curl -X PATCH "$DODO_API/subscriptions/sub_123" \
      -H "Authorization: Bearer $DODO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "cancel_at_next_billing_date": true }'
    ```
  </Tab>
</Tabs>

### Webhook su annullamento

| Evento                      | Quando viene attivato                                                                           |
| --------------------------- | ----------------------------------------------------------------------------------------------- |
| `subscription.cancelled`    | Abbonamento completamente annullato e non più addebitabile                                      |
| `subscription.plan_changed` | `cancel_at_next_billing_date` è stato attivato (annullamento programmato impostato o annullato) |

<Tip>
  Per distinguere gli annullamenti on-demand dagli annullamenti degli abbonamenti programmati nel tuo handler, verifica il flag `on_demand` dell'abbonamento durante l'elaborazione del webhook.
</Tip>

## Monitora i risultati con i webhook

Implementa la gestione dei webhook per tracciare il percorso del cliente. Vedi [Implementazione dei Webhook](/developer-resources/integration-guide#implementing-webhooks).

* **subscription.active**: Mandato autorizzato e abbonamento attivato
* **subscription.failed**: Creazione fallita (es. fallimento del mandato)
* **subscription.on\_hold**: Abbonamento posto in sospeso (es. stato non pagato)
* **subscription.cancelled**: Abbonamento completamente annullato (vedi [Annullamento](#cancellation))
* **payment.succeeded**: Addebito riuscito
* **payment.failed**: Addebito fallito

<Tip>
  Per i flussi on-demand, concentrati su `payment.succeeded` e `payment.failed` per riconciliare gli addebiti basati sull'uso. Quando `payment.failed` è seguito da `subscription.on_hold`, vedi [Gestione degli addebiti falliti](#handling-failed-charges) per recuperare l'abbonamento.
</Tip>

## Test e prossimi passi

<Steps>
  <Step title="Create in test mode">
    Usa la tua chiave API di test per creare l'abbonamento, quindi apri il `checkout_url` restituito e completa il mandato.
  </Step>

  <Step title="Trigger a charge">
    Chiama l'endpoint di addebito con un piccolo `product_price` (ad esempio, `100`) e verifica di ricevere `payment.succeeded`.
  </Step>

  <Step title="Go live">
    Passa alla tua chiave API live una volta che hai validato gli eventi e gli aggiornamenti dello stato interno.
  </Step>
</Steps>

## Risoluzione dei problemi

* **422 Richiesta Non Valida**: Assicurati che `on_demand.mandate_only` sia fornito alla creazione e `product_price` sia fornito per gli addebiti.
* **Errori di valuta**: Se sovrascrivi `product_currency`, conferma che sia supportata per il tuo account e cliente.
* **Nessun webhook ricevuto**: Verifica la tua configurazione dell'URL e del segreto di firma del webhook.
