# 代理钱包扫码充值实现与接口对接说明 > 面向:产品、前端和联调人员 > 范围:代理微信/支付宝桌面扫码自充、平台线下代充来源区分、支付状态轮询 > 接口细节:以 [`docs/admin-openapi.yaml`](../../admin-openapi.yaml) 为准,本文说明前端调用顺序、字段口径和页面状态。 ## 一、先看这几个关键结论 1. **同一个创建接口有两条业务路径**:代理账号使用 `wechat|alipay` 在线自充;平台或超级管理员使用 `offline` 线下代充。 2. **前端必须使用 `recharge_source` 区分来源**:`platform_offline` 是平台线下代充,`agent_online` 是代理在线自充;不要根据账号名称、备注或审批字段猜测。 3. **代理不能选择充值店铺**:在线充值的店铺和主钱包由登录上下文确定,请求不得发送 `shop_id`、支付凭证或备注。 4. **二维码由前端渲染**:后端返回支付渠道原始 `qr_content`,不返回二维码图片。 5. **网络重试不能创建新请求 ID**:同一次提交重试复用原 `request_id`;用户主动发起下一笔充值时生成新的 `request_id`。 6. **支付和钱包到账是两个阶段**:`status=2` 表示第三方已收款、钱包入账处理中;只有 `status=3` 才表示钱包到账完成。 7. **页面轮询只调用本地状态接口**:前端不得直接调用微信、支付宝查单,也不要反复调用创建接口查询状态。 8. **在线金额单位为分**:最低 `10000` 分(100 元),最高 `100000000` 分(100 万元);不要提交浮点元金额。 ## 二、本次需求怎么实现 | 能力 | 后端实现 | 前端要做什么 | | --- | --- | --- | | 可用支付方式 | 根据当前有效支付配置返回真正可用的 `wechat`、`alipay` | 打开充值弹窗时先查询;只展示返回数组中的方式 | | 代理在线自充 | 从登录账号取得当前店铺和主钱包,创建充值单、支付单并向第三方预下单 | 只提交金额、支付方式和请求 ID;用 `qr_content` 渲染二维码 | | 平台线下代充 | 保留既有目标店铺、凭证和企业微信审批流程 | 平台页面继续提交 `offline` 请求并只读展示审批状态 | | 充值来源 | 根据受控创建方式返回稳定来源枚举 | 列表、详情和支付状态统一展示 `recharge_source_name` | | 支付确认 | 微信/支付宝回调确认第三方收款事实 | 前端无需调用回调接口 | | 钱包入账 | Outbox/Worker 幂等增加代理主钱包余额 | `status=2` 展示“入账处理中”,`status=3` 展示“已到账” | | 回调丢失恢复 | Worker 使用原支付单号主动查单并收敛状态 | 页面继续轮询本地状态,无需提供“重新查单”按钮 | ## 三、接口对接表 | 页面动作 | 接口 | 前端调用说明 | | --- | --- | --- | | 打开代理在线充值弹窗 | `GET /api/admin/agent-recharges/payment-methods` | 仅代理账号调用;返回支付方式和金额上下限 | | 创建在线扫码充值 | `POST /api/admin/agent-recharges` | 代理只传 `amount + payment_method + request_id` | | 创建平台线下代充 | `POST /api/admin/agent-recharges` | 平台/超级管理员传 `shop_id + amount + payment_method=offline + payment_voucher_key + remark` | | 查询充值列表 | `GET /api/admin/agent-recharges` | 支持 `recharge_source=platform_offline|agent_online` 筛选 | | 查询充值详情 | `GET /api/admin/agent-recharges/:id` | 使用返回的来源、充值状态和审批状态展示 | | 轮询在线支付状态 | `GET /api/admin/agent-recharges/:id/payment-status` | 仅在线单使用;页面可见时每 3 秒调用,终态停止 | | 支付回调 | 微信/支付宝既有回调地址 | 只由支付渠道调用,前端禁止调用 | 所有接口都使用 Bearer Token,成功响应统一为: ```json { "code": 0, "msg": "success", "data": {}, "timestamp": "2026-07-27T16:00:00+08:00" } ``` 前端应同时检查 HTTP 状态和响应体 `code`,失败时优先展示后端返回的 `msg`。 ## 四、代理在线扫码充值调用流程 ### 4.1 查询可用支付方式 ```http GET /api/admin/agent-recharges/payment-methods Authorization: Bearer {token} ``` 响应示例: ```json { "code": 0, "msg": "success", "data": { "methods": ["wechat", "alipay"], "min_amount": 10000, "max_amount": 100000000 }, "timestamp": "2026-07-27T16:00:00+08:00" } ``` 前端处理规则: - `methods=[]`:隐藏或禁用提交按钮,提示“当前暂无可用在线支付方式”。 - 只显示返回的支付方式,不自行补充微信或支付宝。 - 金额校验直接使用返回的 `min_amount`、`max_amount`,提交值仍为整数分。 - 平台、超级管理员和企业账号不调用此接口。 ### 4.2 创建在线充值单 ```http POST /api/admin/agent-recharges Authorization: Bearer {token} Content-Type: application/json ``` 微信请求示例: ```json { "amount": 10000, "payment_method": "wechat", "request_id": "recharge-20260727-8f73d95d" } ``` 支付宝只需将 `payment_method` 改为 `alipay`。 在线请求禁止发送: - `shop_id` - `payment_voucher_key` - `remark` - 商户号、应用 ID、密钥、支付配置 ID 成功响应示例: ```json { "code": 0, "msg": "success", "data": { "recharge_id": 101, "recharge_no": "ARCH20260727160000123456", "payment_no": "PAY20260727160000987654", "payment_method": "wechat", "recharge_source": "agent_online", "recharge_source_name": "代理在线自充", "amount": 10000, "qr_content": "weixin://wxpay/bizpayurl?pr=...", "status": 1, "status_name": "待支付" }, "timestamp": "2026-07-27T16:00:00+08:00" } ``` 前端处理规则: 1. 将 `qr_content` 原样交给二维码组件,不解析、不改写、不拼接。 2. 二维码页面保存 `recharge_id`,后续轮询使用该 ID,不使用 `payment_no` 查询。 3. 同一次请求超时或网络断开时,重试必须复用原 `request_id`;后端会返回原充值单和原二维码内容。 4. 用户关闭弹窗后重新点击“立即充值”,视为新一笔主动提交,必须生成新的 `request_id`。 5. 相同 `request_id` 改变金额或支付方式会返回冲突,前端不得自动覆盖原请求。 ### 4.3 轮询支付和到账状态 ```http GET /api/admin/agent-recharges/101/payment-status Authorization: Bearer {token} ``` 响应示例: ```json { "code": 0, "msg": "success", "data": { "recharge_id": 101, "recharge_no": "ARCH20260727160000123456", "recharge_source": "agent_online", "recharge_source_name": "代理在线自充", "status": 2, "status_name": "已支付", "payment_status": 1, "payment_status_name": "已支付", "paid_at": "2026-07-27 16:01:20", "completed_at": null }, "timestamp": "2026-07-27T16:01:21+08:00" } ``` 页面建议每 3 秒轮询一次,仅在二维码弹窗可见时执行。按以下状态展示: | `status` | 含义 | 页面展示 | 是否继续轮询 | | --- | --- | --- | --- | | `1` | 待支付 | 等待用户扫码付款 | 是 | | `2` | 已支付 | 支付成功,钱包入账处理中 | 是 | | `3` | 已完成 | 充值成功,钱包已到账 | 否 | | `4` | 已关闭 | 订单已关闭,请重新发起充值 | 否 | | `5` | 已退款 | 已退款 | 否 | | `6` | 已驳回 | 已驳回 | 否 | `payment_status` 的值为:`0=待支付`、`1=已支付`、`2=已失败`、`3=已退款`。页面主流程优先根据充值 `status` 判断;`payment_status` 用于补充支付阶段文案。 进入 `status=3` 后,前端刷新钱包余额和充值列表。不要在 `status=2` 时提前增加页面余额。 ## 五、平台线下代充调用流程 平台或超级管理员继续调用同一个创建接口: ```json { "shop_id": 20, "amount": 50000, "payment_method": "offline", "payment_voucher_key": ["recharge-voucher/20260727/abc.png"], "remark": "银行转账" } ``` 线下代充响应和后续列表/详情会返回: ```json { "recharge_source": "platform_offline", "recharge_source_name": "平台线下代充", "approval_provider": "wecom", "approval_status": 1, "approval_status_name": "审批中" } ``` 前端处理规则: - 代理账号不得展示线下代充表单。 - 平台线下单展示凭证、备注和企微审批状态,不展示扫码二维码。 - 存在 `approval_instance_id` 或 `approval_provider=wecom` 时,只读展示审批进度,不显示旧的本地确认/驳回按钮。 - 在线充值的 `approval_instance_id` 为 `null`,不要显示企微审批区域。 - `/:id/payment-status` 是在线支付状态接口,线下代充详情使用 `GET /:id`,不要轮询支付状态接口。 ## 六、列表和详情对接 列表请求示例: ```http GET /api/admin/agent-recharges?page=1&page_size=20&recharge_source=agent_online&status=1 ``` 可用筛选参数: | 参数 | 说明 | | --- | --- | | `page` | 页码,默认 1 | | `page_size` | 每页条数,默认 20,最大 100 | | `shop_id` | 店铺 ID,按既有数据权限生效 | | `status` | 充值状态 `1~6` | | `recharge_source` | `platform_offline` 或 `agent_online` | | `start_date` | 开始日期,格式 `YYYY-MM-DD` | | `end_date` | 结束日期,格式 `YYYY-MM-DD` | 列表成功响应的分页数据位于: ```json { "data": { "items": [], "total": 0, "page": 1, "size": 20 } } ``` 建议列表至少展示:充值单号、店铺、金额、`recharge_source_name`、支付方式、`status_name`、提交人和创建时间。 详情接口: ```http GET /api/admin/agent-recharges/{id} ``` 页面分支必须以 `recharge_source` 为准: - `agent_online`:展示支付方式、第三方流水号、支付时间、完成时间;隐藏凭证和企微审批操作。 - `platform_offline`:展示支付凭证、备注、提交人和企微审批状态;隐藏二维码和在线轮询区域。 ## 七、充值来源与状态字段 ### 7.1 充值来源 | 字段值 | 中文名 | 创建人和路径 | | --- | --- | --- | | `platform_offline` | 平台线下代充 | 平台/超级管理员提交 `payment_method=offline` | | `agent_online` | 代理在线自充 | 代理提交 `payment_method=wechat|alipay` | 前端展示中文时优先使用后端返回的 `recharge_source_name`,业务分支使用稳定枚举 `recharge_source`。 ### 7.2 充值状态 | 状态 | 名称 | 说明 | | --- | --- | --- | | `1` | 待支付 | 在线单等待扫码;线下单等待审批 | | `2` | 已支付 | 第三方收款已确认,钱包可能仍在入账 | | `3` | 已完成 | 钱包余额和唯一流水已提交 | | `4` | 已关闭 | 在线支付关闭或预下单失败 | | `5` | 已退款 | 充值已退款 | | `6` | 已驳回 | 线下代充审批驳回 | ## 八、权限和错误处理 | 操作 | 平台/超级管理员 | 代理账号 | 企业账号 | | --- | --- | --- | --- | | 创建在线扫码充值 | 禁止 | 仅当前店铺主钱包 | 禁止 | | 创建线下代充 | 允许 | 禁止 | 禁止 | | 查询在线支付方式 | 禁止 | 允许 | 禁止 | | 查询列表/详情 | 按既有数据范围 | 按店铺层级数据范围 | 禁止 | | 查询在线支付状态 | 按既有数据范围 | 按店铺层级数据范围 | 禁止 | 前端不要根据错误文案判断资源是否存在。无权限或资源不可见时,后端可能统一返回“无权限操作该资源或资源不存在”。常见处理: - `400`:保留表单输入并展示参数错误,不自动重试。 - `401`:按现有登录失效流程处理。 - `403`:关闭无权限操作入口,不尝试换账号类型绕过。 - `409` 或业务冲突码:检查是否错误复用了 `request_id`。 - `500`/超时:在线创建可使用原 `request_id` 重试一次;不要生成新 ID,否则可能创建第二张充值单。 ## 九、前端本期最容易漏掉的工作 - 在线充值表单不要复用线下表单对象后整体序列化,避免携带 `shop_id`、凭证或备注。 - 支付方式必须来自 `/payment-methods`,不要写死微信和支付宝都可用。 - 金额显示元、提交分;禁止使用浮点数直接乘除后提交。 - 二维码内容原样渲染,不记录到埋点、错误日志或浏览器持久化缓存。 - 同一次网络重试复用 `request_id`,新一笔主动充值换新 ID。 - `status=2` 只表示已收款、入账中,不能提前刷新成“钱包已到账”。 - 列表、详情的页面分支使用 `recharge_source`,不要用 `payment_method` 或审批字段猜来源。 - 线下单不调用支付状态接口;在线单不展示企微审批按钮。 - 页面隐藏或浏览器切到后台时停止轮询,恢复可见后再继续。 - 进入终态后停止轮询并刷新钱包余额、充值列表。 ## 十、联调和验收边界 - OpenAPI 已包含 `/payment-methods`、在线创建、列表来源筛选、详情来源字段和 `/payment-status`。 - 后端已完成微信 Native、支付宝 PreCreate、支付确认、异步钱包入账和回调丢失恢复的代码装配。 - 当前交付已通过 `go build ./...`、`go vet ./...` 和 OpenSpec 严格校验。 - 按需求方要求,本次未启动 API/Worker,未连接 PostgreSQL/Redis,未调用真实支付渠道,未发送真实回调,也未运行自动化测试。 - 前端联调环境需具备完整支付配置;至少确认微信和支付宝创建响应分别返回非空 `qr_content`。 - 本需求不支持 JSAPI、H5、WAP、同设备拉起支付、主动取消、支付方式切换、二维码图片接口或在线退款。