Webhooks
Notificações assíncronas de eventos — a única forma confiável de saber que um PIX/boleto foi pago (não faça polling em produção).
Configurar
Backoffice → Webhooks → Novo endpoint (informe URL + um secret, usado pra assinar
cada entrega) → inscreva-se nos tipos de evento que te interessam. Não há um endpoint público
em /v1 pra isso nesta entrega — é uma operação de configuração, feita uma vez, via
Backoffice/API admin.
Tipos de evento
| Evento | Quando dispara |
|---|---|
payment.created | Pagamento iniciado. |
payment.processing | Em processamento. |
payment.authorized | Cartão autorizado (não capturado). |
payment.paid | Aprovado — qualquer método. |
payment.failed | Recusado ou erro. |
payment.refunded | Estornado. |
payment.cancelled | Autorização cancelada antes de capturar. |
pix.created | Cobrança PIX emitida. |
pix.paid | PIX pago. |
pix.expired | PIX expirou sem pagamento. |
boleto.created | Boleto emitido. |
boleto.paid | Boleto compensado. |
settlement.created | Repasse agendado. |
settlement.paid | Repasse processado. |
Payload
{
"id": "evt_...",
"type": "payment.paid",
"tenantId": "...",
"merchantId": "...",
"payload": { "paymentId": "...", "amountInCents": 5000, "method": "pix" },
"occurredAt": "2026-08-25T19:44:15.916Z",
"correlationId": "..."
}
Headers de assinatura
Toda entrega chega com 4 headers:
| Header | Conteúdo |
|---|---|
X-Gateway-Signature | HMAC-SHA256 hex de ${timestamp}.${rawBody} |
X-Gateway-Timestamp | Unix timestamp da assinatura |
X-Gateway-Event-Id | Id do evento (mesmo de payload.id) |
X-Gateway-Delivery-Id | Id desta tentativa específica de entrega |
Verificar a assinatura
Sempre use o corpo cru da request (nunca req.body já parseado, que pode re-serializar o
JSON e invalidar a assinatura):
import { verifySignature } from '@gateway/sdk';
app.post('/webhooks/gateway', (req, res) => {
const ok = verifySignature({
payload: req.rawBody,
timestamp: req.headers['x-gateway-timestamp'],
signature: req.headers['x-gateway-signature'],
secret: process.env.GATEWAY_WEBHOOK_SECRET!,
});
if (!ok) return res.status(401).end();
const event = JSON.parse(req.rawBody);
// ...
res.status(200).end();
});
$ok = $webhooks->verifySignature(
file_get_contents('php://input'),
$_SERVER['HTTP_X_GATEWAY_TIMESTAMP'],
$_SERVER['HTTP_X_GATEWAY_SIGNATURE'],
getenv('GATEWAY_WEBHOOK_SECRET'),
);
Duplicidade e ordem
O mesmo evento pode chegar mais de uma vez (retry de rede do nosso lado, ou reenvio) —
sempre trate pelo id do evento (idempotente do seu lado: já processou esse id? ignore).
Eventos também não chegam necessariamente na ordem em que aconteceram — nunca assuma que
payment.processing chega antes de payment.paid; trate cada evento como um snapshot de
status, não como um passo sequencial.
Retry e dead letter
Uma entrega que falhar (timeout, 4xx/5xx da sua URL) é tentada de novo com backoff
crescente: 1min, 5min, 15min, 1h, 6h, 24h. Depois da última tentativa sem sucesso, a entrega
vai pra dead letter — visível no Backoffice, com replay manual disponível (reprocessa
sob demanda, sem esperar o schedule).
Responda rápido
Devolva 2xx assim que tiver validado a assinatura e enfileirado o processamento
internamente — não faça o processamento pesado (chamadas a outros serviços, etc.) antes de
responder; isso conta como timeout do lado do Gateway e aciona um retry desnecessário.