Webhooks y eventos
Cada operación que llega a un estado final emite un evento:
payin.confirmed, payin.failed, payout.confirmed o payout.failed
(y test.ping es el aviso de prueba, abajo). El
evento lleva la operación entera en data. Se lo mandamos a su url por
webhook, y además puede consultarlo con GET /v2/events.
Registrar su url
Sección titulada «Registrar su url»POST /v2/webhook-endpoints con {"url": "https://…"} devuelve el
secreto una sola vez: guárdelo. Una url activa por empresa: registrar
otra la reemplaza, y el secreto anterior sigue valiendo 24 horas para que
cambie su copia sin perder ningún aviso.
Verificar la firma
Sección titulada «Verificar la firma»Cada aviso llega con estas cabeceras:
| Cabecera | Qué trae |
|---|---|
X-Firma |
la firma, v1=<hex>; durante una rotación van dos separadas por espacio |
X-Timestamp |
segundos Unix de cuándo se firmó; se rechaza fuera de la ventana |
X-Evento |
el type del evento |
X-Id-Evento |
el id del evento, para deduplicar |
Algoritmo: HMAC-SHA256 sobre <X-Timestamp> + "." + <cuerpo crudo>, en hex; tolerancia de reloj: 300 segundos.
La firma es HMAC-SHA256 sobre X-Timestamp + "." + cuerpo crudo, en
hexadecimal, con su secreto como llave, con el prefijo v1=. Para
verificarla:
- Lea el cuerpo crudo, antes de parsear el JSON: reserializar cambia los bytes y la firma deja de coincidir.
- Rechace si
X-Timestampestá a más de 5 minutos de su reloj: sin esto, un aviso legítimo capturado una vez sirve para siempre. - Calcule el HMAC y compárelo en tiempo constante con cada firma de
X-Firma(durante una rotación de secreto van dos, separadas por espacio; alcanza con que una coincida). - Sólo entonces parsee el JSON y procese el evento. Deduplique por
id.
Los tres SDK lo hacen por usted (ParseEvent, parseEvent, parse_event).
Es la parte del SDK que más vale la pena usar: verificar mal una firma es el
error que no se nota.
Probar su url
Sección titulada «Probar su url»Desde el dashboard (Webhooks → Enviar aviso de prueba) le mandamos un
aviso test.ping firmado igual que los de verdad, con las mismas cabeceras y
el secreto vigente. Sirve para probar que su url contesta y que su código
verifica la firma antes de que llegue el primer pago.
Su data es una operación de ejemplo: el id en ceros, status
pending, amount 0.00 y metadata.test = true. No corresponde a ningún
movimiento. Decida siempre por type: conteste 2xx a test.ping y no lo
procese. No se reintenta ni aparece en GET /v2/events.
Entrega y reintentos
Sección titulada «Entrega y reintentos»Conteste 2xx en menos de 10 segundos. Si no, reintentamos con espera
creciente durante unas 24 horas (30 s, 2, 10, 30 min, 1, 2, 4, 8 y 8 h) y
recién entonces el aviso se da por agotado; un aviso agotado se reenvía a
pedido. Un mismo evento puede llegarle más de una vez: por eso el id.
Consultar los eventos
Sección titulada «Consultar los eventos»GET /v2/events?cursor=&limit= devuelve sus eventos en orden con un cursor:
sirve para reconciliar, para recuperar lo que no recibió y para integrar sin
webhook. Guarde next_cursor.
Los eventos de sus operaciones
Sección titulada «Los eventos de sus operaciones»/v2/eventsAlcanceoperaciones:leer
El mismo evento que viaja por webhook, en orden, con cursor. Sirve para reponerse de un webhook perdido: guarde el último next_cursor y pida desde ahí. Requiere el alcance operaciones:leer.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
cursor |
query | no | El next_cursor de la respuesta anterior. Sin él, desde el principio. |
limit |
query | no |
| Código | Significa |
|---|---|
200 |
Los eventos. |
400 |
Un parámetro inválido. Códigos: field_invalid. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
Consultar cualquier operación
Sección titulada «Consultar cualquier operación»/v2/transactions/{id}Alcanceoperaciones:leer
Un cobro o un pago por su id, con la misma forma. Requiere el alcance operaciones:leer.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
id |
path | sí | El id de la operación. |
| Código | Significa |
|---|---|
200 |
La operación. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
404 |
No hay una operación con ese identificador en su empresa. Códigos: not_found. |
Registrar la url de avisos
Sección titulada «Registrar la url de avisos»/v2/webhook-endpointsAlcancewebhooks:configurar
Registra la url a la que se le mandan los eventos y devuelve el secreto de firma una sola vez. Una url activa por empresa: registrar otra la reemplaza, y el secreto anterior sigue valiendo 24 horas para que el cambio no pierda avisos.
Requiere el alcance webhooks:configurar. La url tiene que ser https.
Cómo llega un evento
Sección titulada «Cómo llega un evento»POST a su url con el cuerpo de Evento y estas cabeceras: X-Firma (HMAC-SHA256 de <X-Timestamp>.<cuerpo> con su secreto, como v1=<hex>; durante una rotación van dos, separadas por espacio), X-Timestamp (segundos Unix), X-Evento (el type) y X-Id-Evento (el id). Conteste 2xx en menos de 10 segundos; si no, se reintenta con espera creciente durante unas 24 horas (30 s, 2, 10, 30 min, 1, 2, 4, 8 y 8 h, con un ±10 % al azar) y recién entonces se agota; un aviso agotado se reenvía a mano. Deduplique por id.
Mientras no registre nada acá, los eventos van a la url y con el secreto de la versión 1, si los tiene.
| Código | Significa |
|---|---|
201 |
Registrada. Guarde secret: no se vuelve a mostrar. |
400 |
Falta la url o no es https. Códigos: body_invalid, field_invalid, field_required. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
El evento
Sección titulada «El evento»| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
data |
Operacion |
sí | |
id |
string |
sí | |
occurred_at |
string |
sí | |
type |
TipoDeEvento |
sí |
curl -X POST https://api.tucapi.app/v2/webhook-endpoints \ -H "Authorization: Bearer $LLAVE" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.suempresa.com/avisos" }'