client_framework/docs/payment_flow.md
2026-03-24 19:34:04 +08:00

221 lines
6.7 KiB
Markdown
Raw 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. 概述
本文档描述 Android 项目Flutter的完整支付流程包括商品获取、支付方式选择、订单创建、Google Play 内购以及补单机制。
---
## 2. 支付流程总览
```
用户点击 Buy
├─ enableThirdPartyPayment === true 且已登录
│ │
│ ├─ getPaymentMethods(activityId) 获取支付方式
│ ├─ 弹窗选择支付方式_PaymentMethodDialog
│ ├─ createPayment 创建订单
│ │
│ ├─ 若选中的是 Google Payresource/ceremony == "GooglePay"
│ │ ├─ 调起 Google Play 内购
│ │ ├─ 拿到 serverVerificationData
│ │ └─ POST /v1/payment/googlepay 回调验证
│ │
│ └─ 否则(其他支付方式)
│ └─ 打开 payUrl 在外部浏览器完成支付
└─ enableThirdPartyPayment !== true 或未登录
└─ 仅 Android直接调起 Google Play 内购
```
---
## 3. 支付分支依据
| 条件 | 说明 |
|------|------|
| `UserState.enableThirdPartyPayment` | 登录后由 AuthService 从 `/v1/user/common_info` 响应写入 |
| `UserState.userId` | 用户登录后存储的用户 ID |
| **第三方支付** | `enableThirdPartyPayment == true``userId` 非空 |
| **直接谷歌支付** | 其他情况(未开第三方支付或未登录)|
---
## 4. 商品展示与获取
### 4.1 接口
- **Android**: `GET /v1/payment/getGooglePayActivities`
- **iOS**: `GET /v1/payment/getApplePayActivities`
### 4.2 商品字段映射
| 字段API | 字段(客户端映射) | 说明 |
|-------------|-------------------|------|
| helm | code / productId | Google Play 商品 ID |
| warrior | activityId | 活动 ID用于创建订单 |
| guardian | actualAmount | 实际金额 |
| curriculum | originAmount | 原价(带划线)|
| forge | bonus | 赠送积分 |
| glossary | title | 标题 |
### 4.3 代码入口
文件:`lib/features/recharge/recharge_screen.dart`
- `_fetchActivities()`: 获取商品列表
- `_onBuy()`: 用户点击购买入口
---
## 5. 第三方支付流程
### 5.1 步骤
1. **获取支付方式**: `POST /v1/payment/get-payment-methods`
- 参数: `warrior` (activityId), `vambrace` (可选,国家)
2. **弹窗选择**: 展示支付方式列表(`_PaymentMethodSheet`),包含:
- `resource`: 支付方式(如 GOOGLEPAY
- `ceremony`: 子支付方式
- `name`: 显示名称
- `icon`: 图标 URL
- `recommend`: 是否推荐
3. **创建订单**: `POST /v1/payment/createPayment`
- 参数: `sentinel`, `asset`(userId), `warrior`(activityId), `resource`, `ceremony`
- 返回: `federation`(订单ID), `convert`(支付URL)
4. **支付方式分支**:
- **Google Pay**: 调用 `GooglePlayPurchaseService.launchPurchaseAndReturnData()` → 调起内购 → 调用 `PaymentApi.googlepay()` 回调验证
- **其他方式**: 使用 `url_launcher` 打开 `convert` 支付链接
### 5.2 代码位置
- 入口: `recharge_screen.dart``_runThirdPartyPayment()`
- 创建订单: `_createOrderAndOpenUrl()`
- Google Pay 判断: `_isGooglePay()`
---
## 6. 直接谷歌支付流程
仅 Android且不经过 `getPaymentMethods``createPayment`(三方支付关闭时):
1. 调用 `createPayment`resource=GooglePay, ceremony=GooglePay
2. 调起 Google Play 内购
3. 回调验证
### 代码位置
- `recharge_screen.dart``_runGooglePay()`
---
## 7. Google Play 内购统一入口
### 7.1 核心方法
`GooglePlayPurchaseService.launchPurchaseAndReturnData(productId)`
- 调起 Google Play 内购
- 返回 `GooglePayPurchaseResult` 包含:
- `orderId`: Google 订单号
- `payload.purchaseData`: purchaseData用于 merchant
- `payload.signature`: 签名(用于 sample
- `purchaseDetails`: PurchaseDetails 对象
### 7.2 回调验证
`PaymentApi.googlepay(sample, merchant, federation, asset)`
- `sample`: 签名
- `merchant`: purchaseData
- `federation`: 订单ID
- `asset`: userId
### 7.3 核销
`GooglePlayPurchaseService.completeAndConsumePurchase(purchaseDetails)`
- 执行 `completePurchase`
- 执行 `consumePurchase`Android
---
## 8. 补单机制
### 8.1 触发时机
- 进入充值页时调用 `GooglePlayPurchaseService.runOrderRecovery()`
### 8.2 补单流程
1. 获取未核销订单: `getUnacknowledgedPurchases()`
- 合并 `queryPastPurchases``purchaseStream` 的待处理订单
2. 对每笔订单:
- 查询本地存储的 `federation` 映射
- 若存在 federation: 调用 `googlepay` 回调 → 成功后 consume
- 若无 federation: 仅执行 consume 解除「已拥有此内容」
3. 补单成功后刷新账户
### 8.3 存储映射
使用 `SharedPreferences` 存储 `googleOrderId → federation` 映射:
- `saveFederationForGoogleOrderId()`
- `getFederationForGoogleOrderId()`
- `removeFederationForGoogleOrderId()`
---
## 9. API 汇总
| 接口 | 方法 | 说明 |
|------|------|------|
| `/v1/payment/getGooglePayActivities` | GET | 获取 Android 商品列表 |
| `/v1/payment/getApplePayActivities` | GET | 获取 iOS 商品列表 |
| `/v1/payment/get-payment-methods` | POST | 获取支付方式列表 |
| `/v1/payment/createPayment` | POST | 创建支付订单 |
| `/v1/payment/getOrderDetail` | GET | 查询订单状态(轮询)|
| `/v1/payment/googlepay` | POST | Google Pay 回调验证 |
---
## 10. 代码文件位置
| 功能 | 文件路径 |
|------|----------|
| 充值页面 | `lib/features/recharge/recharge_screen.dart` |
| 支付 API | `lib/core/api/services/payment_api.dart` |
| Google Play 内购服务 | `lib/features/recharge/google_play_purchase_service.dart` |
| 支付方式模型 | `lib/features/recharge/models/payment_method_item.dart` |
| 商品模型 | `lib/features/recharge/models/activity_item.dart` |
| 购买结果模型 | `lib/features/recharge/models/google_pay_purchase_result.dart` |
| WebView 支付页 | `lib/features/recharge/payment_webview_screen.dart` |
---
## 11. 常见问题
### 11.1 商品未找到
- 原因: 客户端 `helm` (productId) 与 Google Play 后台「产品 ID」不一致
- 排查: 检查 `docs/google_pay_product_not_found.md`
### 11.2 补单
- 未确认订单可能不会出现在 `queryPastPurchases`
- 应用启动时订阅 `purchaseStream` 接收重新下发
- 补单会合并两者的待处理订单
---
## 12. 注意事项
- 所有 Google Play 内购统一使用 `launchPurchaseAndReturnData()` 方法
- 回调验证成功后必须调用 `completePurchase` + `consumePurchase`
- 支付 URL 打开方式取决于 `createPayment` 返回的 `convert` 字段
- 订单状态轮询: 间隔 1/3/7/15/31/63 秒