状态:设计定稿,待实施(2026-09-09 经 grill-with-docs 访谈收口) 前置条件:① 微信支付商户资质(mchid/APIv3 密钥/证书,小额真实交易测试);② 出口屏硬件采购(现场现状:无任何屏幕) 术语:见根目录
CONTEXT.md;架构决策:docs/adr/0001、docs/adr/0002
车辆通过车牌识别进出场;出口无人值守,非放行车辆一律走"识别 → 计费 → 屏显 → 支付 → 开闸"。车主在出口屏上用微信/支付宝扫码支付(车主扫系统屏上的订单二维码),到账后系统自动开闸。
本期做:出口自助支付全链路(订单、渠道抽象、微信 Native、查单确认、放行联动、出口屏 kiosk 页面)。
本期不做:票机/小票链路(继续搁置,见 doc/票机对接规划.md)、支付宝实现(渠道接口预留)、语音播报、对讲接入、纸质缴费凭证、IC 卡。
| 能力 | 现状 |
|---|---|
| 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) |
车到出口 → 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 轮询,量大再升级推送)。
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 结果定格;过期后重新计费。internal/modules/payment/channel/)CreateOrder(order) (codeURL, err)、QueryOrder(order) (status, err)、HandleNotify(payload)(挂点,暂不启用——ADR-0002)。code_url 渲染成订单二维码)。接口必须以查单为一等能力。paid + payment_record 落库(entry=exit_kiosk、method=wechat,方式按渠道拆——ADR-0001)→ ExitConfirm 结算 → OpenGateWithContext 开闸,审计来源标记支付放行(区别于 manual_raise)。gate_failed,屏显"支付成功,请联系管理员放行";已支付状态保留,人工补开闸不重复收费(免费窗口内)。exit_kiosk(出口自助),启用方式 wechat(支付宝后续)。| 场景 | 处置 |
|---|---|
| 无入场记录 | 关闸 + 屏显提示 + no_entry_exit 落库(已有) |
| 黑名单车 | 关闸 + 屏显"请联系管理员" + 落事件,绝不自动放行 |
| 扫码后不支付/不扫 | 屏幕保持费用页 + 倒计时提示;订单过期自动刷新重新计费;闸保持关闭;落滞留记录 |
| 查单超时/渠道不可用 | 屏显"网络异常,请稍候",订单保持 pending,恢复后继续;超 5 分钟过期重生成 |
| 重复识别(车挪动再触发 LPR) | 命中同一会话复用未过期订单,不重复建单 |
| 开闸指令失败 | gate_failed + 屏显"请联系管理员";已支付保留,人工补开不重复收费 |
| 放行车辆 | 直接开闸,不生成订单 |
| # | 决策 | 结论 |
|---|---|---|
| 1 | 交互形态 | 屏显动态订单二维码,车主扫码支付(现场无屏,需新装) |
| 2 | 运营形态 | 无人自助 |
| 3 | 支付渠道 | 渠道抽象 + 微信先行(ADR-0001),测试用小额真实交易 |
| 4 | 到账确认 | 查单为主(页面轮询 + 定时任务),回调暂不建设(ADR-0002,系统部署在内网主机) |
| 5 | 放行时序 | 到账即自动结算 + 开闸,不依赖相机二次识别;回调幂等 |
| 6 | 订单模型 | 金额锁定 + TTL 5 分钟过期刷新 + 同会话复用未过期订单 |
| 7 | 支付方式建模 | 入口 exit_kiosk + 方式按渠道拆(wechat/alipay) |
| 8 | 兜底范围 | 屏幕感知车辆用轮询;无入场记录/黑名单关闸落事件;滞留不自动放行 |
| 9 | 范围边界 | 只做 LPR + 扫码支付出场;票机 M1 继续搁置 |
currency 字段与金额换算规则,实施前确认。exit_kiosk 入口种子;用调试页面验证全链路。CONTEXT.md —— 术语表(车牌识别、扫码支付、订单、查单、放行车辆等)docs/adr/0001-payment-channel-abstraction.md、docs/adr/0002-query-first-payment-confirmation.mddoc/票机对接规划.md —— 本设计的姊妹规划(入口小票链路,暂缓)doc/中央缴费机对接.md、doc/收费流程.md、doc/设备接入层设计.md