# 支付流程文档 ## 1. 概述 本文档描述 Android 项目(Flutter)的完整支付流程,包括商品获取、支付方式选择、订单创建、Google Play 内购以及补单机制。 --- ## 2. 支付流程总览 ``` 用户点击 Buy │ ├─ enableThirdPartyPayment === true 且已登录 │ │ │ ├─ getPaymentMethods(activityId) 获取支付方式 │ ├─ 弹窗选择支付方式(_PaymentMethodDialog) │ ├─ createPayment 创建订单 │ │ │ ├─ 若选中的是 Google Pay(resource/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 秒