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:
- Cria um registro de pagamento interno com estado
processing - Extrai os dados necessários: valor, moeda, referência do pedido
- 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:
- Recebe a solicitação no modelo de domínio interno
- Traduz para o formato que o provedor espera
- Invoca a API do provedor
- 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:
- O provedor retorna um estado
pendingouprocessing - O microsserviço de pagamentos salva esse estado intermediário
- O provedor processa o pagamento (pode levar segundos ou minutos)
- 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:
- Recebe a requisição HTTP do provedor
- Verifica a assinatura do webhook (para garantir que veio do provedor real)
- Passa o evento ao ACL para tradução
- 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 provedor | Erro interno |
|---|---|
card_declined | PaymentDeclined |
insufficient_funds | PaymentDeclined |
rate_limit | ProviderTemporarilyUnavailable |
invalid_request | PaymentConfigurationError |
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.