yeg
2026-09-17 e3f412c5474311a471345ea508db4875c9ec648e
refs
用户 yeg <1347747024@qq.com>
星期四, 九月 17, 2026 15:47 +0800
提交者 yeg <1347747024@qq.com>
星期四, 九月 17, 2026 15:47 +0800
提交e3f412c5474311a471345ea508db4875c9ec648e
目录 4082f1d0f6635217c0d900df3e0aeee96ad7fae8 目录 | zip | gz
parent 89d1addecbf2f8e5446a6236a4b1ec57d26e0bc2 查看 | 对比
feat(payment): 支付按支付尝试收敛,取消与放弃以关单回执为准

用户报的死锁根因是「支付结果不确定」被当成了整笔订单的停止符:本地明明还持有那张
预支付单的支付参数,服务端却先查通道、拿到「处理中」就抛错,于是订单既付不了也退不出。
判定见 docs/adr/0003。

服务端:
- 支付发起改为**复用优先**:本地有未过期预支付单的支付参数、订单仍待支付时直接交回客户端,
不查通道;新建预支付单的前置不变量改为「上一张已被证明不可能再被支付」(blockingAttempt),
CLOSING / CANCEL_UNCONFIRMED 期间只允许复用。
- 取消与放弃先关单、后用关单回执下结论:已关闭→可取消;原交易已成功→再查一次并落成已支付、
拒绝取消;处理中→支付尝试置新状态 CLOSING(可等待、可重试);其余拒绝→记 CANCEL_UNCONFIRMED
并继续盯防。查单答「处理中」时关单接口现在仍会被调用(旧实现一次都不会调)。
- 关单重试每分钟一次、最多 3 次(close_attempts),到上限落成已取消、保留盯防与问题记录。
- cancel 在未定局时返回 cancellationPending 而不是报错;新增 POST /payment/abandon 只触发关单、
绝不结算;到期与批量两条路径都不再自动取消带 CLOSING / CANCEL_UNCONFIRMED 的订单;超时后
支付接口拒绝补付。
- 对账取行改为按 last_reconciled_at 公平轮转 + 退避,CLOSING / CANCEL_UNCONFIRMED 走 1 分钟快车道,
取代「最早 100 条」的饥饿写法。

迁移 V149:payment_transaction 加 last_reconciled_at(+索引)与 close_attempts;开发库 Flyway 关闭,
另附 scripts/dev-schema-v149-payment-attempt.sql 与通用执行器 scripts/apply-sql-file.ps1,
已在开发库(MySQL 5.7.44)实跑并验证。

人才端:一次点击即支付(建单后立刻发起预支付并拉起收银台,放弃/失败才落到待支付页)、
待支付页三态渲染(支付处理中/取消处理中/已关闭)、退避轮询到约 45 秒 + 手动「刷新支付状态」、
收银台放弃时调「放弃支付」信号。

顺带修一处被测出的缺陷:问题记录的唯一键含 status,同一笔业务被反复「结掉又开出」时,
结掉那一步会撞上已存在的 resolved 行,已改为先清掉被取代的结论行再落新结论。
5个文件已添加
12个文件已修改
1256 ■■■■■ 已修改文件
rencaika/pages/payment-confirm/payment-confirm.uvue 79 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
rencaika/pages/pending-payment/pending-payment.uvue 144 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
rencaika/services/portal.uts 8 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/ApplySqlFile.java 117 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/apply-sql-file.ps1 80 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/dev-schema-v149-payment-attempt.sql 20 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/HuifuPaymentService.java 87 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/PaymentIssueRecorder.java 18 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/PaymentReconciliationService.java 214 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/PaymentSettlementService.java 5 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PendingOrderAbandonRequest.java 12 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PendingOrderPortalController.java 32 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PendingOrderPortalService.java 33 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PendingUsePortalService.java 22 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/db/migration/V149__payment_attempt_convergence.sql 40 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/PaymentSettlementTests.java 2 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/PendingPaymentConfirmationTests.java 343 ●●●●● 对比 | 查看 | 原始文档 | blame | 历史