Vai al contenuto principale
Quando un pagamento fallisce, Dodo Payments ti comunica perché attraverso un error_code standardizzato e un error_message leggibile. Questa guida mostra come leggere quei campi, decidere se vale la pena riprovare e recuperare il pagamento senza esporre informazioni sensibili ai clienti.

Come Dodo Payments Riporta un Fallimento

Ogni pagamento fallito — sia un checkout una tantum che un rinnovo di abbonamento — porta gli stessi campi di fallimento sull’oggetto di pagamento:
CampoTipoDescrizione
statusstringfailed per un pagamento fallito. Altri stati non di successo includono cancelled, requires_customer_action, e requires_payment_method.
error_codestring | nullIl motivo standardizzato del fallimento, per esempio INSUFFICIENT_FUNDS o PROCESSING_ERROR. Vedi il riferimento Transaction Failures per la lista completa.
error_messagestring | nullUna spiegazione leggibile del fallimento.
retry_attemptinteger0 per l’addebito originale. 1 o superiore identifica un tentativo di rinnovo abbonamento programmato.
error_code e error_message sono null finché un pagamento non fallisce effettivamente. Controlla sempre prima status, poi leggi i campi di errore.

Il Webhook payment.failed

Il modo più affidabile per rilevare un fallimento è il webhook payment.failed. L’evento avvolge l’intero oggetto di pagamento in data:
payment.failed payload
{
  "business_id": "bus_P3SXLcppjXgagmHS",
  "type": "payment.failed",
  "timestamp": "2025-08-04T05:36:41.609359Z",
  "data": {
    "payload_type": "Payment",
    "payment_id": "pay_2IjeQm4hqU6RA4Z4kwDee",
    "status": "failed",
    "error_code": "PROCESSING_ERROR",
    "error_message": "An error occurred while processing your card. Try again in a little bit.",
    "retry_attempt": 0,
    "subscription_id": null,
    "currency": "USD",
    "total_amount": 400,
    "payment_method": "card",
    "card_last_four": "0119",
    "card_network": "VISA",
    "payment_link": "https://test.checkout.dodopayments.com/cbq",
    "customer": {
      "customer_id": "cus_8VbC6JDZzPEqfB",
      "email": "test@acme.com",
      "name": "Test user"
    }
  }
}
Un gestore minimale legge error_code e instrada su di esso:
import { Webhook } from "standardwebhooks";
import express from "express";

const app = express();
// Mount the raw body parser so the exact payload is available for verification
app.use(express.raw({ type: "application/json" }));

const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY);

app.post("/webhooks/dodo", async (req, res) => {
  // Verify the signature against the raw body before trusting the payload
  const payload = req.body.toString();
  await webhook.verify(payload, req.headers);

  const event = JSON.parse(payload);

  if (event.type === "payment.failed") {
    const payment = event.data;

    console.log(
      `Payment ${payment.payment_id} failed: ${payment.error_code} (${payment.error_message})`
    );

    if (payment.subscription_id) {
      // Subscription renewal — Dodo retries soft declines for you
      await flagSubscriptionPaymentIssue(payment.subscription_id, payment.error_code);
    } else {
      // One-time payment — prompt the customer to try again
      await notifyCustomerOfFailedPayment(payment.customer.customer_id, payment.error_code);
    }
  }

  res.json({ received: true });
});
Verifica sempre la firma del webhook prima di elaborarlo. Vedi la guida Webhooks per l’intero setup, compresa la verifica della firma e l’idempotenza.

Decidi se Riprovare: Declini Soft vs. Hard

error_code ti dice se vale la pena riprovare con lo stesso metodo di pagamento.
Tipo di declinoCosa significaCosa fare
Declino softTemporaneo o correggibile (per esempio INSUFFICIENT_FUNDS, PROCESSING_ERROR, NETWORK_ERROR, TRY_AGAIN_LATER).Riprovare, dopo un ritardo o una volta che il cliente ha corretto il proprio input, può avere successo.
Declino hardTerminale (per esempio STOLEN_CARD, LOST_CARD, DO_NOT_HONOR, FRAUDULENT).Non riprovare con lo stesso carta. Chiedi al cliente un diverso metodo di pagamento.
Il riferimento Transaction Failures elenca il tipo di declino e l’azione consigliata per ogni error_code.

Gestire i Fallimenti al Checkout vs. al Rinnovo

Come si recupera dipende dal fatto che il cliente sia presente.
Il cliente sta effettuando attivamente il checkout. Mostra un messaggio chiaro e lascia che riprovino immediatamente o utilizzino un’altra carta.
  • requires_payment_method — il cliente non ha mai fornito un metodo di pagamento: non ha inserito i dettagli della carta o è stato richiesto di farlo e non ha agito. Questo è di solito un abbandono del checkout, non un rifiuto: re-ingaggia il cliente per completare il pagamento (vedi Abandoned Cart Recovery).
  • requires_customer_action — è necessaria un’autenticazione aggiuntiva (come 3DS); fai completare al cliente. Vedi gestione 3D Secure.

Riprovare un Pagamento Fallito

  • Abbonamenti: Abilita Subscription Payment Retries per recuperare declini soft senza lavoro di integrazione. Puoi anche innescare il recupero facendo aggiornare al cliente il proprio metodo di pagamento tramite l’API di Aggiornamento Metodo di Pagamento, che addebita eventuali importi in sospeso.
  • Pagamenti una tantum: Invia nuovamente il checkout o payment_link affinché il cliente possa riprovare con un metodo diverso. Non esiste un ripetizione automatica per pagamenti una tantum.
Non riprovare declini hard con la stessa carta. Le reti di carte possono segnalare ripetuti declini come abusivi, il che riduce il tuo tasso di autorizzazione.

Comunicazione degli Errori ai Clienti in Sicurezza

Mostra ai clienti un messaggio amichevole — mai il vero error_code.
Customer-facing messaging
const CUSTOMER_MESSAGES = {
  INSUFFICIENT_FUNDS: "Your card has insufficient funds. Please use another card.",
  EXPIRED_CARD: "Your card has expired. Please use a card with a valid expiry date.",
  INCORRECT_CVC: "The security code (CVC) is incorrect. Please re-enter it.",
};

function customerMessage(errorCode) {
  // Sensitive declines must never reveal the real reason
  const SENSITIVE = ["STOLEN_CARD", "LOST_CARD", "PICKUP_CARD", "FRAUDULENT"];
  if (SENSITIVE.includes(errorCode)) {
    return "Your card was declined. Please contact your bank or use another card.";
  }
  return CUSTOMER_MESSAGES[errorCode] ?? "Your payment could not be processed. Please try another card.";
}
Non rivelare mai la vera ragione per STOLEN_CARD, LOST_CARD, PICKUP_CARD, o FRAUDULENT. Mostrare questi può avvisare un attore fraudolento. Mostra un messaggio di declino generico e registra solo il specifico error_code internamente.

Correlati

Transaction Failures

Ogni codice di declino, il suo tipo e l’azione raccomandata.

Error Codes

Errori di API e logica aziendale che non sono declini di carta.

Subscription Payment Retries

Recupero automatico dei declini soft sui rinnovi degli abbonamenti.

Subscription Dunning

Sequenze email che recuperano declini hard.

Payment Webhooks

Schema completo del payload per eventi di pagamento.

Testing Failures

Carte di test che simulano declini e fallimenti di rinnovo.
Ultima modifica il 18 giugno 2026