Callbacks e webhooks
Avisos assinados de pagamento e de saque — o jeito certo de conciliar sem consultar a API em loop.
A WeetPay avisa o seu sistema de duas formas, que podem ser usadas juntas:
| Callback da transação | Webhook do painel | |
|---|---|---|
| Onde configura | campo callbackUrl de cada cobrança ou saque na API | Webhooks no painel, um endereço por evento |
| Formato | o payload da API compatível (valores em reais) | envelope { id, event, createdAt, data } (valores em centavos) |
| Assinado com | o client secret da API | o segredo do webhook (whsec_…), visível no painel |
| Eventos | pagamento da cobrança; resultado do saque | cobrança criada, paga, devolvida; saque concluído, falhou |
Em ambos: POST com Content-Type: application/json, assinatura no cabeçalho X-WeetPay-Signature e reenvio automático em caso de falha.
Callback de cobrança paga
Enviado ao callbackUrl de Gerar cobrança PIX quando o PIX é pago:
{
"requestNumber": "pedido-12345",
"statusTransaction": "PAID_OUT",
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"typeTransaction": "PIX",
"value": 49.9,
"debtorName": "MARIA DA SILVA",
"debtorDocument": "12345678909",
"date": "2026-10-30T13:03:40.000Z",
"endToEnd": "E18236120202610301303s0123456789"
}| Campo | Descrição |
|---|---|
requestNumber | o identificador que você enviou na criação |
statusTransaction | sempre PAID_OUT (só pagamentos geram callback) |
idTransaction | ID da cobrança na WeetPay |
value | valor pago, em reais (número) |
debtorName, debtorDocument | quem pagou, como informado pelo banco |
date | data/hora do pagamento (ISO 8601, UTC) |
endToEnd | End-to-End ID do PIX |
Callback de saque
Enviado ao callbackUrl de Enviar PIX quando o saque termina:
{
"id": "cmh1x9k2m0004ab12xy98zt76",
"status": "sucess",
"date": "2026-10-30T14:10:05.000Z",
"message": "",
"value": 150,
"endToEnd": "E00416968202610301410abcdEFGH1234"
}status é "sucess" (com essa grafia, mantida por compatibilidade com a API de gateway de origem) ou "not paid". Saque não pago tem o valor e a tarifa estornados automaticamente ao seu saldo; o motivo vem em message.
Webhooks do painel
Cadastre em Webhooks no painel uma URL para cada evento que quiser receber. O corpo vem num envelope:
{
"id": "cmh1xa0b10007ab12qq11ww22",
"event": "TRANSACTION_PAID",
"createdAt": "2026-10-30T13:03:41.000Z",
"data": {
"id": "cmh1x2y3z0001ab12cd34ef56",
"externalId": "pedido-12345",
"status": "PAID",
"amount": 4990,
"fee": 99,
"netAmount": 4891,
"description": "Camiseta básica",
"pixCopiaECola": "00020101021226850014br.gov.bcb.pix…",
"endToEndId": "E18236120202610301303s0123456789",
"payer": { "name": "MARIA DA SILVA", "document": "12345678909" },
"expiresAt": "2026-10-30T14:02:11.000Z",
"paidAt": "2026-10-30T13:03:40.000Z",
"createdAt": "2026-10-30T13:02:11.000Z"
}
}event | Quando | data |
|---|---|---|
TRANSACTION_CREATED | cobrança criada | cobrança |
TRANSACTION_PAID | cobrança paga | cobrança |
TRANSACTION_REFUNDED | cobrança devolvida / contestada | cobrança + refund { id, kind, amount, reason } |
WITHDRAWAL_COMPLETED | saque pago | saque |
WITHDRAWAL_FAILED | saque recusado ou falhou (estornado) | saque |
Na cobrança, externalId é o seu requestNumber; amount, fee e netAmount estão em centavos. O saque traz id, transactionId, status (PAID, FAILED, REJECTED), amount, fee, pixKeyType, pixKey, endToEndId, reason, paidAt e createdAt.
Cabeçalhos
| Cabeçalho | Valor |
|---|---|
X-WeetPay-Signature | t=<unix>,v1=<hex> — veja abaixo |
X-WeetPay-Event | evento (TRANSACTION_PAID, WITHDRAWAL_COMPLETED…) |
X-WeetPay-Delivery | ID da entrega — o mesmo em todas as tentativas; use para descartar repetidos |
User-Agent | WeetPay-Webhooks/1.0 |
Validando a assinatura
v1 é o HMAC-SHA256, em hexadecimal, de "<t>.<corpo bruto>", usando como chave o client secret (callbacks da transação) ou o segredo do webhook (webhooks do painel). Calcule sobre o corpo exatamente como chegou, antes de qualquer parse, compare em tempo constante e recuse avisos com t muito antigo (ex.: mais de 5 minutos).
import { createHmac, timingSafeEqual } from 'node:crypto';
export function validWeetPaySignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries((header ?? '').split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
// Express: app.post('/webhooks/weetpay', express.raw({ type: 'application/json' }), (req, res) => {
// if (!validWeetPaySignature(req.body.toString('utf8'), req.get('X-WeetPay-Signature'), process.env.WEETPAY_CLIENT_SECRET)) return res.sendStatus(401);
// const event = JSON.parse(req.body); … ; res.sendStatus(200);
// });Entrega e reenvio
- Responda com qualquer 2xx em até 10 segundos. Outro status, timeout ou erro de conexão conta como falha.
- Falhas são reenviadas após 1, 5, 15, 60, 180, 360, 720 e 1440 minutos (até 9 tentativas, cerca de 2 dias). Depois disso a entrega fica como falha no histórico; você pode reenviar manualmente pelo painel.
- O mesmo aviso pode chegar mais de uma vez: trate pelo
idTransaction/id(ouX-WeetPay-Delivery) e torne o processamento idempotente. - A URL precisa ser http(s) pública. Redirecionamentos não são seguidos e endereços de rede interna (localhost, 10.x, 192.168.x…) são recusados.
- Responda rápido e processe depois (fila): o aviso não espera o seu sistema terminar o pedido.
Não confie só no corpo do aviso para liberar um pedido de valor alto: valide a assinatura ou confirme com Consultar transação.