client_framework/docs/payment_flow.md
2026-04-22 17:46:22 +08:00

224 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 支付流程(多客户端通用)
## 1. 概述
本文档描述换皮应用可复用的支付全流程,覆盖:商品获取、支付方式选择、订单创建、平台内购回调、订单核销、补单与账户刷新。
文档以“能力与流程”表达,不绑定具体客户端项目、目录路径或某一语言实现。
---
## 2. 支付流程总览
```text
用户点击充值 / Buy
├─ 已登录 且 开启第三方支付
│ │
│ ├─ 拉取支付方式(基于商品/活动)
│ ├─ 用户选择支付方式
│ ├─ 创建订单(服务端生成业务订单)
│ │
│ ├─ 若为平台内购(如 Google Play
│ │ ├─ 调起商店购买
│ │ ├─ 获取 purchaseData + signature + storeOrderId
│ │ └─ 回调服务端校验与入账
│ │
│ └─ 若为外部支付
│ └─ 打开 payUrl 在 Web/外部浏览器完成支付
└─ 未登录 或 未开启第三方支付
└─ 可走平台内购直购分支(由业务策略决定)
```
---
## 3. 支付分支依据(通用)
| 条件 | 说明 |
|------|------|
| 是否登录 | 通常需要用户身份用于订单归属与回调入账 |
| 是否开启第三方支付 | 由服务端用户配置或渠道策略控制 |
| 当前平台能力 | Android / iOS 对内购与外部支付支持差异 |
| 商品配置 | 某些商品只允许特定支付方式 |
建议将分支判断集中在统一策略层,避免同一逻辑散落在多个页面。
---
## 4. 商品展示与获取
### 4.1 获取时机
- 进入充值页时拉取一次;
- 下拉刷新或切换国家/渠道后重新拉取;
- 支付成功后建议刷新,确保档位与余额展示一致。
### 4.2 商品字段(通用语义)
| 字段语义 | 说明 |
|------|------|
| `productId` | 商店商品 ID内购必填 |
| `activityId` | 服务端活动/档位 ID建单常用 |
| `actualAmount` | 实付金额 |
| `originAmount` | 原价(可用于划线价) |
| `bonus` / `bonusCredits` | 赠送积分 |
| `credits` | 到账积分(如与赠送分开展示) |
| `title` | 商品文案(可能由服务端直接下发) |
---
## 5. 第三方支付流程(通用)
### 5.1 拉取支付方式
以商品或活动为上下文请求支付方式列表,常见字段包括:
- `paymentMethod`:主支付方式(例如平台内购、钱包、卡支付等)
- `subPaymentMethod`:子方式(可选)
- `name` / `icon`:展示信息
- `recommend`:是否推荐
### 5.2 创建订单
创建订单请求通常包含(**逻辑字段名**
- 应用标识(`app`
- 用户标识(`userId`
- 商品或活动标识(`activityId` / `productId`
- 支付主方式(`paymentMethod`
- 支付类型(`paymentType`,按渠道语义填写)
- 子支付方式(`subPaymentMethod`,如卡种/钱包子通道)
> 关键点:`subPaymentMethod` 是独立语义字段。
> 仅传 `paymentType` 不会自动补出 `subPaymentMethod`。
#### 5.2.1 推荐入参组合(避免丢子支付方式)
- 卡支付场景(示例):
- `paymentMethod = MIFAPAY`
- `paymentType = MIFAPAY`(或按后端约定值)
- `subPaymentMethod = CreditCard`
- 若传成 `paymentType = CreditCard` 且未传 `subPaymentMethod`,常见结果是:
-`paymentMethod`
-`paymentType`
- **缺少 `subPaymentMethod`**
响应通常返回:
- `orderId`(业务订单号)
- `payUrl`(外部支付链接;仅外部支付场景)
- `status`(订单状态)
- 可选 `federation`(用于与商店订单映射)
### 5.3 分支执行
- **平台内购**:拉起商店购买 -> 取回凭据 -> 回调服务端核验 -> 成功后完成/消费交易。
- **外部支付**:打开 `payUrl` 完成支付 -> 回前台后查询订单状态或拉取账户余额。
---
## 6. 平台内购标准链路Android/iOS 可类比)
1.`productId` 调起商店支付;
2. 拿到商店返回的交易凭据(如 `purchaseData``signature``storeOrderId`
3. 调服务端回调接口完成验签与入账;
4. 服务端成功后,客户端执行“完成交易/消费交易”(防止重复拥有问题);
5. 刷新账户余额与订单状态。
> 关键原则:**先服务端验单成功,再本地完成/消费交易**,避免凭据丢失导致资产不一致。
---
## 7. 补单机制(订单恢复)
### 7.1 触发时机
- 应用启动后;
- 进入充值页时;
- 支付异常返回时(例如网络中断、回调超时)。
### 7.2 恢复流程
1. 拉取未完成/未核销的商店订单(历史订单 + 监听流中的待处理订单);
2. 对每笔订单尝试找到业务订单映射(如 `federation` / `orderId`
3. 若可映射:再次走服务端回调核验,成功后完成/消费交易;
4. 若不可映射:按产品策略处理(仅完成/消费以解除占用,或进入人工排查队列);
5. 恢复结束后刷新账户与充值记录。
---
## 8. API 能力清单(通用命名)
| 能力 | 典型方法 | 说明 |
|------|------|------|
| 获取商品列表 | `GET` | Android/iOS 可分端点 |
| 获取支付方式 | `POST`/`GET` | 入参通常含 `activityId` |
| 创建订单 | `POST` | 返回 `orderId``payUrl` 等 |
| 平台内购回调 | `POST` | 提交 `purchaseData``signature` 等 |
| 订单列表 | `GET` | 历史支付记录 |
| 订单详情 | `GET` | 查询单笔订单状态 |
---
## 9. 核心字段对照(通用)
| 业务含义 | 常见请求字段 | 常见响应字段 |
|------|------|------|
| 应用标识 | `app` | - |
| 用户标识 | `userId` | - |
| 活动/档位 | `activityId` | - |
| 支付方式 | `paymentMethod` | - |
| 子支付方式 | `paymentType` | - |
| 订单标识 | `orderId` / `id` | `orderId` |
| 购买签名 | `signature` | - |
| 购买数据 | `purchaseData` | - |
| 商品 ID | - | `productId` |
| 实付金额 | - | `actualAmount` |
| 原价 | - | `originAmount` |
| 赠送积分 | - | `bonus` / `bonusCredits` |
| 支付链接 | - | `payUrl` |
| 订单状态 | - | `status` |
---
## 10. 常见问题
### 10.1 商品未找到
- **现象**:发起内购时提示商品不存在;
- **常见原因**:客户端 `productId` 与商店后台配置不一致,或商品未上架到当前测试轨道;
- **排查建议**:核对包名/应用 ID、商品 ID、地区、测试账号与轨道配置。
### 10.2 回调成功但积分未更新
- **现象**:支付侧显示成功,账户余额未变;
- **常见原因**:回调后未刷新账户、订单状态未落库、幂等冲突;
- **排查建议**:检查回调响应、订单状态接口与账户刷新链路。
### 10.3 重复购买被拦截(已拥有此内容)
- **现象**:商店提示已拥有,无法再次购买;
- **常见原因**:上一笔交易未完成/未消费;
- **排查建议**:执行补单恢复流程并确保完成/消费逻辑成功。
### 10.4 创建订单请求缺少子支付方式字段
- **现象**:抓包中 `createPayment` 入参有 `paymentMethod`,但缺少 `subPaymentMethod`
- **常见原因**:只传了 `paymentType`,没有传 `subPaymentMethod`
- **排查建议**
- 检查应用侧建单调用是否显式传入 `subPaymentMethod`
- 对照请求日志逐项核对三元组:`paymentMethod` / `paymentType` / `subPaymentMethod`
- 若项目启用了字段映射,再额外确认映射配置与服务端契约一致。
---
## 11. 实施建议(多客户端复用)
- 以“流程能力层”封装支付,不把分支逻辑写死在页面;
- 页面只关心:档位展示、用户选择、状态反馈;
- 建单、回调、补单、轮询统一由支付服务层负责;
- 统一错误码与文案映射,避免各端提示不一致;
- 对关键节点埋点:拉起支付、建单成功、回调成功、补单成功、入账成功;
- 保持接口字段可映射,支持不同换皮应用的字段命名差异。