Como Estruturar um Sistema de Pagamentos Assíncronos Seguro com PHP e MySQL

Aprenda a estruturar o processamento de pagamentos assíncronos em PHP e MySQL utilizando transações ACID e bloqueio pessimista contra falhas de concorrência.

Falhas na integração de gateways de pagamento podem comprometer a receita e a reputação de qualquer plataforma digital. O maior desafio técnico não reside no disparo da requisição HTTP para a API de pagamento, mas sim na resiliência em lidar com instabilidades de rede, concorrência no banco de dados e o processamento duplicado de webhooks.

Quando duas requisições simultâneas tentam atualizar o status de um mesmo pedido, abre-se uma brecha para falhas de concorrência financeira. No ecossistema PHP, solucionar esse problema exige um design de software orientado à idempotência e o uso estratégico dos recursos do MySQL.

Idempotência e Bloqueio Pessimista no MySQL

Para assegurar que cada evento de pagamento seja processado exatamente uma vez, o banco de dados deve atuar como a última linha de defesa. No MySQL, utilizando o motor de armazenamento InnoDB, podemos implementar o bloqueio pessimista (Pessimistic Locking) por meio da instrução SELECT ... FOR UPDATE dentro de uma transação ACID.

Em sistemas PHP que desenvolvo, adoto esse padrão para blindar a escrita. Enquanto uma thread do PHP processa a atualização de um pedido específico, qualquer outra tentativa de acesso concorrente ao mesmo registro é colocada em fila pelo banco de dados até que a transação principal execute o commit ou rollback.

Veja um exemplo prático utilizando PHP estruturado com PDO:

declare(strict_types=1);

try {
    $pdo->beginTransaction();

    // Executa o bloqueio pessimista para evitar concorrência
    $stmt = $pdo->prepare("SELECT status FROM pedidos WHERE uuid = :uuid FOR UPDATE");
    $stmt->execute(['uuid' => $orderUuid]);
    $order = $stmt->fetch();

    if (!$order) {
        throw new Exception('Pedido não localizado.');
    }

    if ($order['status'] === 'pago') {
        $pdo->rollBack();
        return;
    }

    // Atualização segura do status do pedido
    $updateStmt = $pdo->prepare("UPDATE pedidos SET status = 'pago', atualizado_em = NOW() WHERE uuid = :uuid");
    $updateStmt->execute(['uuid' => $orderUuid]);

    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    // Registro estruturado do erro para auditoria
}

Desacoplamento com Filas de Processamento

Chamar APIs externas de pagamento diretamente na requisição síncrona do usuário prejudica a performance da aplicação. A abordagem recomendada é o desacoplamento por meio de filas.

  1. O webhook do gateway de pagamento é recebido por um endpoint leve em PHP.
  2. O payload bruto é salvo imediatamente em uma tabela do MySQL (fila_pagamentos) com status pendente.
  3. O endpoint responde HTTP 200 imediatamente para o gateway, encerrando a conexão.
  4. Um processo em PHP CLI (Worker) executado em background consome os registros da tabela fila_pagamentos, processando-os sequencialmente.

Essa arquitetura baseada no padrão Transactional Outbox mitiga perdas de requisições caso a API de pagamentos apresente instabilidade momentânea, permitindo retentativas automáticas.

Boas Práticas de Arquitetura

Para garantir a manutenibilidade do projeto, o código deve implementar tipos estritos (declare(strict_types=1)) e separar as regras de negócio da infraestrutura de banco de dados por meio do padrão Repository. Valores monetários devem ser manipulados como inteiros (representando centavos) para contornar problemas de precisão de ponto flutuante inerentes ao interpretador do PHP.

Precisa de uma Arquitetura de Pagamentos Blindada?

Estruturar sistemas financeiros exige foco em conformidade, segurança e desempenho. Se a sua empresa utiliza o ecossistema PHP e enfrenta gargalos de performance, falhas na conciliação de transações ou precisa refatorar um sistema legado para suportar alto volume de vendas, entre em contato para uma consultoria especializada.

Preencha o formulário abaixo para que eu consiga entrar em contato com você.