Go Webhook Receiver Starter

Um serviço pequeno em Go que recebe webhooks com segurança: assinatura verificada, sem duplicatas, gravação durável e retries, com o seu código em uma única função.

Comprar na Polar: $24 Compra única · v0.2.1 · download imediato pela Polar

Capa de Go Webhook Receiver Starter
Saída de uma execução real dos testes, não uma montagem.

O que você leva

Um serviço completo que dá para ler em uma tarde, não um framework. Sua lógica entra em uma única função, handle(). Todo o resto já está pronto e testado.

sender --POST--> /webhook/{source}
                   1. read the body (size limit)
                   2. verify the HMAC signature   -> 401 if wrong, nothing stored
                   3. dedupe by delivery id       -> the same event twice is handled once
                   4. insert into SQLite          -> 202 Accepted, fast
worker ----------> handle(event)   retries with backoff, dead after N attempts

Recursos

  • Três esquemas de assinatura. No estilo do GitHub, no estilo do Stripe (com janela contra replay) e o Standard Webhooks, usado pela Polar. A comparação é feita em tempo constante.
  • Rotação de segredo. WEBHOOK_SECRET aceita uma lista separada por vírgulas, então o segredo antigo e o novo valem durante a rotação e nenhum evento se perde.
  • Deduplicação. Pelo id da entrega (no Standard Webhooks, o header webhook-id é usado automaticamente) ou, na falta dele, por um hash do corpo. Apoiada em uma constraint de unicidade, ela continua valendo depois de um restart.
  • Fila durável. SQLite em modo WAL, com a requisição confirmada depois de um único commit. Backoff exponencial, dead-letter depois de MAX_ATTEMPTS tentativas e recuperação dos eventos que um crash deixou em processing.
  • Concorrência. WORKERS=N workers, e um evento nunca é entregue a dois deles.
  • Operação. server replay para os eventos mortos, server purge e RETENTION_DAYS para as linhas antigas, /metrics no formato Prometheus e um log de acesso em JSON com request id, que nunca traz payloads, headers nem segredos.
  • Handlers seguros. worker.Permanent(err) separa um payload que nunca vai dar certo, sem gastar retries. Se o desligamento pegar o handler no meio, o evento volta para a fila sem consumir uma tentativa.
  • Exemplos. Um handler para order.paid da Polar e um roteador JSON genérico, os dois com testes. Dockerfile e unit do systemd incluídos.

Go puro (sem cgo). Dependências: a biblioteca padrão e modernc.org/sqlite.

Medições

Método: o load tester incluso (make loadtest) contra o binário compilado, em um notebook Apple Silicon, com 32 conexões, payloads de 512 bytes, HTTP de verdade via loopback, durabilidade padrão (WAL, synchronous=FULL), 5,000 eventos por execução, três execuções.

O quêResultado
Eventos aceitos por HTTP (verificação, insert durável, 202), estilo GitHubcerca de 7,200 a 9,200 por segundo (p50 2.7 ms, p99 cerca de 17 ms)
Idem, Standard Webhookscerca de 9,400 por segundo
Ponta a ponta, da primeira requisição até a fila vazia (handler que não faz nada), 1 workercerca de 3,100 a 3,200 por segundo
Fila sendo drenada, handler que dorme 5 ms (como uma chamada de rede), com 1 / 8 / 32 workerscerca de 163 / 1,463 / 5,264 por segundo

O custo principal está no banco, não no HTTP: todo acesso ao SQLite passa por uma única conexão. Por isso, mais workers compensam quando o seu handler fica esperando outra coisa, como uma chamada de rede ou outro banco.

Como foi testado

O make test roda go vet e 41 testes com o race detector. Eles cobrem assinaturas adulteradas, vencidas, do futuro e malformadas, o vetor de resposta conhecida do Standard Webhooks tirado da biblioteca Go de referência, rotação de segredo, 401 sem nada gravado, entregas duplicadas, requisições grandes demais ou com método errado, retries e backoff, dead-letter, recuperação após crash, ordenação, vários workers sem nunca processar um evento duas vezes, replay, purge e a atualização de um banco da versão 0.1.0. O binário compilado, gerado a partir do zip do pacote, também foi exercitado com requisições HTTP reais assinadas nos três esquemas, inclusive com o replay da mesma entrega.

Para quem é

Para quem precisa receber webhooks da Polar, do Stripe, do GitHub ou de serviços parecidos e quer as partes pouco glamourosas bem feitas: assinaturas, retries, dedupe e uma fila que sobrevive a um crash.

Foi feito para um servidor só: um único processo, um único escritor no SQLite e eventos tratados em ordem por padrão. A entrega é at-least-once, então o seu handler deve ser idempotente.

Perguntas frequentes

Ele verifica os webhooks da Polar?

Sim. A Polar usa o Standard Webhooks, que é suportado, e o starter inclui um handler de exemplo para os eventos de pedido da Polar.

Posso usar só o código de assinatura?

Pode. Ele está disponível separado, como um módulo pequeno com licença MIT chamado webhooksig. Veja a página de ferramentas grátis.

Preciso de CGO ou de um compilador C?

Não. O driver do SQLite é Go puro.

Qual versão do Go?

Go 1.24 ou mais recente.

Qual é a licença?

Uso nos seus próprios projetos. Não revenda o código-fonte como um starter concorrente.

Changelog

  • 0.2.1: driver SQLite atualizado para v1.44.0 (requer Go 1.24 ou superior).
  • 0.2.0: Standard Webhooks, rotação de segredo, vários workers, comandos replay e purge, /metrics, log de acesso, exemplo da Polar, números medidos.
  • 0.1.0: primeira release.

Ler mais

Quero este

Comprar na Polar: $24

Pagamento, impostos e notas fiscais ficam com a Polar, que atua como merchant of record. Você recebe o link de download assim que concluir a compra.