What to build: 商户扫码核销时,不再因为刚好赶上核销码轮换而报「核销凭证已失效,请重新扫码」;相机不好用或屏幕反光时,可以手输顾客的 6 位数字码完成核销,且失败次数受限。
现状是 60 秒轮换、一生成新码就立刻作废旧码;同时接口下发一个 6 位数字码,但系统里**没有任何地方能输入它**(商户看到数字会尝试手输,而手输没有路)。判定见 ../spec.md 的 D21–D23。
Blocked by: None — can start immediately
Status: 已验收 —— 2026-09-17 用户人工验收通过(真机走查;本轮新增回归见 Comments)
rotatingTheCodeKeepsThePreviousOneUsableDuringTheOverlap(连发两张码后,旧码仍然是 status = 0 且能被扫到)。expires_at,anExpiredNumericCodeIsRefusedLikeAnyOtherInvalidCode 覆盖过期即失效。theSixDigitCodeCanBeTypedInAndConfirmedWithoutDisclosingWhichOrderItBelongsTo(核销记录只写一条,与扫码同一条实现)。consecutiveWrongCodesLockTheNumericEntryAndLockedMeansItIsNotEvenChecked(锁定后连正确的码也被挡在门外,且不产生扫码尝试)、aWrongCodeThatIsFollowedByTheRightOneClearsTheFailureCount。orderNo;撞码与猜错用同一句话回答(aWrongGuessAndACollidingCodeAnswerExactlyTheSameWay)。MerchantVerificationOrderCodeTests、MerchantVerificationServiceTests、OrderFundBoundaryTests)。displayCode 与 refreshAfterSeconds);商户端新增 pages/verification-code-input/ 与扫码页「手输 6 位核销码」入口,**HBuilderX 编译未跑**(本机没有 HBuilderX)。真机:把码刷到即将轮换的那一秒连扫两次,两次都成功;用上一轮的旧码扫,在重叠期内成功、重叠期后失败;在商户端手输 6 位码完成一次核销;连续输错到锁定,确认锁定期间不再校验且提示明确。
ready-for-human。落地要点:issueCode 不再作废旧码,有效期改为「刷新间隔 60 秒 + 重叠 30 秒」;新增 merchant_numeric_code_attempt(V150,create table if not exists,可直接用 scripts/apply-sql-file.ps1 落到开发库,已实跑)与 MerchantNumericCodeAttemptGuard(连续 5 次输错锁 5 分钟,计数走 REQUIRES_NEW 独立事务,否则判定失败抛出时会连计数一起回滚);6 位数字输入复用现有 POST /api/merchant/verification/scan,确认核销新增 POST /api/merchant/verification/numeric/confirm(服务端从扫码尝试反查订单,因此客户端不需要订单号)。