Referencia de la API: webhooks
Los webhooks le envían los eventos a tu servidor en el momento en que ocurren. Pon la URL del webhook en tu app, en Más → API para comerciantes; a partir de ahí la plataforma hace POST de un cuerpo JSON firmado por cada evento al que estés suscrito.
El envoltorio
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00Z",
"payload": { … }
}
update_id se mantiene estable entre reenvíos del mismo evento: basa tu
deduplicación en él. request_date se sella en cada intento de entrega.
payload es el objeto completo del tipo de evento: el objeto de factura para
los eventos de facturas, el objeto de cheque o el objeto de suscripción (las
formas están en las páginas de referencia
correspondientes).
Tipos de eventos
update_type | Se dispara cuando | Payload |
|---|---|---|
invoice_paid | se paga una factura — siempre se entrega | objeto de factura |
invoice_expired | una factura pasa su fecha límite sin pagarse | objeto de factura |
check_activated | se canjea uno de tus cheques | objeto de cheque |
refund_completed | se ejecuta un reembolso | objeto de factura con los campos refunded_* |
subscription_activated | un usuario aprueba un plan | objeto de suscripción |
subscription_charged | se cobra un período — charge.kind dice cuál: initial, renewal o resubscribe | objeto de suscripción + charge: {period_no, kind, asset, amount, fee, paid_at} |
subscription_cancelled | una suscripción se cancela por cualquiera de los dos lados | objeto de suscripción |
subscription_expired | el período de gracia se agota sin pago | objeto de suscripción |
Todo salvo invoice_paid es opcional (una extensión sobre Crypto Bot: un
consumidor con forma estricta de Crypto Bot nunca se topa con un update_type
desconocido salvo que lo hayas pedido). Actívalos por app en Eventos de
webhook adicionales, en la pantalla API para comerciantes: los interruptores
aparecen una vez que hay una URL de webhook, y están etiquetados con los
identificadores crudos de la tabla de arriba.
Verificar la firma
Cada entrega lleva la cabecera TgPayCrypto-API-Signature
(Crypto-Pay-API-Signature es un alias de compatibilidad con el mismo valor):
el HMAC-SHA256 en hexadecimal del cuerpo crudo de la petición, con clave el
resumen SHA-256 del token de API principal de tu app. Es el esquema de
Crypto Bot, así que el código de verificación que ya tengas funciona sin
cambios:
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"])
Verifica sobre los bytes crudos recibidos: un análisis reserializado puede diferir byte a byte y hacer fallar la comprobación. Solo firma el token principal; los tokens restringidos nunca firman. Renovar el token principal vuelve a fijar al instante la clave de firma de los webhooks, así que actualiza el secreto en tu servidor en el mismo movimiento.
Entrega y reintentos
- Una entrega cuenta como exitosa con cualquier respuesta 2xx en menos de 10 segundos.
- Cualquier otra cosa —un estado de error, un tiempo agotado, un fallo de conexión— se reintenta con retroceso exponencial: el primer reintento a los 10 segundos aproximadamente, duplicando el intervalo hasta 8 horas, con hasta 17 intentos repartidos en unos 3 días.
- Después del último intento la entrega se descarta. La URL del webhook en sí nunca se desactiva automáticamente: un endpoint inestable no desuscribe tu app en silencio.
- Como hay reintentos, tu manejador tiene que ser idempotente: deduplica por
update_idantes de actuar.
⚠️ Verifica antes de despachar
Cualquiera puede hacer POST a tu URL de webhook. Hasta que la comprobación de la
firma pase, trata el cuerpo como entrada no confiable: no despaches un pedido,
no le acredites nada a un usuario, no marques nada como pagado. El orden seguro
es: verificar la firma → deduplicar por update_id → actuar.
¿Te sirvió este artículo?
Gracias por tu comentario.