Webhooks e idempotencia en una integración PIX ↔ stablecoins
La regla de oro
1. gere external_id no seu banco 2. persista o pedido como "creating" 3. chame a Lunium com o mesmo external_id 4. em timeout, repita exatamente a mesma chamada 5. aceite webhook repetido sem repetir efeito 6. reconcilie por consulta quando houver dúvida
Consumidor de webhooks
Lee el cuerpo sin procesar
La firma se ha calculado sobre los bytes recibidos. Validar un JSON serializado de nuevo puede producir otra secuencia.
Valida la marca de tiempo y la firma
Rechaza las firmas no válidas y las marcas de tiempo fuera de la ventana permitida. Compara el HMAC en tiempo constante.
Guarda de forma persistente por identificador de evento
Una restricción de unicidad hace que el procesamiento sea idempotente. Un evento repetido debe recibir una respuesta de éxito sin volver a aplicar su efecto.
Aplica una transición válida
No hagas retroceder una operación completada porque haya llegado tarde un evento anterior.
Responde con 2xx después del commit
Si falla el almacenamiento persistente, responde con un error para permitir otra entrega. No confirmes antes de guardar.
Pseudocódigo seguro
const raw = await request.rawBody()
verifyLuniumSignature(request.headers, raw)
const event = JSON.parse(raw)
await db.transaction(async tx => {
if (await tx.events.exists(event.event_id)) return
await tx.events.insert(event.event_id, event)
await tx.orders.applyForwardTransition(event.data)
})
return response.status(204)
Cómo interpretar los fallos
Timeout al crear
Repite con el mismo external_id. No crees un identificador nuevo.
Webhook repetido
Responde con 2xx después de confirmar que el evento ya se ha guardado de forma persistente.
Webhook retrasado
Valida la transición; no sobrescribas un estado terminal más reciente.
Webhook perdido
Usa la consulta de estado y el historial de entregas para conciliar.
Prueba antes de producción
Configura una URL HTTPS pública en la clave y usa POST /webhooks/test. Después valida los reintentos, las firmas incorrectas, los eventos repetidos y la falta temporal de disponibilidad de tu endpoint.
¿Quieres revisar tu consumidor antes del primer PIX?
Envía solo la estructura del flujo y un request_id de prueba, nunca la clave, CPF, billetera ni un cuerpo que contenga secretos.