Ir para o conteúdo
GrandePay

Guias

Validando assinatura HMAC

Compare a assinatura do corpo recebido com HMAC-SHA256.

#Como validar

Leia o corpo bruto. Calcule HMAC-SHA256 hexadecimal com a secret key e compare em tempo constante com X-Signature-256 ou X-Webhook-Signature.

#Exemplo no servidor

server.mjs
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const secret = process.env.GRANDEPAY_SECRET_KEY;

if (!secret) {
  throw new Error("Defina GRANDEPAY_SECRET_KEY no servidor.");
}

function readRawBody(req) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    req.on("data", (chunk) => chunks.push(chunk));
    req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
    req.on("error", reject);
  });
}

function signaturesMatch(received, expected) {
  const receivedBuffer = Buffer.from(received);
  const expectedBuffer = Buffer.from(expected);
  if (receivedBuffer.length !== expectedBuffer.length) {
    return false;
  }
  return timingSafeEqual(receivedBuffer, expectedBuffer);
}

createServer(async (req, res) => {
  const rawBody = await readRawBody(req);
  const header =
    req.headers["x-signature-256"] || req.headers["x-webhook-signature"] || "";
  const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");

  if (!signaturesMatch(String(header), expected)) {
    res.writeHead(401);
    res.end();
    return;
  }

  res.writeHead(204);
  res.end();
}).listen(3001);

#Fixture de teste

Corpo {"event":"transaction.paid","timestamp":"2026-03-13T17:05:32.000Z","data":{"id":"4d78d8ca-a9f1-4c2a-bf15-7f8ca71e3a70","amount_cents":1500}}, segredo sk_docs_fixture_do_not_use, digest ed7d4198d1227586c7ea8ad136d58eb42aabe8dec3157ace1686175b3dece78e. O digest bate no corpo original e falha se um byte do JSON ou o segredo mudam.

Esta página foi útil?