Webhooks Explicados: Payloads Assinados e Retry

Backend2026-10-15TryQuickToolBox

Você acabou de integrar um gateway de pagamento. A documentação diz: "Enviaremos um webhook quando um pagamento for bem-sucedido." Ótimo. Mas então você se pergunta: como sei que a requisição realmente veio deles? E se meu servidor estiver fora do ar quando enviarem? E se enviarem o mesmo evento duas vezes?

Webhooks são poderosos, mas trazem armadilhas. Este guia aborda a mecânica, a segurança e os padrões de confiabilidade necessários para lidar com eles corretamente.

O Que É um Webhook?

Um webhook é uma requisição HTTP POST que um sistema envia a outro quando um evento específico ocorre. Em vez de sua aplicação fazer polling em uma API a cada poucos segundos ("Tem pedido novo? Tem pedido novo?"), o provedor envia os dados para você assim que algo acontece.

Fluxo típico:

  1. Você registra uma URL (ex.: https://yourapp.com/webhooks/payments) no provedor.
  2. Um evento ocorre (pagamento aprovado, usuário cadastrado, arquivo enviado).
  3. O provedor envia uma requisição POST para sua URL com um corpo JSON descrevendo o evento.
  4. Seu servidor processa o evento e responde com 2xx para confirmar o recebimento.

Se você não responder rapidamente (geralmente em 5–30 segundos), o provedor considera a entrega falhou e pode tentar novamente.

Por Que Você Deve Verificar Payloads Assinados

Seu endpoint de webhook é publicamente acessível. Qualquer pessoa que descubra a URL pode enviar eventos falsos. Sem verificação, um atacante poderia forjar um evento de "pagamento aprovado" e obter produtos gratuitamente.

A solução: payloads assinados. O provedor calcula uma assinatura criptográfica sobre o corpo da requisição usando um segredo compartilhado (conhecido apenas por você e ele) e a inclui em um cabeçalho. Você recalcula a assinatura do seu lado e compara.

Como Funciona a Assinatura HMAC

A maioria dos provedores usa HMAC-SHA256. Os passos:

  1. O provedor concatena o corpo bruto da requisição com um timestamp (para prevenir ataques de replay).
  2. O provedor calcula HMAC-SHA256(secret, timestamp + '.' + body).
  3. O provedor envia a assinatura em um cabeçalho, ex.: X-Signature: t=1234567890,v1=abc123....
  4. Você extrai o timestamp e a assinatura, recalcula o HMAC usando sua cópia do segredo e compara usando uma função de comparação de tempo constante.

Aqui está um exemplo mínimo em Node.js:

const crypto = require('crypto');

function verifySignature(req, secret) {
  const signatureHeader = req.headers['x-signature'];
  const [tPart, vPart] = signatureHeader.split(',');
  const timestamp = tPart.split('=')[1];
  const receivedSig = vPart.split('=')[1];

  const payload = timestamp + '.' + JSON.stringify(req.body);
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  // Comparação de tempo constante para prevenir ataques de timing
  return crypto.timingSafeEqual(
    Buffer.from(receivedSig, 'hex'),
    Buffer.from(expectedSig, 'hex')
  );
}

Importante: Sempre use o corpo bruto da requisição para verificação de assinatura. Se seu framework faz parse do JSON e o re-serializa, a sequência de bytes pode diferir, causando falha na verificação. Capture o corpo bruto antes do parse.

Validação de Timestamp

Verifique se o timestamp é recente (ex.: dentro de 5 minutos). Isso previne ataques de replay em que um atacante captura uma requisição válida e a reenvia depois.

Lógica de Retry: Lidando com Falhas de Forma Elegante

Seu servidor pode estar fora do ar, sobrecarregado ou enfrentando uma instabilidade de rede. Os provedores lidam com isso tentando novamente entregas que falharam. Mas você deve projetar seu endpoint para lidar com retries com segurança.

Backoff Exponencial com Jitter

Os provedores geralmente tentam novamente com atrasos crescentes: 1 minuto, 5 minutos, 30 minutos, 2 horas, etc. Isso se chama backoff exponencial. Alguns adicionam jitter aleatório para evitar problemas de thundering herd.

Do seu lado, você não controla o cronograma de retry do provedor, mas deve:

Idempotência: A Chave para Retries Seguros

Retries significam que você pode receber o mesmo evento várias vezes. Seu processamento deve ser idempotente—processar o mesmo evento duas vezes deve ter o mesmo efeito que processá-lo uma vez.

Abordagem comum: inclua um ID de evento único no payload. Antes de processar, verifique se você já tratou esse ID. Se sim, retorne 200 e ignore.

async function handleWebhook(event) {
  const existing = await db.events.findOne({ id: event.id });
  if (existing) {
    return { status: 200, body: 'Já processado' };
  }

  await db.events.insert({ id: event.id, processedAt: new Date() });
  await processEvent(event);
  return { status: 200, body: 'OK' };
}

Esse padrão garante que, mesmo que o provedor envie o evento três vezes, você só aja uma vez.

Comparação: Polling vs Webhooks

Aspecto Polling Webhooks
Latência Depende do intervalo (ex.: 30s) Quase em tempo real
Carga no servidor Constante, mesmo sem eventos Apenas quando eventos ocorrem
Complexidade Simples de implementar Requer endpoint, verificação, retries
Confiabilidade Você controla os retries Depende da política de retry do provedor

Webhooks vencem para atualizações em tempo real, mas polling pode ser um fallback para eventos críticos se você teme entregas perdidas.

Checklist de Boas Práticas

Depurando Problemas de Webhook

Quando webhooks falham, o primeiro passo é inspecionar a requisição bruta. Problemas comuns:

Se você estiver depurando tráfego HTTP, ferramentas como o Nginx Log Analyzer podem ajudar a identificar padrões em falhas de requisição e códigos de resposta.

FAQ

O que é um segredo de webhook e como armazená-lo?

O segredo é uma chave compartilhada usada para assinar payloads. Armazene-o em variáveis de ambiente ou em um gerenciador de segredos—nunca no código ou no controle de versão. Rotacione-o periodicamente se o provedor suportar.

Quantas vezes um provedor tentará novamente um webhook que falhou?

Varia conforme o provedor. Alguns tentam por 24 horas com backoff exponencial, outros por 3 dias. Consulte a documentação do seu provedor para o cronograma exato e o número máximo de tentativas.

Posso usar webhooks com funções serverless?

Sim, mas fique atento aos limites de tempo de execução. Confirme o webhook rapidamente (retorne 200) e transfira o processamento pesado para uma fila ou função em background para evitar timeouts.

Pronto para construir integrações de webhook robustas? Comece verificando assinaturas e implementando idempotência—esses dois passos vão economizar horas de depuração e prevenir incidentes de segurança custosos.