# 谷歌支付流程 本文档描述 Android 上 Google Play 内购的完整流程,与 `recharge_screen.dart`、`GooglePlayPurchaseService`、`PaymentApi` 实现对应。 --- ## 1. 流程总览 - **第三方支付开启**(`enable_third_party_payment === true` 且已登录):先创建订单 → 调起谷歌支付 → 支付成功后回调 `/v1/payment/googlepay`。 - **第三方支付关闭或未登录**:仅 Android 直接调起谷歌支付,不创建订单、不回调 googlepay。 ``` 用户点击 Buy(某商品) │ ├─ 第三方支付开 + 已登录 │ ├─ getPaymentMethods(activityId) │ ├─ 弹窗选择支付方式 │ ├─ 若选「Google Pay」 │ │ ├─ POST /v1/payment/createPayment → 得到 federation(订单 id) │ │ ├─ 调起 Google Play 内购(productId = 商品 code/helm) │ │ └─ 支付成功后 POST /v1/payment/googlepay(见下文) │ └─ 若选其他方式 → 打开 createPayment 返回的 payUrl │ └─ 第三方支付关或未登录 └─ 仅 Android:直接调起 Google Play 内购(productId = 商品 code),无 createPayment、无 googlepay 回调 ``` --- ## 2. 创建订单(仅第三方 + 选 Google Pay 时) | 项目 | 说明 | |------|------| | 接口 | `POST /v1/payment/createPayment` | | 入参 | sentinel, asset(userId), warrior(activityId), resource, ceremony(选 Google Pay 时 resource/ceremony 含 "GooglePay") | | 关键响应 | **federation**:订单 id,后续 googlepay 回调必传;**convert**:其他支付方式的 payUrl,选 Google Pay 时不使用 | - 当返回的 **federation(订单 id)不为空**时,继续调起谷歌支付并可在成功后回调 googlepay。 - 当返回的 federation 为空时:可按业务要求重试创建订单(如最多 3 次),仍失败则提示失败。 --- ## 3. 调起谷歌支付 | 项目 | 说明 | |------|------| | 代码 | `GooglePlayPurchaseService.launchPurchaseAndReturnData(productId)`(所有内购统一使用) | | productId | 当前商品的 **code**(即接口里的 **helm**),须与 Google Play 后台「产品 ID」完全一致 | | 成功结果 | 返回的凭据用于构造 googlepay 回调 body(见下节) | 仅 **Android** 执行;非 Android 提示 "Google Pay is only available on Android"。 --- ## 4. 支付成功后回调:`POST /v1/payment/googlepay` 仅在**第三方支付开 + 选 Google Pay + 已先创建订单**时调用。请求体为 JSON,服务端用于校验并落单。 ### 4.1 请求体字段(body) 请求体为四个顶层字段,语义与取值来源对应关系如下(id / purchaseData / signature / userId 分别对应 federation / merchant / sample / asset): | 请求体字段 | 含义(填入的值) | 客户端取值来源 | |------------|------------------|----------------| | **sample** | 签名(signature) | 谷歌支付成功后,`GooglePlayPurchaseDetails.billingClientPurchase` 的 **signature** | | **merchant** | 购买凭据 JSON(purchaseData) | 同上 **billingClientPurchase** 的 **originalJson** | | **federation** | 支付/订单 id(id) | 创建订单接口 `createPayment` 返回的 **federation** | | **asset** | 用户 id(userId) | 当前登录用户 id(与 createPayment 的 asset 一致) | ### 4.2 示例 body 结构 ```json { "sample": "YbOntv0sVOsZ5d4F8hIYdPNSMy9a4+5oAsV/...", "merchant": "{\"orderId\":\"GPA.3327-0087-2324-9960\",\"packageName\":\"com.xxx.xxxx\",\"productId\":\"com.xxx.xxxx599\",\"purchaseTime\":1773305500428,\"purchaseState\":0,\"purchaseToken\":\"...\",\"quantity\":1,\"acknowledged\":false}", "federation": "1315538320560683421235", "asset": "135303839048" } ``` - **federation**:来自 createPayment 的 **federation**(服务端订单 id)。 - **merchant**:来自 Google Play 的 **originalJson**(整段购买凭据 JSON 字符串)。 - **sample**:来自 Google Play 的 **signature**,服务端用其校验 merchant。 - **asset**:当前用户 id。 ### 4.3 与客户端实现对应 - 内购成功后,从 `GooglePlayPurchaseDetails.billingClientPurchase`(`PurchaseWrapper`)取 **originalJson**、**signature**,分别填入请求体的 **merchant**、**sample**。 - createPayment 返回的 **federation** 填入 **federation**,当前用户 id 填入 **asset**。 --- ## 5. 获取未核销订单 「未核销」指 Google Play 侧尚未被确认(`isAcknowledged == false`)的购买,通常出现在:上次支付成功后未完成 `completePurchase`、或未成功回调服务端即退出应用。 ### 5.1 客户端如何获取 - **方法**:`GooglePlayPurchaseService.getUnacknowledgedPurchases()` - **实现**:仅 Android,通过 `InAppPurchaseAndroidPlatformAddition.queryPastPurchases()` 查询本地/缓存的购买,再筛选 `billingClientPurchase.isAcknowledged == false` 的项。 - **返回**:`List`,每项包含 `orderId`(Google 订单号)、`productId`、`payload`(purchaseData + signature,用于回调 body 的 merchant/sample)。 ### 5.2 典型用法 - **应用启动时**:调用 `getUnacknowledgedPurchases()`,若有未核销订单,可逐条上报 `POST /v1/payment/googlepay`(federation 可用该笔的 `orderId`,若服务端支持;否则需先 createPayment 拿到 federation 再回调),上报成功后对该笔购买调用 `InAppPurchase.instance.completePurchase(purchase)` 完成核销。 - **注意**:`queryPastPurchases` 不包含已消耗(consumed)的商品;未确认的消耗型商品会一直在列表中直到被确认或消耗。 --- ## 6. 代码位置速查 | 步骤 | 位置 | |------|------| | Buy 分支(第三方 vs 直接谷歌) | `recharge_screen.dart`:`_onBuy` → `_runThirdPartyPayment` / `_runGooglePay` | | 创建订单 | `PaymentApi.createPayment`;调用处在 `_createOrderAndOpenUrl` | | 调起内购并拿凭据 | `GooglePlayPurchaseService.launchPurchaseAndReturnData(productId)` | | 回调 googlepay | `PaymentApi.googlepay(...)`,在 `_createOrderAndOpenUrl` 内、内购成功后调用 | | 凭据数据结构 | Android:`GooglePlayPurchaseDetails.billingClientPurchase`(orderId, originalJson, signature) | | 获取未核销订单 | `GooglePlayPurchaseService.getUnacknowledgedPurchases()`,见第 5 节 | --- ## 7. 小结 1. **创建订单**:仅在选择「Google Pay」且第三方支付开启时调用 createPayment;拿到 **federation** 后才会调起谷歌支付并回调 googlepay。 2. **调起谷歌支付**:productId 固定为当前商品的 **code(helm)**,与 Play 后台产品 ID 一致。 3. **回调 googlepay**:body 为四字段 **sample**(signature)、**merchant**(purchaseData/originalJson)、**federation**(订单 id)、**asset**(userId);federation 为空则不回调,可按策略重试创建订单或提示失败。 4. **未核销订单**:通过 `getUnacknowledgedPurchases()` 获取 `isAcknowledged == false` 的购买,可用于启动时补发回调或展示待处理订单。