- 受控配置新增代理在线自充允许范围(仅微信/仅支付宝/同时支持),读侧与创建侧取允许范围与可用商户池交集,两侧失败关闭 - 新增允许范围查询与修改端点,读限代理与平台账号、写限超级管理员,复用受控配置写服务留痕 - tb_agent_recharge_record 新增交易流水号、线下收款方式三列快照与其他凭证列(成对迁移 000213) - 线下申请校验启用的收款方式字典项与必填交易流水号,交易流水号独立于在线渠道交易号、不参与去重 - 扩展 offline_recharge_approval 场景可映射字段白名单与字典引用保护 - 新增付款凭证识别能力与交易流水号预填接口,识别不落库、日志不记录载荷
5.2 KiB
Gateway 接入契约
元数据
- Owner:IoT Gateway 适配维护人
- 实现:
internal/gateway/ - 核验日期:2026-09-11
- 证据:
internal/gateway/client.go、crypto.go、card_status.go、flow_card.go、device.go、payment_voucher.go
当前实际使用范围
Gateway 是运营商流量卡、实名、停复机、限速和设备信息的统一封装入口。具体路径、请求字段和响应字段以同目录详细协议与 internal/gateway/*.go 的实际调用交集为准;文档中出现但代码未调用的接口不视为系统能力。
付款凭证识别(POST /ai/ocr/extract-payment,入参 image_base64)由 internal/gateway/payment_voucher.go 封装,当前唯一调用方是代理线下预存款申请的「交易流水号表单预填」。该能力只消费响应中的 order_number(作为交易流水号预填值);amount、remark、payment_method、payee、payment_time 不进入本系统响应、不预填、不落库,识别结果不是资金事实。核验证据:internal/gateway/payment_voucher.go 的类型定义只对外暴露支付单号,internal/application/agentrecharge/payment_voucher_ocr.go 只返回该字段,接口响应 DTO 仅含 external_transaction_no。
付款凭证识别刻意不走 doRequest / doRequestWithResponse:前者在 Info 级别打印加密前完整请求体、后者在 Info 级别打印完整原始响应,会把凭证图片内容与识别原始结果写进日志。该能力改用 Client.doRequestWithoutPayloadLog,仅记录路径、耗时与结果字节数摘要;既有能力的请求与日志语义保持不变(internal/gateway/client.go 中 executeWithRetry 的 logPayload 分支)。
付款凭证识别的已知限制
- 长号码可能不完整:对位数较多的转账单号,该接口可能只返回前若干位,实测存在识别值与凭证图片所示号码不一致的情况(位数少于凭证所示)。连续多次识别同一凭证所得长度与内容稳定,属上游侧确定性截断,而非本系统侧裁剪;预填值必须由提交人对照凭证人工核对,系统以人工确认值为准。
- 单号缺失即失败:响应未给出单号时,本系统按识别失败返回明确失败(
CodeGatewayInvalidResp,中文提示),不返回空值。 - 字段类型会漂移:响应
data中amount为 JSON 数值而非字符串。本系统只解码order_number,不声明其余字段,故不受类型漂移影响;新增消费字段前必须重新核对上游类型。 - 解析失败不回显原文:响应解码失败时只返回固定中文提示,不携带底层解析错误,避免第三方库的错误消息把识别原始结果带进日志与错误上下文。
- 单次识别只接受单个附件键,图片由后端读取对象存储后编码,Gateway 凭证不下发前端;识别结果不落库、不构成资金事实。
本条限制的核验方式(可复现、不依赖样本取值):对同一图片凭证连续三次调用该识别接口,比较三次返回值的位数与内容是否一致——一致说明是上游确定性行为而非随机抖动;再将该位数与凭证图片所示号码的位数(用等长掩码计数,只比位数)对照,得出是否缺位。判定责任方时看本系统的解码路径 internal/gateway/payment_voucher.go:它只对返回值做 strings.TrimSpace,无截断、无按长度裁剪、无正则截取,因此位数差异只能来自上游。识别结果不落库,复核该接口的返回值需重新发起识别调用,不能从业务表反查。
配置、认证与报文
配置键为 gateway.base_url、gateway.app_id、gateway.app_secret、gateway.timeout。业务参数先包装为 {"params": ...},使用 AppSecret 做 AES-128-ECB 加密;外层请求含 appId、data、sign、timestamp,签名使用 MD5。HTTP 方法统一为 POST,内容类型为 application/json;charset=utf-8。HTTP 200 且 Gateway code=200 才算成功,data 再按具体能力解码。
超时、重试与幂等
客户端默认超时 60 秒;生效配置可覆盖。默认最多重试 2 次,即最多 3 次尝试,退避为 100ms、200ms,更多重试时封顶 300ms。仅客户端超时、连接和 DNS 等网络级错误重试;用户 Context 取消、HTTP 非 200、响应解析失败和 Gateway 业务码失败不重试。每次尝试重新生成时间戳与签名。
查询天然只读;停复机、限速等写操作的幂等和状态条件由调用它的业务 Service 承担,Gateway 客户端本身不提供幂等键。
安全、错误与验证
AppSecret、加密前业务数据、完整身份标识不得写入本文或普通日志;当前客户端存在请求结构日志,运行环境必须依赖日志脱敏策略。Gateway 错误映射为项目 CodeGatewayError、CodeGatewayTimeout 或 CodeGatewayInvalidResp。
可复现静态证据:internal/gateway/client.go、internal/gateway/crypto.go 及同目录能力文件。真实写操作可能改变卡或设备状态,本次只允许静态核对和隔离账号只读验证,不调用生产写接口。
协议、路径、认证、成功码、超时或重试分类变化时更新本文。