vSe alguém chegar aqui com o mesmo problema atente-se para isso:
Ao validar a assinatura de um webhook do Mercado Pago, é importante observar que, embora o parâmetro data.id possa ser recebido com caracteres alfanuméricos em maiúsculo na notificação (ex: ORD01JQ4S...), ele deve ser obrigatoriamente convertido para minúsculo (lowercase) durante o processo de verificação da assinatura para o cálculo do HMAC coincidir.
Isso não está claro na documentação, mas é assim e pode ser o seu caso.
Ex em node:
async function verifyMpSignature(
xSignature: string,
xRequestId: string | null,
dataId: string | null,
secret: string,
): Promise<boolean> {
let ts: string | null = null;
let v1: string | null = null;
for (const part of xSignature.split(',')) {
const [k, v] = part.trim().split('=');
if (k === 'ts') ts = v;
else if (k === 'v1') v1 = v;
}
if (!ts || !v1) return false;
const parts: string[] = [];
if (dataId) parts.push(`id:${dataId.toLowerCase()}`);
if (xRequestId) parts.push(`request-id:${xRequestId}`);
parts.push(`ts:${ts}`);
const manifest = parts.join(';') + ';';
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(manifest));
const hex = Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
return hex === v1;
}