client_framework/docs/payment_flow.md

7.8 KiB
Raw Blame History

支付流程文档

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 == trueuserId 非空
直接谷歌支付 其他情况(未开第三方支付或未登录)

4. 商品展示与获取

4.1 接口

// Androidapp 默认 backendAppTypeAndroid可传 client
final res = await PaymentApi.getGooglePayActivities(
  country: '国家',      // 可选
);

// iOSapp 默认 backendAppTypeIOS
final res = await PaymentApi.getApplePayActivities(
  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: 12345,   // int
  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: 'HAndroid',     // HIOS / HAndroid
  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且不经过 getPaymentMethodscreatePayment(三方支付关闭时):

// 1. 创建订单(直接走谷歌支付)
final createRes = await PaymentApi.createPayment(
  app: 'HAndroid',
  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 补单流程

  1. 获取未核销订单: getUnacknowledgedPurchases()

    • 合并 queryPastPurchasespurchaseStream 的待处理订单
  2. 对每笔订单:

    • 查询本地存储的 federation 映射
    • 若存在 federation: 调用 googlepay 回调 → 成功后 consume
    • 若无 federation: 仅执行 consume 解除「已拥有此内容」
  3. 补单成功后刷新账户


9. API 汇总

接口 方法 返回实体
getGooglePayActivities GET PaymentProductsResponse
getApplePayActivities GET PaymentProductsResponse
getPaymentMethods POST PaymentMethodsResponse
createPayment POST CreatePaymentResponse
getPaymentDetailList GET PaymentOrderListResponse
getOrderDetail GET OrderDetailResponse
googlepay POST GooglePayCallbackResponse

10. 字段映射说明

框架自动完成字段映射,调用层使用原始字段名。

请求 → 响应 字段对照

业务含义 请求字段 响应字段
应用 ID app -
用户 ID userId -
活动 ID activityId -
支付方式 paymentMethod -
支付子类型 paymentType -
订单 / 支付 ID详情 query idDart 仍用参数名 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 秒