Oyun Kitabı

Concurrence optimiste sous Webhook (Concurrence Optimiste Sous Webhook)

Comment le jeton de version et le bail résolvent-ils la course lorsque la réponse synchrone avec le webhook touche le même paiement en même temps ? Lecture du secret client périmé dans le paiement par terminal…

Moteur de paiement distribué

Partie 17 de 22

Une série d'architectures de paiement distribuées qui comblent le fossé entre la capture et l'achèvement.

Distributed payment engine architecture diagram

Dans la section précédente, nous avons vu pourquoi une éventuelle cohérence est inévitable : la PSP ne peut pas participer à votre protocole de transaction, la saga et la réconciliation sont la vraie réponse. Cette section se concentre sur le moment culminant de cette fenêtre d’écart : le moment où le webhook et la réponse synchrone touchent simultanément le même enregistrement de paiement.

L'orchestrateur de paiement envoie une demande de frais ; la passerelle du fournisseur reçoit la réponse de la PSP. Au même moment, parfois quelques millisecondes plus tôt, parfois plus tard, un webhook arrive pour le même paiement. Les deux chemins peuvent transporter des informations précises ; Si les deux tentent d'écrire en même temps, le résultat est soit une mise à jour perdue, soit pire, une réponse client incorrecte basée sur une lecture obsolète sur un terminal de paiement.```text Senkron yanıt ──► Payment #42 (version=3) ──► Captured Webhook ──► Payment #42 (version=3) ──► Captured (tekrar?) │ ▼ Version token + lease → tek kazanan yazar


## Concepts à la première mention```text
📦 Version Token (Optimistic Lock)
Her güncellemede artan sayaç; yazma yalnızca beklenen sürüm eşleşirse başarılı olur.

📦 Lease
Bir worker'ın belirli bir ödeme kaydını işleme hakkını süre sınırlı olarak alması.

📦 Stale Read
İşlem sırasında okunan ama yazma anında artık geçerli olmayan eski sürüm.

📦 Terminal Payment
Captured, Failed veya Refunded gibi geri dönüşü olmayan nihai statü.
```Le bail, c'est lorsqu'un travailleur dit « Je suis en train de traiter ce dossier » ; Le jeton de version signifie « Je vois toujours cette version ». Ensemble, ils empêchent le webhook et le chemin synchrone de s'écraser mutuellement.

## Deux routes, un record : là où commence la course

Dans un paiement basé sur la redirection, le chemin synchrone renvoie généralement `Pending` ; le vrai résultat est livré avec le webhook. Pour le paiement par carte, les deux itinéraires peuvent transporter `Captured` ou `Failed` — et les deux peuvent arriver presque en même temps. L'orchestrateur de paiement tente d'écrire les informations des deux chemins sur la même ligne de paiement.

Erreur classique : les deux gestionnaires lisent l'enregistrement, mettent à jour l'état, enregistrent. La dernière personne à écrire gagne ; La mise à jour intermédiaire disparaît silencieusement. Scénario plus dangereux : l'orchestrateur lit une ancienne version avant de passer au statut de terminal et renvoie un secret client ou une URL de redirection au client qui semble toujours valide – le paiement a en fait déjà été effectué ou a échoué.```text
T=0  Orchestrator: charge gönder
T=1  Webhook gelir → Captured yaz (version 2→3)
T=2  Senkron yanıt gelir → Pending okudu (version 1)
     → client'a redirectUrl döner (bayat!)
T=3  Müşteri redirect'e gider → ödeme zaten Captured
```## Jeton de version : écrivez uniquement si la version correspond

Un champ `version` croissant de manière monotone est conservé dans chaque enregistrement de paiement. La mise à jour se fait avec la condition suivante : `UPDATE ... WHERE id = ? AND version = ?`. S'il n'y a pas de correspondance, la mise à jour affecte zéro ligne — c'est le signal qu'un autre chemin interfère.```text
Webhook handler
  READ payment (version=2, status=Processing)
  → status=Captured, version=3
  UPDATE WHERE version=2 ✓ (1 row)

Senkron handler (bayat okuma)
  READ payment (version=2, status=Processing)  ← webhook henüz commit olmadı
  → status=Captured, version=3
  UPDATE WHERE version=2 ✗ (0 rows — webhook zaten yazdı)
  → yeniden oku, terminal statüyü gör, client secret döndürme
```Le jeton de version seul ne suffit pas ; Il faut également définir la marche à suivre après détection de la lecture périmée : relire, vérifier l'état du terminal, renvoyer uniquement l'état actuel au client.

## Bail : obtention du droit au traitement des webhooks pour une durée limitée

Le gestionnaire du webhook prend un court bail avant de toucher à l'enregistrement : "Je traite le paiement n°42 pendant 30 secondes." Pendant la période de location, un autre collaborateur ne peut pas traiter le même enregistrement dans le webhook ou le flux de récupération.```text
Webhook gelir
  → lease al (paymentId, ttl=30s)
  → lease alınamazsa → defer / retry
  → lease alındı → version token ile güncelle
  → lease bırak
```Le bail empêche le même webhook d’être traité simultanément par deux travailleurs. Le jeton de version résout les conflits entre différents chemins (synchrone ou webhook). Les deux répondent à des problèmes différents ; Ils doivent être utilisés ensemble.

## Fuite du secret client dans le paiement par terminal

Le scénario de lecture obsolète le plus grave consiste à renvoyer un secret client ou une URL de redirection dans l'état du terminal. Le fait de toujours renvoyer `Pending` + redirectUrl au client après le paiement est `Captured` entraînera une deuxième tentative de facturation inutile ou une confusion.

La règle est simple : le secret client, l'URL de redirection ou le jeton de nouvelle tentative ne sont jamais restitués pour un paiement passé au statut de terminal. Le gestionnaire relit l'enregistrement lorsqu'il reçoit un conflit de version ou si une lecture obsolète est suspectée ; S'il voit l'état du terminal, il renvoie uniquement le résultat final.

| Statut | Retour au client |
| --- | --- |
| Traitement, redirection requise | redirectUrl (valide) |
| Capturé (terminal) | Résultat réussi, pas de secret |
| Échec (terminal) | Résultat d'erreur, pas de secret |
| Conflit de version → relire → Capturé | Résultat réussi, pas de secret |

## Distinctions souvent confuses```text
❌ Pessimistic lock her zaman daha güvenlidir
✓ Ödeme akışında kısa süreli lease + version token, throughput'u korurken yarışı çözer

❌ Version conflict = hata, exception fırlat
✓ Version conflict = başka yol kazandı; yeniden oku ve güncel duruma uy

❌ Lease ve version token aynı şeyi yapar
✓ Lease aynı kaydın eşzamanlı işlenmesini engeller; version token eşzamanlı yazmayı
```## Bail vs jeton de version

| Critère | Bail | Jeton de version |
| --- | --- | --- |
| Bloqué | Traitement parallèle du même enregistrement | Mise à jour perdue |
| Durée | TTL limité | Persistant, augmente à chaque écriture |
| Comportement conflictuel | Attendre / différer | Relire/réessayer |

## Liste de contrôle de concurrence optimiste

1. Les mises à jour des paiements sont-elles effectuées avec la condition `WHERE version = ?` ?
2. Lorsqu'un conflit de version est reçu, le gestionnaire relise-t-il et vérifie-t-il l'état du terminal ?
3. Le renvoi du secret client ou de l'URL de redirection dans l'état du terminal est-il bloqué au niveau du code ?
4. Le gestionnaire de webhook est-il loué avant de démarrer l'opération ?
5. La période de location est-elle plus longue que P99 du temps de traitement du webhook ?
6. Le gestionnaire de réponse synchrone et le gestionnaire de webhook partagent-ils la même logique de finalisation ?

## Ce qu'il faut retenir de cet article

1. Le chemin synchrone avec le webhook touche simultanément le même enregistrement ; la concurrence optimiste est la réponse standard à cette course.
2. La perte du jeton de version empêche la mise à jour ; Le bail empêche le traitement parallèle du même enregistrement.
3. Le conflit de version n'est pas une erreur, mais un signal de relecture.
4. Renvoyer le secret client sans lire les informations périmées dans le terminal de paiement est une erreur silencieuse de sécurité et d'UX.

> Vous n'avez pas besoin d'un verrou pessimiste pour résoudre la course ; Ce dont vous avez besoin, c'est d'une combinaison disciplinée de jeton de version et de bail qui empêche les lectures obsolètes d'atteindre le client.

Dans la section suivante, nous passons à l'observabilité pour voir ces courses et finaliser les étapes : corrélation avec l'identifiant de paiement, journal des événements étape par étape et métriques de finalisation différée.

FAQ

Frequently asked questions

Qu'est-ce que le jeton de version (verrouillage optimiste) ?

Compteur augmentant à chaque mise à jour ; L'écriture réussit uniquement si la version attendue correspond.

Qu'est-ce qu'un bail ?

Droit limité dans le temps d'un travailleur de traiter un dossier de paiement particulier.

Est-il vrai que « le verrouillage pessimiste est toujours plus sûr » ?

Le bail court + le jeton de version dans le flux de paiement résolvent la course tout en préservant le débit

Que corrige cette section ?

La concurrence optimiste n’est pas ici une optimisation des performances ; C'est le mécanisme qui permet de ne pas renvoyer de données incorrectes dans l'état du terminal. Avec le webhook, le chemin synchrone touche simultanément le même enregistrement ; la concurrence optimiste est la réponse standard à cette course. Dans la section précédente, nous avons vu pourquoi une éventuelle cohérence est inévitable : la PSP ne peut pas participer à votre protocole de transaction, la saga et la réconciliation sont la vraie réponse. Cette section se concentre sur le moment culminant de cette fenêtre d’écart : le moment où le webhook et la réponse synchrone touchent simultanément le même enregistrement de paiement.

Principes d'ingénierie appris

  • La perte du jeton de version empêche la mise à jour ; le conflit est le signal de relecture.
  • Le bail et le jeton de version résolvent différentes courses ; les deux doivent être utilisés ensemble.
  • Lors du paiement par terminal, le secret client ne doit pas être restitué sans être considéré comme périmé.

Continuer la lecture

Continuer la lecture

Suivant en série

Suivant en série

Même série

Paylaş