GH GambleHub

Webhooks e idempotencia de eventos

TL; DR

Un buen webhook es un evento firmado (HMAC/mTLS), resumible e idempotente, entregado a través del modelo at-least-once con retroceso exponencial y deduplicación en el destinatario. Negocia sobre ('event _ id', 'type', 'ts',' version ',' attempt', 'signature'), ventana de tiempo (≤5 min), códigos de respuesta, retratos, DLQ y status endpoint.


1) Roles y modelo de entrega

Remitente (usted/proveedor): forma un evento, firma, intenta entregar hasta 2xx, retira a 3xx/4xx/5xx (excepto explícitamente «no aceptes»), conduce DLQ, da replay API.
Destinatario (socio/su servicio): verifica la firma/ventana temporal, hace el dedoup y el procesamiento idempotente, responde con el código correcto, proporciona/status y/ack replay por 'event _ id'.

Garantías: at-least-once. El destinatario debe ser capaz de manejar duplicados y cambios de orden.


2) Sobre del evento (envelope)

json
{
"event_id": "01HF7H9J9Q3E7DYT5Y6K3ZFD6M",
"type": "payout.processed",
"version": "2025-01-01",
"ts": "2025-11-03T12:34:56.789Z",
"attempt": 1,
"producer": "payments",
"tenant": "acme",
"data": {
"payout_id": "p_123",
"status": "processed",
"amount_minor": 10000,
"currency": "EUR"
}
}

Campos obligatorios: 'event _ id', 'type', 'version', 'ts',' attempt'.
Reglas de evolución: agregamos campos; eliminación/cambio de tipos - sólo con la nueva 'versión'.


3) Seguridad: firma y enlace

3. 1 firma HMAC (recomendada por defecto)

Encabezados:

X-Signature: v1=base64(hmac_sha256(<secret>, <canonical>))
X-Timestamp: 2025-11-03T12:34:56Z
X-Event-Id: 01HF7...
Cadena canónica:

<timestamp>\n<method>\n<path>\n<sha256(body)>
Comprobación del destinatario:
  • abs(now − `X-Timestamp`) ≤ 300s
  • 'X-Event-Id' no ha sido procesado previamente (dedoup)
  • 'X-Signature' coincide (con una comparación segura en el tiempo)

3. 2 Dop. Medidas

mTLS para webhooks altamente sensibles.
IP/ASN allow-list.
DPoP (opcional) para sender-constrained si el webhook inicia las devoluciones de llamada.


4) Idempotencia y deduplicación

4. 1 Idempotencia del evento

Un evento con el mismo 'event _ id' no debe cambiar el estado de nuevo. Destinatario:
  • almacena 'event _ id' en caché idempotente (KV/Redis/BD) en TTL ≥ 24-72 h;
  • conserva el resultado del procesamiento (éxito/error, artefactos) para volver a devolverlo.

4. 2 Idempotencia de comandos (devoluciones de llamadas)

Si el webhook hace que el cliente tire de la API (por ejemplo, «confirmar payout»), use 'Idempotency-Key' en el volumen de la llamada NAT, guarde el resultado en el lado del servicio (exactly-once outcome).

Modelo KV (mínimo):

key: idempotency:event:01HF7...
val: { status: "ok", processed_at: "...", handler_version: "..." }
TTL: 3d

5) Retrai y backoff

Gráfico recomendado (exponencial con jitter):
  • '5s, 15s, 30s, 1m, 2m, 5m, 10m, 30m, 1h, 3h, 6h, 12h, 24h' (más adelante diario a N días)
Soluciones de código:
  • 2xx - éxito, dejar de retraer.
4xx:
  • '400/ 401/403/404/422' - no retraemos si la firma/formato es aprox (error del cliente).
  • '429' es un retraim por 'Retry-After' o backoff.
  • 5xx/red - retraim.

Los encabezados del remitente son: 'User-Agent', 'X-Webhook-Producer', 'X-Attempt'.


6) Tratamiento en el lado del destinatario

Pseudopapline:
pseudo verify_signature()
if abs(now - X-Timestamp) > 300s: return 401

if seen(event_id):
return 200 // идемпотентный ответ

begin transaction if seen(event_id): commit; return 200 handle(data)       // доменная логика mark_seen(event_id)    // запись в KV/DB commit return 200

Transaccionalidad: la etiqueta «seen» debe colocarse atomicamente con el efecto de la operación (o después de fijar el resultado) para evitar el doble procesamiento cuando falla.


7) Garantías de orden y tapones

El orden no está garantizado. Utilice 'ts' y dominios' seq '/' version 'en' data 'para conciliar la relevancia.
Para largos retrasos/pérdidas: agregue/replay del remitente y/resync del destinatario (obtenga snapshot y delta por la ventana de tiempo/ID).


8) Estado, Replay y DLQ

8. 1 Endpoints del remitente

'POST/webhooks/replay' - por la lista 'event _ id' o por la ventana de tiempo.
'GET/webhooks/events/: id' - mostrar el paquete original y el historial de intentos.
DLQ: eventos «muertos» (se ha agotado el límite de retraídos) → almacenamiento separado, alertas.

8. 2 Endpoints del receptor

`GET /webhooks/status/:event_id` — `seen=true/false`, `processed_at`, `handler_version`.
'POST/webhooks/ack' - (opcional) confirmación de mecanizado manual desde DLQ.


9) Contratos de error (respuesta del destinatario)

http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
Retry-After: 120
X-Trace-Id: 4e3f...

{
"error": "invalid_state",
"error_description": "payout not found",
"trace_id": "4e3f..."
}

Recomendaciones: siempre devuelve un código claro y, si puedes, 'Retry-After'. No devuelva detalles de seguridad detallados.


10) Monitoreo y SLO

Métricas (remitente):
  • delivery p50/p95, tasa de éxito, retray/evento, drop-rate DLQ, compartir 2xx/4xx/5xx, ventana de latencia hasta 2xx.
Métricas (destinatario):
  • verify fail rate (firma/tiempo), dup-rate, latency handler p95, 5xx.
SLO puntos de referencia:
  • Entrega: ≥ 99. El 9% de los eventos reciben 2xx <3 c p95 (después del primer intento exitoso).
  • Cryptoproverk: validación de firma ≤ 2-5 ms p95.
  • Dedoup: 0 efectos repetidos (exactly-once outcome a nivel de dominio).

11) Seguridad de datos y privacidad

No transmita PAN/PII en el cuerpo del webhook; utilice los identificadores y el pull subsiguiente para las piezas a través de la API autorizada.
Enmascarar campos sensibles en los logs; almacenar los cuerpos de eventos sólo al mínimo, con TTL.
Cifre los repositorios DLQ y Replay.


12) Versificación e interoperabilidad

Versión en 'versión' (sobre) y en ruta: '/webhooks/v1/payments '.
Nuevos campos: opcionales; eliminación: sólo después del período 'Sunset'.
Documente los cambios en el changelog machine-readable (para verificaciones automáticas).


13) Casos de prueba (lista de cheques UAT)

  • Volver a enviar el mismo 'event _ id' → un efecto y '200' por duplicado.
  • Firma: clave fiel, clave incorrecta, clave vieja (rotación), 'X-Timestamp' fuera de la ventana.
  • Backoff: el receptor da un '429' de 'Retry-After' → una pausa correcta.
  • Orden: eventos '... processed' viene antes '... created' → procesamiento/espera correctos.
  • Fallo de la DB en el receptor entre el efecto y 'mark _ seen' → atomicidad/repetición.
  • DLQ y replay manual → entrega exitosa.
  • La «tormenta» masiva (proveedor de la manada) → sin pérdida, los límites no estrangulan lo crítico.

14) Mini-snippets

Firma del remitente (pseudo):
pseudo body = json(event)
canonical = ts + "\n" + "POST" + "\n" + path + "\n" + sha256(body)
sig = base64(hmac_sha256(secret, canonical))
headers = {"X-Timestamp": ts, "X-Event-Id": event.event_id, "X-Signature": "v1="+sig}
POST(url, body, headers)
Verificación y dedo del destinatario (pseudo):
pseudo assert abs(now - X-Timestamp) <= 300 assert timingSafeEqual(hmac(secret, canonical), sig)

if kv.exists("idemp:"+event_id): return 200

begin tx if kv.exists("idemp:"+event_id): commit; return 200 handle(event.data)        // доменная логика kv.set("idemp:"+event_id, "ok", ttl=259200)
commit return 200

15) Errores frecuentes

No hay dedoop → efectos repetidos (refandos/peyouts dobles).
Firma sin tiempo de espera/ventana → vulnerabilidad a replay.
Almacena un único secreto HMAC en todos los socios.
Respuestas '200' antes de fijar el resultado → pérdida de eventos en crash.
«Lavado» de detalles de seguridad en respuestas/registros.
Falta de DLQ/replay: los incidentes son irresolubles.


16) Introducción de Spark

Seguridad: HMAC v1 + 'X-Timestamp' + 'X-Event-Id', ventana ≤ 5 min; mTLS/IP allow-list por necesidad.
Конверт: `event_id`, `type`, `version`, `ts`, `attempt`, `data`.
Entrega: at-least-once, backoff con jitter, 'Retry-After', DLQ + replay API.
Idempotencia: caché KV 24-72 h, fijación atómica del efecto + 'mark _ seen'.
Observabilidad: métricas de entrega, firmas, duplicados; seguimiento 'trace _ id'.
Documentación: versión, códigos de respuesta, ejemplos, lista de comprobación UAT.


Resumen

Los webhooks resistentes se construyen sobre tres ballenas: un sobre firmado, entrega en hoja y tratamiento idempotente. Formaliza el contrato, habilita HMAC/mTLS y la ventana de tiempo, implementa retraídas + DLQ y réplicas, almacena marcas idempotentes y captura los efectos de forma atómica. Entonces, los eventos permanecen confiables incluso con fallas en la red, picos de carga y raros «duplicados del destino».

Contact

Póngase en contacto

Escríbanos ante cualquier duda o necesidad de soporte.¡Siempre estamos listos para ayudarle!

Iniciar integración

El Email es obligatorio. Telegram o WhatsApp — opcionales.

Su nombre opcional
Email opcional
Asunto opcional
Mensaje opcional
Telegram opcional
@
Si indica Telegram, también le responderemos allí además del Email.
WhatsApp opcional
Formato: +código de país y número (por ejemplo, +34XXXXXXXXX).

Al hacer clic en el botón, usted acepta el tratamiento de sus datos.