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çãoWebhook do painel
Onde configuracampo callbackUrl de cada cobrança ou saque na APIWebhooks no painel, um endereço por evento
Formatoo payload da API compatível (valores em reais)envelope { id, event, createdAt, data } (valores em centavos)
Assinado como client secret da APIo segredo do webhook (whsec_…), visível no painel
Eventospagamento da cobrança; resultado do saquecobranç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"
}
CampoDescrição
requestNumbero identificador que você enviou na criação
statusTransactionsempre PAID_OUT (só pagamentos geram callback)
idTransactionID da cobrança na WeetPay
valuevalor pago, em reais (número)
debtorName, debtorDocumentquem pagou, como informado pelo banco
datedata/hora do pagamento (ISO 8601, UTC)
endToEndEnd-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"
  }
}
eventQuandodata
TRANSACTION_CREATEDcobrança criadacobrança
TRANSACTION_PAIDcobrança pagacobrança
TRANSACTION_REFUNDEDcobrança devolvida / contestadacobrança + refund { id, kind, amount, reason }
WITHDRAWAL_COMPLETEDsaque pagosaque
WITHDRAWAL_FAILEDsaque 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çalhoValor
X-WeetPay-Signaturet=<unix>,v1=<hex> — veja abaixo
X-WeetPay-Eventevento (TRANSACTION_PAID, WITHDRAWAL_COMPLETED…)
X-WeetPay-DeliveryID da entrega — o mesmo em todas as tentativas; use para descartar repetidos
User-AgentWeetPay-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 (ou X-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.

Nesta página