结算累加与商米扫码支付设计
日期:2026-09-28
目标
在 SanquanConsumption 的结算页面补齐视频中展示的金额累加能力,并在已连接的商米 T1mini 上通过系统扫码组件采集支付宝付款码,调用现有 /api/consume/orderPay 完成真实支付。
范围
金额累加
- 数字键和小数点编辑当前金额项,每项最多六位整数、两位小数。
- 点击“+”后,将当前金额项加入已确认项并开始输入下一项。
- 输入区保留完整表达式,例如
1+3.6+2+0.91;合计区显示精确结果 7.51元。
- 金额使用
BigDecimal 计算,禁止使用 float 或 double 直接累计。
- 当前金额为空、只有小数点或数值为零时,点击“+”不生成无效项。
- 点击删除时删除表达式最后一个字符;删除到“+”时同时回到上一金额项继续编辑。
- 长按删除清空全部金额项和合计。
商米扫码
- 点击“扫码支付”前校验:设备配置完整、合计金额大于零、当前没有支付请求。
- 使用独立的商米扫码封装检测扫码组件,并兼容 T1mini 当前旧版组件的
com.summi.scan Action 与新版 com.sunmi.scanner.qrscanner Action。
- 扫码结果必须包含有效的 QR 类型和非空值;取消、空数据、结构异常或非二维码均不发起支付。
- 按源项目约定规范化扫码结果并去除可选的
C 前缀。
- 付款码属于敏感支付凭证,不在界面显示,不写入普通日志,不长期保存。
支付提交
- 继续使用现有
ApiClient.pay 和 /api/consume/orderPay 接口。
- 请求包含付款码、合计金额、设备编号、鉴权字段和一条自定义菜品明细:
uuid=custom_dish、number=1、money=合计金额。
- 补齐源项目既有的订单上下文字段:
order_type=1、action=consume、创建时间和由设备编号与本次时间组成的消息编号;MQTT 主题仅在当前设备配置存在时透传。
- 支付进行中锁定再次扫码和再次提交;页面销毁时取消仍在执行的支付请求。
- 支付成功后显示付款人、实付金额和余额,清空金额,返回就餐记录页并刷新当天订单。
- 支付失败、HTTP 异常或服务端拒绝时展示可理解的错误信息并保留金额,方便重新扫码;不伪造成功、不创建离线扣款记录。
组件职责
AmountInputController
只管理金额表达式、当前金额项、删除/清空规则和精确合计,不依赖 Android UI 或网络,便于单元测试。
SunmiScannerClient
只负责选择商米扫码 Action、构造 Intent 和解析 Activity 返回结果。它不保存付款码,也不发起支付。
PaymentRequestFactory
只负责根据设备配置、付款码和合计金额构造 ApiModels.PaymentRequest,集中维护 custom_dish、订单类型和消费动作等协议常量,避免请求字段散落在页面代码中。
MainActivity
负责页面状态编排:渲染金额、启动扫码、接收扫码结果、控制单次支付、展示结果、刷新订单及按生命周期取消请求。页面不实现扫码协议解析和金额运算细节。
数据流
- 用户点击左侧二维码进入结算页面。
- 用户输入金额;每次点击“+”确认当前金额项,控制器持续返回表达式和精确合计。
- 用户点击“扫码支付”,页面完成前置校验后启动商米扫码组件。
- 扫码组件返回二维码,
SunmiScannerClient 校验并规范化付款码。
PaymentRequestFactory 以付款码、合计金额和设备配置生成支付请求。
MainActivity 调用 ApiClient.pay,支付期间拒绝第二次提交。
- 成功时清空结算状态、返回记录页并重新加载订单;失败时保留表达式并允许重试。
状态与异常处理
- 未输入金额:提示先输入消费金额。
- 配置不完整:提示先完成设备设置,不启动扫码。
- 商米扫码组件不存在或无法启动:提示扫码组件不可用,不发起网络请求。
- 用户取消扫码:保持结算页面和金额,不显示支付失败。
- 扫码数据异常:提示付款码识别失败,不记录原始付款码。
- 支付进行中:扫码支付按钮不重复响应,并显示支付处理中状态。
- 服务端返回非零业务码:优先显示服务端
message,缺失时使用通用支付失败提示。
- 网络失败:显示网络支付失败提示,保留原金额。
- Activity 暂停或销毁:仅销毁时取消支付请求;从商米扫码页面返回时仍能正常处理结果。
安全边界
- Android 端不保存支付宝密钥,也不直接调用支付宝开放平台。
- 付款码只在一次扫码回调到一次支付请求之间短暂存在。
- 网络诊断日志继续只记录接口路径、HTTP 状态和耗时,不记录请求体、付款码或鉴权字段。
- 不在测试、演示或错误分支中构造虚假支付成功。
验证标准
单元测试
1 + 3.6 + 2 + 0.91 的表达式为 1+3.6+2+0.91,合计为 7.51。
- 小数精度不出现二进制浮点误差。
- 连续点击“+”、零金额和空金额不会产生无效项。
- 删除可跨金额项回退,长按清空恢复
0。
- 单项仍遵守六位整数、两位小数限制。
- 支付请求包含正确的总金额、自定义菜品明细和协议字段。
- 商米扫码返回解析覆盖成功、取消、空数据、非二维码和错误结构。
构建检查
./gradlew :app:testDebugUnitTest :app:assembleDebug :app:lintDebug --offline 通过。
T1mini 实机检查
- 点击左侧二维码进入结算页,金额累加行为与录屏一致。
- 点击“扫码支付”能拉起 T1mini 商米扫码界面,取消后回到原金额页面。
- 有效支付宝付款码只触发一次
/api/consume/orderPay 请求。
- 成功后金额清空、回到记录页并刷新订单;失败后原金额保留。
真实扣款成功依赖有效服务端配置、可用的支付宝付款码和联调环境;在缺少这些条件时,只能验证扫码拉起、请求构造及错误处理,不能宣称支付闭环已通过。
明确不包含
- 外接 USB 键盘式扫码枪适配。
- 引入第三方相机扫码 SDK。
- 刷卡、刷脸和离线支付。
- 后端支付宝签名、查询、撤销或异步通知逻辑。
- 与本次结算和扫码支付无关的页面重构。