Riferimento API: webhook
I webhook inviano gli eventi al tuo server nel momento in cui succedono. Imposta l’URL del webhook sulla tua app in Altro → Merchant API: da lì in poi la piattaforma manda in POST un corpo JSON firmato per ogni evento a cui sei iscritto.
La busta
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00Z",
"payload": { … }
}
update_id resta lo stesso a ogni riconsegna dello stesso evento: usa quello
per deduplicare. request_date viene registrato a ogni tentativo di consegna.
payload è l’oggetto completo del tipo di evento: l’oggetto fattura per gli
eventi delle fatture, l’oggetto buono o l’oggetto abbonamento (le loro forme
stanno nelle rispettive pagine di riferimento).
I tipi di evento
update_type | Quando parte | Payload |
|---|---|---|
invoice_paid | una fattura viene pagata — consegnato sempre | oggetto fattura |
invoice_expired | una fattura supera la scadenza senza essere pagata | oggetto fattura |
check_activated | uno dei tuoi buoni viene riscattato | oggetto buono |
refund_completed | un rimborso viene eseguito | oggetto fattura con i campi refunded_* |
subscription_activated | un utente approva un piano | oggetto abbonamento |
subscription_charged | un periodo viene fatturato — charge.kind dice quale: initial, renewal o resubscribe | oggetto abbonamento + charge: {period_no, kind, asset, amount, fee, paid_at} |
subscription_cancelled | un abbonamento viene disdetto da una delle due parti | oggetto abbonamento |
subscription_expired | il periodo di tolleranza finisce senza pagamento | oggetto abbonamento |
Tutto tranne invoice_paid è facoltativo (è un’estensione rispetto a
Crypto Bot: un consumatore fatto esattamente sulla forma di Crypto Bot non
incontra mai un update_type sconosciuto, a meno che tu non l’abbia chiesto).
Si attivano per app sotto Eventi webhook aggiuntivi, sulla schermata
Merchant API: le opzioni compaiono una volta impostato un URL e portano
gli identificativi grezzi della tabella qui sopra.
Verificare la firma
Ogni consegna porta l’intestazione TgCryptoPay-API-Signature
(Crypto-Pay-API-Signature ne è un alias di compatibilità, con lo stesso
valore): l’HMAC-SHA256 in esadecimale del corpo grezzo della richiesta, con
chiave il digest SHA-256 del token API principale della tua app. È lo schema
di Crypto Bot, quindi il codice di verifica che hai già funziona senza
modifiche:
import hashlib, hmac
secret = hashlib.sha256(API_TOKEN.encode()).digest()
expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, headers["TgCryptoPay-API-Signature"])
Verifica sui byte grezzi ricevuti: un JSON riserializzato dopo il parsing può differire byte per byte e far fallire il controllo. A firmare è solo il token principale; i token con permessi limitati non firmano mai. Rigenerare il token principale cambia all’istante la chiave di firma dei webhook, quindi aggiorna il segreto sul tuo server nello stesso momento.
Consegne e tentativi
- Una consegna si considera riuscita con qualsiasi risposta 2xx entro 10 secondi.
- Qualsiasi altra cosa — uno stato di errore, un timeout, una connessione caduta — viene ritentata con attese crescenti: il primo tentativo dopo circa 10 secondi, poi l’intervallo raddoppia fino a 8 ore, per un massimo di 17 tentativi distribuiti su circa 3 giorni.
- Dopo l’ultimo tentativo la consegna viene lasciata perdere. L’URL del webhook, però, non viene mai disattivato da solo: un endpoint ballerino non ti disiscrive l’app in silenzio.
- Siccome i tentativi si ripetono, il tuo gestore dev’essere idempotente:
deduplica su
update_idprima di agire.
⚠️ Verifica prima di evadere
Al tuo URL può mandare un POST chiunque. Finché il controllo della firma non
passa, tratta il corpo come input non fidato: non spedire un ordine, non
accreditare un utente, non segnare niente come pagato. L’ordine giusto è:
verifica la firma → deduplica su update_id → agisci.
Questa guida ti è stata utile?
Grazie del riscontro.