petsHero-AI/docs/payment_flow.md
2026-03-12 14:30:19 +08:00

136 lines
7.4 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.

# 支付流程(当前实现)
本文档描述充值页「Buy」点击后的完整支付流程`recharge_screen.dart``PaymentApi``GooglePlayPurchaseService` 实现一致。
---
## 1. 流程总览
```
用户点击 Buy
├─ enable_third_party_payment === true 且已登录
│ │
│ ├─ getPaymentMethods(activityId)
│ ├─ 弹窗选择支付方式_PaymentMethodDialog
│ ├─ createPayment(activityId, productId, paymentMethod, subPaymentMethod)
│ │
│ ├─ 若选中的是 Google Payresource/ceremony == "GooglePay"
│ │ ├─ 调起 Google Play 内购productId = item.code
│ │ ├─ 拿到 serverVerificationData
│ │ └─ POST /v1/payment/googlepay(merchant, federation, asset)
│ │
│ └─ 否则(其他支付方式)
│ └─ 打开 createPayment 返回的 payUrlconvert在外部浏览器完成支付
└─ enable_third_party_payment !== true 或未登录
└─ 仅 Android直接调起 Google Play 内购productId = item.code无 createPayment
```
---
## 2. 支付分支依据
- **数据来源**`/v1/user/common_info` 响应中的 **surge**JSON 字符串),解析得到 **enable_third_party_payment**
- **客户端状态**`UserState.enableThirdPartyPayment`(登录后由 AuthService 从 common_info 写入)。
- **分支**
- **第三方支付**`enable_third_party_payment == true``UserState.userId` 非空 → 走「获取支付方式 → 弹窗选择 → 创建订单 → 按支付方式分支」。
- **直接谷歌支付**:否则(未开三方或未登录)→ 仅 Android 下直接调起 Google Play 内购,不调 getPaymentMethods / createPayment。
---
## 3. 支付界面与商品展示
- **界面**`recharge_screen.dart`
- **商品来源**`GET /v1/payment/getGooglePayActivities`Android或 getApplePayActivitiesiOS列表为 data.summonactivitys
- **单条商品字段V2 映射)**
| 字段(映射后) | 说明 |
|----------------|------|
| helm | 产品代码,即 **Google Pay 商品 ID**(与 Play 后台「产品 ID」必须一致 |
| warrior | 活动 IDactivityIdgetPaymentMethods / createPayment 必传 |
| guardian | 实际金额,界面带 **$** 显示 |
| curriculum | 原价,界面**中划线**显示 |
| forge | 赠送积分,界面展示 |
| glossary | 标题greaves=积分数familiar=货币等 |
- **界面规则**:价格 / 原价 / 赠送积分同一行展示;仅当前点击的 item 的 Buy 按钮显示 loading_loadingProductId
---
## 4. 第三方支付流程enable_third_party_payment === true
### 4.1 步骤顺序
| 步骤 | 说明 |
|------|------|
| 1 | 用户点击某商品的 Buy得到该商品的 **activityId**warrior、**productId**helm/code。 |
| 2 | **POST /v1/payment/get-payment-methods**bodywarrior=activityIdvambrace=国家(可选)。 |
| 3 | 弹窗 **支付方式列表**renew用户选择一项 → 得到 resourcepaymentMethod、ceremonysubPaymentMethod。 |
| 4 | **POST /v1/payment/createPayment**bodysentinel, asset(userId), warrior(activityId), resource, ceremony得到 federation订单 ID、convertpayUrl等。 |
| 5a | 若 **resource 或 ceremony 为 "GooglePay"**(不区分大小写):调起 Google Play 内购productId=item.code成功后用 serverVerificationData 作为 merchant 调用 **POST /v1/payment/googlepay**merchant, federation, asset不打开 payUrl。 |
| 5b | 否则:若有 **convert**payUrl则用 url_launcher 在外部浏览器打开;无则仅提示订单已创建。 |
### 4.2 支付方式弹窗
- 在 get-payment-methods 成功后,展示 **Select payment method** 弹窗_PaymentMethodDialog列表项来自 **renew**PaymentMethodItemresource, ceremony, name, icon, recommend 等)。
- 用户选择一项后关闭弹窗,用选中的 resource、ceremony 调用 createPayment取消弹窗则不创建订单并清除 loading。
### 4.3 选中 Google Pay 时的子流程
- 条件:`_isGooglePay(paymentMethod, subPaymentMethod)` 为 true即 paymentMethod 或 subPaymentMethod 转为小写后等于 `"googlepay"`)。
- 仅 Android 执行;非 Android 提示 "Google Pay is only available on Android" 并结束。
- 调用 **GooglePlayPurchaseService.launchPurchaseAndReturnData(productId)**
- productId 为当前商品的 **code**helm
- 成功返回 **serverVerificationData**merchant失败/取消返回 null。
- 若有 merchant 且订单 IDfederation存在调用 **PaymentApi.googlepay(merchant, federation, asset)**根据返回提示成功或失败并打点AdjustEvents
---
## 5. 直接谷歌支付enable_third_party_payment !== true
-**Android**:调用 **GooglePlayPurchaseService.launchPurchase(item.code)**,不请求 getPaymentMethods / createPayment。
- 成功/失败通过 SnackBar 与 AdjustEvents 打点;商品 ID 仍为 **item.code**(须与 Play 后台产品 ID 一致)。
- 非 Android提示 "Google Pay is only available on Android"。
---
## 6. 接口与字段速查
- **GET /v1/payment/getGooglePayActivities**
Querysentinel, portal响应 data.summon / data.cleanse单项含 helm, warrior, guardian, curriculum, forge 等。
- **POST /v1/payment/get-payment-methods**
Bodywarrior(activityId), vambrace(可选)。响应 data.renew每项 resource, ceremony, brigade, greylist, deny 等。
- **POST /v1/payment/createPayment**
Querysentinel, asset(userId)。Bodysentinel, asset, warrior, resource, ceremony(可选)。
响应 datafederation(订单ID), convert(payUrl), destroy(状态), handshake, transplant 等。
- **POST /v1/payment/googlepay**
Querysentinel, asset(userId)。Bodymerchant(购买凭据 serverVerificationData), federation(订单ID), asset, sample(签名可选)。
响应 datafederation, line(状态), awaken(是否加积分) 等。
- 请求/响应 V2 加解密与字段映射以 **petsHeroAI_client_guide.md** 为准。
---
## 7. 代码与文档对应
| 功能 | 位置 |
|------|------|
| 支付分支与 Buy 入口 | recharge_screen.dart_onBuy → enableThirdPartyPayment ? _runThirdPartyPayment : _runGooglePay |
| 第三方:获取支付方式 + 弹窗 | _runThirdPartyPaymentPaymentApi.getPaymentMethods → _PaymentMethodDialog |
| 第三方:创建订单 + Google Pay / 打开链接 | _createOrderAndOpenUrlcreatePayment → _isGooglePay ? launchPurchaseAndReturnData + googlepay : launchUrl(convert) |
| 直接谷歌支付 | _runGooglePay → _launchGooglePlayPurchase → GooglePlayPurchaseService.launchPurchase |
| 谷歌内购 + 凭据上报 | google_play_purchase_service.dartlaunchPurchaseAndReturnData / launchPurchasePaymentApi.googlepay |
| 商品未找到排查 | docs/google_pay_product_not_found.md |
---
## 8. 小结
- **三方开**getPaymentMethods → 弹窗选支付方式 → createPayment → 选 Google Pay 则内购 + googlepay 回调,否则打开 payUrl。
- **三方关**:仅 Android 直接内购item.code无 createPayment。
- **商品 ID**:始终使用接口返回的 **helm**item.code须与 Google Play 后台「产品 ID」完全一致否则会出现「系统无法找到您要购买的商品」。