# 票机对接规划 > 状态:**规划存档,暂缓实施**(2026-09-09 评估复杂度后决定推迟) > 产出方式:grill-with-docs 设计访谈;领域术语见仓库根目录 `CONTEXT.md` > 重启本项目时:先回答[第 6 节 待定决策](#6-待定决策重启时先答),再动工 > > **更新 2026-09-09**:原 M2(出口自助支付)已拆出并演化为独立设计——**LPR + 扫码支付出场**,见 `doc/扫码支付出场设计.md`(车辆识别由"扫小票"改为"车牌识别",订单/渠道/查单/放行联动设计以该文档为准)。本规划仅保留 M1(入场小票闭环)及后续票机增强。 ## 1. 背景与目标 票机是**支付的一种入口**(对应系统现有支付入口分类 `ticket`)。目标流程: ``` 入口:车主触发按钮盒 → 票机打印小票(含 ticket_no 二维码)→ 开闸入场 出口:车主在自助终端扫码小票 → 系统生成订单 + 订单二维码(动态、带金额) → 车主手机扫码支付(微信支付)→ 回调确认到账 → 自动开闸出场 ``` 范围选定为 **C:两条链路都做**—— - **A. 入场小票闭环**:触发端 + 打印机管理前端 + 失败处理(后端打印链路已有 80%) - **B. 出口自助支付**:扫码小票 → 动态订单二维码 → 支付 → 自动出场(全新开发,本期核心) ## 2. 现状盘点(代码事实,2026-09-09) | 模块 | 现状 | |------|------| | `internal/modules/printer/` | 已对接真实票机:CSN USB DLL(`CsnPrinterLibs.dll`)+ 串口 ESC/POS(EMD807 79.5mm)双通道;票面含票号二维码;`POST /ticket-machine/button` 已写好但**全仓库无调用方**(触发端缺失) | | 前端 | **无任何打印机管理/补打 UI**;incident 字典已预留 `print_failed`/`reprint` 事件类型 | | 数字票 `digital-ticket` | 状态机 `created→pending_payment→paid→exited`,票号即二维码内容;票机出的票就是数字票的物理载体 | | 支付 `payment` | 支付成功**无任何打印/出场钩子**;`ticket_qr` 支付方式目前展示**静态收款码 + 人工确认**,动态订单二维码不存在 | | 中央缴费机 `central-payment` | 独立设备(POF 协议、自带打印、`/api/paysuccess` 回调),与出口自助终端是**两类设备,勿混淆** | | 相关设计文档 | `docs/superpowers/specs/2026-07-30-ticket-printer-design.md`、`plans/2026-07-30-ticket-printer-plan.md`、`plans/2026-07-31-usb-ticket-photo-layout-plan.md` | ## 3. 已定决策 | # | 决策点 | 结论 | |---|--------|------| | 1 | 范围 | A + B 都要,入口闭环先行 | | 2 | 票机硬件 | CSN / EMD807 79.5mm 热敏机,已在现场;**串口与 USB 都要支持**(沿用现有双通道) | | 3 | 运营形态 | 按**无人岗亭**设计,失败处理与自助兜底是一等公民 | | 4 | 入口触发 | 按钮盒;**v1 用模拟触发**,不接实物 | | 5 | 出口硬件 | 立式自助终端(参考照片:集成二维码扫描器、IC 读卡区、HELP 对讲按钮、可选出票口);出口车道另有 LPR 摄像机 + LED 屏 | | 6 | 支付渠道 | **微信支付先行**,先打通测试环节;渠道侧保留抽象以便后续接 KHQR/银行(对应 ADR 提案 0001,未定稿) | | 7 | 缴费凭证 | 第一版**不打**纸质凭证,出口不装打印机;数字票页面为唯一凭证 | | 8 | 入口打印失败 | 自动重试 2 次 → 仍失败**照常开闸** + 记 `print_failed` 事件 + 出口按车牌兜底 | ## 4. 核心流程设计(目标态) **入场**:按钮盒触发 → `POST /ticket-machine/button` → 校验通道/绑定设备 → `HandlePassage` 建会话+数字票 → 打印小票(重试策略见决策 8)→ MQTT 开闸。 **出口**:车主把小票对准自助终端扫码器 → 终端(后端服务页面)按 ticket_no 解析数字票 → 计费 → **创建订单**(锁定金额)→ 生成订单二维码(微信 Native `code_url`)渲染上屏 → 车主扫码支付 → 微信回调(幂等)→ 订单已支付、数字票转 paid → 自动开闸 → 屏显成功;超时未支付回到初始屏。 ## 5. 领域术语 见 `CONTEXT.md`(票机、小票、数字票、扫码、订单、订单二维码、支付渠道、支付入口、自助终端、按钮盒、无人岗亭)。**后续讨论与代码命名以此为准。** ## 6. 待定决策(重启时先答) | # | 决策点 | 选项与推荐 | |---|--------|-----------| | 1 | 自助终端内部形态 | (a) 标准外设(USB HID 扫描器 + HDMI 屏)自装浏览器 kiosk ✅推荐;(b) 厂商 SDK/协议对接。需确认:设备是否已选型、内部系统、出票口是否为打印机、"Tap the card" IC 读卡 v1 不做是否成立 | | 2 | 微信商户资料 | 直连/服务商?`mchid`、APIv3 密钥/证书、AppID 有无?收款币种(计费 USD/KHR vs 微信 CNY 的跨境问题)?沙箱 or 小额实付 | | 3 | 道闸控制权 | (a) 归我们系统(MQTT 道闸底座)✅推荐;(b) 厂商终端驱动、我们调它的接口 | | 4 | 丢票兜底范围 | 推荐 v1 做"按车牌找回会话"(出口已有 LPR 硬件);HELP 对讲 v1 只留物理按键不接系统 | | 5 | 模拟触发落点 | 推荐调试接口 + 前端"模拟取票"按钮都要;真实按钮盒通信方式(以太网直连 POST / 边缘盒子 IO)待选型 | | 6 | ADR-0001 | 订单实体 + 支付渠道接口 + 微信先行。提案状态:**待批准**,批准后写入 `docs/adr/0001-payment-channel-abstraction.md` | ## 7. 分期实施建议 - **M1 入场小票闭环**(工作量小,收益立现):模拟触发入口、打印机管理页(状态/测试页/补打)、打印失败重试 + incident 落库、票机-通道绑定展示。 - **M2 出口自助支付**(本期核心):自助终端 kiosk 页面、订单模型与状态机(pending→paid→expired,幂等)、微信 Native 下单 + 回调 + 对账、回调自动开闸、车牌找回兜底、超时回初始屏。 - **M3 增强**:真实按钮盒接入、HELP 对讲、缴费凭证打印(硬件已预留出票口)、IC 卡读卡、多支付渠道(KHQR/银行直连)。 ## 8. 风险与依赖 1. **微信跨境收款**:境外停车场用微信收款需跨境商户资质,币种与汇率结算规则要先和渠道方确认——这是 M2 的最大外部依赖。 2. **自助终端选型**:若采购整机带厂商封闭系统,软件主动权旁落;选型时坚持标准外设(USB HID + HDMI)。 3. **回调可靠性**:支付回调可能丢失/迟到 → 必须幂等 + 主动查单兜底 + 对账;"到账即开闸"的体验依赖回调时延。 4. **金额锁定**:订单创建时刻锁定费用,与"缴费后免费离场时限"的交互要定义清楚(超时后补差 or 重新计费)。 5. **无人值守故障面**:缺纸/离线的远程可观测性(仪表盘状态 + incident)在 M1 就要有,不能等 M3。 ## 9. 相关文档 - `CONTEXT.md` —— 领域术语表 - `docs/superpowers/specs/2026-07-30-ticket-printer-design.md` —— 票机打印原始设计 - `docs/superpowers/plans/2026-07-31-usb-ticket-photo-layout-plan.md` —— USB 票面排版 - `doc/中央缴费机对接.md` —— POF 协议(另一类设备) - `doc/设备接入层设计.md`、`doc/收费流程.md`、`doc/进出场流程详解.md`