Référence de l’API : webhooks
Les webhooks transmettent les événements à votre serveur après qu’ils se sont produits. Définissez l’URL du webhook (facultatif) sur votre application dans Plus → API marchand ; la plateforme y envoie ensuite en POST un corps JSON signé pour chaque événement auquel vous êtes abonné.
L’enveloppe
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00.123456Z",
"payload": { … }
}
update_id reste stable d’un nouvel envoi à l’autre du même événement —
fondez votre déduplication dessus. request_date (UTC, ISO 8601 avec microsecondes) est enregistré à chaque
tentative de livraison. payload est l’objet complet correspondant au
type d’événement : l’objet facture pour les événements de facture, l’objet
chèque, transfert ou abonnement (les structures sont sur les pages des
méthodes : factures, transferts et chèques,
abonnements).
Les types d’événements
update_type | Se déclenche quand | Charge utile |
|---|---|---|
invoice_paid | une facture est payée — sans activation supplémentaire | objet facture |
invoice_expired | une facture dépasse son échéance sans être payée | objet facture |
check_activated | un chèque est récupéré, ou un destinataire récupère sa part | objet chèque ; une récupération dans une liste nominative contient aussi activated_slot |
check_expired | un chèque à liste nominative expire et les parts non récupérées reviennent à l’application | objet chèque avec status: "expired", recipients et leurs valeurs claimed_at |
transfer_completed | un transfert aboutit ou un paiement sur la blockchain est confirmé | objet transfert ou retrait |
transfer_returned | un transfert en attente non récupéré revient à l’application, ou un paiement sur la blockchain échoue et ses fonds réservés sont rendus | objet transfert ou retrait |
refund_completed | un remboursement est exécuté | objet facture avec les champs refunded_* |
subscription_activated | un utilisateur approuve une formule | objet abonnement |
subscription_charged | une période est facturée — charge.kind dit laquelle : initial, renewal ou resubscribe | objet abonnement + charge : {period_no, kind, asset, amount, fee, paid_at} |
subscription_cancelled | un abonnement est annulé par l’une des deux parties | objet abonnement |
subscription_expired | la période de grâce s’écoule sans paiement | objet abonnement |
Les paiements sur la blockchain créés dans l’app utilisent ces mêmes événements de transfert. Leur charge utile contient kind: "withdrawal", des champs comme network, address et txid, et le statut completed ou failed. Leur transfer_id appartient à une séquence d’identifiants distincte. Seule cette variante comporte kind : la charge utile d’un transfert ordinaire n’a aucun champ kind. Traitez donc une charge utile avec kind: "withdrawal" comme un paiement sur la blockchain, et une charge utile sans kind comme un transfert. La référence OpenAPI téléchargeable décrit les deux variantes de charge utile.
Tout sauf invoice_paid est facultatif (une extension par rapport à
Crypto Bot — un consommateur strictement calqué sur Crypto Bot ne
rencontre jamais un update_type inconnu sans l’avoir demandé).
Activez-les par application sous Événements de webhook supplémentaires
dans la section Webhook de l’application ; les interrupteurs portent les identifiants du tableau ci-dessus. Enregistrez une URL de webhook pour recevoir les événements.
Vérifier la signature
Chaque livraison porte l’en-tête TgPayCrypto-API-Signature
(TgCryptoPay-API-Signature et Crypto-Pay-API-Signature sont des alias de
compatibilité avec la même valeur) : le HMAC-SHA256 hexadécimal du corps
brut de la requête, avec pour clé l’empreinte SHA-256 du
token d’API principal de votre application. C’est le schéma de Crypto
Bot, donc le code de vérification existant fonctionne sans modification :
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["TgPayCrypto-API-Signature"])
Vérifiez sur les octets bruts reçus — une résérialisation après analyse peut différer octet pour octet et faire échouer le contrôle. Seul le token principal signe ; les tokens restreints ne signent jamais. Renouveler le token principal change instantanément la clé de signature des webhooks : mettez donc le secret à jour sur votre serveur au même moment.
Livraison et nouvelles tentatives
- Une livraison est considérée comme réussie si une réponse 2xx est reçue dans les 10 secondes. Répondez rapidement et traitez les tâches longues séparément. Les redirections ne sont pas suivies : une réponse 3xx compte comme un échec, enregistrez donc l’URL finale.
- Un statut d’erreur, un délai de réponse dépassé ou un échec de connexion déclenche de nouvelles tentatives : jusqu’à 17, avec un délai qui commence à 10 secondes et double jusqu’à 8 heures (avec une part d’aléatoire), soit environ 3 jours au total. La livraison n’est pas garantie ; utilisez
getInvoicespour rapprocher les paiements après une interruption. - Chaque tentative part vers l’URL du webhook enregistrée à ce moment-là : une URL modifiée reçoit les nouvelles tentatives en attente, et la suppression de l’URL les annule.
- Après la dernière tentative, la livraison est abandonnée. L’URL du webhook elle-même n’est jamais désactivée automatiquement — les erreurs de votre serveur ne désabonnent pas automatiquement votre application.
- Comme les envois peuvent être répétés, votre gestionnaire doit être idempotent :
dédupliquez sur
update_idavant d’agir.
⚠️ Vérifiez avant d’honorer la commande
N’importe qui peut envoyer un POST à votre URL de webhook. Tant que le
contrôle de signature n’est pas passé, traitez le corps comme une entrée
non fiable : n’expédiez pas de commande, ne créditez pas d’utilisateur, ne
marquez rien comme payé. L’ordre sûr est : vérifier la signature →
dédupliquer sur update_id → agir.
Cet article vous a-t-il été utile ?
Merci pour votre retour.