# 支付流程(当前实现) 本文档描述充值页「Buy」点击后的完整支付流程,与 `recharge_screen.dart`、`PaymentApi`、`GooglePlayPurchaseService` 实现一致。 --- ## 1. 流程总览 ``` 用户点击 Buy │ ├─ lucky === true 且已登录 │ │ │ ├─ getPaymentMethods(activityId) │ ├─ 弹窗选择支付方式(_PaymentMethodDialog) │ ├─ createPayment(activityId, productId, paymentMethod, subPaymentMethod) │ │ │ ├─ 若选中的是 Google Pay(resource/ceremony == "GooglePay") │ │ ├─ 调起 Google Play 内购(productId = item.code) │ │ ├─ 拿到 serverVerificationData │ │ └─ POST /v1/payment/googlepay(merchant, federation, asset) │ │ │ └─ 否则(其他支付方式) │ └─ 打开 createPayment 返回的 payUrl(convert)在外部浏览器完成支付 │ └─ lucky !== true 或未登录 └─ 仅 Android:直接调起 Google Play 内购(productId = item.code),无 createPayment ``` --- ## 2. 支付分支依据 - **数据来源**:`/v1/user/common_info` 响应中的 **surge**(JSON 字符串),解析得到 **lucky**。 - **客户端状态**:`UserState.enableThirdPartyPayment`(登录后由 AuthService 从 common_info 写入)。 - **分支**: - **第三方支付**:`lucky == true` 且 `UserState.userId` 非空 → 走「获取支付方式 → 弹窗选择 → 创建订单 → 按支付方式分支」。 - **直接谷歌支付**:否则(未开三方或未登录)→ 仅 Android 下直接调起 Google Play 内购,不调 getPaymentMethods / createPayment。 --- ## 3. 支付界面与商品展示 - **界面**:`recharge_screen.dart`。 - **商品来源**:`GET /v1/payment/getGooglePayActivities`(Android)或 getApplePayActivities(iOS),列表为 data.summon(activitys)。 - **单条商品字段(V2 映射)**: | 字段(映射后) | 说明 | |----------------|------| | helm | 产品代码,即 **Google Pay 商品 ID**(与 Play 后台「产品 ID」必须一致) | | warrior | 活动 ID(activityId),getPaymentMethods / createPayment 必传 | | guardian | 实际金额,界面带 **$** 显示 | | curriculum | 原价,界面**中划线**显示 | | forge | 赠送积分,界面展示 | | glossary | 标题;greaves=积分数,familiar=货币等 | - **界面规则**:价格 / 原价 / 赠送积分同一行展示;仅当前点击的 item 的 Buy 按钮显示 loading(_loadingProductId)。 --- ## 4. 第三方支付流程(lucky === true) ### 4.1 步骤顺序 | 步骤 | 说明 | |------|------| | 1 | 用户点击某商品的 Buy,得到该商品的 **activityId**(warrior)、**productId**(helm/code)。 | | 2 | **POST /v1/payment/get-payment-methods**,body:warrior=activityId,vambrace=国家(可选)。 | | 3 | 弹窗 **支付方式列表**(renew),用户选择一项 → 得到 resource(paymentMethod)、ceremony(subPaymentMethod)。 | | 4 | **POST /v1/payment/createPayment**,body:sentinel, asset(userId), warrior(activityId), resource, ceremony;得到 federation(订单 ID)、convert(payUrl)等。 | | 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**(PaymentMethodItem:resource, 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 且订单 ID(federation)存在,调用 **PaymentApi.googlepay(merchant, federation, asset)**;根据返回提示成功或失败并打点(AdjustEvents)。 --- ## 5. 直接谷歌支付(lucky !== true) - 仅 **Android**:调用 **GooglePlayPurchaseService.launchPurchaseAndReturnData(item.code)**,不请求 getPaymentMethods / createPayment;凭据可用于后续服务端回调。 - 成功/失败通过 SnackBar 与 AdjustEvents 打点;商品 ID 仍为 **item.code**(须与 Play 后台产品 ID 一致)。 - 非 Android:提示 "Google Pay is only available on Android"。 --- ## 6. 接口与字段速查 - **GET /v1/payment/getGooglePayActivities** Query:sentinel, portal;响应 data.summon / data.cleanse,单项含 helm, warrior, guardian, curriculum, forge 等。 - **POST /v1/payment/get-payment-methods** Body:warrior(activityId), vambrace(可选)。响应 data.renew:每项 resource, ceremony, brigade, greylist, deny 等。 - **POST /v1/payment/createPayment** Query:sentinel, asset(userId)。Body:sentinel, asset, warrior, resource, ceremony(可选)。 响应 data:federation(订单ID), convert(payUrl), destroy(状态), handshake, transplant 等。 - **POST /v1/payment/googlepay** Query:sentinel, asset(userId)。Body:merchant(购买凭据 serverVerificationData), federation(订单ID), asset, sample(签名可选)。 响应 data:federation, line(状态), awaken(是否加积分) 等。 - 请求/响应 V2 加解密与字段映射以 **petsHeroAI_client_guide.md** 为准。 --- ## 7. 代码与文档对应 | 功能 | 位置 | |------|------| | 支付分支与 Buy 入口 | recharge_screen.dart:_onBuy → enableThirdPartyPayment ? _runThirdPartyPayment : _runGooglePay | | 第三方:获取支付方式 + 弹窗 | _runThirdPartyPayment:PaymentApi.getPaymentMethods → _PaymentMethodDialog | | 第三方:创建订单 + Google Pay / 打开链接 | _createOrderAndOpenUrl:createPayment → _isGooglePay ? launchPurchaseAndReturnData + googlepay : launchUrl(convert) | | 直接谷歌支付 | _runGooglePay → _launchGooglePlayPurchase → GooglePlayPurchaseService.launchPurchaseAndReturnData | | 谷歌内购 + 凭据上报 | google_play_purchase_service.dart:launchPurchaseAndReturnData;PaymentApi.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」完全一致,否则会出现「系统无法找到您要购买的商品」。