HuraPay

Webhooks

O que são Webhooks?

Webhooks são requisições HTTP POST enviadas automaticamente para uma URL de sua escolha sempre que eventos específicos ocorrem na plataforma. Isso permite que sua aplicação seja notificada instantaneamente sobre mudanças no status de pagamentos e saques, sem a necessidade de fazer polling constante na API.

Como Configurar Webhooks

Você pode gerenciar webhooks pelo painel (Menu Lateral → Desenvolvedor → Webhooks) ou via API com sua chave (POST /webhook — veja Referência da API). Criar, atualizar e excluir também estão disponíveis pelo painel; atualizar e excluir via API ainda não são suportados com X-API-KEY (exigem sessão do painel) — liste e crie webhooks pela API normalmente, mas gerencie edição/remoção pelo painel por enquanto.

Importante

Sua URL de webhook precisa ser pública (HTTPS recomendado) e aceitar requisições POST. URLs apontando para localhost, IPs privados (10.x, 172.16-31.x, 192.168.x) ou domínios reservados (example.com, .test, .local) são rejeitadas na criação.

Campos do Webhook

  • URL do Endpoint: URL onde você receberá as notificações
  • Nome: identificador do webhook
  • Eventos: lista de eventos que você quer receber (veja abaixo)
  • Secret: gerado na criação, mostrado uma única vez — use pra validar a assinatura de cada entrega (veja "Verificando a Assinatura")

Eventos Disponíveis

Consulte a lista completa e atualizada em GET /webhook/events (Referência da API). Os eventos hoje disponíveis:

EventoQuando dispara
charge.pendingCobrança criada, aguardando pagamento
charge.processingPagamento em processamento na adquirente
charge.paidPagamento confirmado
charge.expiredCobrança expirou sem ser paga
charge.receivedReservado para uso futuro — nenhum fluxo atual dispara este evento
subscription.authorizedAssinatura Pix Automático autorizada pelo cliente
subscription.rejectedAutorização de assinatura recusada
subscription.charge.paidCobrança recorrente de uma assinatura paga
subscription.charge.failedCobrança recorrente de uma assinatura falhou
withdraw.completedSaque confirmado pela adquirente — veja Saldo e Saques
dispute.chargebackChargeback recebido numa cobrança

Sobre charge.received

Esse evento existe na lista de eventos selecionáveis, mas nenhum fluxo do sistema o dispara atualmente. Não configure lógica que dependa dele. Pra saber quando um valor fica disponível pra saque, consulte GET /account-balance — não existe hoje uma notificação push pra liberação de reserva financeira.

Formato do Payload

Toda entrega é um POST com este formato:

{
  "event": "charge.paid",
  "data": {
    "chargeId": "chg_abc123",
    "companyId": "cmp_xyz789",
    "paymentStatus": "PAID",
    "previousStatus": "PROCESSING",
    "total": 10000,
    "externalId": null,
    "customer": {
      "id": "cli_abc123",
      "name": "Nome do Cliente",
      "email": "cliente@exemplo.com",
      "phone": "+5511999999999"
    },
    "paidAt": "2026-08-07T10:30:00.000Z",
    "expiresAt": null
  },
  "timestamp": "2026-08-07T10:30:00.000Z"
}

total está em centavos. paymentStatus reflete o status real da cobrança (PENDING, PROCESSING, PAID, EXPIRED, REFUNDED). customer e previousStatus podem vir null/ausentes dependendo do evento.

Verificando a Assinatura

Cada entrega inclui os headers:

  • X-Webhook-Event: nome do evento (ex. charge.paid)
  • X-Webhook-Delivery: ID único da entrega
  • X-Webhook-Timestamp: timestamp ISO da entrega
  • X-Webhook-Signature: sha256=<hex> — HMAC-SHA256 do corpo bruto (JSON serializado exatamente como enviado), usando o secret do webhook como chave
const crypto = require('crypto');

function isValid(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Use o corpo bruto (antes de qualquer JSON.parse) pra calcular o HMAC — corpos re-serializados podem mudar a ordem das chaves e invalidar a comparação.

Implementação do Endpoint

Seu endpoint deve:

  1. Aceitar POST com Content-Type: application/json
  2. Responder com status 200-299 pra confirmar recebimento
  3. Responder rápido — timeout de 10 segundos por tentativa
  4. Validar a assinatura antes de processar (seção acima)
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!isValid(req.body, req.headers['x-webhook-signature'], WEBHOOK_SECRET)) {
    return res.status(401).send('invalid signature');
  }

  const event = JSON.parse(req.body);

  switch (event.event) {
    case 'charge.paid':
      // ...
      break;
    case 'charge.expired':
      // ...
      break;
    case 'withdraw.completed':
      // ...
      break;
  }

  res.status(200).send('OK');
});

Tentativas de Entrega

  • Tentativas: até 5
  • Intervalo: backoff exponencial a partir de 5s (≈5s, 10s, 20s, 40s, 80s entre tentativas)
  • Timeout por tentativa: 10 segundos
  • Status de sucesso: 200-299

Atenção

Depois de esgotar as 5 tentativas de uma entrega, o webhook é desativado automaticamente (não é só a entrega que falha — o webhook inteiro para de receber novas entregas até você reativá-lo pelo painel). Monitore os logs de entrega (GET /webhook/{id}/logs) se seu endpoint tiver instabilidade.

Logs de Webhook

Consulte GET /webhook/{id}/logs (Referência da API) pra ver o histórico de entregas (sucesso/falha, status HTTP, duração). Pra testar sem esperar um evento real, use POST /webhook/{id}/test.

Perguntas Frequentes

Suporte

Problemas com webhooks ou dúvidas de implementação: fale com o suporte da sua instância pelos canais disponíveis no painel de controle.

On this page