Paddle: como funciona a entrega de webhooks

Até 60 tentativas de entrega ao longo de cerca de 2 dias em produção, cerca de 10 na primeira hora. O formato de assinatura ts/h1 da Paddle e um verificador Node.

Comportamento de entrega

Lido na documentação do próprio provedor, verificado pela última vez em 2026-08-06. Onde o provedor não informa um número, esta tabela também não traz nenhum.

Prazo de resposta5 s

Verifique se seu handler responde a tempo

Novas tentativasProdução: até 60 tentativas ao longo de cerca de 2 dias, cerca de 10 na primeira hora e cerca de 45 no primeiro dia. Sandbox: 3 tentativas em cerca de 2 min
Desistenão publicado pelo provedor
Fontehttps://developer.paddle.com/webhooks/about/respond-to-webhooks

Assinatura

Verificada com um verificador funcional, comprovado por uma suíte de testes, e não lida na documentação. Última verificação em 2026-08-20.

AlgoritmoHMAC-SHA256
Payload assinado{timestamp}:{body}
Codificaçãohex
Cabeçalhopaddle-signature = ts=X;h1=hex
Tolerância5 s

Verifique a assinatura

O corpo bruto da requisição, byte a byte, antes de qualquer parsing de JSON. Todos os esquemas desta página quebram no momento em que um framework serializa o payload de novo.

const crypto = require("node:crypto");

// paddle-signature: "ts=1700000000;h1=abc..." — HMAC-SHA256 over "{ts}:{body}"
const pairs = Object.fromEntries(
  req.headers["paddle-signature"].split(";").map((p) => p.split("=")),
);
const expected = crypto
  .createHmac("sha256", secretKey) // per destination: pdl_ntfset_...
  .update(`${pairs.ts}:${rawBody}`)
  .digest("hex");
const received = Buffer.from(pairs.h1 ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  received.length === wanted.length && crypto.timingSafeEqual(received, wanted);

O SDK da Paddle aplica uma tolerância de 5 segundos ao timestamp, a menor entre os remetentes desta página. Atrás de um relay ou de uma fila, compare o timestamp com o momento em que o evento chegou à edge, não com o momento em que seu worker o pegou.

Armadilhas

A curva de novas tentativas é densa onde a da Stripe é esparsa: cerca de 10 tentativas na primeira hora, cerca de 45 no primeiro dia, até 60 em cerca de 2 dias. Um handler que está apenas lento roda de novo a cada poucos minutos no início e, sem idempotência, cada execução repete seus efeitos colaterais.

O sandbox não se parece em nada com produção (3 tentativas em cerca de 2 minutos), então um tratamento de novas tentativas que parece bom nos testes viu só 5% da pressão.

Leia também

O AnyHook fica entre Paddle e seus endpoints: responde em cerca de um segundo, tenta de novo segundo um cronograma próprio quando seu servidor está fora do ar e guarda cada evento disponível para replay. Troque uma URL e mantenha seu código; para aceitar novas tentativas e replays, verifique o cabeçalho AnyHook-Signature, porque a assinatura original do remetente (Paddle) expira após 5 s.

Como funciona →