# 扫码支付出场设计 > 状态:**设计定稿,待实施**(2026-09-09 经 grill-with-docs 访谈收口) > 前置条件:① 微信支付商户资质(mchid/APIv3 密钥/证书,小额真实交易测试);② 出口屏硬件采购(现场现状:无任何屏幕) > 术语:见根目录 `CONTEXT.md`;架构决策:`docs/adr/0001`、`docs/adr/0002` ## 1. 背景与范围 车辆通过**车牌识别**进出场;出口无人值守,非放行车辆一律走"识别 → 计费 → 屏显 → 支付 → 开闸"。车主在出口屏上用微信/支付宝**扫码支付**(车主扫系统屏上的订单二维码),到账后系统自动开闸。 **本期做**:出口自助支付全链路(订单、渠道抽象、微信 Native、查单确认、放行联动、出口屏 kiosk 页面)。 **本期不做**:票机/小票链路(继续搁置,见 `doc/票机对接规划.md`)、支付宝实现(渠道接口预留)、语音播报、对讲接入、纸质缴费凭证、IC 卡。 ## 2. 现状盘点(代码事实) | 能力 | 现状 | |------|------| | LPR 出场识别 | 已自动化:`CameraEvent`(MQTT)→ `HandlePassage.handleExit` → `ExitConfirm` 自动匹配会话;**已支付票自动核销并自动开闸**;临时车有费用时拒绝放行(需岗亭处理)——这是要被本功能替换的人工环节 | | 放行车辆 | 月卡/白名单/RFID 免密放行已有(`trigger="rfid"`、`free` 方式) | | 缴费后免费离场 | `FeeConfig.FreeExitMinutes`(默认 15 分钟),`paid_at` 起算;超时补差逻辑在 `buildExitPreview`/`ExitConfirm`。**本功能"即时支付"路径天然不触发超时**,该机制继续保护"别处付费开到出口"的老路径 | | 在线支付 | **不存在**。`ticket_qr` 只是打印票号的预留(InputMode=external,默认停用),无任何支付网关代码 | | 支付成功联动 | 中央缴费机 `/api/paysuccess` 回调刻意**不开闸**(靠车开到出口再被识别);本功能车已停在出口,必须回调/查单后主动结算+开闸——与先例的关键分叉 | | 出口屏/播报 | 全仓无任何屏幕、语音集成;`no_entry_exit`、`gate_failed` 异常落库已有 | | 道闸 | MQTT 道闸 + 模拟道闸双通道(`GateController`),人工兜底 `POST /parking/gate/open`(审计 `manual_raise`) | ## 3. 端到端流程 ``` 车到出口 → LPR 识别(channel.Direction=out)→ HandlePassage: ├─ 放行车辆 → 直接开闸(现状逻辑,不进支付) ├─ 无入场记录 → 落 no_entry_exit + 屏显"无入场记录,请联系管理员",保持关闸 ├─ 黑名单 → 落事件 + 屏显"请联系管理员",保持关闸 └─ 临停车(有费用)→ 事件拒绝放行(现状),出口屏感知后进入支付流程: 屏显费用 + 创建订单(锁定金额,TTL 5 分钟)→ 渲染订单二维码(微信 Native code_url) → 车主扫码支付 → 查单确认到账(主要手段,见 ADR-0002) → 订单 paid、payment_record 落库(entry=exit_kiosk, method=wechat) → ExitConfirm 结算出场(paid_at=now,免费窗口自此起算) → 开闸(来源=支付放行)→ 屏显"支付成功,一路平安" 超时未支付 → 订单过期 → 屏幕重新计费刷新(复用会话,不重复建单) ``` 出口屏感知"车来了":kiosk 页面每 3 秒轮询本通道最近出场识别 + 费用预览(v1 轮询,量大再升级推送)。 ## 4. 组件设计 ### 4.1 订单模块(新增 `internal/modules/order/`) - 实体:`order{order_no, ticket_no/session_id, plate, amount, currency, channel, channel_txn_id, state, expire_at, event_log}` - 状态机:`pending → paid`(查单确认)/ `pending → expired`(TTL 5 分钟),CAS 原子转移,风格对齐 `digital_ticket` 的 `Transition`。 - 幂等:同一会话存在未过期 `pending` 订单则复用;支付成功只结算、开闸一次。 - 金额锁定:订单创建时按 `ExitPreview` 结果定格;过期后重新计费。 ### 4.2 支付渠道抽象(新增 `internal/modules/payment/channel/`) - 接口:`CreateOrder(order) (codeURL, err)`、`QueryOrder(order) (status, err)`、`HandleNotify(payload)`(挂点,暂不启用——ADR-0002)。 - 实现 v1:微信 Native(`code_url` 渲染成订单二维码)。接口必须以查单为一等能力。 ### 4.3 到账确认与放行联动 - 页面轮询(2s)驱动即时查单;服务端定时任务(30s)对未关闭订单兜底查单。 - 确认到账后单事务:订单转 `paid` + `payment_record` 落库(entry=`exit_kiosk`、method=`wechat`,方式按渠道拆——ADR-0001)→ `ExitConfirm` 结算 → `OpenGateWithContext` 开闸,审计来源标记**支付放行**(区别于 `manual_raise`)。 - 开闸失败:落 `gate_failed`,屏显"支付成功,请联系管理员放行";已支付状态保留,人工补开闸不重复收费(免费窗口内)。 ### 4.4 出口屏 kiosk 页面(前端新增) - 大字自助页面:等待识别 → 费用 + 订单二维码 → 轮询订单状态 → 成功/失败/过期提示。 - 复用现有 Vue 技术栈与鉴权(页面持设备 token);硬件为新增工控屏/平板,跑浏览器全屏。 - 新增支付入口配置 `exit_kiosk`(出口自助),启用方式 `wechat`(支付宝后续)。 ## 5. 异常与边界处置 | 场景 | 处置 | |------|------| | 无入场记录 | 关闸 + 屏显提示 + `no_entry_exit` 落库(已有) | | 黑名单车 | 关闸 + 屏显"请联系管理员" + 落事件,绝不自动放行 | | 扫码后不支付/不扫 | 屏幕保持费用页 + 倒计时提示;订单过期自动刷新重新计费;闸保持关闭;落滞留记录 | | 查单超时/渠道不可用 | 屏显"网络异常,请稍候",订单保持 pending,恢复后继续;超 5 分钟过期重生成 | | 重复识别(车挪动再触发 LPR) | 命中同一会话复用未过期订单,不重复建单 | | 开闸指令失败 | `gate_failed` + 屏显"请联系管理员";已支付保留,人工补开不重复收费 | | 放行车辆 | 直接开闸,不生成订单 | ## 6. 已定决策汇总(访谈结论) | # | 决策 | 结论 | |---|------|------| | 1 | 交互形态 | 屏显动态订单二维码,车主扫码支付(现场无屏,**需新装**) | | 2 | 运营形态 | 无人自助 | | 3 | 支付渠道 | 渠道抽象 + 微信先行(ADR-0001),测试用小额真实交易 | | 4 | 到账确认 | **查单为主**(页面轮询 + 定时任务),回调暂不建设(ADR-0002,系统部署在内网主机) | | 5 | 放行时序 | 到账即自动结算 + 开闸,不依赖相机二次识别;回调幂等 | | 6 | 订单模型 | 金额锁定 + TTL 5 分钟过期刷新 + 同会话复用未过期订单 | | 7 | 支付方式建模 | 入口 `exit_kiosk` + 方式按渠道拆(`wechat`/`alipay`) | | 8 | 兜底范围 | 屏幕感知车辆用轮询;无入场记录/黑名单关闸落事件;滞留不自动放行 | | 9 | 范围边界 | 只做 LPR + 扫码支付出场;票机 M1 继续搁置 | ## 7. 外部前置条件(实施前必须落实) 1. **微信商户资质**:mchid、APIv3 密钥/证书、绑定 AppID——**尚缺,为第一关键路径**(渠道接口可先按 mock 实现,联调前必须到位)。 2. **币种确认**:系统计费币种与微信收款币种(CNY)的关系、跨境资质——影响订单的 `currency` 字段与金额换算规则,实施前确认。 3. **出口屏硬件**:工控屏或 HDMI 平板(跑浏览器 kiosk),采购与安装。 ## 8. 实施分期 - **M1 后端核心链路**:订单模块 + 渠道抽象 + 微信实现(mock 可切换)+ 查单任务 + 放行联动 + `exit_kiosk` 入口种子;用调试页面验证全链路。 - **M2 出口屏上线**:kiosk 页面 + 现场部署联调(真屏、真闸、真相机)。 - **M3 增强**:服务端对账报表、支付宝渠道、语音播报、订单看板。 ## 9. 相关文档 - `CONTEXT.md` —— 术语表(车牌识别、扫码支付、订单、查单、放行车辆等) - `docs/adr/0001-payment-channel-abstraction.md`、`docs/adr/0002-query-first-payment-confirmation.md` - `doc/票机对接规划.md` —— 本设计的姊妹规划(入口小票链路,暂缓) - `doc/中央缴费机对接.md`、`doc/收费流程.md`、`doc/设备接入层设计.md`