# 停车场管理系统对接中央缴费机技术文档 ------ ## 1. 概述 本文档定义了停车场管理系统(SmartParking)与中央缴费机(如 pof-parking-payment-machine)之间的对接规范,涵盖通信架构、接口定义、业务流程及错误码说明。目标是使功能开发者能够依据本文档独立完成缴费机与停车管理系统的集成开发。 ------ ## 2. 通信架构 ### 2.1 网络通信方式 中央缴费机采用 **TCP/IP** 网络通信方式接入系统 [4],即缴费机与后端服务器通过局域网或互联网建立网络连接。 > ⚠️ **重要**:对接 **不是** 通过串口串联方式,**不是** MQTT 长连接,而是基于 HTTP/HTTPS 的 RESTful API 请求-响应模式。每次业务操作均为独立的 HTTP 请求 [2][7][16]。 ### 2.2 协议选型 实际对接中存在两种协议形式,取决于缴费机型号与系统版本: | 协议 | 传输层 | 数据格式 | 适用场景 | | ----------------------------- | ------ | -------- | ---------------------------------------------------------- | | **SmartParking 标准集成协议** | HTTPS | JSON | 外部客户平台(通用缴费机)与 SmartParking 系统集成 [2][17] | | **POF 缴费机专用协议** | HTTP | JSON | 睿泊科技 POF 缴费机与停车系统后端对接 [7] | 下文分别详述两套协议的接口定义。 ------ ## 3. SmartParking 标准集成协议(HTTPS RESTful API) 该协议用于外部客户平台(如中央缴费机)与 SmartParking 系统的标准化集成 [2][16][17]。 ### 3.1 用户认证 **接口**: `POST /sp/login` **用途**: 获取 `accessToken`,用于后续所有接口调用的权限验证 [2] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------- | ------ | ---- | ------------------------- | | userName | String | 是 | 用户名 | | passWord | String | 是 | 32位大写 MD5 加密后的密码 | **请求示例**: ```json { "userName": "paystation001", "passWord": "E10ADC3949BA59ABBE56E057F20F883E" } ``` **成功返回**: ```json { "accessToken": "xxxxxxxxxxxx", "sessionId": "yyyyyyyyyyyy", "expireInSeconds": 1800 } ``` **字段说明**: - `accessToken`: 访问令牌,**有效期 30 分钟**,后续接口调用时需使用此值作为会话凭证 [2] - `sessionId`: 会话 ID - `expireInSeconds`: 令牌有效时长(秒) ------ ### 3.2 按车牌查询费用 **接口**: `POST /sp/charge/payCost` **用途**: 实时计算当前车辆的停车费用,供缴费机完成预支付 [2][17] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------------- | ------ | ---- | ------------------------- | | sessionId | String | 是 | 登录返回的 accessToken | | carNum | String | 是 | 车牌号 | | parkingLotCode | String | 是 | 停车场 ID(默认 0001)[2] | **成功返回**: ```json { "carAccessCurId": "车辆业务ID", "freeOutTime": 15, "inParkTime": 1714000000, "receivableMoney": 12.00, "payedMoney": 0.00, "favorableAmount": 0.00, "ticketNum": "TICKET001" } ``` **字段说明**: | 字段 | 类型 | 说明 | | --------------- | ------- | --------------------------------------------------- | | carAccessCurId | String | 车辆业务 ID,车辆入场后生成,一次进出场业务唯一 [2] | | freeOutTime | Int | 支付后免费出场分钟数 [2] | | inParkTime | Long | 入场时间戳 [2] | | receivableMoney | Decimal | 应收金额(消费金额)[2] | | payedMoney | Decimal | 已付金额 [2] | | favorableAmount | Decimal | 优惠金额 [2] | | ticketNum | String | 票据号 [2] | ------ ### 3.3 支付成功通知 **接口**: `POST /sp/charge/notify` **用途**: 客户平台将支付账单插入 SmartParking,通知支付成功并触发放行 [2][17] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------------- | ------- | ---- | ------------------------------------------------------------ | | sessionId | String | 是 | 登录返回的 accessToken | | carAccessCurId | String | 是 | 车辆业务 ID(查询费用时返回)[2] | | carNum | String | 是 | 车牌号或 UUID(无牌车)[2] | | consumeMoney | Decimal | 是 | 消费金额 [2] | | consumeTimeStr | String | 是 | 支付时间 [2] | | feeAmount | Decimal | 是 | 实际支付金额 [2] | | feeType | Int | 是 | 支付方式类型:1-现金,9-支付宝,10-微信支付,16-微信公众号支付 [2] | | orderStatus | String | 是 | 订单状态 [2] | | parkingLotCode | String | 是 | 停车场 ID [2] | | serialNumber | String | 是 | 订单支付流水号(**需保证唯一**)[2] | | ticketNum | String | 否 | 票据号 [2] | **说明**: [2] - 支付成功后 SmartParking 自动查询并放行(未配置出口确认规则时) - 若 `serialNumber` 重复,返回错误码 `0021`(流水号已存在) ------ ### 3.4 全局错误码 | 错误码 | 说明 | | ------ | --------------------------------- | | 0000 | 成功 | | 0001 | 失败(具体错误见 errMsg 字段)[2] | | 0004 | 登录密码错误 [2] | | 0006 | 认证失败,请重新登录 [2] | | 0007 | 请求参数不存在 [2] | | 0017 | 支付金额不足 [2] | | 0021 | 流水号已存在(重复通知)[2] | ------ ## 4. POF 缴费机专用协议(HTTP POST JSON) 该协议定义 POF 停车缴费机与停车系统后端之间的完整接口规范 [7]。 ### 4.1 通用说明 - **接口类型**: HTTP POST - **编码**: UTF-8 - **数据格式**: JSON - **调用失败统一返回**: `{"Result":0, "Data":{}, "ReMark":"失败原因"}` [7] - **成功返回**: `{"Result":1, ...}` - **认证机制**: 除登录接口外,所有业务接口均需携带 `Token` 参数 [1] ### 4.2 操作员登录 **接口**: `POST /api/login` **用途**: 操作员登录,获取 Token,用于后续接口鉴权 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------- | ------ | ---- | -------- | | UserName | String | 是 | 用户名 | | Password | String | 是 | 登录密码,当前必须传明文 UTF-8 字符串;不支持 MD5 或 bcrypt 哈希 | **请求示例**: ```json {"UserName":"admin","Password":"admin"} ``` **成功返回**: ```json {"Result":1,"Token":"123456"} ``` ------ ### 4.3 查询车辆费用 **接口**: `POST /api/queryprice` **用途**: 按车牌查询车辆停车费用 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------------- | ------ | ---- | ---------------------------- | | Token | String | 是 | 认证令牌 | | Data.CarNo | String | 是 | 查询车牌 | | Data.QueryTime | String | 是 | 请求时间 yyyy-MM-dd HH:mm:ss | **成功返回**: ```json { "Result": 1, "Data": { "OrderId": "123456", "CarNo": "B88888", "InTime": "2023-04-25 12:00:01", "OutTime": "2023-04-26 09:03:23", "StopTime": "1小时20分钟", "Price": "12", "CardNo": "00001", "Remark": "收费标准0001", "InPic": "" } } ``` **字段说明**: [7] | 字段 | 说明 | | -------- | --------------------------------------- | | OrderId | 订单号(后续所有操作的核心关联标识)[1] | | CarNo | 车牌号 | | InTime | 入场时间 | | OutTime | 出场时间 | | StopTime | 停放时长 | | Price | 应收金额 | | CardNo | 卡号(月租/会员卡) | | Remark | 计费规则说明 | | InPic | 入场图片 | ------ ### 4.4 支付成功通知 **接口**: `POST /api/paysuccess` **用途**: 缴费机完成支付后通知后端 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | ------------ | ------ | ---- | ------------------------------------------ | | Token | String | 是 | 认证令牌 | | Data.OrderId | String | 是 | 订单号 | | Data.CarNo | String | 是 | 车牌 | | Data.Money | String | 是 | 缴费金额 | | Data.PayTime | String | 是 | 支付时间 yyyy-MM-dd HH:mm:ss | | Data.PayType | num | 是 | 支付方式:**0-现金,1-刷卡** [1][7] | | Data.PayDev | num | 是 | 支付设备:**1-岗亭,2-POF,3-平板** [1][7] | | Data.DevId | String | 是 | 设备标识(如 "PoF-01")[1] | | Data.ReMark | String | 是 | 备注 | ------ ### 4.5 心跳检测 **接口**: `POST /api/heartbeat` **用途**: 心跳检测,返回服务器时间 [1] **请求参数**: Token(认证令牌) **返回**: 服务器系统时间。 ------ ### 4.6 推送优惠券 **接口**: `POST /api/ticket` **用途**: 向订单推送优惠券 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | --------------- | ------ | ---- | -------------------------------------------------- | | Token | String | 是 | 认证令牌 | | Data.OrderId | String | 是 | 订单号 | | Data.CarNo | String | 是 | 车牌 | | Data.TicketNo | String | 是 | 优惠券编号 | | Data.TicketType | num | 是 | 优惠券类型:**0-减免金额,1-减免时长,2-全免** [1] | | Data.Money | String | 是 | 减免金额 | | Data.Minutes | num | 是 | 减免时间(分钟) | | Data.OperTime | String | 是 | 操作时间 | | Data.ShopName | String | 是 | 商户名称或卡号 | | Data.ReMark | String | 是 | 备注 | ------ ### 4.7 投币明细上报 **接口**: `POST /api/paylist` **用途**: 上报缴费机投币/找零明细 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | ----------------- | -------- | ---- | ---------------------- | | Token | String | 是 | 认证令牌 | | Data.OrderId | String | 是 | 订单号 | | Data.VoucherNo | String | 是 | 凭证编号 | | Data.DevIp | String | 是 | 设备 IP | | Data.EventDate | Datetime | 是 | 发生时间 | | Data.InOutType | num | 是 | **0-接收,1-找零** [7] | | Data.CoinCashType | num | 是 | **0-硬币,1-纸币** [7] | | Data.MValue | String | 是 | 面值 | | Data.Num | Int | 是 | 数量 | ------ ### 4.8 找零失败记录 **接口**: `POST /api/payfail` **用途**: 记录现金支付找零失败事件 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------------- | -------- | ---- | ------------------------------------------------- | | Token | String | 是 | 认证令牌 | | Data.OrderId | String | 是 | 订单号 | | Data.CarNo | String | 是 | 凭证编号(字段名与备注可能不符,需与后端确认)[7] | | Data.Intime | String | 是 | 入场时间 | | Data.Change1 | decimal | 是 | 应收金额 | | Data.Change2 | decimal | 是 | 实收金额 | | Data.Change3 | decimal | 是 | 应找零金额 | | Data.Change4 | decimal | 是 | 实际找零金额 | | Data.EventTime | Datetime | 是 | 找零时间 | ------ ### 4.9 设备状态上报 **接口**: `POST /api/devstatus` **用途**: 上报缴费机设备运行状态 [7] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | ------------------------- | ------ | ---- | ---------------------------------------------- | | Token | String | 是 | 认证令牌 | | Data.Printer | string | 是 | **0-余量充足,1-余量不足,2-无可用打印纸** [7] | | Data.Temperature | string | 是 | 设备温度(摄氏度) | | Data.Payment.CurrencyInfo | array | 是 | 货币类型/面值/数量 | | Data.Payment.Online | bool | 是 | POS 在线状态 | ------ ### 4.10 交班登录 **接口**: `POST /api/pof-login` **用途**: 操作员交班身份验证 [7][19] **请求参数**: 与操作员登录接口相同(UserName + Password),返回的 Token 用于交班场景 [7]。 ------ ### 4.11 查询收费总额 **接口**: `POST /api/pof-querymoney` **用途**: 查询指定设备在指定时间范围内的收费总额,用于交班对账 [7][19] **请求参数**: | 参数 | 类型 | 必填 | 说明 | | -------------- | ------ | ---- | ----------------------- | | Token | String | 是 | 认证令牌 | | Data.DevId | String | 是 | 设备标识(如 "PoF-01") | | Data.QueryTime | String | 是 | 请求时间 | **成功返回**: ```json {"Result":1,"Data":{"TotalMoney":"12","Remark":"收费标准0001"}} ``` ------ ### 4.12 待完善接口 | 接口 | 现状 | 说明 | | -------------------- | ---------- | ------------------------------------------------------- | | `/api/paystatus` | 已实现 | 按 `OrderId` 查询数字票和支付状态;具体兼容字段见 4.13 | | `/api/checkpassword` | 参数已知 | 密码验证(`Data.Password`),但具体用途待确认 [7] | ### 4.13 支付状态查询(第二阶段已实现) **接口**: `POST /api/paystatus` **用途**: 支付通知超时、网络异常或缴费机重启后,查询订单在管理端的最终支付状态。该接口只读数据库,不会重复扣款或新增支付记录。 **请求示例**: ```json {"Token":"登录返回的Token","Data":{"OrderId":"数字票号","CarNo":"湘A12345","DevId":"设备标识"}} ``` `OrderId` 必填;`CarNo` 和 `DevId` 用于日志及一致性核对,可选。系统仍以 Token 绑定的设备身份认证,并校验设备所属停车场权限。 **成功返回**: ```json {"Result":1,"Data":{"OrderId":"...","CarNo":"湘A12345","State":"paid","PayStatus":"paid","PaymentStatus":"paid","Fee":"12.00","Money":"12.00","PaidAmount":"12.00","PayTime":"2026-09-04 12:00:00"},"ReMark":"success"} ``` `PayStatus` 为 `unpaid` 或 `paid`;`State` 还可能为 `pending_payment`、`exited` 等数字票生命周期状态。订单不存在、车牌不匹配、设备无权访问或 Token 无效时返回 `Result=0` 及失败原因。 > ⚠️ 上述接口在实际对接时需与后端开发确认完整定义。 [1][7] ------ ## 5. 对接业务流程 ### 5.1 正常支付流程 缴费机从查询费用到完成支付的完整流程如下 [9]: ``` ┌─────────┐ ┌──────────────┐ ┌─────────┐ │ 缴费机 │ │ 停车系统后端 │ │ 用户 │ └────┬────┘ └──────┬───────┘ └────┬────┘ │ 1. 用户输入车牌 │ │ │──────────────────→│ │ │ 2. /api/login │ │ │──────────────────→│ 返回 Token │ │←──────────────────│ │ │ 3. /api/queryprice│ │ │ (或 /sp/charge/ │ │ │ payCost) │ │ │──────────────────→│ 返回车辆信息+费用 │ │←──────────────────│ │ │ 4. 用户支付 │ │ │ │ │←─── 现金/刷卡/扫码 │ 5. /api/paysuccess│ │ │ (或 /sp/charge/ │ │ │ notify) │ │ │──────────────────→│ 记录支付+放行 │ │←──────────────────│ 返回成功 │ │ 6. 打印小票 │ │ ``` **流程步骤说明**: [9] 1. 用户在缴费机输入车牌号 2. 缴费机调用登录接口获取 Token(若 Token 未过期可复用) 3. 调用查询费用接口获取订单信息(订单号、入场时间、应收金额等) 4. 用户完成支付(现金、刷卡或扫码) 5. 缴费机调用支付成功通知接口,携带订单号、金额、支付方式、设备标识等 6. 后端确认支付成功后标记订单已缴费;车辆到出口后由既有出口确认流程校验缴费状态并放行 7. 缴费机打印小票(可选步骤,调用优惠券接口进行减免)[9] ### 5.2 优惠券抵扣流程(可选) 在支付完成前或完成后,可调用优惠券接口(`/api/ticket`)应用减免 [9]。 ### 5.3 找零失败处理 现金支付产生找零时,若找零失败,通过 `/api/payfail` 上报记录,便于后续人工处理 [9]。 ### 5.4 交班对账流程 ``` ┌─────────┐ ┌──────────────┐ │ 缴费机 │ │ 停车系统后端 │ └────┬────┘ └──────┬───────┘ │ 1. /api/pof-login│ │──────────────────→│ 交班登录验证 │←──────────────────│ 返回 Token │ 2. /api/pof-querymoney│ │──────────────────→│ 查询收费总额 │←──────────────────│ 返回 TotalMoney │ 3. 核对金额,完成交班│ ``` **流程说明**: [19] 1. 接班操作员调用交班登录接口进行身份验证,获取交班会话 Token 2. 查询当前设备累计收费总额(携带 DevId) 3. 双方核对金额完成交接 4. 系统记录交班日志 ------ ## 6. 关键字段速查表 | 字段 | 所属协议 | 说明 | | ----------------------- | -------- | -------------------------------------------------- | | Token / accessToken | 两套协议 | 认证令牌,接口鉴权凭证 [2][7] | | OrderId | POF 协议 | 订单号,核心关联标识 [1] | | carAccessCurId | 标准协议 | 车辆业务 ID [2] | | CarNo / carNum | 两套协议 | 车牌号(无牌车用 UUID)[2][7] | | Price / receivableMoney | 两套协议 | 应收金额 [2][7] | | Money / feeAmount | 两套协议 | 实付金额 [2][7] | | PayType | POF 协议 | 支付方式:0-现金,1-刷卡 [1] | | feeType | 标准协议 | 支付方式:1-现金,9-支付宝,10-微信,16-公众号 [2] | | PayDev | POF 协议 | 支付设备:1-岗亭,2-POF,3-平板 [1] | | DevId | POF 协议 | 设备标识(如 "PoF-01")[1] | | serialNumber | 标准协议 | 支付流水号(唯一)[2] | | TicketType | POF 协议 | 优惠券类型:0-减金额,1-减时长,2-全免 [1] | ------ ## 7. 异常与边界处理建议 ### 7.1 支付金额不足 标准协议中,若实际支付金额低于应收金额,系统返回错误码 `0017`(支付金额不足)[2]。测试用例显示系统允许部分支付(生成支付记录但不视为完全支付)[6],缴费机应根据业务需求决定是否接受部分支付。 ### 7.2 重复通知 若支付通知的流水号已存在,返回错误码 `0021`。缴费机收到该错误码时应视为已通知成功,避免重复处理 [2]。 ### 7.3 Token 过期 标准协议中 accessToken 有效期 30 分钟 [2]。收到 `0006`(认证失败)时应重新调用登录接口获取新 Token。 ### 7.4 网络异常 - 缴费机侧应实现请求超时重试机制 - 所有接口为 HTTP 请求-响应模式,无长连接维护 [2][7] - 对于离线场景(如断网),建议增加本地缓存和补传机制(PoF 刷卡支付场景尤其重要) ### 7.5 待确认事项 实际对接时需与后端确认以下问题: [1][7] 1. `/api/devstatus` 的完整请求/响应定义缺失;`/api/paystatus` 已按本项目兼容格式实现 2. `/api/paylist` 的 `OrderId` 参数逻辑(投币明细通常与设备会话相关,而非订单) 3. `/api/payfail` 的 `CarNo` 参数备注为"凭证编号",是否为笔误 4. `/api/checkpassword` 的具体业务用途 ------ ## 8. 开发建议 1. **优先确定协议版本**:确认缴费机型号和所对接的后端系统版本,选择对应的协议(标准集成协议或 POF 专用协议) 2. **模块化封装**:按 微型三层架构 将缴费机对接封装为独立模块,便于维护和扩展 3. **幂等处理**:支付通知接口必须实现幂等,处理重复调用场景 [2] 4. **日志记录**:完整记录请求/响应日志,便于排查对账问题 5. **测试覆盖**:参照 [智慧停车系统 — 测试用例报告](wikilink:智慧停车系统 — 测试用例报告) 中部分支付、重复入场、无收费配置兜底等场景设计测试用例 [6] ------ ## 9. 本项目 POF 实现约定与模拟桩联调 ### 9.1 已实现范围 本项目已按 POF 专用协议实现以下公开 HTTP POST 接口: | 接口 | 实现状态 | 约定 | | --- | --- | --- | | `/api/login` | 已实现 | 使用中央缴费机设备账号登录,返回 30 分钟有效的设备 Token | | `/api/queryprice` | 已实现 | 按当前在场会话实时计费,返回数字票号作为 `OrderId` | | `/api/paysuccess` | 已实现 | 写入支付记录、更新数字票和在场车辆为已支付;不会主动开闸或关闭停车会话 | | `/api/heartbeat` | 已实现 | 返回服务器时间 | 接口统一挂载在后端服务的公开 `/api` 路径,并在每个业务接口内校验 POF 设备 Token;不会使用后台 Web 用户的 JWT,也不会使用 `admin/123456`。 ### 9.2 订单、支付和出口规则 1. `OrderId` 固定映射为 `digital_ticket.ticket_no`,是本项目 POF 支付的唯一订单标识。 2. `/api/queryprice` 只允许查询仍在场、数字票状态为 `pending_payment` 且未支付的车辆。费用以服务端当前时间实时计算。 3. `/api/paysuccess` 必须携带 `PayDev=2`。`DevId` 作为协议和审计字段记录,但不参与阻断式认证;系统以 Token 绑定的设备身份为准。`PayType=0` 映射 `cash`(展示名“现金支付”),`PayType=1` 映射 `pos`;若管理员停用了 POS 支付方式,刷卡通知会被拒绝,需先在支付方式配置中启用。 4. 支付通知前会重新实时算费;`Money` 与当前应收金额不一致时,返回“金额已变化,请重新查询”。 5. 缴费成功只标记订单和停车会话已支付。车辆在出口由既有出口流程确认离场和放行,以保留“缴费后免费离场时限”的统一校验。 6. 厂商协议没有唯一支付流水号。当前按 `OrderId + 支付方式 + 应收金额` 实现订单级幂等:相同通知重试成功但不会产生第二条收费记录;数据不一致的重复通知失败。所有请求(Token 已脱敏)写入 `central_payment_request_log` 供联调与对账。 ### 9.3 查询时间兼容 厂商资料的 `/api/queryprice` 参数表使用 `Data.QueryTime`,示例却使用 `Data.InTime`。当前服务同时接收这两个字段,但不将其作为入场时间或计费时间,均以系统已记录的入场时间和服务器当前时间为准。 ### 9.4 模拟桩 启动应用一次后,系统会创建仅用于本地联调的模拟设备: | 项目 | 值 | | --- | --- | | 设备 ID | `POF-SIM-01` | | 用户名 | `admin` | | 密码 | `admin` | | 支付入口 | `central_payment_machine` | > 安全提示:上述凭据仅限本机或隔离测试网。真机安装前必须禁用或删除该模拟设备,并为每台真机创建独立设备 ID、账号和密码。 先在系统中准备一条未支付的在场车辆记录,再从 `lc_garage` 目录运行: ```powershell go run ./cmd/pof-simulator -base-url http://127.0.0.1:8888 -plate 粤A12345 ``` 模拟桩会依次调用 `login -> queryprice -> paysuccess -> heartbeat`,并打印每个接口原始 JSON 响应。仅验证登录、查询和心跳时使用: ```powershell go run ./cmd/pof-simulator -base-url http://127.0.0.1:8888 -plate 粤A12345 -no-pay ``` 生产或局域网部署时,缴费机访问的是后端监听地址,例如 `http://<服务器IP>:8888/api/login`;不是桌面前端 WebView 内的代理地址。实际端口以 `config.yaml` 的 `system.addr` 为准。 ### 9.5 联调日志与登录密码格式 中央缴费机请求进入管理端后,后端运行日志会依次输出“收到请求”和“处理成功/失败”两类记录。登录请求包含以下不敏感联调字段: - `source_ip`:缴费机来源 IP; - `username`:提交的设备账号; - `password_format`:密码格式识别结果; - `password_length`:密码字符长度。 系统不会记录登录密码明文、密码哈希或设备 Token。`Password` 当前必须按 JSON 字符串传输明文,例如 `"Password":"admin"`;日志中的 `plain-text_expected` 表示格式符合当前协议。出现 `md5-32-hex_unsupported` 或 `bcrypt-hash_unsupported` 时,说明缴费机传入了摘要/哈希值,当前服务会按密码错误处理,设备应改为发送明文密码。 处理结果同时写入 SQLite 的 `central_payment_request_log` 表,包含 `endpoint`、`source_ip`、`device_id`、`result`、`remark` 和已脱敏的 `request_body`,可用于重启后的联调追溯。 应用使用默认配置时,运行日志按日期写入 `log\YYYY-MM-DD\info.log`(收到请求、处理成功)和 `log\YYYY-MM-DD\warn.log`(解析或鉴权失败)。实时观察登录联调日志可执行: ```powershell Get-Content (Join-Path .\log (Join-Path (Get-Date -Format 'yyyy-MM-dd') 'info.log')) -Wait | Select-String '中央缴费机' ``` 若登录失败,再查看同日期的 `warn.log`。通过项目管理脚本以 `split` 模式启动时,标准输出还会同步写入 `.run\backend.stdout.log`。 代码库还提供不依赖真实数据库或硬件的 HTTP 联调回归测试。它以内存 SQLite 建立一条在场订单,并按模拟桩顺序验证 `login -> queryprice -> paysuccess -> paystatus -> 重试 paysuccess -> heartbeat`,确保状态查询可确认支付结果且重复支付通知不会产生第二笔支付记录: ```powershell $env:GOCACHE = (Resolve-Path '.gocache').Path go test ./internal/modules/central-payment/... -run TestPOFSimulatorHTTPFlow -count=1 ``` ### 9.5 厂商待确认项 以下内容未在当前实现范围内,需在真机到位前向厂商确认后再扩展: 1. `/api/checkpassword` 仅给出 `Data.Password`,没有业务语义,当前未实现。 2. `/api/ticket` 的优惠券时机、抵扣规则与本系统收费规则尚未定义,当前未实现。 3. `/api/paylist` 投币/找零明细的 `VoucherNo` 唯一性、补传重试和对账口径未定义,当前未实现。 4. `/api/payfail` 的 `CarNo` 字段说明写为“凭证编号”,字段语义冲突,当前未实现。 5. `/api/devstatus` 中 `Payment.CurrencyInfo` 的数据结构和设备故障处置规则未定义,当前未实现。 6. POF 支付成功通知没有唯一外部流水号。建议厂商在 `Data` 中补充稳定且唯一的 `VoucherNo` 或 `SerialNo`,以支持跨订单、跨重试窗口的精确幂等和财务对账。 7. 厂商样例中存在中文引号、缺少逗号等非合法 JSON 的写法;设备必须发送合法 UTF-8 JSON,服务端不会对非 JSON 文本进行猜测性修复。 8. 厂商资料明确列出 `/api/pof-login` 和 `/api/pof-querymoney`,但没有定义交班时段/班次边界,也没有说明 `QueryTime` 是查询时点还是统计区间。当前未实现,待现场交班和对账口径确认后扩展。 ------ ## 10. 参考资料 | 编号 | 资料 | | ---- | ------------------------------------- | | [1] | 停车系统缴费接口说明补全polan(4) | | [2] | 自助缴费机集成网络协议(英文版 2025) | | [4] | POF 停车缴费机硬件规格 | | [6] | 智慧停车系统 — 测试用例报告 | | [7] | 缴费机 API 接口文档 | | [9] | 缴费机支付流程 | | [16] | 自助缴费机集成协议 | | [17] | SmartParking 集成协议 | | [19] | 交班对账机制 |