让代理充值复用现有网页支付能力
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m7s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m7s
微信按当前 v2/v3 配置分别生成 MWEB/H5 链接,支付宝复用 C 端 WAP 链接,并让可用支付方式基于生效配置判断。 Constraint: 支付链接统一通过 qr_content 返回,由前端渲染二维码;按要求不运行测试 Rejected: 微信 Native 与支付宝当面付 | 会引入非当前商户配置所需的额外产品开通 Confidence: high Scope-risk: moderate Directive: 微信 H5/MWEB 二维码仅承诺系统相机或外部浏览器扫码链路 Tested: 相关 Go 包编译通过;gofmt 与 git diff --check 通过 Not-tested: 按用户要求未运行自动化测试及真实支付联调
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
|新增02:企业微信审批接入|企微配置、模板映射、扫码绑定、审批运行和业务详情|Token、模板版本、上传提交、回调解密、轮询补偿和异常恢复|5~7小时|8~10小时|企微可信域名、模板 ID 变化、回调网络和真实账号权限会增加 4~8 小时|
|
||||
|新增03:站内通知|顶部铃铛、通知抽屉、通知中心和受控跳转|通知表、模板注册、接收人解析、未读/已读 API|3~4小时|3~4小时|前端多端布局差异、接收人关系不完整会增加 2~3 小时|
|
||||
|新增04:全局多视角审计|审计中心七个视角、详情抽屉和敏感字段展示|Audit Event、Integration Log、旧写入口切换、历史投影和脱敏|6~8小时|8~11小时|旧审计调用点遗漏、查询性能和历史字段差异会增加 5~10 小时|
|
||||
|新增05:代理钱包扫码充值|支付方式选择、二维码、倒计时和支付状态轮询|微信 Native、支付宝 PreCreate、支付单分发和钱包入账恢复|3~4小时|5~7小时|支付渠道配置、真实回调、微信 v2/富友差异会增加 3~6 小时|
|
||||
|新增05:代理钱包扫码充值|支付方式选择、二维码、倒计时和支付状态轮询|微信 v3 H5/v2 MWEB、支付宝 WAP 支付 URL、支付单分发和钱包入账恢复|3~4小时|5~7小时|支付渠道配置、真实回调、富友差异会增加 3~6 小时|
|
||||
|全链路联调与发布|联调全部页面状态、修复交互、准备发布版本|数据核对、存量回填、旧入口清理、停机发布和恢复检查|3~4小时|4~5小时|生产数据与预期差异、外部回调不可达会增加 4~8 小时|
|
||||
|**合计**|**前端约 59~89 小时**|**后端约 93~128 小时**|**约 8~12 人日**|**约 12~16 人日**|**1 后端 + 1 前端并行时,正常目标 12~15 个工作日;历史数据或换货迁移超预期时可能到 16 个工作日**|
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
| D-16 | 数据同步保留轮询兜底,关键业务事件按立即、3 分钟、5 分钟触发;超频不建立退避状态 | 数据同步 |
|
||||
| D-17 | 企微发起身份按账号类型分流:平台/超级管理员必须扫码绑定并使用本人 `userid`,代理使用部署配置中的固定企微账号代提交;真实业务提交人始终独立进入审批表单、通知和审计 | 企微审批 |
|
||||
| D-18 | 全系统审计本次一次性切换到 Audit Event + Integration Log,多视角 API 和前端同时发布 | 全局 |
|
||||
| D-19 | 代理在线充值最低 100 元,支持微信 Native 和支付宝 PreCreate,支付成功直接入主钱包且不审批 | 21、新增充值 |
|
||||
| D-19 | 代理在线充值最低 100 元,按配置使用微信 v3 H5/v2 MWEB 和支付宝 WAP 支付 URL,支付成功直接入主钱包且不审批 | 21、新增充值 |
|
||||
| D-20 | 资产层只展示一个预计最终到期时间;当前套餐和全部排队主套餐共同参与推算,临期也使用同一结果 | 06、11、22 |
|
||||
| D-21 | 换货完成时新资产自动继承旧资产店铺;旧资产保留原店铺用于历史查询和权限追踪 | 禅道 #98 |
|
||||
| D-22 | 店铺可选绑定一个平台业务员;该字段只表达业务归属,不恢复需求16的分销和佣金关系 | 禅道 #96 |
|
||||
@@ -1179,7 +1179,7 @@ CHECK (
|
||||
|------|------|
|
||||
| 修改角色默认额度 | 只更新角色模板和审计,不扫描、不修改任何已有店铺钱包 |
|
||||
| 创建店铺 | 在创建事务中读取默认角色模板并初始化代理主钱包实际额度 |
|
||||
| 修改额度 | 拒绝代理账号;校验平台独立权限、加载主钱包、校验新可用金额、按 `version` 条件更新、写信用变更审计 |
|
||||
| 修改额度 | 拒绝代理账号;校验平台独立权限、加载主钱包、校验新可用金额、按服务端读取的 `version` 条件更新、写信用变更审计 |
|
||||
| 钱包扣款 | 使用有效额度计算可用金额,同事务更新余额、版本和资金流水 |
|
||||
| 查询/导出 | Query 返回 `credit_enabled`、`credit_limit`、`available_balance`、`is_in_debt`、`debt_amount` |
|
||||
|
||||
@@ -1190,7 +1190,7 @@ PUT /api/admin/shops/{id}/credit-limit 独立调整信用额度
|
||||
GET /api/admin/shops/fund-summary 返回信用和可用金额
|
||||
```
|
||||
|
||||
角色页面显示“新建代理默认信用额度”,并明确提示“修改后不会影响已有店铺”。店铺资金页面独立显示和修改实际信用额度。前端只展示接口返回的可用金额,不自行重新计算;额度调整弹框显示修改前后金额预览,并发冲突时刷新最新钱包版本。
|
||||
角色页面显示“新建代理默认信用额度”,并明确提示“修改后不会影响已有店铺”。店铺资金页面独立显示和修改实际信用额度。前端只展示接口返回的可用金额,不自行重新计算;额度调整弹框显示修改前后金额预览,乐观锁版本由后端读取和校验。
|
||||
|
||||
资金概况中的 `is_in_debt` 表示 `balance < 0`,`debt_amount = max(-balance, 0)`;冻结金额只影响现金可用金额和总可用金额,不直接记为欠款。额度调整只改变资金边界,不伪造一条金额为零的钱包交易流水;变更前后值进入全局 Audit Event。
|
||||
|
||||
@@ -1344,7 +1344,7 @@ sequenceDiagram
|
||||
#### 代理在线扫码充值
|
||||
|
||||
- 代理只能为当前店铺主钱包充值,最低 `10000` 分(100 元)。
|
||||
- 支持微信 Native 和支付宝 `alipay.trade.precreate`,创建本地充值单和 `tb_payment(order_type=agent_recharge)` 后再预下单。
|
||||
- 按当前配置使用微信 v3 H5 或 v2 MWEB,并复用支付宝 `alipay.trade.wap.pay` 支付 URL,创建本地充值单和 `tb_payment(order_type=agent_recharge)` 后生成链接。
|
||||
- 每次主动创建都生成新的充值单和支付单;`request_id` 只防同一次 HTTP 提交重试,不按金额或已有待支付单复用。后端原样返回第三方 `qr_content`,前端使用二维码组件渲染,不生成后端图片文件。
|
||||
- 不返回 `expires_at`,也不展示本地推算的精确倒计时;支付是否成功、关闭或失效以第三方回调和受控查单结果为准。前端每 3 秒轮询的轻量接口只读取本地状态,不直接触发第三方查单。
|
||||
- 微信/支付宝回调按支付单类型分发,校验渠道、支付配置、金额、第三方交易号和业务单关联。
|
||||
@@ -1426,7 +1426,7 @@ POST /api/admin/agent-recharges/{id}/reject
|
||||
- 统计待审批退款、平台员工线下充值和历史终态记录数量。
|
||||
- 轮换用户 demo 中泄露的企微 Secret、Token 和 EncodingAESKey,配置可信域名、应用可见范围和回调地址。
|
||||
- 发布并验证退款、线下充值企微模板控件映射,验证代理固定代提交成员可用,并要求会发起审批的平台/超级管理员完成扫码绑定。
|
||||
- 验证微信 Native、支付宝 PreCreate 配置和回调地址,确认代理充值支付渠道可用。
|
||||
- 验证微信 v3 H5/v2 MWEB、支付宝 WAP 配置和回调地址,确认代理充值支付渠道可用且支付宝无需开通当面付。
|
||||
- 盘点旧账号、资产、轮询日志的写入口和查询入口,确认统一审计切换清单。
|
||||
- 停机窗口内由业务使用超级管理员配置普通角色导出字段并抽样验证;不运行默认授权迁移,永久禁止导出的字段不进入代码目录。
|
||||
|
||||
|
||||
@@ -1514,7 +1514,7 @@
|
||||
|
||||
接口:GET /api/admin/agent-recharges/payment-methods;POST /api/admin/agent-recharges;GET /api/admin/agent-recharges/{id}/payment-status;GET /api/admin/agent-recharges/{id};下线offline-pay和reject旧接口。
|
||||
|
||||
在线规则:代理只充当前店铺且不提交shop_id,最低10000分;支持微信Native和支付宝PreCreate;每次主动创建都是全新充值单和支付单;统一返回qr_content且不返回expires_at。支付回调和受控查单进入同一幂等确认用例并校验金额、配置、交易号和业务单;先固化支付成功与入账Outbox,再由可靠Worker独立事务更新主钱包、版本、唯一流水、充值完成状态和审计。重复回调或任务不重复入账,迟到成功不能吞掉已付资金。
|
||||
在线规则:代理只充当前店铺且不提交shop_id,最低10000分;按当前配置使用微信v3 H5或v2 MWEB,并复用支付宝WAP支付URL,前端自行渲染二维码,支付宝无需开通当面付;每次主动创建都是全新充值单和支付单;统一返回qr_content且不返回expires_at。支付回调和受控查单进入同一幂等确认用例并校验金额、配置、交易号和业务单;先固化支付成功与入账Outbox,再由可靠Worker独立事务更新主钱包、版本、唯一流水、充值完成状态和审计。重复回调或任务不重复入账,迟到成功不能吞掉已付资金。
|
||||
|
||||
线下规则:仅平台/超管创建,金额大于0,目标店铺和1~5个结构化付款凭证必填,金额提交后固定;创建后提交企微,审批只能同意或拒绝。通过后自动增加代理主钱包并写流水,无操作密码;recharge:{recharge_no}防重;驳回终结原单且不支持退回/重提,撤销/删除为已关闭;通过后撤销且已入账不自动扣回。
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
| #55 套餐分配生效条件 | 套餐和分配接口已支持默认值、覆盖值和最终生效值 | 创建/编辑套餐传 `expiry_base`;分配时传 `expiry_base_override`,展示 `effective_expiry_base` |
|
||||
| #60 店铺联系电话搜索 | 店铺列表已支持 11 位联系电话精确查询 | 将输入值作为 `contact_phone` 查询参数传给店铺列表接口 |
|
||||
| #86 资产换货标识和跳转 | 资产解析接口已返回 `exchange_trace.previous_asset/next_asset` 和 `can_view` | 仅 `can_view=true` 且存在资产 ID 时允许跳转;该需求前端已对接可保持现状 |
|
||||
| #38 代理信用额度 | 角色默认额度、店铺实际额度、资金概况和负可用余额均已有接口 | 使用分单位字段;更新时携带钱包 `version`;余额为负数时正常展示 |
|
||||
| #38 代理信用额度 | 角色默认额度、店铺实际额度、资金概况和负可用余额均已有接口 | 使用分单位字段;调额不传钱包 `version`,并发控制由后端负责;余额为负数时正常展示 |
|
||||
| #94 状态同步和运营商回调 | 后端回调、定时触发和原轮询链路已装配 | 通常无前端新调用;状态页面继续读取现有资产状态字段 |
|
||||
| #96 店铺业务员 | 店铺创建/更新、候选人、列表/详情和筛选都已支持业务员 | 创建/编辑店铺选择 `business_owner_account_id`;列表可按该 ID 筛选并展示名称 |
|
||||
| #98 换货新资产继承旧店铺 | 换货完成时后端自动继承旧资产店铺归属 | 前端继续调用原换货完成接口,不新增分配步骤 |
|
||||
@@ -89,7 +89,7 @@
|
||||
| --- | --- | --- |
|
||||
| #60 联系电话搜索 | `GET /api/admin/shops?contact_phone=11位号码` | 精确查询;可与店铺名称、编号等条件组合 |
|
||||
| #96 店铺业务员 | `GET /api/admin/shops/business-owner-candidates`;`POST /api/admin/shops`;`PUT /api/admin/shops/:id`;`GET /api/admin/shops` | 创建/更新传 `business_owner_account_id`;列表可用同名参数筛选,响应展示账号 ID、名称和可用状态 |
|
||||
| #38 信用额度 | `PUT /api/admin/roles/:id/default-credit`;`PUT /api/admin/shops/:id/credit-limit`;`GET /api/admin/shops/fund-summary` | 角色接口配置新建代理默认值;店铺接口传 `credit_enabled + credit_limit + version`;金额单位均为分 |
|
||||
| #38 信用额度 | `PUT /api/admin/roles/:id/default-credit`;`PUT /api/admin/shops/:id/credit-limit`;`GET /api/admin/shops/fund-summary` | 角色接口配置新建代理默认值;店铺接口只传 `credit_enabled + credit_limit`,乐观锁由后端管理;金额单位均为分 |
|
||||
| #55 套餐默认生效条件 | `POST /api/admin/packages`;`PUT /api/admin/packages/:id`;`GET /api/admin/packages/:id` | 请求使用 `expiry_base=from_activation|from_purchase`;响应展示默认生效条件名称 |
|
||||
| #55 分配覆盖 | `POST /api/admin/shop-package-batch-allocations`;`PATCH /api/admin/shop-package-allocations/:id/expiry-base` | 分配时使用 `expiry_base_override`;`null` 表示跟随套餐默认值 |
|
||||
| #43 系列套餐多选 | `POST /api/admin/shop-series-grants`;`PUT /api/admin/shop-series-grants/:id/packages` | `packages` 是 1~100 项数组,每项包含 `package_id`、`cost_price`,删除时传 `remove=true` |
|
||||
|
||||
@@ -426,7 +426,7 @@ GET /api/admin/bulk-purchases/{task_id}/items?status=4&page=1&page_size=50
|
||||
### 在线支付与入账
|
||||
|
||||
- 继续使用现有支付配置,只按 `wechat` 或 `alipay` 选择当前可用配置,不建设多通道自动路由、优先级或故障转移。
|
||||
- 微信使用 Native,支付宝使用 `alipay.trade.precreate`。后端把第三方返回的字符串或 HTTPS URL 原样映射为 `qr_content`,前端渲染二维码;后端不生成二维码图片或新增二维码生成接口。
|
||||
- 微信复用 C 端 H5,支付宝复用 `alipay.trade.wap.pay`。后端把支付 HTTPS URL 原样映射为 `qr_content`,前端渲染二维码;后端不生成二维码图片或新增二维码生成接口,支付宝无需开通当面付。
|
||||
- 创建接口不返回 `expires_at`,前端不展示本地推算的精确倒计时。本地时间不能判定第三方支付单是否失效,支付成功或关闭以回调和后端受控查单为准。
|
||||
- `request_id` 只防止同一次提交重试。代理每次主动创建或再次拉起支付都使用新 `request_id` 并产生新的充值单和支付单,旧单等待第三方自然收敛,不复用、不主动取消。
|
||||
- 支付回调或查单先在事务中固化真实收款事实:支付单已支付、充值单 `2=已支付`、`processing_status=1`并可靠写入钱包入账 Outbox;随后 Worker 在独立事务中更新钱包和版本、创建唯一流水、将充值单改为 `3=已完成`和 `processing_status=2`并写资金审计。入账失败使用 `processing_status=3`可靠重试,不回滚支付事实。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 新增需求 05:代理钱包扫码充值
|
||||
|
||||
> 状态:已冻结,本文保留为实施明细;如有冲突,以标准评审稿和 UR#34 PRD 为准。
|
||||
> 状态:2026-07-30 修正支付产品选择,本文保留为实施明细;如有冲突,以标准评审稿和 UR#34 PRD 为准。
|
||||
> 评审主文档:`../../7月迭代技术方案-标准评审稿.md`
|
||||
> 实施 PRD:`../../../../.scratch/ur34-agent-recharge/PRD.md`
|
||||
> 范围:代理在后台使用微信或支付宝扫码充值代理主钱包。
|
||||
@@ -12,8 +12,8 @@
|
||||
3. 单笔最低充值金额为 100 元,即 `10000` 分。
|
||||
4. 代理在线充值不进入企业微信审批;支付成功事实先落库,再由可靠 Worker 幂等增加代理主钱包余额。
|
||||
5. 平台员工线下代充值仍按 `02-企业微信审批接入.md` 走企微审批,与本方案隔离。
|
||||
6. 后端返回支付二维码内容,前端使用现有二维码组件渲染,不由后端生成或保存二维码图片文件。
|
||||
7. 微信使用 Native 支付,支付宝使用 `alipay.trade.precreate` 当面付预创建。
|
||||
6. 后端返回支付 URL,前端使用现有二维码组件渲染,不由后端生成或保存二维码图片文件。
|
||||
7. 微信按当前配置使用 v3 H5 或 v2 MWEB,支付宝复用 C 端 `alipay.trade.wap.pay` 手机网站支付;不要求开通 Native 或当面付产品。
|
||||
8. 支付回调、钱包入账、钱包流水和审计必须幂等,重复回调不能重复加钱。
|
||||
9. 当前代理充值复杂写逻辑迁移到 Application/Domain,旧 Service 不再保留另一套在线入账逻辑。
|
||||
|
||||
@@ -46,9 +46,9 @@ sequenceDiagram
|
||||
Agent->>Web: 输入金额并选择支付方式
|
||||
Web->>API: 创建扫码充值单
|
||||
API->>DB: 创建充值单和支付单
|
||||
API->>Pay: Native/PreCreate 预下单
|
||||
Pay-->>API: 二维码内容
|
||||
API-->>Web: 原样返回 qr_content
|
||||
API->>Pay: 生成 H5/WAP 支付链接
|
||||
Pay-->>API: HTTPS URL
|
||||
API-->>Web: URL 写入 qr_content 返回
|
||||
Web-->>Agent: 展示二维码并轮询支付状态
|
||||
Agent->>Pay: 扫码完成支付
|
||||
Pay->>Callback: 异步支付通知
|
||||
@@ -87,9 +87,9 @@ internal/
|
||||
│ ├── post_wallet.go 可靠任务执行钱包入账
|
||||
│ ├── sync_pending.go 受控查询待支付第三方订单
|
||||
│ └── get_payment_status.go 轻量支付状态查询
|
||||
├── infrastructure/adapter/payment/
|
||||
│ ├── wechat_native.go 微信 Native 预下单
|
||||
│ └── alipay_precreate.go 支付宝当面付预创建
|
||||
├── infrastructure/payment/
|
||||
│ ├── wechat_web.go 微信 H5/MWEB 支付链接与查单
|
||||
│ └── alipay_wap.go 支付宝 WAP 支付链接与查单
|
||||
├── infrastructure/persistence/
|
||||
│ └── agent_recharge_repository.go
|
||||
└── query/agentrecharge/
|
||||
@@ -123,7 +123,7 @@ internal/
|
||||
- 钱包乐观锁冲突时由 Application 重新加载后有限重试,不能重复创建流水。
|
||||
- 支付渠道成功不等于业务已经完成;只有钱包事务成功后充值单才变为已完成。
|
||||
|
||||
## 五、支付预下单
|
||||
## 五、支付链接
|
||||
|
||||
### 5.1 统一支付单
|
||||
|
||||
@@ -147,35 +147,35 @@ tb_payment.amount = recharge_amount
|
||||
tb_payment.payment_config_id = 创建时使用的配置ID
|
||||
```
|
||||
|
||||
先完成本地事务,再调用第三方预下单。预下单失败时把支付单标记为失败并关闭本次充值单,代理重新创建,不复用来源不明确的旧二维码。
|
||||
先完成本地事务,再生成支付链接。链接生成失败时把支付单标记为失败并关闭本次充值单,代理重新创建,不复用来源不明确的旧链接。
|
||||
|
||||
### 5.2 微信扫码
|
||||
|
||||
微信使用 Native 下单:
|
||||
微信按当前配置选择 H5/MWEB 下单:
|
||||
|
||||
```text
|
||||
微信支付 v3 TransactionNative
|
||||
-> 返回 code_url
|
||||
provider_type=wechat -> 微信 v3 TransactionH5 -> h5_url
|
||||
provider_type=wechat_v2 -> 微信 v2 MWEB -> mweb_url
|
||||
```
|
||||
|
||||
现有微信 SDK 已包含 `TransactionNative`,需要在项目支付 Adapter 中封装,不在 Handler 直接调用 SDK。
|
||||
代理充值 Adapter 复用统一 `CreateH5Order` 入口,不在 Handler 判断协议或重复调用 SDK。
|
||||
|
||||
若当前生效支付配置为:
|
||||
|
||||
- `wechat`:使用微信 v3 Native。
|
||||
- `wechat_v2`:补充 v2 Native 统一下单实现。
|
||||
- `fuiou`:只有现有富友配置明确支持后台扫码产品时才返回微信可用;不支持时前端隐藏微信扫码入口,不擅自用 JSAPI 代替。
|
||||
- `wechat`:使用微信 v3 H5。
|
||||
- `wechat_v2`:使用微信 v2 MWEB,并复用 v2 查单与回调验签。
|
||||
- `fuiou`:不在本需求扩展富友后台扫码产品,不返回微信可用。
|
||||
|
||||
### 5.3 支付宝扫码
|
||||
|
||||
支付宝使用当前 SDK 已提供的:
|
||||
支付宝复用 C 端现有手机网站支付链接:
|
||||
|
||||
```text
|
||||
alipay.trade.precreate
|
||||
-> 返回 qr_code
|
||||
alipay.trade.wap.pay
|
||||
-> 返回签名 HTTPS URL
|
||||
```
|
||||
|
||||
不复用现有 WAP 支付 URL。创建时校验当前支付配置中的:
|
||||
该方式不依赖支付宝当面付。创建时校验当前支付配置中的:
|
||||
|
||||
```text
|
||||
ali_app_id
|
||||
@@ -184,11 +184,11 @@ ali_public_key
|
||||
ali_notify_url
|
||||
```
|
||||
|
||||
配置不完整时支付宝方式显示为不可用,不能创建只有本地记录而没有有效二维码的充值单。
|
||||
配置不完整时支付宝方式显示为不可用,不能创建只有本地记录而没有有效支付链接的充值单。
|
||||
|
||||
### 5.4 二维码响应
|
||||
|
||||
后端统一返回二维码内容,不返回二维码图片:
|
||||
后端统一返回支付 URL,不返回二维码图片:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -197,7 +197,7 @@ ali_notify_url
|
||||
"payment_no": "ARCH20260715143000000001",
|
||||
"payment_method": "wechat",
|
||||
"amount": 10000,
|
||||
"qr_content": "weixin://wxpay/bizpayurl?...",
|
||||
"qr_content": "https://pay.example.com/...",
|
||||
"status": 1,
|
||||
"status_name": "待支付",
|
||||
"payment_status": 0,
|
||||
@@ -207,7 +207,7 @@ ali_notify_url
|
||||
}
|
||||
```
|
||||
|
||||
微信返回 `code_url`、支付宝返回 `qr_code`,Application 统一映射为 `qr_content`。
|
||||
微信返回 `h5_url`、支付宝返回签名 WAP URL,Application 统一映射为 `qr_content`,前端自行渲染二维码。
|
||||
|
||||
本地无法准确知道第三方订单的真实失效时间,因此接口不返回 `expires_at`,前端不展示本地推算的精确倒计时。第三方支付成功或关闭以回调和后端受控查单为准。
|
||||
|
||||
@@ -404,14 +404,14 @@ GET /api/admin/agent-recharges/{id}/payment-status
|
||||
|
||||
```text
|
||||
代理创建充值单
|
||||
支付预下单成功/失败
|
||||
支付链接生成成功/失败
|
||||
微信/支付宝支付回调成功/失败
|
||||
钱包入账成功/失败
|
||||
重复回调被幂等忽略
|
||||
第三方查单确认支付关闭或失效
|
||||
```
|
||||
|
||||
支付渠道交互写 `tb_integration_log`,钱包余额变化写关键 `Audit Event`,并关联充值单、支付单、代理钱包和钱包流水。
|
||||
微信 H5/MWEB 下单与支付渠道查单写 `tb_integration_log`;支付宝 WAP URL 本地签名不伪造外部调用日志。钱包余额变化写关键 `Audit Event`,并关联充值单、支付单、代理钱包和钱包流水。
|
||||
|
||||
在线充值不产生审批通知。目标代理主钱包实际入账后必须生成“充值到账”站内通知;在线实际提交账号与目标代理主账号不同时,两者分别通知并按充值单与接收人防重。平台线下代充值的真实提交人只接收 UR#37 的审批结果通知,除非其本身也是到账通知接收人。
|
||||
|
||||
@@ -423,8 +423,8 @@ GET /api/admin/agent-recharges/{id}/payment-status
|
||||
- `CreateAgentRechargeRequest.payment_method` 增加 `alipay`,金额校验改为 `min=10000`。
|
||||
- 创建代理在线充值时同时创建 `tb_payment` 记录。
|
||||
- 新增 `PaymentOrderTypeAgentRecharge`。
|
||||
- 微信支付 Adapter 增加 Native 预下单。
|
||||
- 支付宝 Adapter 增加 `TradePreCreate`。
|
||||
- 微信支付 Adapter 复用 C 端 H5 下单并返回 `h5_url`。
|
||||
- 支付宝 Adapter 复用 C 端 `BuildWapPayURL`。
|
||||
- 支付宝回调增加代理充值分发。
|
||||
- 微信/富友代理充值回调统一改为按支付单分发,不只依赖 `ARCH` 前缀。
|
||||
- 原 `agent_recharge.Service.HandlePaymentCallback` 迁入 `ConfirmAgentRechargePayment` 用例。
|
||||
@@ -444,8 +444,8 @@ GET /api/admin/agent-recharges/{id}/payment-status
|
||||
|
||||
1. 验证 99.99 元被后端拒绝,100 元可以创建充值单。
|
||||
2. 验证代理只能为自己的店铺创建微信或支付宝充值。
|
||||
3. 验证微信 Native 返回有效 `code_url`,前端能够扫码支付。
|
||||
4. 验证支付宝 PreCreate 返回有效 `qr_code`,前端能够扫码支付。
|
||||
3. 分别验证微信 v3 H5 与 v2 MWEB 返回有效 HTTPS URL,前端能够渲染二维码并通过外部浏览器扫码支付。
|
||||
4. 验证支付宝 WAP 返回有效签名 HTTPS URL,前端能够渲染二维码并扫码支付,且商户无需开通当面付。
|
||||
5. 验证支付回调通过支付单类型分发到代理充值用例。
|
||||
6. 验证微信、支付宝回调金额不一致时不会增加钱包余额。
|
||||
7. 验证支付成功后不创建企微审批实例,先固化支付事实,再由可靠 Worker 完成钱包入账。
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
1. **同一个创建接口有两条业务路径**:代理账号使用 `wechat|alipay` 在线自充;平台或超级管理员使用 `offline` 线下代充。
|
||||
2. **前端必须使用 `recharge_source` 区分来源**:`platform_offline` 是平台线下代充,`agent_online` 是代理在线自充;不要根据账号名称、备注或审批字段猜测。
|
||||
3. **代理不能选择充值店铺**:在线充值的店铺和主钱包由登录上下文确定,请求不得发送 `shop_id`、支付凭证或备注。
|
||||
4. **二维码由前端渲染**:后端返回支付渠道原始 `qr_content`,不返回二维码图片。
|
||||
4. **二维码由前端渲染**:后端在 `qr_content` 返回支付 HTTPS URL,不返回二维码图片。
|
||||
5. **网络重试不能创建新请求 ID**:同一次提交重试复用原 `request_id`;用户主动发起下一笔充值时生成新的 `request_id`。
|
||||
6. **支付和钱包到账是两个阶段**:`status=2` 表示第三方已收款、钱包入账处理中;只有 `status=3` 才表示钱包到账完成。
|
||||
7. **页面轮询只调用本地状态接口**:前端不得直接调用微信、支付宝查单,也不要反复调用创建接口查询状态。
|
||||
@@ -20,7 +20,7 @@
|
||||
| 能力 | 后端实现 | 前端要做什么 |
|
||||
| --- | --- | --- |
|
||||
| 可用支付方式 | 根据当前有效支付配置返回真正可用的 `wechat`、`alipay` | 打开充值弹窗时先查询;只展示返回数组中的方式 |
|
||||
| 代理在线自充 | 从登录账号取得当前店铺和主钱包,创建充值单、支付单并向第三方预下单 | 只提交金额、支付方式和请求 ID;用 `qr_content` 渲染二维码 |
|
||||
| 代理在线自充 | 从登录账号取得当前店铺和主钱包,创建充值单、支付单并按配置生成微信 v3 H5/v2 MWEB 或支付宝 WAP 支付 URL | 只提交金额、支付方式和请求 ID;用 `qr_content` 渲染二维码 |
|
||||
| 平台线下代充 | 保留既有目标店铺、凭证和企业微信审批流程 | 平台页面继续提交 `offline` 请求并只读展示审批状态 |
|
||||
| 充值来源 | 根据受控创建方式返回稳定来源枚举 | 列表、详情和支付状态统一展示 `recharge_source_name` |
|
||||
| 支付确认 | 微信/支付宝回调确认第三方收款事实 | 前端无需调用回调接口 |
|
||||
@@ -124,7 +124,7 @@ Content-Type: application/json
|
||||
"recharge_source": "agent_online",
|
||||
"recharge_source_name": "代理在线自充",
|
||||
"amount": 10000,
|
||||
"qr_content": "weixin://wxpay/bizpayurl?pr=...",
|
||||
"qr_content": "https://pay.example.com/...",
|
||||
"status": 1,
|
||||
"status_name": "待支付"
|
||||
},
|
||||
@@ -320,7 +320,7 @@ GET /api/admin/agent-recharges/{id}
|
||||
## 十、联调和验收边界
|
||||
|
||||
- OpenAPI 已包含 `/payment-methods`、在线创建、列表来源筛选、详情来源字段和 `/payment-status`。
|
||||
- 后端已完成微信 Native、支付宝 PreCreate、支付确认、异步钱包入账和回调丢失恢复的代码装配。
|
||||
- 后端已完成微信 v3 H5/v2 MWEB、支付宝 WAP 支付 URL、支付确认、异步钱包入账和回调丢失恢复的代码装配。
|
||||
- 当前交付已通过 `go build ./...`、`go vet ./...` 和 OpenSpec 严格校验。
|
||||
- 按需求方要求,本次未启动 API/Worker,未连接 PostgreSQL/Redis,未调用真实支付渠道,未发送真实回调,也未运行自动化测试。
|
||||
- 前端联调环境需具备完整支付配置;至少确认微信和支付宝创建响应分别返回非空 `qr_content`。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 功能范围
|
||||
|
||||
代理账号可在后台为当前所属店铺主钱包创建微信 Native 或支付宝当面付扫码充值。平台与超级管理员继续使用既有线下代充和企业微信审批,两个创建路径不共用权限。
|
||||
代理账号可在后台为当前所属店铺主钱包创建微信 H5/MWEB 或支付宝 WAP 支付链接扫码充值。微信按当前配置选择 v3 H5 或 v2 MWEB,前端自行渲染二维码,支付宝无需开通当面付。平台与超级管理员继续使用既有线下代充和企业微信审批,两个创建路径不共用权限。
|
||||
|
||||
充值订单相关响应统一返回:
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
响应包含 `recharge_id`、`payment_no`、`qr_content`、`recharge_source` 和充值状态。前端直接使用 `qr_content` 渲染二维码,不展示或记录支付配置、商户身份和密钥。
|
||||
响应包含 `recharge_id`、`payment_no`、`qr_content`、`recharge_source` 和充值状态。`qr_content` 是支付 HTTPS URL,前端直接渲染二维码,不展示或记录支付配置、商户身份和密钥。
|
||||
|
||||
平台线下代充使用 `payment_method=offline`,并按既有契约提交目标 `shop_id`、支付凭证和备注;响应来源为 `platform_offline`。
|
||||
|
||||
|
||||
103
docs/feature-034-agent-wallet-qr-recharge/微信扫码支付产品边界核对.md
Normal file
103
docs/feature-034-agent-wallet-qr-recharge/微信扫码支付产品边界核对.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 微信扫码支付产品边界核对
|
||||
|
||||
## 结论
|
||||
|
||||
1. 微信支付产品和接口协议版本是两个不同维度:`JSAPI`、`Native`、`H5` 是支付产品/用户场景;API v2、API v3 是商户后台调用微信支付的协议版本。
|
||||
2. 桌面代理后台展示二维码、用户拿手机微信扫码支付,应使用 **Native 支付**。Native 下单返回 `code_url`,前端把它生成二维码。这正是微信官方定义的扫码支付场景。
|
||||
3. JSAPI 确实可以正常支付,但它要求支付页面运行在微信内置浏览器中,并取得当前用户 `openid`,然后由页面调用微信支付控件;它不是“PC 页面展示二维码供另一台手机扫码”的产品。
|
||||
4. H5 支付用于手机浏览器(非微信客户端内)跳转拉起微信,v2 返回 `mweb_url`,v3 返回 `h5_url`。这两个 URL 在技术上当然都能编码成二维码;但能否支付取决于扫码后打开它的浏览器环境。微信“扫一扫”会在微信内置浏览器打开,而官方 H5 支付明确面向微信客户端外的浏览器,因此不能把这条路径当作可用的微信扫码支付流程;系统相机或其他扫码工具若把链接交给手机外部浏览器,则可能按 H5 流程拉起微信,但仍须满足支付域名、`Referer` 等官方校验。
|
||||
5. 微信支付的能力并非天然“只接受 v3”。官方 API v2 的统一下单用 `trade_type=JSAPI/NATIVE/MWEB`,API v3 则分别提供 JSAPI、Native、H5 下单接口;两代协议都覆盖这三类产品。实际能否调用还取决于商户是否开通对应产品、AppID 与商户号绑定等平台条件。
|
||||
6. 项目已按最终产品决定支持两种当前配置:`provider_type=wechat` 调用 v3 H5,`provider_type=wechat_v2` 调用 v2 MWEB;两者分别返回 `h5_url`、`mweb_url`,统一映射为 `qr_content`。
|
||||
|
||||
## 产品边界
|
||||
|
||||
| 产品 | 官方适用场景 | 下单主要返回物 | 客户端完成支付方式 | 是否适合本需求 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| JSAPI | 用户已经在微信客户端内打开商户网页 | `prepay_id`;商户再生成前端调起支付所需参数 | 页面调用微信支付控件,且需传用户 `openid` | 否。除非把代理充值页改成微信内页面并增加 OAuth/OpenID 链路 |
|
||||
| Native | PC 网站、实体物料等展示二维码,用户使用微信“扫一扫” | `code_url` | 商户把 `code_url` 生成二维码,用户扫码 | **是,和当前需求完全匹配** |
|
||||
| H5(v2 名称 MWEB) | 用户在手机系统浏览器等微信客户端外的移动网页发起支付 | `h5_url`(v2 返回字段为 `mweb_url`) | 当前手机浏览器跳转该 URL 拉起微信 | 有条件可用:系统相机/外部扫码工具进入外部浏览器时可能完成;微信“扫一扫”进入微信内置浏览器时不符合官方 H5 场景 |
|
||||
|
||||
因此,“JSAPI 能正常支付”和“它适合桌面扫码”并不矛盾:前者描述支付能力,后者描述用户入口。选型依据应是入口场景,而不是某一种产品是否能够完成扣款。
|
||||
|
||||
## `mweb_url` / `h5_url` 生成二维码后的准确边界
|
||||
|
||||
先区分两件事:前端二维码组件可以渲染任意 URL,这只证明二维码可以被识别;是否能完成支付由微信支付产品规则和扫码后的浏览器环境决定。API v2 的 `mweb_url` 与 API v3 的 `h5_url` 在这一点上没有产品语义差异,都是 H5 支付跳转地址。
|
||||
|
||||
| 扫码入口 | 实际打开环境 | 官方产品边界 | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| 微信“扫一扫” | 微信内置浏览器 | 微信官方将 H5 支付定义为在微信客户端外的移动浏览器中调起微信支付 | 二维码能识别、URL 也能打开,但不能据此认定 H5 支付可完成;这不是官方支持的 H5 入口 |
|
||||
| 系统相机、系统扫码器或其他把链接交给浏览器的工具 | Safari、Chrome 等微信外部移动浏览器 | 符合 H5 支付的浏览器场景;浏览器跳转 `mweb_url`/`h5_url` 后拉起微信 | 可以作为 H5 跳转方式,但必须满足商户已开通 H5、支付域名配置、请求来源等校验 |
|
||||
|
||||
还有一个容易遗漏的限制:官方开发指引要求 H5 调起链路携带符合配置的 `Referer`,微信支付中间页会进行 H5 权限和安全校验。因此,将下单返回的裸 `mweb_url`/`h5_url` 直接编码进二维码,会让最终行为依赖扫码工具如何打开链接、是否保留合法来源;它不像 Native 的 `code_url` 那样是官方专门定义的“生成二维码后由微信扫码”凭据。更稳妥的 H5 二维码做法是二维码指向商户自己的已配置 H5 页面,再由该页面在外部浏览器中跳转微信返回的 H5 地址。
|
||||
|
||||
所以用户提出的说法应修正为:**`mweb_url`/`h5_url` 可以由前端渲染成二维码,系统相机扫码后进入外部浏览器时有条件可支付;但微信“扫一扫”打开的是微信内置浏览器,不满足官方 H5 支付场景。若产品要求用户明确使用微信扫一扫,仍应使用 Native `code_url`。**
|
||||
|
||||
## API v2 与 API v3
|
||||
|
||||
### 官方能力
|
||||
|
||||
- API v2 使用统一下单接口,通过 `trade_type` 区分产品:`JSAPI`、`NATIVE`、`MWEB`。对应返回分别围绕 `prepay_id`、`code_url`、`mweb_url`。
|
||||
- API v3 将三类下单拆成独立接口:
|
||||
- JSAPI:`POST /v3/pay/transactions/jsapi`,返回 `prepay_id`;
|
||||
- Native:`POST /v3/pay/transactions/native`,返回 `code_url`;
|
||||
- H5:`POST /v3/pay/transactions/h5`,返回 `h5_url`。
|
||||
|
||||
所以正确表述是:**v2 和 v3 都可以承载 JSAPI、Native、H5/MWEB;项目是否支持,取决于对应协议分支有没有实现该产品的下单、签名、查单、关单和回调验签。**
|
||||
|
||||
### 本项目当前实现
|
||||
|
||||
1. `WechatConfig.ProviderType` 明确把 `wechat` 定义为 v3、`wechat_v2` 定义为 v2;同一条配置模型同时有 `wx_api_v3_key`、证书/私钥/序列号和 `wx_api_v2_key` 字段。
|
||||
2. 通用加载器按 `provider_type` 选择实现:
|
||||
- `wechat` 构建 PowerWeChat v3 服务;
|
||||
- `wechat_v2` 构建本地 XML+MD5 v2 服务。
|
||||
3. v3 服务目前实现 JSAPI 和 H5;仓库曾有/SDK具备 Native 调用能力,但当前代理充值 Adapter 调的是 `CreateH5Order -> TransactionH5`,把返回的 `H5URL` 放入 `QRContent`。
|
||||
4. v2 服务保留 JSAPI,并已增加 `trade_type=MWEB` 下单、`mweb_url` 解析和 v2 查单;通用 v2 Adapter 同步开放 H5/MWEB 与查单能力。
|
||||
5. `GET /api/admin/agent-recharges/payment-methods` 读取唯一生效配置,再调用 Adapter 的 `Available`:v3 校验 v3 Key、证书和序列号,v2 校验 APIv2Key;两种配置均可返回 `wechat`。
|
||||
|
||||
因此测试环境生效配置为 `provider_type=wechat_v2` 且 APIv2Key、商户号、AppID 和回调地址完整时,接口会返回 `wechat`,创建时使用 MWEB。
|
||||
|
||||
## “基于当前支付配置”的准确含义
|
||||
|
||||
“基于当前支付配置”不等于“看到哪组密钥非空就自动尝试哪套协议”。当前代码的配置解析规则是:
|
||||
|
||||
```text
|
||||
唯一 is_active=true 的 WechatConfig
|
||||
│
|
||||
├─ provider_type=wechat → 选择 v3 实现 → 校验 v3 凭据
|
||||
├─ provider_type=wechat_v2 → 选择 v2 实现 → 校验 APIv2Key
|
||||
└─ provider_type=fuiou → 选择富友实现
|
||||
```
|
||||
|
||||
也就是说,字段值提供凭据,`provider_type` 决定协议和 Adapter。即使同一行同时填了 v2、v3 字段,代码也不会自动降级或跨协议尝试。支付宝字段则是同一配置行中的并存能力,当前可用性判断不通过 `provider_type=alipay` 分流。
|
||||
|
||||
## 本项目最终采用方式
|
||||
|
||||
用户已确认接受“系统相机或外部扫码工具打开手机外部浏览器”的 H5/MWEB 使用方式,项目据此采用:
|
||||
|
||||
1. `provider_type=wechat`:调用 v3 H5 下单和查单,返回 `h5_url`。
|
||||
2. `provider_type=wechat_v2`:调用 v2 MWEB 下单和查单,返回 `mweb_url`。
|
||||
3. 两个分支继续使用各自协议的回调验签;支付 URL 统一保存并返回为 `qr_content`。
|
||||
4. 前端负责渲染二维码,同时明确提示使用系统相机或外部浏览器扫码;本方案不承诺微信“扫一扫”入口。
|
||||
|
||||
## 仓库证据
|
||||
|
||||
- `internal/model/wechat_config.go`:`wechat`/`wechat_v2` 的协议含义及两套密钥字段。
|
||||
- `pkg/payment/loader.go`:按 `provider_type` 构建 v3 或 v2 服务;v2 Adapter 转发 JSAPI、H5/MWEB 与查单。
|
||||
- `pkg/wechat/payment_v2.go`:v2 XML+MD5 的 JSAPI、MWEB 下单与查单实现。
|
||||
- `pkg/wechat/payment.go`:v3 JSAPI 返回 `prepay_id/pay_config`,H5 返回 `h5_url`。
|
||||
- `internal/infrastructure/payment/wechat_web.go`:代理充值按 `provider_type` 调用 v3 H5 或 v2 MWEB,并把支付 URL 写入 `QRContent`。
|
||||
- `internal/application/agentrecharge/online_creation.go`:支付方式接口读取 `is_active=true` 配置,并只返回 Adapter 判定可用的方式。
|
||||
|
||||
## 微信支付官方来源
|
||||
|
||||
- [JSAPI 支付产品/开发指引](https://pay.weixin.qq.com/doc/v3/merchant/4012791856)
|
||||
- [JSAPI 下单 API v3](https://pay.weixin.qq.com/doc/v3/merchant/4012791858)
|
||||
- [Native 支付产品/开发指引](https://pay.weixin.qq.com/doc/v3/merchant/4012791874)
|
||||
- [Native 下单 API v3](https://pay.weixin.qq.com/doc/v3/merchant/4012791875)
|
||||
- [H5 支付产品/开发指引](https://pay.weixin.qq.com/doc/v3/merchant/4012791897)
|
||||
- [H5 下单 API v3](https://pay.weixin.qq.com/doc/v3/merchant/4012791902)
|
||||
- [API v2 统一下单(官方旧版文档)](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
|
||||
- [API v2 Native 支付(官方旧版文档)](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1)
|
||||
- [API v2 H5 支付(官方旧版文档)](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1)
|
||||
|
||||
> 核对日期:2026-07-30。上述来源均为微信支付官方域名;旧版 API v2 文档可能由官方站点重定向到新版文档中心,但其统一下单字段和产品语义也可由仓库现有 v2 实现交叉核对。
|
||||
Reference in New Issue
Block a user