tgpay cryptoAPI
crypto-payapiwebhookssignature

Référence de l’API : webhooks

4 min de lectureMis à jour le 2 nov. 2026

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_typeSe déclenche quandCharge utile
invoice_paidune facture est payée — sans activation supplémentaireobjet facture
invoice_expiredune facture dépasse son échéance sans être payéeobjet facture
check_activatedun chèque est récupéré, ou un destinataire récupère sa partobjet chèque ; une récupération dans une liste nominative contient aussi activated_slot
check_expiredun chèque à liste nominative expire et les parts non récupérées reviennent à l’applicationobjet chèque avec status: "expired", recipients et leurs valeurs claimed_at
transfer_completedun transfert aboutit ou un paiement sur la blockchain est confirméobjet transfert ou retrait
transfer_returnedun transfert en attente non récupéré revient à l’application, ou un paiement sur la blockchain échoue et ses fonds réservés sont rendusobjet transfert ou retrait
refund_completedun remboursement est exécutéobjet facture avec les champs refunded_*
subscription_activatedun utilisateur approuve une formuleobjet abonnement
subscription_chargedune période est facturée — charge.kind dit laquelle : initial, renewal ou resubscribeobjet abonnement + charge : {period_no, kind, asset, amount, fee, paid_at}
subscription_cancelledun abonnement est annulé par l’une des deux partiesobjet abonnement
subscription_expiredla période de grâce s’écoule sans paiementobjet 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 getInvoices pour 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_id avant 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.