更新记录

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 参数增加 oldPurchaseTokenreplacementMode
  • 优化:示例项目重构,增加可视化调试面板与操作引导
查看更多

平台兼容性

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.INTERNET
  • com.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:acknowledgePurchase 5) App 启动/用户登录/恢复购买入口:queryPurchases 恢复权益

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 未找到商品详情 未先查询商品或缓存丢失 queryProductslaunchPurchase
9000005 订阅缺少 offerToken 订阅未选 offer queryProducts,再传入 offers[0].offerToken

订阅 offerToken 如何获取

订阅商品购买通常需要指定 offer。插件会在 queryProducts 返回中尽量提供易用信息:

  • ProductDetails.offerToken:默认 offerToken(通常取第一个)
  • ProductDetails.offers:简化的 offer 列表(每个 offer 有 offerToken 与各个 pricing phases 的价格)
  • 同时保留 _raw(原始 Google ProductDetails 对象),用于你需要按 basePlan/offerTag 等更复杂策略做筛选

推荐用法(优先用 offers): 1) queryProducts 得到 products 2) const offerToken = products[0].offers?.[0]?.offerToken 3) launchPurchase({ productType:'subs', productId, offerToken })

如果你希望展示“试用价/首月价/续费价”等多阶段价格,可读取 offers[0].pricingPhases

订阅升级/降级/切换

当用户已经拥有一个订阅(如月卡),想要切换到另一个订阅(如年卡)时,需要使用“订阅替换”功能,否则会出现“您已拥有该订阅”的错误。

使用方法

在调用 launchPurchase 时,额外传入 oldPurchaseTokenreplacementMode

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) 客户端拉起购买成功后,将 purchaseTokenproductIdorderNo 回传服务端做绑定 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 订单数据):

  • obfuscatedAccountId
  • obfuscatedProfileId

建议传入 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

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.INTERNET,com.android.vending.BILLING

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: