手册
付款对账工人施工 (Building A Payment Reconciliation Worker)
扫地机如何改善漂移:虽然PSP成功,但本地注册可能会过期;如何恢复老化的 FinalizePending。
分布式支付引擎
部分 14 的 22
一系列分布式支付架构弥合了捕获和完成之间的差距。
在上一节中,我们了解了如何通过租赁机制安全地拥有企业。但即使是最稳健的租约也无法改变一个事实:有时,工作人员在收到 PSP 的明确响应之前超时,流程崩溃,或者租约到期并且工作被标记为“过期”——而此时 PSP 方面的付款实际上已成功。
这就是调节工作者存在的理由:一个定期扫描并纠正本地系统和 PSP 自己的记录之间偏差的清理器。```text Local kayıt: Payment #123 → Expired PSP kaydı: Payment #123 → Succeeded │ ▼ Mutabakat worker sürüklenmeyi tespit eder │ ▼ Local kayıt → Captured olarak düzeltilir
## 第一次提到的概念```text
📦 Reconciliation (Mutabakat)
İki bağımsız kaynağın (local sistem ve PSP) kayıtlarını karşılaştırıp farkı düzeltme süreci.
📦 Drift (Sürüklenme)
Local durumun PSP'nin gerçek kaydından farklılaşması; genellikle bir hata veya zaman aşımı sonucu.
📦 Sweeper
Belirli bir kritere uyan (örn. 'aged' veya 'expired') kayıtları periyodik olarak tarayan arka plan işi.
📦 FinalizePending
Ödemenin PSP tarafında sonuçlanmış olabileceği ama local sistemde henüz kesin bir duruma geçmediği ara statü.
```和解并不是实时修复;这是一个安全网。主流程(webhook、同步响应)大部分时间都能正常工作;共识清除了“大多数时候”被排除在外的少数人。
## 漂移的真正来源
漂移很少是随机的;通常来自特定的、重复的场景:worker 向 PSP 发送请求,在响应到达之前进程崩溃;由于网络错误,响应始终未到达,但 PSP 端交易已完成;或者租用期限设置得短于 PSP 的响应时间,并且作业被提前标记为“过期”。```text
Senaryo 1: Worker çöktü
Request gönderildi → PSP işledi → Worker cevabı hiç okuyamadı
Senaryo 2: Ağ hatası
Request gönderildi → PSP işledi → yanıt ağda kayboldu
Senaryo 3: Lease erken doldu
Request gönderildi → PSP yavaş yanıt verdi → lease expired → job stuck watcher tarafından sıfırlandı → ama PSP zaten başarılı olmuştu
```这三种情况的共同点是,**本地注册表**仍处于不确定或错误状态,而**PSP 自己的注册表**已经知道真实结果。
## Sweeper的查询:扫描哪些记录
调节工作人员不会不断地将每条记录与 PSP 进行比较——这是昂贵且不必要的。它仅针对“可疑”记录:已经超过一定年龄(老化)但仍处于中间状态(`FinalizePending`、`Expired`、`Processing` 很长一段时间)的记录。```sql
SELECT id, provider_ref FROM payments
WHERE status IN ('FinalizePending', 'Expired')
AND updated_at < now() - interval '10 minutes';
```“10 分钟”的门槛不是任意的;它来自 SLA,规定正常流程需要多长时间才能产生结果。低于此阈值的记录尚不“可疑”,它们可能只是速度较慢。
## 查询PSP并做出决策
对于每个候选记录,工作人员会检查 PSP 的状态查询 API(如果可用)或其自己的存档 Webhook 历史记录。可能出现三种结果:```text
PSP: Succeeded → local kaydı Captured'a taşı, semantik event yayınla
PSP: Failed → local kaydı Failed'a taşı
PSP: Not Found / Unknown → local kaydı gerçekten sonuçsuz say, telafi akışına yönlendir
```这里的关键点是,这种转换也必须是幂等的:即使协调工作程序两次处理相同的记录,结果也不应该改变(例如,如果记录已经是 `Captured`,则它不应该再次发出相同的事件)。
## 警报:扫地机不应静默运行
共识工作者发现的每个偏差都应该产生一个可观察性信号。如果漂移数量突然增加,这通常表明主流程(webhook 处理、租赁期限、网络)中存在问题 - 协调应该使该问题可见,而不是隐藏它。
|公制|它说什么?
| --- | --- |
|扫描的候选人注册数量 |主流的运行有多“干净”?
|修正的漂移数量 |实际数据差异量 |
|尚未解决的记录数量 |需要人工审核的队列 |
## 经常混淆的区别```text
❌ Mutabakat gerçek zamanlı bir düzeltmedir
✓ Mutabakat periyodik bir güvenlik ağıdır, ana akışın yerini almaz
❌ Sürüklenme sayısı sıfır olmalı, aksi halde sistem bozuk
✓ Düşük ve stabil bir sürüklenme oranı normaldir; artan oran bir sinyaldir
❌ Her kayıt PSP ile karşılaştırılmalı
✓ Sadece 'aged' ve ara statüdeki kayıtlar hedeflenmeli
```## 与主流共识的作用
|尺寸|主流(webhook/同步)|调解员 |
| --- | --- | --- |
|速度|秒|分钟-小时 |
|范围 |每次付款 |仅限可疑/陈旧记录 |
|目的|正常方式 |安全网|
## 安装调节工作程序时的清单
1. 扫描器查询是根据业务SLA确定“老化”阈值还是随机数?
2. 查询PSP是否符合其速率限制和重试策略?
3. 漂移校正是幂等的——同一条记录处理两次结果不会改变吗?
4. 无法修复的记录是否会落入可见队列(在 PSP 上也找不到)?
5. 漂移数量是否作为指标进行监控并针对突然增加生成警报?
## 本文中您应该记住的内容
1、共识是主流的补充,而不是替代;主流大部分时间都工作正常,清扫器清理剩下的少数。
2. Sweeper 的目标是符合年龄和状态标准的可疑记录,而不是每条记录。
3. PSP 查询后更正必须是幂等的并遵守其重试规则。
4、漂移数是可观测性信号;预计将悄然逼近零,突然增加是一个警告。
> 共识工作者展示了系统的诚实程度,而不是它的完美程度。
在下一节中,我们将考虑这种漂移最烦人的形式:客户已在 PSP 端付费,但系统中没有订单 - 我们如何安全地解决这个问题?
FAQ
Frequently asked questions
什么是和解?
比较两个独立来源(本地系统和 PSP)的录音并纠正差异的过程。
什么是漂移?
本地状态与PSP实际录制的状态不同;通常是由于错误或超时造成的。
难道真的是“对账即实时修正”吗?
共识是一个周期性的安全网,它不会取代主流
本节修复了什么?
这就是调节工作者存在的理由:一个定期扫描并纠正本地系统和 PSP 自己的记录之间偏差的清理器。共识是主流的补充,而不是替代;主流大部分时间都工作正常,清扫器清理剩下的少数。在上一节中,我们了解了如何通过租赁机制安全地拥有企业。但即使是最稳健的租约也无法改变一个事实:有时,工作人员在收到 PSP 的明确响应之前超时,流程崩溃,或者租约到期并且工作被标记为“过期”——而此时 PSP 方面的付款实际上已成功。
学到的工程原理
- 共识是对主流的补充,而不是替代。
- 清理程序的目标是陈旧和可疑的记录,而不是每条记录。
- 漂移数是一个信号;如果它悄无声息地接近零,突然增加就需要发出警告。
继续阅读
继续阅读
系列中的下一个
已付款但无订单:改进
事件响应指南:客户已付费但未下订单;多方故意篮子杂乱;仔细清理重复数据。
系列中的下一个
数据库支持租赁作品
带条件更新的租赁,可以挽救陷入困境的工作的观察者,以及为什么仅仅取消消息不足以支付生产费用。
同系列
为什么最终一致性优于分布式事务
在PSP、订单和财务之间设置2PC是一个陷阱。 Saga 和和解是分布式支付一致性的真正答案。