7.0 KiB
7.0 KiB
谷歌支付流程
本文档描述 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 结构
{
"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<UnacknowledgedGooglePayPurchase>,每项包含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. 小结
- 创建订单:仅在选择「Google Pay」且第三方支付开启时调用 createPayment;拿到 federation 后才会调起谷歌支付并回调 googlepay。
- 调起谷歌支付:productId 固定为当前商品的 code(helm),与 Play 后台产品 ID 一致。
- 回调 googlepay:body 为四字段 sample(signature)、merchant(purchaseData/originalJson)、federation(订单 id)、asset(userId);federation 为空则不回调,可按策略重试创建订单或提示失败。
- 未核销订单:通过
getUnacknowledgedPurchases()获取isAcknowledged == false的购买,可用于启动时补发回调或展示待处理订单。