Autenticação de Webhooks
Todos os webhooks enviados pela API do Pagou, incluindo eventos de boletos (ex.: charge.created
, charge.paid
) e Pix (ex.: qrcode.completed
, qrcode.refunded
), incluem os headers X-Pagou-Signature
e X-Pagou-Timestamp
para verificar a autenticidade e integridade das notificações. Esta seção detalha como validar esses headers, incluindo o processo de verificação da assinatura HMAC-SHA256 e do timestamp, exemplos de código em Python e JavaScript, e boas práticas para segurança.
Visão Geral Técnica
Os webhooks da API do Pagou são enviados como requisições HTTP POST
com corpo JSON para o notification_url
configurado (ex.: https://your-webhook.com/notifications). Cada webhook inclui dois headers de autenticação:
X-Pagou-Signature
: Assinatura HMAC-SHA256 do conteúdotimestamp + payload
(onde payload é a string JSON bruta do corpo da requisição). A assinatura é gerada com a chave de API do cliente.X-Pagou-Timestamp
: Timestamp em segundos desde o epoch (ex.: 1754332106 para 2025-08-04 18:28:26).
Validação dos Headers
Para garantir que o webhook é confiável, valide os headers X-Pagou-Signature e X-Pagou-Timestamp:
Obter a chave de API
Validar
X-Pagou-Signature
:Extraia o
<hex>
do headerX-Pagou-Signature
.Concatene o valor de
X-Pagou-Timestamp
com o payload JSON bruto (ex.:1754329142
+{"name":"charge.created",...}).
Calcule o HMAC-SHA256 do resultado usando a chave de API.
Compare o hash calculado com o
<hex>
do header usando uma comparação resistente a ataques de tempo (ex.:crypto.timingSafeEqual
em JavaScript,hmac.compare_digest
em Python).
Rejeitar se inválido:
Retorne
401 Unauthorized
se a assinatura ou timestamp forem inválidos.Registre tentativas inválidas para monitoramento.
Nota: A validação dos headers é altamente recomendada para segurança, mas os webhooks são enviados mesmo que a validação não seja implementada.
Exemplos de Código
const crypto = require('crypto');
function verifySignature(payload, timestamp, signature, secret) {
const receivedDigest = signature;
const message = timestamp + payload;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(message);
const computedDigest = hmac.digest('hex');
try {
const receivedBuffer = Buffer.from(receivedDigest, 'hex');
const computedBuffer = Buffer.from(computedDigest, 'hex');
if (receivedBuffer.length !== computedBuffer.length) {
return false;
}
return crypto.timingSafeEqual(receivedBuffer, computedBuffer);
} catch (e) {
return false;
}
}
// Example usage:
const payload = '{"name":"charge.created","data":{"id":"7a86b3b7-779e-4e9f-af7d-bf2e512ddae1","transaction_id":"0","client_code":"7a86b3b7-779e-4e9f-af7d-bf2e512ddae1","payload":{"transaction_id":"123","bank_emissor":"bradesco","bank_number":"137","bank_agency":"0001","bank_account":"12345678","bar_code":"123456789098765434321","line":"1234323465654758868986","bank_assignor":"Celcoin"}}}';
const timestamp = '1754329886'; // From X-Pagou-Timestamp header
const signature = 'ff502eeda47ceb3a6c0dc32a34d9503f32224f6fd8c9ad30a25c0f7cf0ca358c'; // From X-Pagou-Signature header
const apiKey = '07ab896a-d830-418b-8c55-47874dc6760e'; // API Key
const isValid = verifySignature(payload, timestamp, signature, apiKey);
console.log('Signature valid:', isValid);
Boas Práticas Técnicas
Segurança: Sempre valide
X-Pagou-Signature
eX-Pagou-Timestamp
para garantir a origem e integridade do webhook.Comparação Segura: Use funções como
crypto.timingSafeEqual
(JavaScript) ouhmac.compare_digest
(Python) para evitar ataques de tempo.Prevenção de Replay Attacks: Rejeite webhooks com
X-Pagou-Timestamp
fora de ±5 minutos do horário atual.Monitoramento: Registre
X-Pagou-Timestamp
,X-Pagou-Signature
, e resultados da validação em logs para auditoria.HTTPS: Use um certificado SSL válido para o
notification_url
.
Last updated