Webhook do Pix: como ler e validar o corpo do aviso
Quando um Pix cai na sua chave, o banco avisa o seu sistema com uma requisição. O corpo parece simples, mas a maior parte dos problemas vem de três detalhes: o valor é texto, o aviso traz uma lista e o horário vem em UTC.
O que o webhook faz
Receber Pix por uma API de banco ou de provedor de pagamento significa que o seu sistema não precisa ficar perguntando "já pagaram?". Você cadastra um endereço de notificação para a sua chave e o banco chama esse endereço quando algo acontece. Segundo a especificação da API Pix, o cadastro é um PUT /webhook/{chave}, e os avisos chegam por POST em {seu endereço}/pix.
Há dois motivos para o aviso: um ou mais Pix recebidos nessa chave, e uma devolução que chegou a um status final (DEVOLVIDO ou NAO_REALIZADO). O prazo é definido por cada banco, e o seu endereço deve responder 200 para confirmar o recebimento.
Como é o corpo
É um objeto com uma única chave, pix, cujo valor é uma lista. O banco pode juntar vários Pix da mesma chave em um aviso só. Este é o formato, com base no exemplo da própria especificação:
{
"pix": [
{
"endToEndId": "E87654321202009091221dfghi123456",
"txid": "971122d8f37211eaadc10242ac120002",
"valor": "110.00",
"horario": "2020-09-09T20:15:00.358Z",
"infoPagador": "0123456789"
}
]
}
| Campo | O que é | Cuidado |
|---|---|---|
endToEndId | Identificador do Pix de ponta a ponta (32 caracteres alfanuméricos) | Use como chave única do pagamento no seu sistema. |
txid | Identificador da cobrança (26 a 35 caracteres) | Opcional. Sem ele, não há cobrança para conciliar. |
valor | Valor recebido | É texto com ponto e duas casas ("110.00"), não número. |
horario | Quando o banco processou o Pix | Data e hora RFC 3339, em geral com Z (UTC). |
devolucoes | Devoluções ligadas a esse Pix | É uma lista; cada item tem status. |
Lendo o aviso com segurança
O código abaixo valida o envelope, percorre todos os Pix (não só o primeiro) e converte o valor para centavos sem passar por número com vírgula, o que evita erros de arredondamento. Ele também descarta repetições do mesmo endToEndId.
function lerWebhookPix(corpo, jaProcessados) {
if (!corpo || !Array.isArray(corpo.pix)) throw new Error('Corpo inesperado: falta a lista "pix"');
const vistos = jaProcessados || new Set();
const novos = [];
for (const p of corpo.pix) {
if (!/^[a-zA-Z0-9]{32}$/.test(p.endToEndId)) throw new Error('endToEndId inválido');
if (typeof p.valor !== 'string' || !/^\d{1,10}\.\d{2}$/.test(p.valor)) throw new Error('valor inválido: ' + p.valor);
if (vistos.has(p.endToEndId)) continue;
vistos.add(p.endToEndId);
const [reais, centavos] = p.valor.split('.');
novos.push({ endToEndId: p.endToEndId, txid: p.txid ?? null, valorCentavos: Number(reais) * 100 + Number(centavos), horarioUtc: p.horario });
}
return novos;
}
Em produção, o conjunto de identificadores já processados deve ficar em um banco de dados, não na memória, para sobreviver a reinícios. Isso é uma boa prática, não uma regra da especificação.
Os erros mais comuns
- Valor como número. Se o seu sistema transforma
"110.50"em110.5e depois compara com texto, a conciliação falha. Mantenha o valor como texto até converter para centavos. - Ler só
pix[0]. Como o banco pode agrupar avisos, ignorar os demais itens perde pagamentos. - Esquecer o fuso.
2020-09-09T20:15:00.358Zé 17h15 em Brasília. Converta antes de mostrar ao usuário ou de comparar com a data de vencimento. - Devolução como objeto. A especificação define
devolucoescomo lista. No exemplopixWebhook1do próprio documento, o campo aparece como objeto, uma inconsistência que vale conhecer. Se o seu banco enviar um objeto, aceite os dois formatos no seu código. - Tratar o aviso como prova de pagamento. O aviso diz que algo aconteceu; para liberar algo de alto valor, confirme também pela consulta de Pix do banco.
Devoluções: o segundo tipo de aviso
Quando você devolve um Pix, o mesmo endereço recebe um aviso quando a devolução termina. Cada devolução tem id, rtrId, valor, status e um horario com solicitacao e, quando liquidada, liquidacao. Os status possíveis são EM_PROCESSAMENTO, DEVOLVIDO e NAO_REALIZADO. Só os dois últimos são finais e disparam o aviso. Trate NAO_REALIZADO como um alerta para a sua equipe, não como sucesso.
Como testar sem movimentar dinheiro
Cole o corpo no validador de webhook do Pix: ele confere cada campo contra o esquema oficial, mostra os valores e o horário de Brasília e aponta o caminho exato do erro (por exemplo, pix[0].valor). Para montar os seus testes, comece pelos exemplos da especificação e altere um campo por vez para ver o que o seu código faz com valor número, horário local e lista vazia.
Limites
Este guia segue a versão 2.11.0-rc1 da especificação, que é uma versão candidata. Bancos e provedores podem estar em versões anteriores ou acrescentar campos próprios; consulte a documentação do seu provedor. O que está descrito aqui não substitui o Manual de Padrões para Iniciação do Pix, que também trata da autenticação do serviço (mTLS).