Pular para o conteúdo
xFlow Payments

Blog

Webhook Pix não chega: como descobrir o motivo

Quando o webhook Pix não chega, confira nesta ordem: se o endpoint está ativo e inscrito no evento certo, se a URL é HTTPS, pública e alcançável, se firewall ou proxy bloqueiam o POST, se o handler responde 2xx rápido e se a assinatura falha por corpo reprocessado. Enquanto investiga, consulte a cobrança pela API para não deixar pedidos parados.

Atualizado em · Equipe xFlow Payments · 7 min de leitura

Quando o webhook Pix não chega, a causa quase sempre está em um de seis pontos: o endpoint não está ativo ou não assina o evento, a URL não é alcançável de fora, um firewall barra o POST, o handler não responde 2xx, a assinatura é recusada porque o corpo foi reprocessado, ou o evento esperado simplesmente não existe. O roteiro abaixo verifica cada um, na ordem do mais barato para o mais trabalhoso. O funcionamento do webhook da xFlow, com eventos e código do handler, está no guia de webhook Pix.

Primeiro: não chega ou chega e é recusado?

São dois problemas diferentes, e o log decide qual é o seu. Registre toda requisição que bate na rota do webhook logo na entrada, antes de qualquer validação: horário, método, caminho, o header x-webhook-event e o tamanho do corpo.

  • Nada aparece no log. A entrega não está alcançando o seu código. Siga os passos 1 a 3.
  • A requisição aparece, mas a resposta é 401, 403, 404 ou 5xx. A entrega chega e o seu lado recusa. Para a xFlow, isso é falha, e o evento entra em reenvio até desistir. Siga os passos 4 e 5.

1. O endpoint está ativo e assina o evento

Liste os endpoints da conta:

bash
curl https://app.xflowpayments.com/api/v1/webhooks \
  -u "pk_live_...:sk_live_..."

Para cada item, confira:

  • url: o endereço exato, com o caminho certo. Uma barra a mais ou a menos no fim já pode levar a uma rota diferente no seu servidor.
  • events: o endpoint só recebe os eventos da lista. Se transaction.paid não está lá, a confirmação de pagamento não chega.
  • status: um endpoint pausado com PATCH /api/v1/webhooks/{id} e { "active": false } não recebe nada até ser reativado.

Confira também se o evento que você espera existe. Não há evento de cobrança expirada. Os eventos dispute.* só chegam quando a conta tem o recurso de disputas ativo. E com chave de teste não há saque, então não há eventos withdrawal.* no sandbox.

2. A URL é HTTPS, pública e responde de fora

No cadastro, a URL precisa ser https:// e pública, e a xFlow recusa com 400 invalid_url um endereço inacessível ou de rede interna. Mas uma URL aceita no cadastro pode deixar de funcionar depois: o DNS mudou, o certificado venceu, o túnel de teste fechou ou o servidor trocou de endereço.

Teste de uma máquina fora da sua rede, como um celular sem Wi-Fi da empresa ou um servidor em outra nuvem:

bash
curl -i -X POST https://seusistema.com.br/webhooks/xflow \
  -H "Content-Type: application/json" \
  -d '{"teste":true}'

A resposta esperada é o 401 do seu próprio handler, porque o corpo não tem assinatura válida. Esse 401 prova que a rota é alcançável pela internet. Se vier erro de certificado, tempo esgotado, 404 ou uma página HTML de outro sistema, o problema está no caminho até o handler.

Dois cuidados nessa etapa:

  • Certificado válido. Use um certificado emitido por autoridade reconhecida e com a cadeia completa. A documentação não detalha a validação feita na entrega, então não conte com certificado autoassinado.
  • Sem redirecionamento. A documentação não diz se a entrega segue redirecionamentos. Cadastre a URL final, que responde direto, sem passar por 301 ou 302.

3. Firewall, WAF e proxy

Um POST sem cookie, sem navegador e vindo de fora é exatamente o que muitas proteções bloqueiam. Procure nos registros do firewall, do WAF ou do CDN, no horário do pagamento, por requisições barradas na rota do webhook. Causas comuns:

  • regra de proteção contra robôs que devolve uma página de desafio;
  • limite de requisições por origem que corta rajadas de eventos;
  • limite de tamanho de corpo muito baixo;
  • lista de IPs permitidos no seu servidor.

A documentação da xFlow não publica os IPs de origem das entregas. Não filtre o webhook por IP: libere a rota e use a assinatura como controle. A allowlist de IP do painel da xFlow é outra coisa: ela limita de onde saem as chamadas que você faz à API.

4. O handler responde 2xx, e rápido

A xFlow considera a entrega confirmada quando recebe 2xx e reenvia até 3 vezes quando não recebe. A documentação não informa o tempo limite de cada tentativa nem o intervalo entre elas. Por isso, o handler deve fazer o mínimo antes de responder: validar a assinatura, gravar o evento e devolver 200. E-mail, nota fiscal e liberação de acesso vão para uma fila.

Respostas fora de 2xx que costumam passar despercebidas:

  • 302 ou 401 de autenticação. Um middleware de login protege todas as rotas, inclusive a do webhook.
  • 403 ou 419 de CSRF. A proteção CSRF de frameworks como Laravel, Django e Rails pode recusar um POST sem token. Exclua a rota do webhook dessa proteção; a assinatura HMAC faz esse papel.
  • 404 ou 405. A rota existe só para GET, ou o caminho cadastrado é diferente do publicado.
  • 500 depois do trabalho feito. O pedido foi processado, mas uma exceção no fim derrubou a resposta. A xFlow reenvia, e um handler sem idempotência processa de novo.

5. A assinatura é recusada: o corpo foi reprocessado

O header x-webhook-signature traz sha256= e o HMAC-SHA256 do corpo cru, calculado com o segredo do endpoint. Se o seu framework transforma o corpo em objeto e você serializa de novo para validar, os bytes mudam e a assinatura não bate. O seu handler responde 401, a xFlow reenvia, e para quem olha de fora parece que o webhook "não chega".

Pontos para conferir:

  • Leitura do corpo cru. No Express, um express.json() global roda antes da rota; use express.raw só na rota do webhook. No Next.js, leia com await request.text(), e não com request.json(). No Laravel, use $request->getContent().
  • Segredo certo. Cada endpoint tem o seu segredo, mostrado uma única vez na criação. Se você apagou e recriou o endpoint, o segredo mudou.
  • Segredo limpo. Uma quebra de linha no fim da variável de ambiente, comum em arquivos .env e em segredos criados com echo, muda o HMAC.
  • Comparação. Compare a string inteira, com o prefixo sha256=, em tempo constante.

Para tirar a dúvida, grave o corpo cru e o header de uma entrega recusada e cole os dois, com o segredo, no validador de assinatura de webhook. Se lá confere e no seu código não, o corpo está sendo alterado antes do cálculo.

6. Reproduza no sandbox

Com a chave de teste, crie uma cobrança e chame POST /api/v1/test/charges/{id}/pay. A cobrança vira paid e o evento transaction.paid é disparado com livemode: false. Se esse evento chega à sua URL, a entrega funciona e a causa está na configuração de produção.

Atenção a um falso negativo: um handler correto descarta eventos com livemode: false antes de dar baixa em pedidos. No teste, registre o evento antes desse filtro, ou você vai concluir que ele não chegou. Para testar na sua máquina, com túnel HTTPS e reenvio manual de eventos, siga como testar webhook localmente.

Enquanto isso: a consulta como plano B

Webhook é o caminho principal, não o único. Enquanto investiga, não deixe pedidos parados: consulte GET /api/v1/charges/{id} para as cobranças em aberto e rode uma varredura periódica com GET /api/v1/charges?status=paid e o período, para recuperar pagamentos cujo aviso se perdeu. Quando e como usar cada caminho está em como saber se o Pix foi pago pela API.

Sintomas e onde olhar

Sintoma Onde olhar
Nenhum evento, de nenhum tipo GET /api/v1/webhooks: URL, status e events; teste de fora com curl
Chegam pending, mas não paid Lista events do endpoint
Parou de chegar de um dia para o outro Certificado, DNS, mudança de servidor, regra nova no WAF
Chega em duplicidade O handler não respondeu 2xx a tempo; torne-o idempotente
Log mostra 401 do seu handler Corpo reprocessado ou segredo errado
Só não chega no teste Filtro de livemode antes do log

Quando falar com a xFlow

Antes de abrir chamado, veja a página de status, que mostra a situação dos webhooks. Se o problema continuar, envie o id do endpoint, o id da cobrança, o evento esperado e o horário do pagamento pelos canais da página de contato. A referência completa dos webhooks está na documentação da API.

Perguntas frequentes

Quantas vezes a xFlow tenta entregar um webhook?

A documentação diz que a entrega é reenviada até 3 vezes e que a sua URL confirma o recebimento respondendo 2xx. O intervalo entre as tentativas e o tempo limite de cada uma não são informados, por isso o handler deve responder o mais rápido possível.

Existe webhook para cobrança expirada?

Não. Os eventos de cobrança são transaction.pending, transaction.paid, transaction.refunded e transaction.refund_failed. Para saber que uma cobrança expirou, consulte o status pela API.

Posso liberar só os IPs da xFlow no firewall?

A documentação não publica os IPs de origem das entregas. Em vez de filtrar por IP, libere o caminho do webhook e confie na assinatura HMAC-SHA256 do header x-webhook-signature para recusar o que não veio da xFlow.

Como saber se o problema está do lado da xFlow?

Consulte a página de status em app.xflowpayments.com/status, que mostra a situação dos webhooks, e repita o teste no sandbox. Se o evento simulado chega, a entrega funciona e a causa está na configuração ou no seu handler.

Continue lendo

BlogWebhook Pix: eventos, assinatura HMAC e boas práticasWebhook Pix é o aviso que a API envia para a sua URL quando algo acontece com uma cobrança ou um saque, como o pagamento confirmado. Na xFlow, cada aviso é um POST JSON assinado com HMAC-SHA256, reenviado até 3 vezes até você responder 2xx.BlogComo testar webhook localmentePara testar webhook localmente, exponha a porta do seu servidor com um túnel HTTPS público, cadastre essa URL como endpoint usando a chave de teste e simule o pagamento com POST /api/v1/test/charges/{id}/pay. Para repetir um evento sem chamar a API, assine o corpo com o segredo e envie com curl.BlogComo saber se o Pix foi pago pela APIPara saber se um Pix foi pago pela API da xFlow, assine o webhook transaction.paid e, como reserva, consulte GET /api/v1/charges/{id} até o status sair de pending. Só libere o pedido com status paid, assinatura válida, livemode: true e valor igual ao do pedido.FerramentaValidador de assinatura de webhookInforme o segredo do endpoint, o corpo cru da requisição e o valor do header x-webhook-signature. A página calcula o HMAC-SHA256 como o seu servidor deveria calcular e diz se a assinatura confere.BlogChecklist de integração Pix para produçãoAntes de colocar a integração Pix em produção, confirme: credenciais de produção guardadas só no servidor, Idempotency-Key em toda cobrança e saque, webhook com assinatura validada e handler idempotente, conciliação pelo End-to-end ID, tratamento de limites e erros, fluxo de estorno e monitoramento. Abaixo, a lista para marcar item a item.