¿Por qué fallan los webhooks?
En integraciones profesionales, el 80% de errores de webhooks no son “código roto”, sino detalles: el payload no es exactamente el mismo que se firma, se cambia el orden de campos, se convierte a JSON bonito, se re-encodea en UTF-8/UTF-16, o se concatena un prefijo distinto al esperado. Además, distintos proveedores tienen convenciones: algunos firman timestamp + '.' + payload, otros firman el cuerpo sin tocar, y otros usan varias firmas rotatorias.
Este tester te permite reproducir la firma exactamente como debe salir y comparar con la que llega a tu endpoint. Si coincide, sabes que tu secret y tu payload están bien; si no coincide, toca revisar normalización, contenido y cabeceras.
Examples prácticos con código
// Node.js - HMAC SHA256 (hex)
import crypto from "crypto";
const payload = "RAW_BODY_AQUÍ";
const secret = "TU_SECRET";
const sig = crypto.createHmac("sha256", secret).update(payload, "utf8").digest("hex");
console.log(sig);
// PHP - HMAC SHA256 (hex)
$payload = $rawBody; // el cuerpo exacto recibido
$secret = "TU_SECRET";
$sig = hash_hmac("sha256", $payload, $secret);
echo $sig;
# cURL - ejemplo de envío (simulado)
curl -X POST https://tu-dominio.com/webhook \
-H "Content-Type: application/json" \
-H "X-Signature: TU_FIRMA" \
-d '{"id":"evt_123","type":"payment_intent.succeeded"}'
FAQ
¿Debo firmar JSON “bonito”?
No. Debes firmar el cuerpo bruto tal como llega (raw body). Si lo parseas y lo vuelves a serializar, puede cambiar.
¿Qué hago si el proveedor usa timestamp?
Firma exactamente el string que indica el proveedor, por ejemplo ${timestamp}.${payload}. Añade el timestamp aquí dentro del payload antes de firmar.
¿Esto envía mi payload a terceros?
No. Se calcula localmente en el navegador; aun así evita usar datos sensibles reales.