PagnovoPagnovodocs

Documentação

Boas práticas e confiabilidade

Integrar pagamentos é, acima de tudo, tratar o incerto com cuidado. A maioria dos problemas em produção (pagamento duplicado, cliente marcado como "falha" sem ter falhado, estorno indevido) vem de interpretar um timeout como erro. Esta página reúne as regras que evitam isso.

1. Todo valor é inteiro em centavos [#valores]

Nunca use ponto flutuante. R$ 19,90 é 1990; R$ 1.000,00 é 100000. Float introduz arredondamento que, em dinheiro, vira divergência de conciliação.

2. Idempotência é a regra de ouro dos saques [#idempotencia]

  • Sempre envie um externalId único por solicitação de saque (ex.: um UUID ou o id do saque no seu sistema).
  • Reenviar a mesma solicitação com o mesmo externalId devolve o saque original (a resposta traz idempotentReplay: true) — não cria um segundo nem debita de novo.
  • Nunca reutilize um externalId para solicitações diferentes.

Com externalId, um retry depois de um timeout é seguro: no pior caso você recebe de volta o saque que já existia.

3. Timeout e erro NÃO são a mesma coisa [#classes-de-erro]

RespostaSignificadoO que fazer
2xxAceito. Saque nasce WITHDRAW_REQUEST (assíncrono); o resultado final vem por webhook.Aguarde o webhook / consulte o status.
4xx rápido (400/403/422)Recusa definitiva (saldo insuficiente, validação, IP não autorizado).Seguro tratar como recusa. Ajuste e reenvie se fizer sentido.
429Rate limit.Respeite o header retry-after + backoff exponencial com jitter.
5xx / timeout / sem respostaINCERTO — o recurso pode ter sido criado.NÃO trate como falha. Reconcilie por externalId antes de qualquer ação.

Saldo insuficiente retorna 400 em menos de 1 segundo, antes de qualquer criação. Um 500 ou um timeout não é saldo insuficiente — é estado incerto.

4. Nunca estorne nem reprocesse às cegas [#reconciliacao]

Antes de estornar o cliente ou reenviar depois de um timeout/5xx, confirme o estado real:

E nunca faça retry automático em estado intermediário (WITHDRAW_REQUEST / WITHDRAW_PROCESSING): aguarde o webhook final. Retry sem confirmação é a causa nº 1 de empresas pagarem o mesmo destinatário duas vezes.

5. Webhooks: confirme, não confie [#webhooks]

  • Responda 2xx em até 10 segundos e não redirecione (redirect conta como falha).
  • Seja idempotente: deduplique pelo id da entrega (você pode receber o mesmo evento 2–3x em retries).
  • Confirme por reconsulta antes de liberar produto/serviço — o retorno no navegador do comprador não é fonte de verdade, só o status/API é.
  • Valide a assinatura (HMAC) nos endpoints cadastrados no painel. O destino individual por postbackUrl não é assinado → para esses, confirme sempre por GET.

Detalhes em Webhooks e Verificação de assinatura.

6. Respeite os limites [#rate-limit]

Cada rota tem um limite por janela de 60s (veja cada endpoint). Em 429, use o retry-after. Prefira webhooks a polling intenso de status — é mais rápido pra você e não gasta seu limite.

7. Segurança das credenciais [#seguranca]

  • A secretKey só no backend. Nunca em front-end, app, repositório, URL ou bundle do cliente.
  • Use allowlist de IP nas chaves (principalmente para saque).
  • Para rotacionar: crie uma chave nova, migre as integrações e revogue a antiga.

8. Facilite a conciliação e o suporte [#suporte]

  • Guarde externalId, transaction_id e withdrawal_id no seu banco, ligados ao seu pedido.
  • Ao abrir um chamado, mande o ref/trace id do erro (veja Trace ID e debug) — localiza a chamada exata sem expor dados sensíveis.
  • Em saques, use restrictReceiverDocument: true sempre que souber para quem está pagando (protege contra reatribuição de chave PIX).

Checklist rápido [#checklist]

  • Valores em centavos (inteiros), nunca float
  • externalId único em todo saque
  • Timeout / 5xx → reconcilia por externalId, nunca estorna às cegas
  • 4xx rápido = recusa definitiva; 5xx/timeout = incerto
  • 429 → retry-after + backoff exponencial
  • Sem retry automático em WITHDRAW_REQUEST/WITHDRAW_PROCESSING
  • Webhook idempotente + confirma por reconsulta antes de liberar
  • secretKey só no servidor