更新记录
1.0.3(2026-03-09)
优化代码
1.0.2(2026-03-03)
- 新增:
launchPurchase支持传obfuscatedAccountId/obfuscatedProfileId用于服务端关联 - 优化:订阅购买自动选择默认
offerToken,并补充offers结构,便于前端展示/选择 - 优化:补充错误码
12(网络错误)与9000005(订阅缺少 offerToken),并增强 debugMessage 中文化
1.0.1(2026-03-02)
- 新增:支持订阅切换功能(升级/降级/更换套餐)
- 新增:
launchPurchase参数增加oldPurchaseToken和replacementMode - 优化:示例项目重构,增加可视化调试面板与操作引导
平台兼容性
uni-app(4.52)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 5.0 | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.52)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | 5.0 | - | - | - |
jwh-GooglePay
Google Play Billing(Google 内购/订阅)UTS 插件,面向 uni-app uni-appx。
特性
- 基于 Google Play Billing Library(PBL)8.x
- 支持 内购(inapp) / 订阅(subs)
- 内置本地 AAR 依赖,避免云打包拉取 Maven 失败
- 支持完整流程:初始化、查询商品、发起购买、消耗、确认、查询已购
- 失败回调返回统一错误码与中文说明
支持平台
- Android(uni-app uni-app x)
- iOS / Harmony:暂不支持
前置条件
- Android
minSdkVersion >= 21 - 设备需具备并可用:Google Play 商店、Google Play 服务(GMS)
- 开发/测试建议使用:
- 真机:已认证且带完整 GMS 的设备
- 模拟器:Android Studio AVD 的 “Google Play” 系统镜像
权限说明
插件默认声明:
android.permission.INTERNETcom.android.vending.BILLING
快速开始(建议先跑通)
推荐按顺序跑通以下 5 步,确保 Play Console / 测试轨道 / 设备环境都没问题:
1) initBilling 初始化连接
2) queryProducts 查询商品(inapp/subs)
3) launchPurchase 拉起收银台
4) 购买成功后:
- inapp:按业务选择
consumePurchase(可重复购买的消耗型道具)或不消耗(一次性权益) - subs:
acknowledgePurchase(订阅必须确认,否则可能退款) 5)queryPurchases查询已购(恢复购买/校对权益)
你也可以直接运行示例页面进行验证(示例工程里的页面):(https://ext.dcloud.net.cn/plugin?id=26842)
目录结构
utssdk/app-android/libs/billing8.aar: Google Play Billing AAR(已内置)utssdk/interface.uts: 接口定义utssdk/unierror.uts: 错误码定义utssdk/app-android/index.uts: Android 原生实现
安装与使用
1. 安装插件
将插件安装到 uni_modules/jwh-GooglePay 后即可使用。
2. Play Console 配置(非常重要)
Google 内购/订阅必须在 Play Console 配置完成并满足测试条件,否则会出现商品不可用、计费服务不可用、无法拉起收银台等问题。
建议检查:
- 已创建应用并完成基础信息
- 已创建并激活商品(inapp/subs),商品 ID 与代码一致
- 应用已上传到测试轨道(内部测试/封闭测试等)
- 测试账号已加入测试人员列表,并在设备 Play 商店登录该账号
- 设备区域/商品销售区域匹配
3. 推荐接入流程(客户端)
推荐顺序:
1) 页面初始化或点击按钮:initBilling
2) 展示商品列表前:queryProducts(会缓存 ProductDetails,购买必须依赖缓存)
3) 用户点击购买:launchPurchase
4) 购买成功后:
- 建议先将
purchaseToken上传服务端校验(防止本地伪造) - 服务端校验通过后:
- inapp:按你的商品类型决定是否
consumePurchase - subs:
acknowledgePurchase5) App 启动/用户登录/恢复购买入口:queryPurchases恢复权益
- inapp:按你的商品类型决定是否
4. 代码示例(最小可用)
4.1 初始化
import { initBilling } from '@/uni_modules/jwh-GooglePay';
initBilling({
success: () => {
console.log("初始化成功");
},
fail: (err) => {
console.error("初始化失败", err);
}
});
4.2 查询商品
import { queryProducts } from '@/uni_modules/jwh-GooglePay';
queryProducts({
productIds: ['your_product_id'],
productType: 'inapp', // 或 'subs'
success: (products) => {
console.log("商品详情", products);
}
});
4.3 发起购买(内购 / 订阅)
import { launchPurchase } from '@/uni_modules/jwh-GooglePay';
launchPurchase({
productId: 'your_product_id',
productType: 'inapp', // 或 'subs'
success: (res) => {
console.log("购买成功", res);
// 接下来需要消耗或确认
}
});
4.4 消耗/确认购买
注意:购买完成后需要按产品类型执行 消耗(consume) 或 确认(acknowledge)。建议在服务端校验购买凭证后再发放权益。
import { consumePurchase, acknowledgePurchase } from '@/uni_modules/jwh-GooglePay';
// 内购项目 (消耗)
consumePurchase({
purchaseToken: '...',
success: (token) => console.log("消耗成功")
});
// 订阅项目 (确认)
acknowledgePurchase({
purchaseToken: '...',
success: (token) => console.log("确认成功")
});
4.5 查询已购(恢复购买)
import { queryPurchases } from '@/uni_modules/jwh-GooglePay';
queryPurchases({
productType: 'subs',
success: (list) => {
console.log('已购订阅', list);
}
})
API 说明
initBilling(options)
- 作用:初始化并连接 BillingClient
- 常见失败:设备无 GMS、Play 服务不可用、网络不可达等
queryProducts(options)
- 作用:查询商品详情(并缓存 ProductDetails 用于购买流程)
- 参数:
productIds: string[]productType: "inapp" | "subs"
- 返回:
ProductDetails[](包含_raw字段) - 订阅返回补充字段:
offerToken:默认会给一个可用 offerToken(通常是第一个 offer)offers:简化后的 offer 列表(包含各阶段价格),用于你在前端选择某个 offer
launchPurchase(options)
- 作用:发起购买
- 注意:
- 必须先
queryProducts,插件会从缓存里取ProductDetails - 订阅(subs)建议传
offerToken;若不传,插件会自动选择一个默认 offerToken(通常是第一个 offer)
- 必须先
- 参数增强(订阅切换):
oldPurchaseToken: string(可选,仅切换订阅时需要)replacementMode: number(可选,切换模式,默认 1)
- 参数增强(关联用户标识):
obfuscatedAccountId: string(可选,建议传 hash/脱敏后的用户标识,用于服务端关联)obfuscatedProfileId: string(可选,建议传 hash/脱敏后的 profile 标识,用于服务端关联)
consumePurchase(options)
- 作用:消耗购买(通常用于消耗型道具)
acknowledgePurchase(options)
- 作用:确认购买(订阅/不可消耗产品通常需要确认)
queryPurchases(options)
- 作用:查询当前账号下已购买项目(恢复购买/校对权益)
常见接入场景
场景 A:消耗型道具(可重复购买)
1) 用户购买成功拿到 purchaseToken
2) 服务端校验通过后发放道具
3) 立即调用 consumePurchase,让同一商品可再次购买
场景 B:非消耗型内购(一次购买永久权益)
1) 用户购买成功拿到 purchaseToken
2) 服务端校验通过后发放永久权益
3) 不调用 consumePurchase(否则会变成可重复购买)
场景 C:订阅(自动续费)
1) 用户订阅成功拿到 purchaseToken
2) 服务端校验通过后开通会员
3) 调用 acknowledgePurchase(建议在服务端确认无误后再确认)
4) 后续权益更新依赖服务端对订阅状态的轮询校验或 RTDN 通知
错误码与失败原因(中文)
插件的失败回调统一返回 UniError(包含 errCode / errMsg)。其中:
errCode:尽量与 Google Play Billing 的BillingResponseCode保持一致;另外提供了 9 开头的自定义错误码。errMsg:默认会从utssdk/unierror.uts中的BillingErrors映射取中文;如果 Google 返回了debugMessage,会尽量翻译为中文并拼接说明(少量情况仍可能保留英文原文)。
常见 errCode 对照
| errCode | 中文含义 | 常见触发场景 | 处理建议 |
|---|---|---|---|
| -3 | 服务超时 | 与 Play Billing 服务通信超时 | 检查网络、重试、确保 Play 商店可用 |
| -2 | 功能不支持 | 当前设备/版本不支持对应能力 | 升级 Play 商店/系统,或降级能力使用 |
| -1 | 服务连接已断开 | Billing 服务断开 | 重新 initBilling 连接 |
| 1 | 用户取消支付 | 用户在收银台主动取消 | 视为正常流程,提示用户即可 |
| 2 | 服务不可用 | Play 服务临时不可用 | 检查网络/区域/Play 商店状态,稍后重试 |
| 3 | 计费服务不可用 | 设备不支持计费/缺少 Play 商店 | 确保安装官方 Play 商店并登录可用账号 |
| 4 | 商品不可用 | 商品在当前账号/区域不可售 | 检查 Play Console 配置、上架状态、测试账号、区域 |
| 5 | 开发者错误 | 参数/签名/配置错误 | 检查商品 ID、签名、调用时机与参数 |
| 6 | 未知错误 | Google 返回通用错误 | 结合 errMsg(debugMessage)进一步定位 |
| 7 | 商品已购买/已拥有 | 重复购买了不可重复商品 | 内购非消耗品/订阅已在有效期,走“恢复/查询已购” |
| 8 | 未拥有该商品 | 尝试消耗/确认未拥有的订单 | 先查询已购,确保 token 正确且未过期 |
| 12 | 网络错误 | 网络不稳定/Play 服务异常 | 切换网络、关闭代理/VPN、稍后重试 |
| 9000001 | 获取 Context 失败 / 初始化 BillingClient 失败 | initBilling 获取不到 Context |
确认在 App 正常启动后调用,避免过早执行 |
| 9000002 | BillingClient 未初始化或未连接 | 未先 initBilling 或连接已断开 |
先调用 initBilling,必要时重连 |
| 9000003 | 未获取到当前 Activity | 发起购买时无前台 Activity | 确保在前台页面触发购买,不要在后台任务中拉起 |
| 9000004 | 未找到商品详情 | 未先查询商品或缓存丢失 | 先 queryProducts 再 launchPurchase |
| 9000005 | 订阅缺少 offerToken | 订阅未选 offer | 先 queryProducts,再传入 offers[0].offerToken |
订阅 offerToken 如何获取
订阅商品购买通常需要指定 offer。插件会在 queryProducts 返回中尽量提供易用信息:
ProductDetails.offerToken:默认 offerToken(通常取第一个)ProductDetails.offers:简化的 offer 列表(每个 offer 有offerToken与各个 pricing phases 的价格)- 同时保留
_raw(原始 GoogleProductDetails对象),用于你需要按 basePlan/offerTag 等更复杂策略做筛选
推荐用法(优先用 offers):
1) queryProducts 得到 products
2) const offerToken = products[0].offers?.[0]?.offerToken
3) launchPurchase({ productType:'subs', productId, offerToken })
如果你希望展示“试用价/首月价/续费价”等多阶段价格,可读取 offers[0].pricingPhases。
订阅升级/降级/切换
当用户已经拥有一个订阅(如月卡),想要切换到另一个订阅(如年卡)时,需要使用“订阅替换”功能,否则会出现“您已拥有该订阅”的错误。
使用方法
在调用 launchPurchase 时,额外传入 oldPurchaseToken 和 replacementMode。
launchPurchase({
productId: 'new_yearly_sub_id', // 新订阅 ID
productType: 'subs',
oldPurchaseToken: 'old_monthly_token_xxx', // 当前正在生效的旧订阅 Token
replacementMode: 1, // (可选) 替换模式
success: (res) => {
console.log("切换成功", res);
}
});
ReplacementMode 说明
| 值 | 模式常量 | 说明 |
|---|---|---|
| 1 | WITH_TIME_PRORATION |
(默认) 剩余时间按比例折算并立即生效新订阅 |
| 2 | CHARGE_PRORATED_PRICE |
收取差价,立即生效 |
| 3 | WITHOUT_PRORATION |
不折算,新订阅立即生效(旧订阅剩余时间作废) |
| 4 | DEFERRED |
推迟生效(旧订阅过期后,新订阅自动生效) |
| 5 | CHARGE_FULL_PRICE |
收取全额,立即生效 |
购买如何关联用户/订单(服务端发放权益)
Google Play Billing 不支持在收银台购买请求里携带任意自定义字段(例如你方订单号),Google 的服务端回调(RTDN)也不会把你方自定义字段带回。
推荐做法(标准链路)
1) 客户端先向你方服务端创建“待支付订单”,服务端返回 orderNo
2) 客户端拉起购买成功后,将 purchaseToken、productId、orderNo 回传服务端做绑定
3) 服务端通过 Google Play Developer API 校验 purchaseToken 后发放权益
4) 后续收到 RTDN(Real-time Developer Notifications)时,通过通知里的 purchaseToken 找到 orderNo / userId 做续期、取消、退款等补偿与对账
为什么一定要服务端校验
purchaseToken是发放权益的关键凭证,必须在服务端通过 Google 官方接口校验其真实性与状态- 前端回调只代表“本机收银台流程完成”,不代表“你方应该立即发权益”(例如 Pending、未确认、已退款等情况)
建议的后端接口设计(示例)
你可以按以下思路设计接口(仅示例,路径与字段可按你方规范调整):
1) 创建订单(预下单)
POST /iap/order/create
{
"userId": "u_123",
"productId": "vip_yearly",
"productType": "subs"
}
响应:
{
"orderNo": "GP20260303XXXX"
}
2) 绑定并校验(支付成功后调用)
POST /iap/purchase/verify
{
"orderNo": "GP20260303XXXX",
"productId": "vip_yearly",
"productType": "subs",
"purchaseToken": "xxxx"
}
服务端动作建议:
- 用 Google Play Developer API 校验
purchaseToken(校验通过才发权益) - 保存一条“purchaseToken -> orderNo/userId/productId”的映射
- 确保接口幂等:同一个
purchaseToken重复请求不能重复发放
RTDN(服务端回调/订阅续期通知)如何关联用户
Google 的 RTDN 通知里会包含本次变更对应的 purchaseToken(订阅/一次性商品都会有对应的通知类型)。你方服务端收到通知后:
1) 从通知里取出 purchaseToken
2) 用本地映射表查到 orderNo / userId / productId
3) 再去调用 Google Play Developer API 取最新状态,做续期、取消、退款、暂停等权益变更
不建议依赖 orderId 做强关联:它可能为空或在不同场景下不可用,且不如 purchaseToken 稳定可靠。
插件额外支持传递 Google 官方提供的关联字段(会进入 Google 订单数据):
obfuscatedAccountIdobfuscatedProfileId
建议传入 hash/脱敏值,不要直接传手机号/邮箱等敏感信息。
提示:obfuscatedAccountId/obfuscatedProfileId 是 Google 官方支持的“关联标识”,但它们不是你方订单号;你仍然需要在你方服务端保存 purchaseToken -> userId/orderNo 的映射来做精确对账。
示例页面(调试面板)
示例工程提供了一个“全功能示例页”,用于验证:
- 初始化连接、查询商品、购买/订阅
- 订阅切换:填写旧订阅
purchaseToken并选择replacementMode - 查询已购、手动消耗/确认、日志复制
文件位置:index.vue
云打包失败 / 找不到 AAR
- 确保
uni_modules/jwh-GooglePay/utssdk/app-android/libs/billing8.aar存在
初始化失败:Billing service unavailable on device
- 设备缺少/禁用 Google Play 商店或 Google Play 服务(GMS)
- 使用 Android Studio AVD 时请选择带 “Google Play” 的系统镜像
- 第三方模拟器(含部分 MuMu 版本)通常不稳定或不可用,建议换真机或 AVD
无法调起支付/收银台
- 检查 Play Console:测试轨道、测试账号、商品状态、区域是否匹配
- 确保设备 Play 商店已登录测试账号,且网络可访问 Google 服务
如何更新 Billing AAR
本插件内置 billing8.aar,如果你需要升级到更高版本,可从 Google Maven 下载对应版本的 AAR,并替换 utssdk/app-android/libs/billing8.aar。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 23
赞赏 0
下载 11502213
赞赏 1882
赞赏
京公网安备:11010802035340号