Pular para o conteúdo

Como integrar um gateway de pagamento sem abrir uma brecha de segurança

Veja como integrar um gateway de pagamento com mais segurança: valide o backend, proteja webhooks, evite replay e teste falhas antes do lançamento.

Fluxo de pagamento entre carrinho, backend, gateway e confirmação do pedido

Fluxo editorial baseado na OWASP Cheat Sheet Series.

O checkout pode estar bonito, o pagamento pode passar e mesmo assim a integração ter um problema sério. O erro mais perigoso costuma aparecer na confirmação: o sistema recebe um retorno do navegador, acredita que a compra foi paga e libera o pedido sem conferir a informação no servidor. Em uma loja, isso pode virar produto enviado sem pagamento. Em um SaaS, pode liberar uma assinatura que nunca foi quitada.

A OWASP recomenda tratar o gateway como um serviço externo, não como uma fonte que o frontend pode validar sozinho. O navegador ajuda a iniciar a jornada, mas o backend deve criar a cobrança, conferir o resultado e decidir quando o pedido muda de estado. Este roteiro parte dessa separação e serve para checkout único, cobrança recorrente e marketplace.

Fluxo de pagamento entre carrinho, backend, gateway e confirmação do pedido
Fluxo editorial baseado na OWASP Cheat Sheet Series. Crédito: OWASP, CC BY-SA 4.0.

O navegador inicia a compra, mas não confirma o pagamento

O primeiro cuidado é não aceitar do cliente valores que deveriam vir do seu próprio sistema. O frontend pode enviar os produtos escolhidos e a quantidade, mas o backend precisa buscar os preços atuais, aplicar descontos permitidos e recalcular o total. Se a aplicação confiar em um campo como total=1 enviado pelo navegador, uma alteração simples no pedido pode produzir uma cobrança incorreta.

Uma sequência mais segura é criar um pedido interno com um identificador único, salvar os itens e o valor calculado no servidor e só então pedir ao gateway uma transação. A resposta do provedor pode trazer um payment_id ou uma URL de checkout. O sistema deve guardar essa relação entre o pedido interno e a transação externa antes de redirecionar a pessoa.

O retorno para a loja é útil para mostrar uma tela de andamento, mas seus parâmetros continuam sendo dados não confiáveis. Não marque o pedido como pago porque a URL contém status=approved. Use o identificador recebido para consultar o gateway no servidor e compare o resultado com o pedido que você criou.

Valide identidade, valor, moeda e pedido

A confirmação precisa responder a mais perguntas do que “o gateway disse que deu certo?”. O backend deve confirmar a autenticidade da notificação, quando o provedor oferece assinatura HMAC, token secreto ou outro mecanismo equivalente. Depois, compare o identificador da transação, o ID do pedido, o valor total e a moeda esperada.

Essa comparação evita um erro comum: aceitar uma confirmação válida, mas referente a outro pedido ou a um valor menor. Em uma cobrança de R$ 199,90, uma notificação de R$ 19,90 não deve liberar o mesmo produto. O código também precisa tratar estados intermediários, como pagamento pendente, recusado, cancelado ou estornado, em vez de reduzir tudo a aprovado ou falhou.

As chaves privadas ficam no backend e nunca entram no JavaScript enviado ao navegador. Também não coloque números completos de cartão, tokens de pagamento ou respostas brutas com dados sensíveis em logs comuns. Registre o identificador interno, o identificador externo, o estado recebido e o horário. Se precisar guardar o corpo original para investigação, aplique retenção e controle de acesso compatíveis com a sensibilidade desses dados.

Webhook exige assinatura, idempotência e proteção contra replay

O webhook é a notificação servidor a servidor que informa uma mudança na transação. Ele é mais confiável do que o redirecionamento do navegador, mas não deve ser aceito sem validação. Primeiro, confira a assinatura e rejeite mensagens malformadas. Em seguida, confirme o status diretamente na API do gateway quando essa for a orientação do provedor. Só depois altere o pedido ou entregue o produto.

O mesmo evento pode chegar mais de uma vez. Isso acontece por retry do gateway, lentidão da rede ou uma resposta que não foi reconhecida a tempo. Por isso, salve uma chave de idempotência, normalmente o ID do evento ou uma combinação segura do evento e da transação, e faça o processamento de modo que a segunda entrega não envie outro e-mail, crie outro crédito ou dispare uma segunda remessa.

Também existe o replay: alguém tenta reapresentar uma notificação antiga para repetir uma ação. Use expiração, controle de eventos já consumidos e, quando o provedor permitir, verificação de timestamp. A ordem dos eventos merece atenção. Um cancelamento pode chegar depois da aprovação, e o modelo de estados precisa dizer quais transições são válidas. Não transforme automaticamente qualquer mensagem recebida no estado final do pedido.

Ordem de validação de um webhook antes da atualização do pedido
Use a confirmação do gateway e o controle de idempotência antes de cumprir o pedido. Crédito: OWASP, CC BY-SA 4.0.

Teste falhas antes de testar só o caminho feliz

O ambiente de testes deve exercitar mais do que um cartão aprovado. Simule atraso, timeout, webhook duplicado, assinatura inválida, valor divergente, evento fora de ordem, transação expirada e queda do seu endpoint. Confira se o pedido continua pendente quando a confirmação não pode ser verificada e se uma nova entrega retoma o processamento sem duplicar o efeito.

Teste também a operação. Uma equipe precisa saber localizar uma transação pelo ID, entender por que o pedido não foi liberado e reprocessar um evento de maneira controlada. Alertas para um pedido marcado como pago sem confirmação do gateway e para muitas tentativas no mesmo pedido ajudam a encontrar fraude ou defeito de integração.

Antes do lançamento, faça uma revisão curta: preços e descontos são recalculados no backend; as chaves não aparecem no frontend; o retorno do navegador não libera o pedido; webhooks têm assinatura e idempotência; valor, moeda e IDs são comparados; eventos antigos são rejeitados; logs não expõem dados de cartão; e estados pendentes, recusados e estornados têm tratamento próprio.

Uma boa integração sobrevive à troca de provedor

O gateway pode mudar, mas os limites de confiança não mudam. Mantenha uma camada interna que represente pedido, pagamento, tentativa e estado. Adapte a resposta de cada provedor nessa camada, em vez de espalhar nomes específicos do SDK pelo checkout inteiro. Isso facilita trocar a API, adicionar outro método de pagamento e investigar um incidente sem reescrever a regra de negócio.

A integração não precisa ser enorme para ser segura. Ela precisa saber quem pode declarar um pagamento, quais dados sustentam essa decisão e o que acontece quando a rede, o usuário ou o provedor se comportam de forma diferente do esperado. A Cheat Sheet da OWASP sobre gateways de pagamento de terceiros reúne as recomendações usadas neste roteiro e deve ser lida junto da documentação específica do seu provedor.

Resumo da semana

Leve contexto para a caixa de entrada.

Uma edição semanal, sem barulho e sem promessas vazias.