Verificar la firma
HMAC SHA-256 con el signing secret, en Node, C#, Python, PHP y Go.
La firma es un HMAC SHA-256 de {timestamp}.{cuerpo crudo} usando el signing secret del webhook (whsec_...), que no es la API key.
Verificá siempre sobre el cuerpo crudo. Si tu framework parsea el JSON y lo vuelve a serializar, los bytes cambian y la firma no coincide.
Compará con una función de tiempo constante y rechazá timestamps de más de cinco minutos para evitar reenvíos.
Implementaciones
import crypto from "node:crypto";export function verifyTinkay(rawBody, headers, secret) { const timestamp = headers["x-tinkay-timestamp"]; const received = String(headers["x-tinkay-signature"] || "").replace("sha256=", ""); if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(received, "hex"); return a.length === b.length && crypto.timingSafeEqual(a, b);}Rotar el secreto
Desde el detalle del webhook podés rotar el signing secret. Durante 24 horas aceptamos firmas con el secreto anterior, así podés desplegar sin ventana de error.
Guardá el secreto en tu gestor de variables de entorno, nunca en el repositorio.
Cómo saber que quedó bien
- Una entrega legítima pasa la verificación.
- Una entrega con el cuerpo modificado se rechaza con 400.
