feat(verification): 核销码轮换放宽与手输 6 位数字码核销
商户扫码刚好赶上 60 秒轮换就报「核销凭证已失效」,而页面上那个 6 位数字码在系统里
没有任何地方能输入——这两件事一起把商户卡在现场(判定见 spec 的 D21–D23)。
- 轮换放宽:issueCode 不再一生成新码就立刻作废旧码,有效期改为「刷新间隔 60 秒 + 重叠 30 秒」,
扫到翻转瞬间不再失败;重叠期之外仍按 expires_at 自动失效。
- 新增按 6 位数字码核销:复用 POST /verification/scan(纯 6 位数字走数字码分支),确认核销新增
POST /verification/numeric/confirm —— 服务端从扫码尝试反查订单,因为数字码的扫码答复刻意
不返回订单号(6 位只有 100 万空间,返回归属等于给枚举一张映射表;撞码与猜错用同一句话回答)。
- 限次与锁定:新表 merchant_numeric_code_attempt(V150)按「商户 + 门店 + 操作员」记连续输错,
5 次锁 5 分钟,锁定期间连校验都不做;计数写在 REQUIRES_NEW 独立事务里,否则判定失败抛出时
会连计数一起回滚,限次就是一句空话。
- 商户端新增 pages/verification-code-input/ 页面与扫码页「手输 6 位核销码」入口(cover-view 入口 +
独立页输入,因为 cover-view 里放不下 input)。
回归:MerchantNumericCodeVerificationTests(手输核销成功、不泄露归属、撞码与猜错同一答复、
连续输错锁定且锁定期间不校验、输对清零、过期失效、轮换后新旧码都可用)。