Conciliação Pix por API: extrato, identificadores e paginação

Conciliação Pix por API: extrato, identificadores e paginação

Equipe Hodle5 min de leitura

Conciliação Pix por API é o cruzamento entre os registros do seu produto e os registros de pagamento. Um webhook reduz o tempo até saber de uma mudança; o extrato permite conferir o conjunto e investigar uma notificação perdida. As duas fontes trabalham juntas.

Na Hodle, esse cruzamento também precisa distinguir o Pix em reais da entrega de um ativo digital. O pagador pode ter concluído o Pix enquanto a entrega ainda está em processamento. A página de conciliação Pix apresenta os fluxos; aqui você implementa uma leitura paginada com Node.js.

Defina o que será conciliado

Comece por uma janela fixa e uma pergunta verificável: quais operações do período têm correspondência no meu banco, e quais precisam de investigação? Guarde os identificadores devolvidos na criação, sem depender de nome, valor ou horário para adivinhar um vínculo depois.

IdentificadorUso na integração
externalIdVínculo escolhido por você para um depósito; use-o para consultar esse depósito.
walletCharge e trackIdReferências devolvidas pelo fluxo correspondente; preserve quando disponíveis.
operation.idIdentidade da linha do extrato, útil para atualizar o mesmo registro sem duplicá-lo.
endToEndIdReferência do Pix quando informada pelo trilho; pode estar ausente.
txHash ou transactionHashReferência da etapa on-chain; o nome do campo depende do endpoint.

Não presuma que todas essas chaves aparecem em toda linha. O extrato não publica um externalId obrigatório por operação. Seu sistema precisa preservar a relação entre pedido e resposta original, e consultar o endpoint específico quando o extrato não basta. Compare os contratos de extrato e consulta de depósito.

Leia uma janela inteira, não apenas a primeira página

O endpoint GET /api/account/statement aceita from, to, limit e cursor. O início é inclusivo e o fim é exclusivo. A documentação limita a janela a 90 dias e cada página a 200 operações. Escolher to uma vez evita mover o limite de busca durante a paginação.

Baixe requestHodle.mjs e readStatement.mjs para a mesma pasta. O leitor mantém a janela, acompanha nextCursor e falha se o cursor se repetir. Ele também limita a leitura a 100 páginas; para volumes maiores, divida a janela e persista páginas incrementalmente, em vez de acumular tudo na memória.

Com Node.js 20 ou superior, salve statement.mjs:

import { readStatement } from './readStatement.mjs'

const apiKey = process.env.HODLE_API_KEY

if (!apiKey?.startsWith('hodle_test_')) {
  throw new Error('Configure uma chave hodle_test_ para este exemplo')
}

const result = await readStatement({
  baseUrl: 'https://sandbox-api.hodle.com.br',
  apiKey,
  from: '2026-09-01T00:00:00Z',
  to: '2026-09-02T00:00:00Z',
})

if (!result.success) {
  throw new Error(`${result.error} (HTTP ${result.status})`)
}

console.log({ operationsRead: result.operations.length })

Execute node statement.mjs. Ajuste as datas para um intervalo em que sua conta de teste tenha operações. Uma lista vazia pode ser um resultado correto; ela não prova que a integração está quebrada. Este exemplo usa Bearer e não exibe o extrato completo no log.

Os ambientes têm dados separados. A consulta de sandbox não retorna transações de produção. Para o uso real, configure o host e a chave de produção e confirme a aprovação cadastral e os recursos da conta. O teste local abaixo independe da disponibilidade do extrato no seu ambiente.

Demonstração técnica reproduzível: duas páginas e um cursor repetido

Esta é uma demonstração de software com dados sintéticos, não um caso de cliente. O teste está em scripts/tests/pixExamples.test.mjs e executa o mesmo readStatement.mjs disponível para download.

Na primeira chamada, a resposta de teste contém a operação op-1, ativo BRL, valor textual 25.00 e nextCursor: 'page-2'. Na segunda, contém op-2, ativo USDC, valor textual 0.10000000 e nextCursor: null. O servidor é substituído na fronteira HTTP; o código de paginação roda de verdade.

VerificaçãoResultado esperadoResultado observado no teste local
Janela de consultaMesmo from e to nas duas chamadasPreservada nas duas chamadas
ContinuaçãoSegunda chamada leva cursor=page-2Cursor enviado corretamente
OperaçõesDuas linhas, sem somar moedas distintasDuas linhas retornadas
Precisão textual0.10000000 permanece stringValor preservado integralmente
Cursor sempre igualInterromper com erro, sem loopResultado de erro retornado

A metodologia verifica as URLs solicitadas e o resultado devolvido pelo módulo. Ela não mede latência de produção, completude do extrato de um cliente, retorno financeiro ou disponibilidade de parceiros. O teste não chama a API real.

Para reproduzir no repositório do site:

node --test scripts/tests/pixExamples.test.mjs

Compare valores na unidade correta

amount usa a unidade do ativo, enquanto valueInBrl, quando disponível, descreve o valor em reais. O extrato traz quantias como strings decimais. Preserve essa representação até a camada de cálculo; converter tudo para Number pode introduzir arredondamento binário.

Não some BRL, USDC e USDT na mesma coluna. Defina grupos por ativo e rede, trate taxas separadamente e escolha uma representação decimal exata para o relatório. balances é a posição atual devolvida pela consulta; não é o saldo histórico reconstruído no fim da janela.

Também não trate todas as linhas do extrato como recebimento Pix: ele inclui depósitos, saídas, transferências e outras operações. Identifique o tipo, a direção e o provedor quando informado. Um hash on-chain comprova uma etapa diferente de um endToEndId do Pix.

Mantenha divergências abertas até ter evidência

Uma rotina de produção pode atualizar operações pelo id, comparar o vínculo com o pedido e separar pendências. Pagamento sem pedido, pedido sem operação e valor divergente precisam de investigação, não de uma correção automática de saldo. Ao repetir uma janela, atualize o mesmo registro em vez de inserir outra cópia.

O leitor de exemplo não promete um snapshot imutável enquanto operações mudam de estado. Faça novas leituras de uma janela com sobreposição e use atualização idempotente no armazenamento. Nunca marque a conciliação como concluída depois de ler apenas parte das páginas.

Para depósitos, consulte o externalId quando precisar distinguir FIAT_PAID, PROCESSING, COMPLETED e estorno. Para checkout, consulte o pedido pelo trackId; o contrato público de checkout tem estados próprios. Não substitua um pelo outro.

Complete esse caminho com o tutorial de webhook e idempotência e o tutorial de integração Node.js. Para selecionar os recursos adequados ao produto, consulte a API Pix.

Perguntas frequentes

Posso somar amount para obter o total recebido em reais?
Não. amount está na unidade do ativo da operação. Uma linha pode representar USDC e outra BRL. Preserve as strings decimais, agrupe por ativo e rede e use o campo monetário adequado ao relatório.
O saldo atual prova quais pedidos foram pagos?
Não. O saldo é uma posição, enquanto as operações registram movimentos. A conciliação precisa cruzar cada operação com o pedido, o identificador e o estado correspondentes.
Como evitar perder operações na paginação?
Fixe from e to durante a leitura e siga nextCursor até não existir outra página. Se uma chamada falhar, não apresente o conjunto parcial como conciliação concluída.

Mais da Hodle

Ver todos os artigos