Pagamento com provedor externo

Fluxo de pagamento com um provedor externo: desde o microsserviço de pedidos até o provedor de pagamentos, passando pelo ACL e o tratamento de callbacks.

Por que o pagamento externo é um fluxo especial

Integrar um provedor de pagamentos externo (Stripe, PayPal, MercadoPago) traz desafios que não existem na comunicação entre serviços internos:

  • O provedor tem seu próprio modelo de dados e API
  • Os tempos de resposta são imprevisíveis
  • A comunicação inclui callbacks assíncronos (webhooks)
  • O domínio interno não deve ser contaminado com o modelo do provedor

Este fluxo mostra como o Anti-Corruption Layer (ACL) protege o domínio e como os callbacks são tratados de forma segura.

O fluxo completo

sequenceDiagram
    participant ORD as ms-orders
    participant EB as Event Bus
    participant PAY as ms-payments
    participant ACL as ACL (Adapter)
    participant EXT as Proveedor Externo
    participant WH as Webhook Handler

    ORD->>EB: OrderPlaced
    EB->>PAY: OrderPlaced

    PAY->>PAY: Crear intención de pago
    PAY->>ACL: Solicitar cobro
    ACL->>ACL: Traducir al modelo del proveedor
    ACL->>EXT: POST /charges (API del proveedor)
    EXT-->>ACL: charge_id, status: pending
    ACL-->>PAY: paymentId, status: processing

    Note over EXT: El proveedor procesa el pago

    EXT->>WH: Webhook: charge.completed
    WH->>WH: Verificar firma del webhook
    WH->>ACL: Traducir evento externo
    ACL->>PAY: Pago completado
    PAY->>PAY: Actualizar estado
    PAY->>EB: PaymentCompleted

    EB->>ORD: PaymentCompleted
    ORD->>ORD: Actualizar estado de la orden

Fase 1: Iniciar o pagamento

Microsserviço de pagamentos (ms-payments)

Quando recebe o evento OrderPlaced:

  1. Cria um registro de pagamento interno com estado processing
  2. Extrai os dados necessários: valor, moeda, referência do pedido
  3. Invoca o ACL para solicitar a cobrança ao provedor externo

Anti-Corruption Layer (ACL)

O ACL é o adaptador entre o domínio interno e o provedor externo:

  1. Recebe a solicitação no modelo de domínio interno
  2. Traduz para o formato que o provedor espera
  3. Invoca a API do provedor
  4. Traduz a resposta do provedor para o modelo interno

Exemplo de tradução:

Modelo interno:

{
  "orderId": "order-456",
  "amount": 150.00,
  "currency": "USD",
  "customerId": "cust-789"
}

Modelo do provedor (Stripe):

{
  "amount": 15000,
  "currency": "usd",
  "metadata": { "order_id": "order-456" },
  "customer": "cus_stripe_789"
}

O ACL trata as diferenças: o provedor usa centavos em vez de decimais, letras minúsculas para a moeda e tem seu próprio sistema de IDs de cliente.

Fase 2: Aguardar o resultado

Muitos provedores de pagamento não confirmam a cobrança de forma imediata. O fluxo se torna assíncrono:

  1. O provedor retorna um estado pending ou processing
  2. O microsserviço de pagamentos salva esse estado intermediário
  3. O provedor processa o pagamento (pode levar segundos ou minutos)
  4. Quando termina, o provedor envia um webhook ao sistema

Fase 3: Receber o webhook

Webhook Handler

O webhook handler é um endpoint público que recebe notificações do provedor:

  1. Recebe a requisição HTTP do provedor
  2. Verifica a assinatura do webhook (para garantir que veio do provedor real)
  3. Passa o evento ao ACL para tradução
  4. O ACL converte o evento externo para o modelo interno

Verificação de assinatura

Os provedores assinam seus webhooks para prevenir falsificações:

// El proveedor envía un header con la firma
X-Signature: sha256=abc123...

// El handler verifica la firma usando el secret compartido
const expectedSignature = hmac(secret, requestBody);
if (signature !== expectedSignature) {
  return 401; // Rechazar
}

Tradução do evento

O ACL traduz o evento do provedor para o modelo interno:

Evento do provedor:

{
  "type": "charge.succeeded",
  "data": {
    "id": "ch_stripe_123",
    "amount": 15000,
    "currency": "usd",
    "status": "succeeded",
    "metadata": { "order_id": "order-456" }
  }
}

Evento interno:

{
  "type": "PaymentCompleted",
  "payload": {
    "paymentId": "pay-internal-789",
    "orderId": "order-456",
    "amount": 150.00,
    "currency": "USD",
    "providerReference": "ch_stripe_123"
  }
}

O papel do ACL em detalhe

O ACL não é apenas um tradutor de formatos. Ele também encapsula:

Tratamento de erros do provedor

Cada provedor tem seus próprios códigos de erro. O ACL os traduz para erros de domínio:

Erro do provedorErro interno
card_declinedPaymentDeclined
insufficient_fundsPaymentDeclined
rate_limitProviderTemporarilyUnavailable
invalid_requestPaymentConfigurationError

Novas tentativas

Se a API do provedor falhar por um erro transitório (timeout, 503), o ACL pode tentar novamente com backoff exponencial. O microsserviço de pagamentos não precisa conhecer essa lógica.

Troca de provedor

Se for decidido trocar o Stripe por outro provedor, apenas o ACL é modificado. O microsserviço de pagamentos e o restante do sistema não são afetados.

Cenários de erro

O webhook não chega

Se o provedor não enviar o webhook (ou ele se perder), o sistema precisa de um mecanismo de reconciliação:

  • Um job periódico consulta o provedor por pagamentos pendentes
  • Se o pagamento já foi processado, atualiza o estado interno
  • Se passou tempo demais, marca o pagamento como timeout

Webhook duplicado

Os provedores podem enviar o mesmo webhook mais de uma vez. O handler deve ser idempotente: se já processou esse evento, o ignora.

Pagamento recusado

Se o provedor recusar o pagamento, o microsserviço de pagamentos publica PaymentFailed e o microsserviço de pedidos executa as compensações correspondentes (liberar estoque, notificar o usuário).

Resumo

O fluxo de pagamento com provedor externo mostra como o ACL protege o domínio das particularidades de sistemas externos. A comunicação combina chamadas síncronas (iniciar a cobrança) com callbacks assíncronos (webhooks). A verificação de assinaturas, a tradução de modelos e o tratamento de erros específicos do provedor ficam encapsulados no ACL, permitindo que o restante do sistema trabalhe com um modelo de domínio limpo.