Como integrar pagamentos ao seu sistema via API: arquitetura, segurança e checklist de produção
Da primeira requisição no sandbox à operação resiliente em produção: veja como desenhar uma integração de pagamentos com idempotência, webhooks, estados, retries, segurança, observabilidade e conciliação.
Integrar uma API de pagamentos é fácil quando o teste consiste em enviar uma cobrança e receber approved. O trabalho real começa quando a aplicação precisa sobreviver a timeout, resposta duplicada, webhook fora de ordem, credencial expirada, retry do cliente, indisponibilidade de parceiro, estorno parcial, chargeback e conciliação no fechamento financeiro. Em produção, pagamento é um sistema distribuído, não uma chamada HTTP isolada.
É por isso que integrações aparentemente simples costumam gerar problemas meses depois. O time implementa apenas o caminho feliz, associa o pedido diretamente ao retorno da primeira requisição e presume que toda falha significa “não cobrou”. Quando a resposta se perde no caminho ou um evento chega duas vezes, aparecem cobranças duplicadas, pedidos em estado incorreto e horas de investigação manual.
Este guia apresenta uma arquitetura prática para integrar pagamentos via API com foco em resiliência. Vamos falar de autenticação, tokenização, idempotência, máquina de estados, webhooks, timeout, retries, filas, segurança, observabilidade, testes e go-live. O objetivo é ajudar equipes a construir uma integração que não apenas funcione no sandbox, mas continue operável quando volume e complexidade aumentarem.
1. Comece modelando o domínio, não o endpoint
Antes de escrever código, defina os objetos de negócio. Pedido, cliente, intenção de pagamento, tentativa, transação, autorização, captura, estorno, disputa e liquidação são conceitos diferentes. Se tudo for salvo em uma única tabela “payments” com meia dúzia de status, a complexidade aparecerá depois em forma de exceções.
Uma boa modelagem distingue o pedido comercial da tentativa financeira. Um pedido pode ter várias tentativas. Uma tentativa pode falhar tecnicamente sem que o pedido seja cancelado. Uma transação aprovada pode ser estornada. Uma cobrança recorrente pertence a um contrato, mas cada execução possui identidade própria.
Essa separação permite que o e-commerce ofereça outro meio de pagamento sem criar um segundo pedido, reconcilie eventos posteriores e preserve histórico. Também evita acoplar o estado do negócio à resposta imediata do provedor.
2. Defina uma máquina de estados explícita
Pagamentos evoluem ao longo do tempo. Em cartão, pode haver criado, enviado, autorizado, capturado, recusado, cancelado, estornado, em disputa e chargeback. Em Pix, pode haver criado, aguardando, pago, expirado e devolvido. O conjunto exato depende do produto, mas deve ser explícito.
Uma máquina de estados define transições válidas. created -> processing -> approved faz sentido. charged_back -> approved normalmente não. Isso impede atualizações acidentais causadas por eventos atrasados ou bugs.
Também é útil manter dois níveis: estado normalizado interno e estado original do provedor. Assim, a aplicação trabalha com taxonomia consistente sem perder informação necessária para auditoria e suporte.
3. Gere seu próprio identificador antes de chamar o provedor
Nunca dependa apenas do ID retornado pela API externa. Gere uma referência interna antes da chamada e envie-a ao provedor sempre que houver campo de referência, metadata ou merchant order ID.
Esse identificador permite responder a uma pergunta crítica: “essa tentativa já foi enviada?”. Se houver timeout, você ainda possui uma chave para pesquisar o estado. Se receber webhook, pode correlacioná-lo ao objeto correto. Se migrar de fornecedor, seu histórico não perde identidade.
IDs internos também ajudam a conciliar múltiplos parceiros. Cada transação pode possuir payment_id, attempt_id, provider_transaction_id e order_id, cada um com função clara.
4. Idempotência deve existir desde o primeiro dia
Idempotência significa que repetir uma operação com a mesma intenção não deve produzir efeito duplicado. Em pagamentos, é uma das proteções mais importantes contra cobrança em dobro.
Imagine que o cliente clica em pagar, a API do gateway processa a transação, mas a resposta não volta por perda de conexão. O frontend tenta novamente. Sem idempotência, a segunda chamada pode criar outra cobrança. Com uma chave idempotente associada à mesma tentativa, o servidor consegue devolver o resultado já existente ou impedir uma segunda criação.
O RFC 9110 descreve semântica de métodos HTTP idempotentes, mas aplicações de pagamento frequentemente precisam de idempotência também em operações POST. Por isso, muitos provedores adotam cabeçalho ou chave específica. A aplicação cliente deve persistir essa chave antes de transmitir a requisição e reutilizá-la apenas para a mesma intenção lógica.
5. Uma chave idempotente não deve ser aleatória a cada retry
Gerar UUID novo a cada repetição derrota o objetivo. A chave deve representar uma tentativa lógica. Se o usuário apertar o botão duas vezes para a mesma tentativa, use a mesma chave. Se ele explicitamente trocar o cartão e iniciar nova tentativa, crie outra.
No backend, guarde associação entre chave, payload relevante e resultado. Se a mesma chave chegar com payload incompatível, rejeite. Isso evita reutilização acidental.
Também defina tempo de retenção. Uma chave não precisa existir para sempre, mas o período deve cobrir janelas realistas de retry e inconsistência de rede.
6. Trate timeout como estado desconhecido
Timeout não significa recusa. Significa que seu sistema não recebeu resposta dentro do limite. A operação externa pode ter sido concluída.
Portanto, evite marcar automaticamente como failed. Um estado como unknown ou pending_confirmation é mais honesto. Em seguida, consulte a API do provedor por referência, aguarde webhook ou execute reconciliação.
A lógica correta é: primeiro descobrir; depois decidir se pode repetir. Essa disciplina elimina uma grande classe de duplicidades.
7. Retries precisam ser específicos por operação
Retry automático é útil para erros temporários, como 503, conexão reiniciada ou timeout de leitura. Mas nem toda chamada pode ser repetida cegamente. Uma consulta é diferente de criar uma cobrança.
Use backoff exponencial com jitter para evitar tempestades quando um provedor está instável. Limite número de tentativas e abra circuit breaker quando a taxa de falha ultrapassar um limiar. Em vez de manter dezenas de threads aguardando, devolva estado controlado e processe confirmação em background quando o produto permitir.
Retries em autorização devem obedecer semântica e regras de pagamento, não apenas lógica de rede. Uma recusa definitiva do emissor não deve virar loop automático.
8. Webhook é evento, não comando confiável de primeira entrega
Webhooks notificam mudanças de estado, mas a internet não garante exatamente uma entrega nem ordem. Seu endpoint deve assumir duplicidade, atraso e reenvio.
Primeiro, autentique o evento conforme mecanismo do provedor: assinatura HMAC, certificado, token ou outro método documentado. Segundo, grave o evento bruto com um ID único. Terceiro, responda rápido e processe em fila. Quarto, torne o consumidor idempotente.
Não execute lógica pesada antes de devolver 2xx. Se o endpoint demora dez segundos, o provedor pode considerar falha e reenviar, ampliando carga justamente durante incidente.
9. Valide assinatura antes de confiar no conteúdo
Um endpoint público de webhook pode receber requisições de qualquer origem. Não basta esconder a URL. Verifique assinatura, timestamp e segredo conforme documentação do provedor.
Proteja contra replay quando aplicável, mantendo IDs processados e janela temporal. Faça rotação de secrets sem indisponibilidade, aceitando temporariamente chave atual e anterior durante transição controlada.
A OWASP mantém o API Security Top 10 e destaca problemas de autenticação, autorização, consumo irrestrito de recursos, configuração e inventário. Webhooks pertencem ao mesmo perímetro de API e devem receber o mesmo rigor.
10. Nunca confie apenas no frontend para confirmar pagamento
O navegador pode fechar, a conexão pode cair e o usuário pode manipular dados. O frontend deve apresentar experiência, não ser a fonte final de verdade financeira.
Depois de iniciar pagamento, a aplicação consulta o backend. O backend usa resposta assinada do provedor, consulta server-to-server e webhooks para atualizar estado. Em Pix, por exemplo, o redirecionamento do cliente não deve ser suficiente para marcar pedido como pago.
Essa separação reduz fraude e inconsistência.
11. Capture dados de cartão da forma mais segura possível
Quanto menos seu backend manipular PAN e dados sensíveis, menor a superfície de risco. Use componentes de tokenização ou métodos recomendados pelo provedor que reduzam exposição, observando seu escopo PCI DSS.
O PCI DSS 4.0.1 é a referência corrente do PCI Security Standards Council. O escopo exato depende da arquitetura. Um formulário hospedado, campos seguros incorporados e captura direta pelo servidor podem criar obrigações diferentes.
Nunca registre PAN completo, CVV ou segredo de API em logs. Redação de dados deve existir por padrão.
12. Separe segredo de configuração
Chaves de API não pertencem ao repositório. Armazene-as em secret manager, variáveis de ambiente protegidas ou infraestrutura equivalente. Tenha credenciais separadas por ambiente e, idealmente, por aplicação.
Implemente rotação. Registre qual chave foi usada apenas por identificador não sensível. Aplique menor privilégio: se uma credencial precisa apenas consultar transações, não conceda permissão de estorno.
Em times maiores, audite quem acessa produção. Um vazamento de segredo de pagamentos tem impacto financeiro imediato.
13. Sandbox deve simular falhas, não apenas aprovações
Um bom plano de testes inclui cartão aprovado, recusado, autenticação necessária, timeout, erro 500, webhook duplicado, webhook fora de ordem, cancelamento, estorno parcial e total, chargeback simulado quando disponível.
Teste também clique duplo, refresh, perda de rede, browser fechado e retry da fila. Bugs de pagamento aparecem nos cantos do fluxo.
Mantenha fixtures reproduzíveis e testes automatizados de contrato. Se o provedor mudar um campo ou enum, sua CI deve detectar antes da produção.
14. Não acople seu domínio ao JSON do provedor
Crie uma camada adapter. A aplicação chama uma interface interna, como authorize(paymentAttempt), e o adapter traduz para o formato do parceiro. A resposta externa é normalizada em objetos do domínio.
Isso reduz lock-in e facilita multiadquirência. Também impede espalhar nomes de campos específicos por dezenas de serviços.
Preserve o payload bruto em armazenamento seguro para troubleshooting quando necessário, respeitando minimização de dados. O domínio usa apenas o que precisa.
15. Modele erros de forma útil
PaymentException para tudo não ajuda. Separe validação, credencial inválida, recusa do emissor, autenticação requerida, risco, timeout, indisponibilidade, rate limit e erro desconhecido.
Cada categoria deve definir comportamento: mostrar mensagem, tentar outra rota, aguardar, consultar estado, solicitar outro meio ou escalar incidente.
Mantenha o código original do provedor. Normalização serve para decisão; o original serve para diagnóstico.
16. Filas são essenciais, mas não para esconder tudo
Webhooks, e-mails, conciliação, geração de comprovante e pós-processamento podem ir para filas. A autorização principal geralmente precisa devolver resposta em tempo compatível com checkout.
Não transforme o fluxo em uma cadeia infinita de jobs sem visibilidade. Cada job deve ser idempotente, ter timeout, retry e dead-letter queue ou mecanismo equivalente para falhas persistentes.
Monitore tamanho de fila e idade da mensagem mais antiga. Uma fila “saudável” com 100 mil itens atrasados não é saudável.
17. Observabilidade precisa falar linguagem de pagamento
CPU e memória não bastam. Meça latência p50, p95 e p99 por endpoint e provedor, taxa de erro técnico, aprovação, timeout, webhook atrasado, jobs pendentes e estados desconhecidos.
Adicione tracing distribuído quando possível. Um correlation_id deve acompanhar request, job, chamada externa e webhook. Assim, suporte consegue reconstruir jornada sem acessar cinco sistemas manualmente.
Crie dashboards por parceiro e alertas baseados em desvio. Uma queda de aprovação de 90% para 75% pode ser mais urgente que CPU alta.
18. Logs devem ajudar sem vazar dados
Registre IDs, timestamps, duração, endpoint, status normalizado, código externo e contexto operacional. Nunca registre CVV. Masque PAN caso apareça em qualquer payload legado. Evite logar tokens reutilizáveis.
Defina retenção e controle de acesso. Logs financeiros podem conter PII e devem obedecer governança de dados.
Estruture logs em JSON ou formato consultável. Texto livre dificulta análise em incidentes.
19. Conciliação é parte da integração
Não considere projeto encerrado quando a compra aprova. Você precisa confirmar que autorizações, capturas, estornos, taxas e liquidações correspondem ao seu ledger.
Implemente rotina diária que compara base interna com relatórios/API do provedor. Procure transações ausentes, valores divergentes, status incompatíveis e settlements não conciliados.
Essa reconciliação também corrige eventos perdidos. Se um webhook não chegou, a rotina encontra a diferença.
20. Multiadquirência começa com contrato de interface
Mesmo que hoje exista apenas um parceiro, desenhe interface pensando em dois. Métodos como createPayment, capture, refund, getStatus e listSettlements devem refletir seu domínio, não o nome do endpoint externo.
Quando adicionar outra rota, implemente novo adapter. O roteador decide elegibilidade por seller, bandeira, produto, risco e disponibilidade.
Não force o menor denominador comum. Recursos exclusivos podem existir como capabilities. Pergunte ao adapter se suporta partial_capture, network_token, installments etc.
21. Feature flags reduzem risco de lançamento
Ative nova integração por percentual, seller, usuário interno ou região. Comece com tráfego baixo e compare métricas com a rota anterior.
Defina kill switch. Se erro ou latência aumentar, volte sem deploy. Feature flags são especialmente úteis em migração de gateway.
Registre qual versão da regra roteou cada transação para permitir auditoria.
22. O frontend precisa ser resiliente também
Desabilite o botão enquanto uma tentativa está em processamento, mas permita recuperação se a página for recarregada. Use o ID da tentativa para consultar estado.
Não mostre “pagamento recusado” em timeout. Use linguagem como “estamos confirmando seu pagamento” quando o estado é realmente desconhecido.
Se houver falha definitiva, ofereça outro meio sem apagar carrinho. Conversão depende da forma como a exceção é tratada.
23. Segurança de API além do pagamento
Proteja endpoints contra Broken Object Level Authorization: um usuário não pode consultar pagamento de outro apenas trocando ID. Use IDs não previsíveis, mas sobretudo autorização server-side.
Aplique rate limits, validação de schema, limites de payload e proteção contra abuso. Documente versões. Remova endpoints obsoletos.
Faça threat modeling: o que um atacante faria com endpoint de refund? E com criação de customer? Segurança precisa considerar impacto financeiro.
24. Versionamento evita noites ruins
Não altere significado de campo existente sem versão. Prefira evoluções compatíveis. Quando precisar de breaking change, publique nova versão e período de migração.
No lado cliente, fixe versões de SDK e acompanhe changelog. Atualizar biblioteca crítica automaticamente em produção pode introduzir mudança inesperada.
Mantenha testes de regressão contra versões suportadas.
25. Checklist de go-live
Antes de produção, valide credenciais, DNS, TLS, secrets, timeouts e limites de conexão. Confirme idempotência, assinatura de webhook, retries e consulta de status.
Teste aprovação, recusa, timeout, duplicidade, estorno, cancelamento e recuperação. Configure dashboards e alertas. Garanta que suporte conhece IDs e painel.
Confirme conciliação, política de logs, PCI, LGPD, controle de acesso e plano de rollback. Agende janela com responsáveis técnicos e comerciais disponíveis.
26. As primeiras 72 horas de produção
Monitore de perto aprovação, erro técnico, latência, duplicidade e diferença de valor. Compare com baseline anterior.
Faça amostragem manual de transações e settlements. Verifique se webhooks chegam no tempo esperado e se filas estão drenando.
Não espere cliente abrir chamado para descobrir problema. Lançamento de pagamento precisa de war room proporcional ao risco.
27. Faça reconciliação de estados em tempo quase real
Além do fechamento diário, operações críticas podem manter um reconciliador leve para transações que permanecem em processing, unknown ou pending_confirmation além de um limite. A rotina consulta o provedor por ID ou referência e corrige estados sem esperar o dia seguinte. Isso reduz pedidos presos e diminui chamados de clientes que pagaram, mas continuam vendo “aguardando”.
Esse processo precisa ser controlado para não transformar consulta em ataque involuntário ao parceiro. Use filas, backoff e lotes. Priorize transações recentes e de maior impacto. Se o provedor estiver indisponível, suspenda a pressão e aguarde recuperação.
O reconciliador também pode gerar métricas de qualidade: quantas transações precisaram de correção porque o webhook não chegou? Quanto tempo um estado desconhecido leva para convergir? Se a taxa aumenta, existe um problema de integração ou entrega de eventos que merece investigação.
28. Documente runbooks para incidentes de pagamento
Código resiliente não substitui operação preparada. Crie runbooks para queda total do provedor, aumento de timeout, webhook parado, fila acumulada, credencial inválida, discrepância de settlement e suspeita de duplicidade. Cada documento deve dizer como detectar, quem acionar, qual ação é segura e qual ação é proibida.
Em particular, deixe explícito que “reenviar todas as transações com erro” raramente é um procedimento aceitável. Primeiro classifique o estado. Também defina como comunicar atendimento e negócio durante incidente. Um status interno consistente evita que cada área conte uma versão diferente ao cliente.
Após o incidente, faça post-mortem sem foco punitivo: causa, impacto, linha do tempo, detecção, resposta e ações preventivas. Pagamentos confiáveis são construídos pelo aprendizado acumulado dessas exceções.
Conclusão
Uma integração de pagamentos profissional é um conjunto de mecanismos de confiança. Idempotência protege contra duplicidade. Máquina de estados preserva consistência. Webhooks sincronizam eventos. Conciliação encontra divergências. Observabilidade explica o que aconteceu. Segurança protege credenciais e endpoints.
O caminho feliz continua importante, mas ele representa apenas uma parte do sistema. Em escala, o diferencial está na maneira como a arquitetura lida com falha e ambiguidade.
Se seu time conseguir responder com precisão “essa cobrança aconteceu?”, “por que falhou?”, “foi liquidada?” e “posso tentar de novo?” sem depender de investigação manual, a integração está no caminho certo.
FAQ
O que é idempotência em pagamentos?
É o mecanismo que impede que a mesma intenção gere efeitos duplicados quando uma requisição é repetida, como em timeout ou clique duplo.
Webhook pode chegar duas vezes?
Sim. O consumidor deve ser idempotente e registrar IDs de eventos processados.
Timeout significa que o pagamento falhou?
Não. Significa que a resposta não chegou no tempo esperado. Consulte o status antes de repetir.
Devo armazenar dados de cartão?
Evite quando possível e use tokenização/componentes seguros. Se seu ambiente manipula dados, avalie cuidadosamente o escopo PCI DSS.
Qual timeout usar?
Depende do provedor e da experiência. Defina connect/read timeout com dados de latência e tenha estratégia para estado desconhecido.
Preciso de fila?
Para webhooks, pós-processamento e conciliação, geralmente sim. A autorização síncrona pode permanecer no request quando necessária.
Como preparar multiadquirência?
Crie interfaces e adapters internos, normalize estados e códigos e preserve IDs próprios desde a primeira integração.
Como a IOPAY pode ajudar
Uma API de pagamentos precisa continuar confiável quando a rede falha. A IOPAY oferece infraestrutura e APIs desenhadas para integrar pagamentos com observabilidade, segurança e capacidade de evolução. Fale com nosso time técnico para discutir seu cenário.
Referências
- RFC 9110 — HTTP Semantics: https://www.rfc-editor.org/rfc/rfc9110
- OWASP API Security Top 10: https://owasp.org/API-Security/
- PCI Security Standards Council — Document Library: https://www.pcisecuritystandards.org/document_library/
- Visa Acceptance Solutions — Payments: https://corporate.visa.com/en/solutions/acceptance/payments.html
Os primeiros 30 dias depois do go-live
Colocar a API em produção não encerra o projeto. Nas primeiras semanas, a prioridade deve ser confirmar que o comportamento observado em homologação se mantém sob tráfego real. Monitore a distribuição dos status HTTP, os códigos de erro de negócio, a latência p50, p95 e p99, a quantidade de retries, o atraso dos webhooks e qualquer divergência entre o status do pedido e o status da transação. Esse período também ajuda a encontrar cenários que dificilmente aparecem em testes controlados, como conexões móveis instáveis, clientes que atualizam a página repetidamente e integrações externas que respondem fora de ordem.
Outra prática importante é criar uma rotina de reconciliação. Mesmo uma integração orientada a webhooks deve possuir um mecanismo periódico para identificar transações que ficaram em estado intermediário ou eventos que não foram refletidos corretamente no sistema interno. Essa rotina não precisa ser pesada: pode começar consultando somente pagamentos pendentes além de uma janela esperada e comparando os identificadores internos com o estado informado pelo provedor.
Por fim, mantenha um changelog operacional. Alterações em timeout, política de retry, payload, versão de SDK ou regras de negócio devem ser associadas a data e responsável. Quando uma métrica muda, essa trilha permite responder rapidamente se houve mudança do provedor, do comportamento do consumidor ou da própria aplicação. Em pagamentos, capacidade de explicar uma mudança é tão importante quanto a mudança em si.