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 :

  1. Il crée un enregistrement de paiement interne avec le statut processing
  2. Il extrait les données nécessaires : montant, devise, référence de la commande
  3. 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 :

  1. Il reçoit la requête dans le modèle du domaine interne
  2. Il la traduit dans le format attendu par le prestataire
  3. Il invoque l’API du prestataire
  4. 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 :

  1. Le prestataire renvoie un statut pending ou processing
  2. Le microservice de paiements enregistre cet état intermédiaire
  3. Le prestataire traite le paiement (cela peut prendre quelques secondes ou minutes)
  4. 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 :

  1. Il reçoit la requête HTTP du prestataire
  2. Il vérifie la signature du webhook (pour s’assurer qu’elle provient bien du vrai prestataire)
  3. Il transmet l’événement à l’ACL pour traduction
  4. 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 prestataireErreur interne
card_declinedPaymentDeclined
insufficient_fundsPaymentDeclined
rate_limitProviderTemporarilyUnavailable
invalid_requestPaymentConfigurationError

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.