Paiement avec un prestataire externe
Flux de paiement avec un prestataire externe : du microservice de commandes jusqu'au prestataire de paiement, en passant par l'ACL et la gestion des callbacks.
Pourquoi le paiement externe est un flux particulier
Intégrer un prestataire de paiement externe (Stripe, PayPal, MercadoPago) introduit des défis qui n’existent pas dans la communication entre services internes :
- Le prestataire possède son propre modèle de données et sa propre API
- Les temps de réponse sont imprévisibles
- La communication inclut des callbacks asynchrones (webhooks)
- Le domaine interne ne doit pas être contaminé par le modèle du prestataire
Ce flux montre comment l’Anti-Corruption Layer (ACL) protège le domaine et comment les callbacks sont gérés de façon sécurisée.
Le flux complet
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
Phase 1 : Initier le paiement
Microservice de paiements (ms-payments)
Lorsqu’il reçoit l’événement OrderPlaced :
- Il crée un enregistrement de paiement interne avec le statut
processing - Il extrait les données nécessaires : montant, devise, référence de la commande
- Il invoque l’ACL pour demander l’encaissement au prestataire externe
Anti-Corruption Layer (ACL)
L’ACL est l’adaptateur entre le domaine interne et le prestataire externe :
- Il reçoit la requête dans le modèle du domaine interne
- Il la traduit dans le format attendu par le prestataire
- Il invoque l’API du prestataire
- Il traduit la réponse du prestataire vers le modèle interne
Exemple de traduction :
Modèle interne :
{
"orderId": "order-456",
"amount": 150.00,
"currency": "USD",
"customerId": "cust-789"
}
Modèle du prestataire (Stripe) :
{
"amount": 15000,
"currency": "usd",
"metadata": { "order_id": "order-456" },
"customer": "cus_stripe_789"
}
L’ACL gère les différences : le prestataire utilise des centimes au lieu de décimales, des minuscules pour la devise, et dispose de son propre système d’identifiants client.
Phase 2 : Attendre le résultat
De nombreux prestataires de paiement ne confirment pas l’encaissement de manière immédiate. Le flux devient asynchrone :
- Le prestataire renvoie un statut
pendingouprocessing - Le microservice de paiements enregistre cet état intermédiaire
- Le prestataire traite le paiement (cela peut prendre quelques secondes ou minutes)
- Une fois terminé, le prestataire envoie un webhook au système
Phase 3 : Recevoir le webhook
Webhook Handler
Le webhook handler est un endpoint public qui reçoit les notifications du prestataire :
- Il reçoit la requête HTTP du prestataire
- Il vérifie la signature du webhook (pour s’assurer qu’elle provient bien du vrai prestataire)
- Il transmet l’événement à l’ACL pour traduction
- L’ACL convertit l’événement externe vers le modèle interne
Vérification de la signature
Les prestataires signent leurs webhooks pour prévenir les falsifications :
// 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
}
Traduction de l’événement
L’ACL traduit l’événement du prestataire vers le modèle interne :
Événement du prestataire :
{
"type": "charge.succeeded",
"data": {
"id": "ch_stripe_123",
"amount": 15000,
"currency": "usd",
"status": "succeeded",
"metadata": { "order_id": "order-456" }
}
}
Événement interne :
{
"type": "PaymentCompleted",
"payload": {
"paymentId": "pay-internal-789",
"orderId": "order-456",
"amount": 150.00,
"currency": "USD",
"providerReference": "ch_stripe_123"
}
}
Le rôle de l’ACL en détail
L’ACL n’est pas seulement un traducteur de formats. Il encapsule également :
Gestion des erreurs du prestataire
Chaque prestataire possède ses propres codes d’erreur. L’ACL les traduit en erreurs de domaine :
| Erreur du prestataire | Erreur interne |
|---|---|
card_declined | PaymentDeclined |
insufficient_funds | PaymentDeclined |
rate_limit | ProviderTemporarilyUnavailable |
invalid_request | PaymentConfigurationError |
Nouvelles tentatives
Si l’API du prestataire échoue à cause d’une erreur transitoire (timeout, 503), l’ACL peut réessayer avec un backoff exponentiel. Le microservice de paiements n’a pas besoin de connaître cette logique.
Changement de prestataire
Si l’on décide de passer de Stripe à un autre prestataire, seul l’ACL est modifié. Le microservice de paiements et le reste du système ne sont pas affectés.
Scénarios d’erreur
Le webhook n’arrive pas
Si le prestataire n’envoie pas le webhook (ou s’il est perdu), le système a besoin d’un mécanisme de réconciliation :
- Un job périodique interroge le prestataire au sujet des paiements en attente
- Si le paiement a déjà été traité, il met à jour l’état interne
- Si trop de temps s’est écoulé, il marque le paiement comme
timeout
Webhook dupliqué
Les prestataires peuvent envoyer le même webhook plusieurs fois. Le handler doit être idempotent : s’il a déjà traité cet événement, il l’ignore.
Paiement refusé
Si le prestataire refuse le paiement, le microservice de paiements publie PaymentFailed et le microservice de commandes exécute les compensations correspondantes (libérer l’inventaire, notifier l’utilisateur).
Résumé
Le flux de paiement avec un prestataire externe montre comment l’ACL protège le domaine des particularités des systèmes externes. La communication combine des appels synchrones (initier l’encaissement) avec des callbacks asynchrones (webhooks). La vérification des signatures, la traduction des modèles et la gestion des erreurs spécifiques au prestataire restent encapsulées dans l’ACL, permettant au reste du système de travailler avec un modèle de domaine propre.