手册

Webhook 下的乐观并发 (Webhook 2)

当使用 webhook 的同步响应同时触及相同的付款时,版本令牌和租约如何解决竞争?终端支付中陈旧的读取客户端秘密...

分布式支付引擎

部分 17 的 22

一系列分布式支付架构弥合了捕获和完成之间的差距。

Distributed payment engine architecture diagram

在上一节中,我们了解了为什么最终一致性是不可避免的:PSP 无法参与您的事务协议,saga 和 reconciliation 才是真正的答案。本节重点介绍差异窗口的峰值时刻 - Webhook 和同步响应同时触及同一支付记录的时刻。

结帐协调器发送收费请求;提供商网关接收来自 PSP 的响应。与此同时(有时提前几毫秒,有时晚几毫秒),相同付款的网络钩子也会到达。两条路径都能承载准确的信息;如果两者尝试同时写入,结果要么是更新丢失,要么更糟,是基于终端支付的陈旧读取而导致错误的客户端响应。```text Senkron yanıt ──► Payment #42 (version=3) ──► Captured Webhook ──► Payment #42 (version=3) ──► Captured (tekrar?) │ ▼ Version token + lease → tek kazanan yazar


## 第一次提到的概念```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ü.
```租赁是指工作人员说“我正在处理此记录”;版本标记的意思是“我仍然看到这个版本”。它们共同防止 Webhook 和同步路径相互覆盖。

## 两条路,一份记录:比赛开始的地方

在基于重定向的支付中,同步路径通常返回`Pending`;真正的结果来自于 webhook。对于卡支付,两条路线都可以携带 `Captured` 或 `Failed` — 并且几乎可以同时到达。 Checkout Orchestrator 尝试将两个路径中的信息写入同一付款行。

经典错误:两个处理程序都读取记录、更新状态、保存。最后写的人获胜;中间的更新悄然消失。更危险的场景:编排器在切换到终端状态之前读取旧版本,并将客户端密钥或重定向 URL 返回到仍然有效的客户端 - 支付实际上已经完成或失败。```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
```## 版本标记:仅在版本匹配时写入

每条支付记录中都会保存一个单调递增的 `version` 字段。更新是在以下条件下完成的:`UPDATE ... WHERE id = ? AND version = ?`。如果没有匹配,更新会影响零行——这是另一条路径正在干扰的信号。```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
```仅有版本令牌是不够的;还需要定义检测到陈旧读取后要执行的操作:再次读取、检查终端状态、仅将当前状态返回给客户端。

## 租赁:在有限的时间内获得webhook处理的权利

Webhook 处理程序在接触记录之前会进行短暂租用:“我正在处理付款 #42,持续 30 秒。”在租用期内,其他工作人员无法处理 Webhook 或恢复流中的相同记录。```text
Webhook gelir
  → lease al (paymentId, ttl=30s)
  → lease alınamazsa → defer / retry
  → lease alındı → version token ile güncelle
  → lease bırak
```Lease 可以防止两个工作线程同时处理同一个 webhook。版本令牌解决了不同路径之间的冲突(同步与 Webhook)。两者回答不同的问题;它们必须一起使用。

## 终端支付客户端机密泄露

最严重的过时读取场景是在终端状态下返回客户端密钥或重定向 URL。在付款为 `Captured` 后仍将 `Pending` + redirectUrl 返回给客户将导致不必要的第二次收费尝试或混乱。

规则很简单:对于已传递到终端状态的支付,永远不会返回客户端密钥、重定向 URL 或重试令牌。当处理程序收到版本冲突或怀疑读取过时时,它会重新读取记录;如果它看到终端状态,则仅返回最终结果。

|状态 |返回客户 |
| --- | --- |
|需要处理、重定向 |重定向网址(有效)|
|捕获(终端)|成功结果,不是什么秘密|
|失败(终端)|错误结果,没有秘密 |
|版本冲突→重读→已捕获|成功结果,不是什么秘密|

## 经常混淆的区别```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ı
```## 租赁与版本令牌

|标准|租赁|版本令牌 |
| --- | --- | --- |
|已屏蔽 |并行处理同一条记录|丢失更新 |
|持续时间 | TTL 有限 |持久性,随着每次写入而增加 |
|冲突行为|等待/推迟|重读/重试 |

## 乐观并发检查表

1. 付款更新是否符合条件 `WHERE version = ?`?
2. 当收到版本冲突时,处理程序是否重新读取并检查终端状态?
3. 终端状态下返回客户端密码或重定向 URL 是否在代码级别被阻止?
4. 开始操作之前是否已租用 Webhook 处理程序?
5. 租用期限是否长于Webhook处理时间的P99?
6. 同步响应处理程序和 webhook 处理程序是否共享相同的终结逻辑?

## 本文中您应该记住的内容

1.带有webhook的同步路径同时触及同一条记录;乐观并发是这场竞赛的标准答案。
2、版本token丢失导致无法更新;租约可防止并行处理同一记录。
3.版本冲突不是错误,而是重读信号。
4. 在终端支付中不读取陈旧信息而返回客户端密钥是一个无声的安全和用户体验错误。

> 你不需要悲观锁来解决竞争;您需要的是版本令牌和租约的严格组合,以防止过时的读取到达客户端。

在下一节中,我们将继续观察这些竞赛并最终确定步骤:与付款 ID 的关联、逐步事件日志和延迟的最终确定指标。

FAQ

Frequently asked questions

什么是版本令牌(乐观锁)?

计数器随着每次更新而增加;仅当预期版本匹配时写入才会成功。

什么是租赁?

工人处理特定付款记录的有时限权利。

难道真的是“悲观锁总是更安全”吗?

支付流程中的短租+版本令牌解决了竞争,同时保留了吞吐量

本节修复了什么?

这里的乐观并发并不是性能优化;它是终端状态下不返回错误数据的机制。使用webhook,同步路径同时触及同一条记录;乐观并发是这场竞赛的标准答案。在上一节中,我们了解了为什么最终一致性是不可避免的:PSP 无法参与您的事务协议,saga 和 reconciliation 才是真正的答案。本节重点介绍差异窗口的峰值时刻 - Webhook 和同步响应同时触及同一支付记录的时刻。

学到的工程原理

  • 版本令牌丢失导致无法更新;冲突是重读信号。
  • 租赁和版本代币解决不同的竞赛;两者应该一起使用。
  • 在终端支付中,客户端秘密不应在未将其读取为过时的情况下返回。

继续阅读

继续阅读

系列中的下一个

系列中的下一个

同系列

Paylaş