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údo timestamp + 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:

  1. Obter a chave de API

  2. Validar X-Pagou-Signature:

    • Extraia o <hex> do header X-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).

  3. 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 e X-Pagou-Timestamp para garantir a origem e integridade do webhook.

  • Comparação Segura: Use funções como crypto.timingSafeEqual (JavaScript) ou hmac.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