Herramienta gratis · Mercado Pago

Validar la firma de un webhook
de Mercado Pago (x-signature)

Pegá lo que te mandó Mercado Pago y mirá si la firma coincide, qué manifest se firmó y el hash esperado al lado del recibido. Pensado para cuando tu handler de webhooks responde 401 y no sabés por qué.

Todo se calcula en tu navegador. La firma se calcula en esta página con la Web Crypto API. Nada de lo que escribís se envía a ningún servidor y el secreto no sale de esta página.

El valor completo, con la forma ts=…,v1=…

Dejalo vacío si la notificación no lo traía.

Sale del query string de la URL de la notificación (?data.id=…). Dejalo vacío si no está.

La clave secreta de tu aplicación, en la configuración de Webhooks del panel de desarrolladores de Mercado Pago.

Cómo firma Mercado Pago un webhook

Cada notificación de webhook trae una firma HMAC-SHA256 para que puedas comprobar que viene de Mercado Pago y que no es un replay. Verificarla lleva cinco pasos.

  1. 01

    Separá el header x-signature

    El header se ve así: x-signature: ts=1704908010,v1=618c8534…. Cortalo por las comas y después por el primer signo igual de cada parte. ts es el timestamp de la firma (el ejemplo oficial tiene 10 dígitos, es decir segundos; aceptá segundos o milisegundos) y v1 es el hash.

  2. 02

    Juntá las tres entradas

    data.id sale del query string de la URL de la notificación (?data.id=123456), x-request-id es un header del request y ts viene del paso uno.

  3. 03

    Armá el manifest

    Concatená id:<data.id>;request-id:<x-request-id>;ts:<ts>; exactamente así, con el punto y coma final. Si falta data.id o x-request-id, omití ese segmento completo. El SDK de Go pasa data.id a minúsculas antes de usarlo.

  4. 04

    Calculá el HMAC-SHA256

    Firmá el manifest usando como clave el secreto de webhook de tu aplicación y tomá el digest en hexadecimal minúscula.

  5. 05

    Compará y después revisá el timestamp

    Comparé tu digest con v1 en tiempo constante y rechazá el request si ts es demasiado viejo. Una ventana de cinco minutos es una elección habitual.

La misma verificación en Node.js

Una implementación sin dependencias de los cinco pasos, con node:crypto. Ponela en una ruta de Express o Next.js y llamala con los headers del request, el parámetro data.id y tu secreto.

verify-mercadopago-webhook.ts
 1  import { createHmac, timingSafeEqual } from 'node:crypto';
 2  
 3  export function verifyMercadoPagoWebhook(req, secret: string): boolean {
 4    const xSignature = req.header('x-signature') ?? '';
 5    const xRequestId = req.header('x-request-id') ?? '';
 6    const dataId = String(req.query['data.id'] ?? '').toLowerCase();
 7  
 8    const parts = Object.fromEntries(
 9      xSignature.split(',').map((p) => p.trim().split('=')),
10    );
11    const { ts, v1 } = parts;
12    if (!ts || !v1) return false;
13  
14    let manifest = '';
15    if (dataId) manifest += `id:${dataId};`;
16    if (xRequestId) manifest += `request-id:${xRequestId};`;
17    manifest += `ts:${ts};`;
18  
19    const expected = createHmac('sha256', secret).update(manifest).digest('hex');
20    const a = Buffer.from(expected);
21    const b = Buffer.from(v1);
22    if (a.length !== b.length || !timingSafeEqual(a, b)) return false;
23  
24    // Replay protection: reject a signed timestamp older than 5 minutes.
25    // Mercado Pago's example ts has 10 digits (seconds); accept seconds or milliseconds.
26    const tsMs = Number(ts) < 1e12 ? Number(ts) * 1000 : Number(ts);
27    return Math.abs(Date.now() - tsMs) <= 5 * 60 * 1000;
28  }

¿Por qué mi firma de Mercado Pago es inválida?

¿Por qué no coincide la firma de mi webhook de Mercado Pago?+
Casi siempre es una de cuatro cosas: el manifest se arma distinto de lo que firmó Mercado Pago (orden equivocado, falta el punto y coma final, un segmento incluido cuando había que omitirlo), el secreto es de otra aplicación, data.id se leyó del lugar equivocado o con mayúsculas, o un proxy modificó los headers. Esta página muestra el manifest que firmó para que lo compares con el tuyo.
¿data.id tiene que ir en minúsculas?+
El SDK de Go pasa data.id a minúsculas antes de ponerlo en el manifest. Los ids numéricos de pago no se ven afectados, así que la diferencia solo aparece con ids alfanuméricos. Este validador pasa data.id a minúsculas por vos; si tu handler no lo hace, las notificaciones con ids alfanuméricos pueden fallar.
¿De dónde sale data.id?+
Del parámetro data.id del query string de la URL de la notificación, que coincide con data.id del cuerpo JSON. Usá el mismo valor en los dos lados y no lo reformatees.
¿Puedo usar el secreto de otra aplicación?+
No. El secreto pertenece a una aplicación: lo encontrás en Tus integraciones, en la aplicación, dentro de Webhooks. Si tenés varias aplicaciones o entornos, usá el secreto de la aplicación que envió la notificación.
La firma es válida acá pero mi servidor la rechaza. ¿Por qué?+
El hash puede estar bien y el timestamp ser viejo. La protección contra replay rechaza un ts firmado que está muy lejos de la hora actual; el servidor de useDeploy usa una ventana de cinco minutos. Cuando pegás una notificación vieja, esta página te muestra su antigüedad en segundos y te avisa.
¿Es seguro pegar mi secreto de webhook acá?+
El cálculo ocurre en tu navegador con la Web Crypto API y la página no hace ningún request de red con lo que escribís, cosa que podés comprobar en la pestaña de red de tu navegador. Si igual preferís no pegar un secreto de producción, usá el de una aplicación de prueba o rotá el secreto después.
¿Qué notificaciones traen el header x-signature?+
Las notificaciones de Webhooks traen el header x-signature con ts y v1. Este validador necesita los dos: si tu notificación no trae el header x-signature, no hay nada para validar.
useDeploy en un vistazo
PrecioFounder $99 o Lifetime $199, pago único (sin suscripción)
LicenciaCódigo fuente completo, uso comercial, proyectos ilimitados. Founder es una versión congelada; Lifetime incluye todas las versiones futuras.
StackFrontend en Next.js, API en Express, runtime Bun, Prisma, BetterAuth
AutenticaciónBetterAuth: sesiones, 2FA, magic links, reseteo de contraseña
Multi-tenancy y RBACOrganizaciones con miembros, invitaciones, roles y permisos
PagosPolar: facturación completa (checkout, portal de cliente, webhooks, sincronización de planes). MercadoPago: pagos únicos con Checkout Pro y webhooks firmados; las suscripciones todavía no están soportadas. Stripe: scaffold de adapter, todavía no cobra.
Guardrails para agentes de IACapas de arquitectura forzadas con ESLint, hooks de Claude Code y un AGENTS.md
Reembolso14 días
Probalo: cuenta gratisRegistrate gratis y explorá el dashboard: organizaciones, roles, facturación, registro de auditoría. La licencia se compra desde adentro. https://usedeploy.dev/register
DocsDocumentación en inglés, con traducciones al español y al portugués de las guías principales