---
title: "Avvisi di pagamento fallito su Stripe: contestazioni, rifiuti e webhook morti"
description: "Stripe ti avvisa via e-mail di contestazioni e pagamenti falliti. Come trasformare gli eventi webhook di Stripe in push, avvisi urgenti o telefonate."
date: 2026-09-11
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - Stripe
  - pagamenti
  - webhook
  - contestazioni
  - SaaS
  - alerting
---

# Avvisi di pagamento fallito su Stripe: contestazioni, rifiuti e webhook morti

Stripe sa già quando viene aperta una contestazione, quando il pagamento di un abbonamento fallisce o quando un bonifico rimbalza. Te lo dice via e-mail. Per qualcosa di più rumoroso, registra in Stripe un secondo endpoint webhook che punta all'URL di un canale Echobell, iscrivilo a una manciata di tipi di evento e scegli il tipo di notifica in base alla scadenza attaccata a ciascuno. Contestazioni e avvisi precoci di frode hanno un orologio che gira; un rinnovo fallito di solito no.

Questa guida copre quali eventi di Stripe meritano di interromperti, come collegare Stripe a Echobell in circa cinque minuti, i campi dei template che esistono davvero su ogni oggetto e il compromesso che accetti saltando la verifica della firma.

## Gli eventi che meritano davvero un'interruzione

Gli avvisi sui pagamenti sbagliano sempre allo stesso modo: qualcuno si iscrive a `payment_intent.succeeded` perché fa piacere, il telefono vibra quaranta volte al giorno e sei settimane dopo una notifica di contestazione scorre via senza essere letta. Parti invece dalla scadenza. Se perdere l'evento per otto ore non costa nulla, non deve raggiungerti in otto secondi.

| Evento | Perché conta | Tipo consigliato |
| --- | --- | --- |
| `charge.dispute.created` | Hai una finestra limitata per rispondere — [di solito da 7 a 21 giorni, a seconda del circuito](https://docs.stripe.com/disputes/responding). Se la perdi, perdi automaticamente. | Chiamata |
| `radar.early_fraud_warning.created` | L'emittente della carta ha segnalato a Stripe che un addebito potrebbe essere fraudolento. Rimborsare prima che diventi una contestazione formale è l'azione che ti resta, e la finestra è breve. | Chiamata |
| `payout.failed` | I soldi incassati da Stripe non arrivano in banca. Tutto ciò che ne dipende — stipendi, calcolo del runway — adesso è sbagliato. | Chiamata |
| `invoice.payment_failed` | Churn involontario. Si risolve da solo abbastanza spesso da rendere la chiamata eccessiva, ma gli account più grandi meritano un'occhiata in giornata. | Urgente |
| `customer.subscription.deleted` | Churn volontario. Vale la pena saperlo oggi, non vale una sveglia. | Normale |
| `payment_intent.succeeded` | Non è rotto niente. È proprio questo evento ad allenarti a ignorare gli altri cinque. | Niente |

I livelli corrispondono ai tre [tipi di notifica](/it/docs/notification) di Echobell: **Normale** è un push ordinario, **Urgente** attraversa la maggior parte delle modalità Full Immersion e **Chiamata** si presenta come una telefonata in arrivo, quindi suona anche con Non disturbare. Ogni iscritto sceglie il proprio livello canale per canale: la cofondatrice può ricevere le contestazioni come chiamata mentre un collega del supporto le riceve come push.

## Cosa ti serve

- Un account Stripe con accesso alla [scheda Webhooks in Workbench](https://dashboard.stripe.com/webhooks)
- Echobell installato ([App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-stripe-payment-failure-alerts-it&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid))
- Cinque minuti. Niente server, niente deploy, niente codice — a meno che tu non voglia la verifica della firma, che sta nell'ultima sezione.

## Passo 1 — Un canale per tipo di evento

Viene la tentazione di fare un unico canale "Stripe" e mandarci dentro tutto. Non farlo. Template del corpo e link alla dashboard cambiano tra contestazione, fattura e bonifico, perché ognuno trasporta un oggetto diverso — e il senso di tutta questa configurazione è che la notifica ti dica cosa è successo senza aprire nulla.

Crea un canale con il nome dell'evento: `Stripe Disputes`. Scrivi i template di titolo e corpo perché si leggano al volo sulla schermata di blocco:

```
Title: 🔴 Dispute opened — {{data.object.reason}}
Body: Amount: {{data.object.amount}} {{data.object.currency}}
Charge: {{data.object.charge}}
Status: {{data.object.status}}
```

Imposta il template del link nelle impostazioni avanzate, così il record della notifica apre la pagina giusta:

```
https://dashboard.stripe.com/disputes/{{data.object.id}}
```

Poi iscriviti con tipo **Chiamata** e copia l'URL del webhook dalla vista dettaglio del canale. Ha questa forma: `https://hook.echobell.one/t/<channel-token>`.

<Callout type="info">Attiva **POST Only** nelle impostazioni avanzate del canale. Stripe invia sempre in POST, e quell'interruttore fa sì che incollare l'URL in una chat o in una scheda del browser non possa più far scattare un falso avviso di contestazione.</Callout>

## Passo 2 — Punta Stripe sul canale

Nella dashboard di Stripe apri la [scheda Webhooks](https://dashboard.stripe.com/webhooks) e crea una destinazione eventi:

<Steps>

<Step>

Clicca **Create an event destination**, seleziona **Your account** e lascia la versione API sul valore predefinito del tuo account.

</Step>

<Step>

Seleziona esattamente un tipo di evento — `charge.dispute.created` per questo canale. È Stripe stessa a consigliare di [iscriversi solo agli eventi che servono alla tua integrazione](https://docs.stripe.com/webhooks); qui in più mantiene onesto il template, perché ogni payload che arriva ha la stessa forma.

</Step>

<Step>

Scegli **Webhook endpoint** come tipo di destinazione e incolla l'URL del canale Echobell.

</Step>

<Step>

Salva, poi usa **Send test event** — oppure `stripe trigger charge.dispute.created` dalla CLI — e verifica che il telefono squilli.

</Step>

</Steps>

Ripeti per ogni canale creato. Stripe consente fino a 16 endpoint webhook per account, più che sufficienti per uno per livello di avviso.

## Passo 3 — Cosa arriva davvero

Stripe invia l'[oggetto Event](https://docs.stripe.com/api/events/object) in JSON. Echobell legge il corpo così com'è, quindi ogni campo è raggiungibile in template e condizioni con la notazione a punti:

```json
{
  "id": "evt_1P...",
  "type": "charge.dispute.created",
  "livemode": true,
  "created": 1757548800,
  "data": {
    "object": {
      "id": "dp_1P...",
      "amount": 4900,
      "currency": "usd",
      "reason": "fraudulent",
      "status": "needs_response",
      "charge": "ch_3P...",
      "evidence_details": { "due_by": 1759449600 }
    }
  }
}
```

Tre cose di questo payload sorprendono quasi tutti:

**Gli importi sono interi nella minima unità di valuta.** Un `amount` di `4900` sono 49,00 $. I template di Echobell interpolano e confrontano valori ma non fanno aritmetica, quindi `{{data.object.amount}}` rende `4900`. O lo etichetti onestamente (`Amount: 4900 (cents)`), o usi il forwarder dell'ultima sezione per dividere per 100 prima dell'invio.

**I timestamp sono secondi Unix.** `{{data.object.evidence_details.due_by}}` viene reso come `1759449600`, non come data. Se conta più l'esistenza della scadenza che l'ora esatta, toglilo dal template — la pagina della contestazione la mostra — e lascia lavorare il template del link.

**I nomi dei campi cambiano per oggetto.** Una contestazione ha `amount`; una fattura ha `amount_due`, `customer_email`, `attempt_count` e `hosted_invoice_url`; un bonifico ha `failure_message` e `arrival_date`; un avviso precoce di frode ha `fraud_type`, `actionable` e `charge` come semplice ID testuale. Una variabile mancante viene resa come stringa vuota anziché come errore, quindi un template copiato dal canale sbagliato fallisce in silenzio. È questo il motivo pratico di un canale per tipo di evento.

## Passo 4 — Filtra con le condizioni, non con la forza di volontà

Le [condizioni](/it/docs/conditions) di canale usano la stessa sintassi delle espressioni dei template, senza le graffe, e vengono valutate prima di ogni consegna.

Quella da mettere su ogni canale Stripe:

```
livemode == true
```

Il traffico in modalità test — i tuoi `stripe trigger`, un collega che smanetta in una sandbox — non arriva più sul telefono. Aggiungila dopo aver verificato che il collegamento funziona, non prima.

Per il canale delle fatture fallite una soglia tiene i conti piccoli fuori dalle tue serate:

```
livemode == true && data.object.amount_due > 20000
```

Si legge "oltre 200 $", in centesimi. E se preferisci vedere i tentativi davvero bloccati invece di ogni primo fallimento:

```
livemode == true && data.object.attempt_count > 1
```

Se invece hai puntato un endpoint con più tipi di evento su un unico canale, le condizioni li rimettono in ordine:

```
type == "charge.dispute.created" || type == "payout.failed"
```

## Passo 5 — Tieni il percorso dell'avviso fuori da ciò che si rompe

Questa parte vale più dei template.

Il tuo endpoint webhook di produzione è dove avviene il fulfillment: concede accessi, scrive sul database, manda la ricevuta. È quindi anche l'endpoint che cade quando cade la tua applicazione. Quando succede, Stripe [riprova fino a tre giorni con backoff esponenziale](https://docs.stripe.com/webhooks) e ti manda un'e-mail — e un'e-mail sui webhook non consegnati è identica a ogni altra e-mail di Stripe, ed è per questo che la si trova il lunedì.

La morte silenziosa di un webhook ha cause noiose. Stripe considera un redirect `3xx` un fallimento, quindi un endpoint che inizia a rimandare da `http` a `https` o ad aggiungere lo slash finale smette di ricevere eventi. Richiede TLS 1.2 o superiore, quindi basta un certificato scaduto o mal configurato. Anche un `403` da una regola WAF aggiunta la settimana scorsa.

Un secondo endpoint puntato dritto su Echobell non condivide nulla di tutto ciò. È un altro URL su un altro host con un altro certificato, e continua a suonare mentre la tua applicazione è a terra. La regola si generalizza: *il percorso che ti dice che qualcosa è rotto non dovrebbe passare attraverso la cosa rotta.*

Vuoi comunque che il guasto del tuo endpoint sia visibile. Controlla la scheda **Event deliveries** in Workbench quando qualcosa non torna — mostra `Delivered`, `Pending` e `Failed` per evento, con lo stato HTTP di ogni tentativo. Stripe consente di rinviare un evento fino a 15 giorni dalla dashboard, o 30 con `stripe events resend` dalla CLI: un buco individuato entro due settimane è recuperabile.

<Callout type="warn">L'URL del canale Echobell è una credenziale bearer: chi ce l'ha può far scattare il canale. Puntarci Stripe direttamente significa che nessuno verifica l'header `Stripe-Signature`, quindi un URL trapelato è una macchina per falsi avvisi, non una violazione di dati. Tienilo fuori da repository e screenshot, usa **Reset Token** se sfugge, e leggi la sezione successiva se il compromesso ti dà fastidio.</Callout>

## Facoltativo — Verifica prima la firma

Se vuoi che la firma di Stripe venga davvero verificata e gli importi formattati come denaro, metti davanti un piccolo forwarder. Questo Cloudflare Worker verifica l'evento, risponde subito `200` come chiede Stripe e invia a Echobell un payload piatto:

```js
import Stripe from "stripe";

export default {
  async fetch(request, env, ctx) {
    const stripe = new Stripe(env.STRIPE_SECRET_KEY);
    const body = await request.text();

    let event;
    try {
      event = await stripe.webhooks.constructEventAsync(
        body,
        request.headers.get("stripe-signature"),
        env.STRIPE_WEBHOOK_SECRET,
      );
    } catch {
      return new Response("invalid signature", { status: 400 });
    }

    const invoice = event.data.object;
    ctx.waitUntil(
      fetch(env.ECHOBELL_HOOK_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          customer: invoice.customer_email || invoice.customer,
          amount: (invoice.amount_due / 100).toFixed(2),
          currency: invoice.currency.toUpperCase(),
          attempt: invoice.attempt_count,
          externalLink: invoice.hosted_invoice_url,
        }),
      }),
    );

    return new Response("ok", { status: 200 });
  },
};
```

Il template dall'altra parte diventa molto più pulito, perché la formattazione è già avvenuta nel codice:

```
Title: 💳 Payment failed — {{currency}} {{amount}}
Body: Customer: {{customer}}
Attempt #{{attempt}}
```

`externalLink` è una variabile speciale: senza template del link impostato, Echobell la usa come link del record di notifica, così la fattura ospitata è a un tocco di distanza.

Nota la forma del compromesso: ora è un pezzo di infrastruttura che può guastarsi a sua volta, esattamente ciò contro cui mette in guardia il passo 5. Una via di mezzo ragionevole è verificare le firme sul canale ad alto volume, dove i falsi avvisi darebbero fastidio, e lasciare il canale delle contestazioni collegato direttamente: lì il costo di uno squillo spurio è uno sguardo perplesso, quello di uno squillo mancato è l'importo contestato.

## Cosa questa configurazione non ti dà

- **Nessuna reperibilità a turni né escalation.** Tutti gli iscritti a un canale di chiamata squillano insieme. È un pregio in quattro e un problema in quaranta; a quaranta serve una piattaforma di incident management.
- **Nessuna deduplicazione.** Stripe [non garantisce l'ordine degli eventi](https://docs.stripe.com/webhooks) e può consegnare lo stesso evento più di una volta. Due squilli per una contestazione sono possibili.
- **Nessun riscontro.** Niente registra che un umano l'abbia visto, e niente passa a una seconda persona se nessuno risponde.
- **Nessun avviso "i pagamenti si sono fermati".** Stripe emette eventi quando le cose accadono, mai quando smettono. Se il checkout si rompe, non parte alcun evento. Per quello serve un job schedulato dalla tua parte che chiami un canale quando gli addebiti dell'ultima ora sono zero — un [dead man's switch basato su cron](/it/blog/cron-job-failure-alerts).

## Risoluzione dei problemi

**L'evento di test mostra `200` su Stripe ma non è arrivata alcuna notifica.** Echobell risponde `200` con un corpo JSON anche quando non consegna — guarda il corpo della risposta nella scheda Event deliveries. `success: false` con un token di lunghezza valida significa token del canale sbagliato. Se `success` è `true`, la causa probabile è una condizione: `livemode == true` blocca ogni evento di test, per progetto.

**Stripe segnala `405 Method Not Allowed`.** Il canale ha POST Only attivo e qualcosa ha inviato un GET. Stripe usa sempre POST, quindi è un'anteprima di link o una scheda del browser, non Stripe.

**La notifica arriva con campi vuoti.** Il template punta all'oggetto sbagliato — `{{data.object.amount}}` su un canale fatture, dove il campo è `amount_due`. Manda un evento reale, aprilo nella dashboard e leggi il JSON.

**Le consegne iniziano a fallire dopo settimane di funzionamento.** Controlla il certificato e ogni redirect davanti all'URL. Con l'URL del canale usato direttamente è raro; con un forwarder che hai messo in produzione tu, è il sospetto abituale.

## Domande frequenti

### Stripe può telefonarmi quando si apre una contestazione?

Da solo no. Stripe avvisa via e-mail, nella dashboard, con l'evento `charge.dispute.created` e via push se usi l'app Stripe Dashboard. Per uno squillo vero, instrada quell'evento su un canale il cui tipo di iscrizione è **Chiamata**.

### Serve scrivere codice per collegare Stripe a Echobell?

No. Stripe invia JSON a qualsiasi URL HTTPS pubblico, e l'URL di un canale Echobell lo è. Il codice serve solo se vuoi verificare l'header `Stripe-Signature` o riformattare gli importi.

### È sicuro dare a Stripe un URL webhook di terze parti?

È un compromesso consapevole. Il payload inviato da Stripe contiene metadati di cliente e pagamento, ed Echobell non conserva in modo permanente i payload grezzi — la notifica renderizzata resta sul tuo dispositivo. Ciò a cui rinunci è la verifica della firma: chi conosce l'URL può mandarti un falso convincente. Trattalo come una chiave API e usa lo schema del forwarder per tutto ciò che preferisci verificare.

### Perché il mio avviso mostra 4900 invece di 49,00 $?

Stripe invia gli importi come interi nella minima unità di valuta, e i template di Echobell non fanno aritmetica. Indica l'unità nel template, oppure dividi per 100 in un forwarder prima dell'invio.

### Come evito che gli eventi in modalità test mi sveglino?

Aggiungi la condizione `livemode == true` al canale. Stripe marca ogni evento sandbox e `stripe trigger` come `livemode: false`.

### Il mio cofondatore può ricevere gli stessi avvisi senza pagare una postazione?

Sì. Condividi il link del canale; ogni iscritto sceglie il proprio tipo di notifica. Una persona prende le contestazioni come chiamata mentre un'altra le riceve come push normale, e non c'è alcun prezzo per postazione sugli iscritti.

### Dovrei impostare avvisi sui pagamenti riusciti?

Solo per poco, e solo finché l'attività è abbastanza piccola da rendere ciascuno ancora un avvenimento. Nel momento in cui la notifica di pagamento riuscito diventa routine, comincia a erodere la tua reazione a quelle che contano — il meccanismo centrale della [alert fatigue](/it/blog/fix-alert-fatigue-developer-guide).

## In sintesi

L'intera configurazione è una destinazione eventi Stripe per ogni canale Echobell, una condizione `livemode == true` e la disciplina di riservare il livello chiamata agli eventi con un orologio attaccato. Contestazioni e avvisi precoci di frode ce l'hanno. Un rinnovo fallito su un piano da 9 $ no, e far finta del contrario è esattamente il modo in cui si finisce per dormire durante quello che ce l'aveva.

[Scarica Echobell per iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-stripe-payment-failure-alerts-it&mt=8) o [prendilo su Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), crea prima il canale delle contestazioni e lancia un `stripe trigger charge.dispute.created` prima di affidare a questo percorso qualcosa di serio.

---

## Correlati

- [Documentazione sull'integrazione webhook](/it/docs/webhook)
- [Riferimento alle condizioni di canale](/it/docs/conditions)
- [Guida per sviluppatori contro l'alert fatigue](/it/blog/fix-alert-fatigue-developer-guide)
- [Avvisi di fallimento dei cron job](/it/blog/cron-job-failure-alerts)
- [Notifiche webhook di Zapier sul telefono](/it/blog/zapier-webhook-notifications-to-phone)
