Skip to Content
WebhooksVisão geral
View as Markdown

Webhooks — Visão geral

Webhooks são o sinal oficial. Callbacks do navegador (onSuccess) e visões do dashboard são conveniência; webhooks são a verdade.

Garantias de entrega

  • Pelo menos uma vez. Um único evento pode ser entregue até 6 vezes se o seu servidor não retornar 2xx dentro do timeout. Torne o seu handler idempotente — deduplique em X-Delivery (o payload não tem campo event_id; o UUID estável da entrega é a chave de idempotência).
  • Um evento por requisição HTTP. Sem batching.
  • Isolamento por endpoint. Se você tem múltiplos endpoints registrados, cada um tem a própria trilha de entrega e retry. Um endpoint lento não atrasa os outros.
  • Assinado. Todo payload leva um header X-Signature (e durante a janela de 24 horas depois de uma rotação, também um X-Signature-Prev). Verifique antes de fazer qualquer coisa com o body. Veja Verificação de assinatura.

Tipos de evento que se pode assinar

EventoDispara quando…
payment.settledA transferência on-chain atingiu a contagem de confirmações da rede. Use isto para marcar pedidos como pagos.
payment.failedUm pagamento fiat foi rejeitado pelo provedor de pagamento. Não dispara para timeouts de cripto — esses afloram como checkout.expired, e pagamentos cripto curtos afloram como payment.underpaid.
payment.underpaidFundos chegaram, mas abaixo do total do pedido (típico: taxa de transferência de stablecoin descontada do valor).
payment.overpaidFundos chegaram em excesso ao total do pedido. O excedente é registrado mas não é auto-reembolsado.
order.createdUm novo pedido foi aberto — pela sua chamada de API B2B ou por uma conversão de checkout-session.
order.canceledUm pedido foi movido para cancelado. O data.reason do payload distingue cancel manual de payment_timeout (um pedido não pago expirou por timeout).
order.resolvedUm pedido PARTIAL_PAID foi resolvido para PAID — o lojista aceitou o déficit.
order.reopenedUm pedido previamente auto-cancelado (canceled_reason=payment_timeout) foi reaberto pelo lojista.
checkout.createdUm comprador abriu o checkout para um pedido.
checkout.completedO fluxo do lado do comprador terminou (não implica liquidação on-chain — use payment.settled para isso).
checkout.expiredO comprador abandonou e o TTL da sessão acabou.
payment.refund.requestedUm registro de reembolso foi criado — seja a partir de uma chamada de API iniciada pelo lojista, seja a partir de um formulário de pedido de reembolso submetido pelo cliente.
payment.refund.approvedUm reembolso pendente passou pelo seu workflow de aprovação.
payment.refund.rejectedUm reembolso pendente foi negado.
payment.refund.executedA transferência on-chain do reembolso foi confirmada e o registro foi para o estado terminal executed.
refund_request.createdUm token de pedido de reembolso foi criado. data.source é b2b / dashboard / renewal. Opcional inscrever-se — útil para pipelines de auditoria que acompanham qual token está ativo no momento por pedido.
refund_request.renewal_requestedUm comprador clicou em “Pedir link novo” depois que o token expirou. Fortemente recomendado inscrever-se — é o sinal de que o widget de renovação tem um novo item para agir.
refund_request.renewedUma renovação foi aprovada e um novo token substituiu o antigo. data.old_token / data.new_token formam a cadeia de auditoria.
refund_request.canceledUm lojista moveu um token para CANCELED pelo dashboard (por exemplo, negou um pedido de renovação, matou um link vivo). Idempotente — só a primeira transição emite. data.reason é a nota opcional do lojista.

Planejados (em breve)

Em breve. Esses eventos pertencem a faturas recorrentes e assinaturas, que ainda não estão disponíveis. Eles não estão na tabela de eventos assináveis acima e não podem ser assinados hoje. Veja Faturas recorrentes.

Evento planejadoDispara quando…
subscription.createdUma assinatura é criada.
invoice.createdUma fatura de um ciclo de cobrança é criada.
invoice.paidUma fatura é paga.
subscription.past_dueUma fatura continua sem pagamento após o vencimento.
subscription.canceledUma assinatura é cancelada.

O formulário de endpoint do dashboard lista os mesmos eventos. Tentar se inscrever em um evento que não existe é rejeitado quando você salva o endpoint.

Eventos de teste não são assináveis. O botão Send Test por endpoint no dashboard envia um evento webhook.test.ping para aquele único endpoint imediatamente, sem retentativas. Ele não aparece no catálogo acima: você o recebe por ter um endpoint registrado, não por ter se inscrito.

Inscreva-se só nos eventos que você trata. Cada endpoint tem o próprio filtro de evento; o wildcard "*" significa “todo evento, incluindo os adicionados no futuro”. Inscrever-se em menos eventos deixa o seu handler mais simples e significa menos retentativas quando o seu endpoint tem erros.

Payload + headers

O corpo HTTP é o objeto de dados específico do evento, direto. Sem envelope externo estilo Stripe — campos como tipo do evento, ID da entrega e timestamp de emissão ficam nos headers. Para payment.settled o body fica assim:

{ "receipt_id": "rcp_…", "order_id": "ord_…", "payment_intent_id": "pin_…", "checkout_session_id": "cst_…", "merchant_id": "mer_…", "customer_id": "cus_…", "total": "49.00", "currency": "USD", "payment_method": "crypto", "token": "USDC", "network": "polygon", "tx_hash": "0x…", "deposit_address": "0x…", "treasury_address": "0x…", "amount_received": "49.00", "confirmations": 5, "metadata": { /* por evento */ } }

Outros eventos carregam os próprios campos. Nomes de campo são estáveis (lower snake_case); o tx hash on-chain é sempre tx_hash.

tx_hash é o identificador da transação no formato próprio da rede (0x… em redes EVM; o hash ou a assinatura nativos em TRON, Solana e TON). deposit_address pode estar ausente em TRON, Solana e TON, porque lá os compradores pagam diretamente a sua carteira de Tesouraria, e confirmations segue Redes e ativos.

Headers na requisição de entrada

Content-Type: application/json X-Event: payment.settled X-Delivery: 7c9e6679-7425-40de-944b-e07fc1f90ae7 Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7 X-Timestamp: 1729536000 X-Signature: sha256=9a8b7c… X-Signature-Prev: sha256=fa31b2… (só durante uma janela de tolerância de rotação)
HeaderO que é
X-EventO tipo do evento (por exemplo, payment.settled). Rotear nisso na camada de proxy se você quer pular o parse do JSON.
X-DeliveryUUID que identifica a linha de entrega. Estável em todas as retentativas do mesmo par (event, endpoint) — use como sua chave de idempotência.
Idempotency-KeyEspelha o X-Delivery (mesmo valor). Definido em toda entrega.
X-TimestampUnix-segundos do momento em que a tentativa foi enviada. Assinado dentro do payload para que um par (body, X-Signature) capturado não possa ser replayado indefinidamente — rejeite entregas cujo timestamp esteja fora da sua janela de tolerância.
X-Signaturesha256=<hex> de HMAC-SHA256(secret, X-Timestamp + "." + raw_body). Veja Verificação de assinatura.
X-Signature-PrevMesmo algoritmo com o segredo anterior. Presente só na janela de 24 horas depois que você rotaciona — deixa verificadores rodando qualquer das duas chaves continuarem aceitando entregas durante o cutover. Depois da janela fechar o header para de ser enviado.

Cronograma de retry

Se o seu endpoint não retornar 2xx dentro do timeout, a gente retenta neste cronograma (timestamps relativos à primeira tentativa):

TentativaAtrasoAcumulado
10s0s
2+1 min1m
3+5 min6m
4+15 min21m
5+1 hora1h 21m
6+6 horas7h 21m

Depois da tentativa 6 falhar, a entrega é marcada como Failed e o e-mail da sua conta é notificado. Você pode reenviar eventos com falha pelo painel Developers → Webhooks → Delivery history do dashboard. Cada reenvio é uma nova entrega com o próprio X-Delivery.

Registrar um endpoint

No dashboard do lojista :

  1. Developers → Webhooks → + Add endpoint
  2. Cole a sua URL — só https://… (HTTP puro é rejeitado; o formulário de criação também bloqueia localhost, ranges de IP privados e URLs com userinfo)
  3. Escolha os eventos para assinar (ou * para todos)
  4. Escolha o ambiente — test ou live (cada um tem o próprio segredo; eles nunca se cruzam)
  5. Salve → o dashboard mostra o segredo de assinatura (whsec_…) uma vez. Guarde do lado do servidor; você vai precisar para as próximas duas features.

Você pode registrar até 10 endpoints por ambiente por lojista (por exemplo, um para entrega em produção, um para espelhamento em staging, um para um notificador no Slack). Cada um mantém o próprio estado de retry e segredo.

Ações de ciclo de vida em cada endpoint

O menu ⋮ em cada card de endpoint expõe:

  • Edit — muda a URL, descrição ou lista de inscrições. A URL nova é re-validada com as mesmas regras https:///SSRF da criação.
  • Send Test — faz POST síncrono de um envelope webhook.test.ping assinado com o seu segredo atual. O dashboard mostra o status HTTP, latência e um snippet de 512 bytes da sua resposta. Pings de teste não são retentados, então você recebe a resposta imediatamente.
  • Rotate Secret — gera um segredo novo. O anterior permanece válido por 24 horas (entregas levam tanto X-Signature quanto X-Signature-Prev durante a janela para que verificadores rodando qualquer das duas chaves continuem aceitando eventos enquanto você redeploya).
  • Reveal Secret — re-exibe o segredo existente. Condicionado a verificação 2FA recente e registrado no log de auditoria; use só quando você perdeu a sua cópia e o Rotate não é aceitável.
  • Enable / Disable — liga ou desliga o endpoint sem perder o histórico de entrega. Endpoints desabilitados ficam no dashboard mas não recebem novas entregas.
  • Delete — permanente. Use Disable se você pode reabilitar depois.

Dicas para handlers

  1. Retorne 2xx rápido. Confirme com 200 OK antes de fazer trabalho pesado — mova a entrega para um job em background. O timeout por tentativa é de 10 segundos; segurar a resposta mais do que isso dispara um retry. O timeout é do lado da plataforma e não é configurável pelo lojista — contate o suporte se o seu handler genuinamente precisa de mais tempo.
  2. Deduplique em X-Delivery (ou Idempotency-Key — mesmo valor). Mesmo que você retorne 2xx, um proxy upstream pode derrubar a conexão e disparar um retry; o ID da entrega é estável em toda retentativa da mesma linha de entrega, então é a chave certa.
  3. Tolere tipos de evento desconhecidos. Eventos novos podem aparecer; retorne 200 e no-op no lugar de 4xx, ou essas entregas vão continuar sendo retentadas.
  4. Logue X-Delivery junto com a sua lógica de negócio. Quando algo dá errado, essa é a chave de junção entre o nosso lado e o seu.

Próximos passos