Webhooks Explicados: Payloads Assinados e Retentativas
Você acabou de integrar um gateway de pagamento. A documentação diz: "Enviaremos um webhook para o seu endpoint quando um pagamento for bem-sucedido." Você configura uma rota simples, testa localmente e faz o deploy. Então, em produção, você percebe eventos duplicados, notificações perdidas e erros 500 misteriosos. Webhooks parecem simples — até não serem.
Este guia explica como os webhooks realmente funcionam, por que payloads assinados são importantes e como construir uma lógica de retentativa que mantém seu sistema sincronizado mesmo quando as coisas dão errado.
O que é um Webhook?
Um webhook é um callback HTTP: um sistema envia uma requisição HTTP POST para uma URL que você fornece quando um evento específico ocorre. Diferente do polling, onde você pergunta repetidamente "Aconteceu alguma coisa?", os webhooks enviam eventos para você quase em tempo real.
Usos comuns incluem:
- Processadores de pagamento notificando você sobre cobranças bem-sucedidas.
- Serviços de hospedagem Git acionando pipelines de CI/CD no push.
- Plataformas de comunicação alertando você sobre novas mensagens.
O remetente (ex.: Stripe, GitHub) é o provedor de webhook; seu servidor é o consumidor de webhook.
Por que Payloads Assinados São Essenciais
Seu endpoint de webhook é publicamente acessível. Qualquer pessoa que saiba a URL pode enviar eventos falsos. Sem verificação, um atacante poderia falsificar um evento de "pagamento bem-sucedido" e você poderia enviar mercadorias gratuitamente.
Payloads assinados resolvem isso incluindo uma assinatura criptográfica nos cabeçalhos da requisição, gerada com um segredo compartilhado conhecido apenas por você e pelo provedor. Quando você recebe um webhook, você recalcula a assinatura e compara. Se corresponderem, o payload é autêntico.
Como Funcionam as Assinaturas HMAC
A maioria dos provedores usa HMAC-SHA256. O provedor calcula:
signature = HMAC-SHA256(secret, payload)e envia em um cabeçalho como X-Signature. Seu servidor faz o mesmo cálculo e verifica a igualdade. Como o segredo é secreto, apenas o provedor pode gerar uma assinatura válida.
Verificando uma Assinatura na Prática
Aqui está um exemplo mínimo em Node.js usando Express:
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const payload = req.body; // raw Buffer
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(payload);
console.log('Received event:', event.type);
res.status(200).send('OK');
});
app.listen(3000);Importante: Sempre use o corpo bruto da requisição para verificação de assinatura. Se você analisar o JSON primeiro, a string serializada pode diferir (espaços em branco, ordem das chaves) e a verificação da assinatura falhará.
Armadilhas Comuns
- Usar o segredo errado: Alguns provedores têm segredos separados para modos de teste e produção.
- Ataques de temporização: Use uma função de comparação de tempo constante (ex.:
crypto.timingSafeEqual) para evitar vazamento de informações. - Ignorar cabeçalhos de timestamp: Muitos provedores incluem um timestamp para prevenir ataques de replay. Rejeite requisições mais antigas que alguns minutos.
Retentativas: Lidando com Falhas de Forma Elegante
Redes falham, servidores reiniciam e implantações acontecem. Provedores de webhook esperam que seu endpoint possa estar temporariamente indisponível, então eles tentam novamente entregas falhas. Mas as retentativas introduzem novos desafios: eventos duplicados, entrega fora de ordem e loops infinitos se você não tomar cuidado.
Como os Provedores Fazem Retentativas
A maioria dos provedores usa backoff exponencial: eles esperam um curto período após a primeira falha, depois intervalos progressivamente maiores. Por exemplo, a Stripe tenta novamente por até 3 dias com atrasos crescentes. O GitHub tenta até 8 vezes. O cronograma exato varia, mas o padrão é consistente.
Seu endpoint deve retornar um código de status 2xx rapidamente para confirmar o recebimento. Qualquer outro status (4xx, 5xx) sinaliza falha e dispara uma retentativa.
Construindo Handlers Idempotentes
Como as retentativas podem entregar o mesmo evento várias vezes, seu handler deve ser idempotente: processar o mesmo evento duas vezes deve ter o mesmo efeito que processá-lo uma vez.
Estratégias comuns:
- Rastreamento de ID de evento: Armazene o ID único do evento (fornecido no payload ou cabeçalhos) em um banco de dados. Antes de processar, verifique se já foi tratado.
- Upserts: Use operações de banco de dados que são naturalmente idempotentes (ex.:
INSERT ... ON CONFLICT DO NOTHING). - Máquinas de estado: Projete sua lógica de negócios para tolerar transições repetidas (ex.: "marcar pedido como pago" apenas se ainda não estiver pago).
Exemplo usando um conjunto simples em memória (para ilustração; use um armazenamento persistente em produção):
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
const event = req.body;
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed');
}
// Process event...
processedEvents.add(event.id);
res.status(200).send('OK');
});Respondendo Rapidamente
Provedores frequentemente têm um timeout (ex.: 5 segundos). Se seu handler faz processamento pesado, confirme imediatamente e processe de forma assíncrona. Por exemplo, coloque o evento em uma fila e retorne 200.
Melhores Práticas para Webhooks Confiáveis
- Sempre verifique assinaturas usando o corpo bruto e uma comparação de tempo constante.
- Use HTTPS para seu endpoint para prevenir interceptação e adulteração.
- Implemente idempotência para lidar com entregas duplicadas.
- Registre tudo — payloads recebidos, assinaturas e suas respostas — para depuração.
- Monitore falhas e configure alertas para erros repetidos.
- Teste com eventos reais usando painéis de provedores ou ferramentas de CLI.
- Lide com eventos fora de ordem incluindo timestamps ou números de sequência em sua lógica.
Comparação: Polling vs. Webhooks
| Aspecto | Polling | Webhooks |
|---|---|---|
| Latência | Depende do intervalo | Quase em tempo real |
| Carga do servidor | Alta (requisições constantes) | Baixa (apenas em eventos) |
| Complexidade | Simples de implementar | Requer endpoint, segurança, retentativas |
| Confiabilidade | Você controla as retentativas | O provedor controla as retentativas |
FAQ
O que é uma assinatura de webhook?
Uma assinatura de webhook é um hash criptográfico (geralmente HMAC-SHA256) do payload, gerado com um segredo compartilhado. Ele permite que o receptor verifique que a requisição veio do remetente esperado e não foi adulterada.
Como lidar com eventos de webhook duplicados?
Torne seu handler idempotente rastreando IDs de eventos ou usando operações de banco de dados que são naturalmente idempotentes. Verifique se um evento já foi processado antes de aplicar alterações.
O que meu endpoint de webhook deve retornar?
Retorne um código de status 2xx (ex.: 200 OK) assim que você receber e validar o evento. Se precisar fazer processamento pesado, confirme imediatamente e lide com o trabalho de forma assíncrona.
Pronto para testar seus payloads de webhook? Use nosso JSON Formatter para inspecionar e validar a estrutura JSON dos eventos recebidos.