7.6 KiB
7.6 KiB
支付流程文档
1. 概述
本文档描述 Android 项目(Flutter)的完整支付流程,包括商品获取、支付方式选择、订单创建、Google Play 内购以及补单机制。
2. 支付流程总览
用户点击 Buy
│
├─ enableThirdPartyPayment === true 且已登录
│ │
│ ├─ getPaymentMethods(activityId) 获取支付方式
│ ├─ 弹窗选择支付方式
│ ├─ createPayment 创建订单
│ │
│ ├─ 若选中的是 Google Pay
│ │ ├─ 调起 Google Play 内购
│ │ ├─ 拿到 purchaseData + signature
│ │ └─ 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
final res = await PaymentApi.getGooglePayActivities(
app: '应用ID', // 可选
country: '国家', // 可选
);
// iOS
final res = await PaymentApi.getApplePayActivities(
app: '应用ID', // 可选
country: '国家', // 可选
);
4.2 返回实体
class PaymentProductsResponse {
List<PaymentProductItem>? productList;
}
class PaymentProductItem {
String? productId; // 商品 ID (对应 helm)
String? activityId; // 活动 ID (对应 warrior)
String? actualAmount; // 实际金额 (对应 guardian)
String? originAmount; // 原价 (对应 curriculum)
int? bonus; // 赠送积分 (对应 forge)
String? title; // 标题 (对应 glossary)
}
5. 第三方支付流程
5.1 获取支付方式
final res = await PaymentApi.getPaymentMethods(
activityId: '活动ID',
country: '国家', // 可选
);
if (res.isSuccess) {
final methods = res.data!.paymentMethods;
// methods 包含:
// - paymentMethod: 支付方式 (如 GOOGLEPAY)
// - subPaymentMethod: 子支付方式
// - name: 显示名称
// - icon: 图标 URL
// - recommend: 是否推荐
}
5.2 创建订单
final res = await PaymentApi.createPayment(
app: '应用ID',
userId: '用户ID',
activityId: '活动ID',
paymentMethod: '支付方式',
paymentType: '支付子类型', // 可选
);
if (res.isSuccess) {
final order = res.data!;
final orderId = order.orderId; // 订单 ID
final payUrl = order.payUrl; // 支付链接
}
5.3 支付分支
- Google Pay: 调起内购 → 获取 purchaseData/signature → 调用
googlepay回调 - 其他方式: 打开
payUrl在外部浏览器
6. 直接谷歌支付流程
仅 Android,且不经过 getPaymentMethods 和 createPayment(三方支付关闭时):
// 1. 创建订单(直接走谷歌支付)
final createRes = await PaymentApi.createPayment(
app: '应用ID',
userId: '用户ID',
activityId: '活动ID',
paymentMethod: 'GooglePay',
paymentType: 'GooglePay',
);
// 2. 调起 Google Play 内购
final purchaseResult = await GooglePlayPurchaseService.launchPurchaseAndReturnData(
productId: '商品ID',
);
// 3. 回调验证
if (purchaseResult != null) {
final res = await PaymentApi.googlepay(
signature: purchaseResult.payload.signature,
purchaseData: purchaseResult.payload.purchaseData,
orderId: orderId ?? purchaseResult.orderId,
userId: userId,
);
}
7. Google Play 内购统一入口
7.1 调起内购
final result = await GooglePlayPurchaseService.launchPurchaseAndReturnData(
productId: '商品ID',
);
if (result != null) {
// result.orderId: Google 订单号
// result.payload.purchaseData: 用于 merchant
// result.payload.signature: 用于 signature
// result.purchaseDetails: PurchaseDetails 对象
}
7.2 回调验证
final res = await PaymentApi.googlepay(
signature: result.payload.signature,
purchaseData: result.payload.purchaseData,
orderId: orderId,
userId: userId,
);
if (res.isSuccess) {
// 核销订单
await GooglePlayPurchaseService.completeAndConsumePurchase(
result.purchaseDetails,
);
}
7.3 响应实体
class GooglePayCallbackResponse {
String? orderId;
String? status; // SUCCESS / FAILED
bool? creditsAdded; // 是否已加积分
}
8. 补单机制
8.1 触发时机
- 进入充值页时调用
GooglePlayPurchaseService.runOrderRecovery()
8.2 补单流程
-
获取未核销订单:
getUnacknowledgedPurchases()- 合并
queryPastPurchases和purchaseStream的待处理订单
- 合并
-
对每笔订单:
- 查询本地存储的
federation映射 - 若存在 federation: 调用
googlepay回调 → 成功后 consume - 若无 federation: 仅执行 consume 解除「已拥有此内容」
- 查询本地存储的
-
补单成功后刷新账户
9. API 汇总
| 接口 | 方法 | 返回实体 |
|---|---|---|
getGooglePayActivities |
GET | PaymentProductsResponse |
getApplePayActivities |
GET | PaymentProductsResponse |
getPaymentMethods |
POST | PaymentMethodsResponse |
createPayment |
POST | CreatePaymentResponse |
getOrderDetail |
GET | OrderDetailResponse |
googlepay |
POST | GooglePayCallbackResponse |
10. 字段映射说明
框架自动完成字段映射,调用层使用原始字段名。
请求 → 响应 字段对照
| 业务含义 | 请求字段 | 响应字段 |
|---|---|---|
| 应用 ID | app | - |
| 用户 ID | userId | - |
| 活动 ID | activityId | - |
| 支付方式 | paymentMethod | - |
| 支付子类型 | paymentType | - |
| 订单 ID | orderId | orderId |
| 购买签名 | signature | - |
| 购买数据 | purchaseData | - |
| 商品 ID | - | productId |
| 实际金额 | - | actualAmount |
| 原价 | - | originAmount |
| 赠送积分 | - | bonus |
| 支付链接 | - | payUrl |
| 订单状态 | - | status |
11. 代码文件位置
| 功能 | 文件路径 |
|---|---|
| 充值页面 | 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 |
12. 常见问题
12.1 商品未找到
- 原因: 客户端
productId与 Google Play 后台「产品 ID」不一致 - 排查: 检查 Play 后台产品 ID 配置
12.2 补单
- 未确认订单可能不会出现在
queryPastPurchases中 - 应用启动时订阅
purchaseStream接收重新下发 - 补单会合并两者的待处理订单
13. 注意事项
- 所有 Google Play 内购统一使用
launchPurchaseAndReturnData()方法 - 回调验证成功后必须调用
completePurchase+consumePurchase - 支付 URL 打开方式取决于
createPayment返回的payUrl字段 - 订单状态轮询: 间隔 1/3/7/15/31/63 秒