更新记录

1.3.3(2026-08-11)

增加跨平台兜底入口,解决非 Android 平台使用插件代码打包报错问题

1.3.2(2026-08-04)

升级 Google Play Billing Library 至 9.1.0

1.2.0(2025-08-28)

初始化增加参数配置,优化插件代码,demo增加vue页面示例

查看更多

平台兼容性

uni-app(4.27)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × × √ √ 6.0 × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.27)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × 6.0 × × ×

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
√ √ √ √

Google Play Billing UTS 插件

基于 Google Play Billing Library 9.1.0,支持 Android 端一次性商品和订阅,适用于 uni-app x。

  • 仅支持 Android。
  • 最低 Android API 23(Android 6.0),不支持 API 21/22。
  • Google Play Billing 9 的新应用和更新截止期限为 2028 年 8 月 31 日,延期截止日期为 2028 年 11 月 1 日。

安全提示:支付成功后,应把 purchaseToken 发送到可信服务端,通过 Google Play Developer API 验证购买并授予权益,再消耗或确认交易。不要仅根据客户端回调发放权益。

升级与兼容说明

  • 新版插件始终使用 Billing 9.1.0,不会回退或调用旧版 Billing SDK 的订阅替换逻辑。
  • doPay 和 doPayAll 使用同一套支付实现,参数及行为完全一致;保留两个方法名是为了兼容已有项目。
  • doUpdateSubs 只接受 Billing 9 商品级 replacementMode:1–6,0 无效。
  • doAcknowledgePurchase 推荐直接传 purchaseToken。旧项目仍可传 original + signature,插件会从 original 中提取 purchaseToken,最终仍使用 Billing 9 API 确认交易。

非 UTS 版本插件:插件市场

相关文档

API 说明

doInit(params, callback)

初始化并连接 BillingClient。

参数 类型 必填 默认值 说明
enableAutoServiceReconnection boolean 否 true 启用 Google Play 自动服务重连
enablePrepaidPlans boolean 否 false 支持订阅预付方案的待处理交易
enableExternalOffer boolean 否 false 启用外部优惠计划
enableAlternativeBillingOnly boolean 否 false 启用仅限备选结算模式
enableUserChoiceBilling boolean 否 false 启用用户选择结算方式

doRelease(callback)

结束 BillingClient 连接并释放插件持有的商品及支付回调数据。

getConnectionState(callback)

检查 BillingClient 是否已连接并可用。

doQuerySku(params, callback)

查询商品详情。方法名中的 Sku 为兼容已有项目而保留,底层使用 Billing 9 ProductDetails。

参数 类型 必填 说明
inapp string[] 否 一次性商品 ID 列表
subs string[] 否 订阅商品 ID 列表

至少需要传入一个商品 ID。返回值中的 data 是成功获取的商品,unfetchedProducts 是未能获取的商品及状态码。

doPay(params, callback) / doPayAll(params, callback)

两个方法参数和行为一致。

参数 类型 必填 说明
productId string 是 doQuerySku 返回的商品 ID
offerToken string 订阅必填 订阅优惠令牌;一次性商品选择指定购买选项或优惠时传入
accountId string 否 混淆后的账号 ID,不要传明文用户信息
profileId string 否 混淆后的资料 ID,不要传明文用户信息
isOfferPersonalized boolean 否 是否为个性化价格,用于欧盟价格披露

doUpdateSubs(params, callback)

使用 Billing 9 SubscriptionProductReplacementParams 更新订阅。

参数 类型 必填 说明
productId string 是 新订阅商品 ID,调用前必须通过 doQuerySku 查询
offerToken string 是* 新订阅优惠令牌;replacementMode = 6 时不要传
oldPurchaseToken string 是 当前订阅的购买令牌
oldProductId string 否 当前订阅商品 ID;建议传入。仅单商品订阅可省略,插件会根据 oldPurchaseToken 自动查询
replacementMode number 是 Billing 9 商品级替换模式,只允许 1–6,不允许 0
accountId string 否 混淆后的账号 ID
profileId string 否 混淆后的资料 ID
isOfferPersonalized boolean 否 是否为个性化价格
externalTransactionId string 否 原订阅来自备选结算时的外部交易 ID

replacementMode 取值:

值 Billing 9 模式 说明
1 WITH_TIME_PRORATION 立即替换,按剩余时间折算
2 CHARGE_PRORATED_PRICE 立即替换并收取剩余周期差价
3 WITHOUT_PRORATION 立即替换,下个续订日按新价格收费
4 CHARGE_FULL_PRICE 立即替换并收取新方案全价
5 DEFERRED 延后到旧方案到期时生效
6 KEEP_EXISTING 保持现有方案;oldProductId 必须等于 productId,并且不要传 offerToken

其他方法

  • doConsume({ purchaseToken }, callback):消耗一次性商品。
  • doAcknowledgePurchase({ purchaseToken }, callback):确认非消耗型一次性商品或订阅;兼容 { original, signature }。
  • doQueryPurchases({ productType, includeSuspendedSubscriptions? }, callback):查询当前拥有的商品。productType 为 inapp 或 subs;查询订阅时可传 includeSuspendedSubscriptions: true。
  • doShowInAppMessages({ inAppMessageCategoryId }, callback):显示 Google Play 应用内消息。

通用返回值

  • code:插件结果码,200 表示插件调用成功。
  • errorCode:Google Play Billing BillingResponseCode。
  • subResponseCode:购买失败细分原因;0 无细分原因、1 余额不足、2 用户不符合订阅优惠资格。
  • data:接口返回的数据。
  • unfetchedProducts:doQuerySku 未能获取的商品列表及状态码。

引用插件

import {
    doInit,
    doRelease,
    getConnectionState,
    doQuerySku,
    doPay,
    doPayAll,
    doUpdateSubs,
    doConsume,
    doAcknowledgePurchase,
    doQueryPurchases,
    doShowInAppMessages,
    GtCommonResult,
} from "@/uni_modules/gt-google-pay"

uni-app x 示例

以下代码基于 uni-app x,完整用法请查看项目 Demo;uni-app 项目需要按自身类型系统调整。

const isInapp = ref<boolean>(true)
const payList = ref<UTSJSONObject[]>([])
const prdList = ref<UTSJSONObject[]>([])

/**
 * 显示提示
 */
const showToast = (title : string) => {
    uni.showToast({
        title: title,
        mask: true,
        duration: 1500,
        icon: 'none'
    });
}

/**
 * 初始化
 */
const handleInit = () => {
    // 参考链接:https://developer.android.com/reference/com/android/billingclient/api/BillingClient.Builder
    doInit({
            enableAutoServiceReconnection:true,  //自动服务重连,不传默认为true
            enablePrepaidPlans:false, //是否支持订阅预付方案的待处理交易
            enableExternalOffer:false , //启用外部优惠,不传默认为false
            enableAlternativeBillingOnly:false, //仅使用备选结算,不传默认为false
            enableUserChoiceBilling:false, //用户选择支付方式,适用于 EEA、印度、韩国等要求支持 UCB 的市场,不传默认为false
        }, e => {
        console.log("doInit: ", e);
        if (e.code == 200) {
            // 初始化成功
            console.log('初始化成功');
            showToast('初始化成功');
        } else {
            // 初始化失败
            console.log('初始化失败');
            showToast('初始化失败');
        }
    });
}

const handleRelease = () => {
    doRelease((result : GtCommonResult) => {
        console.log('释放连接:', result)
    })
}

const handleGoodsTypeChange = (e : RadioGroupChangeEvent) => {
    console.log("handleGoodsTypeChange: ", e);
    isInapp.value = e.detail.value == 'true'
    prdList.value = [] as UTSJSONObject[]
}

/**
 * 查询商品
 */
const handleQuerySku = () => {
    let params : UTSJSONObject;
    if (isInapp.value) { //消耗商品
        params = {
            inapp: ["google_product_22", "google_product_21", "google_product_11",
                "google_product_12", "google_product_15"
            ]
        }
    } else { //订阅商品
        params = {
            // subs: ["10000", "10001"],
            subs: ["10000", "10001", "svip_month", "vip_month"],
        }
    }
    doQuerySku(params, (result : GtCommonResult) => {
        console.log('查询结果:', result);
        showToast('查询结果:' + JSON.stringify(result));
        if (result.code == 200 && result.data != null) {
            // 查询成功  
            prdList.value = result.data as UTSJSONObject[];
            // console.log('that.prdList : ', prdList.value);
            // const list = JSON.parse<UTSJSONObject[]>(result.data['list'])
            //  const list = JSON.parse<UTSJSONObject[]>(JSON.stringify(result.data['list']))
        } else {
            console.log('查询失败');
        }
    });
}

/**
 * 发起支付
 * 支付参数为查询sku结果的某一项
 */
const handlePay = () => {
    console.log('do pay function.');
    // console.log("that.prdList: ", prdList.value)
    if (prdList.value.length == 0 || prdList.value[0].productId == null) {
        showToast('产品不能为空,不可支付');
        return;
    }

    let payPrams = {
        productId: prdList.value[0].productId,
        accountId: "test-accountId-00000001", // 可选,用户Id
        profileId: "test-profileId-00000001", // 可选,个人资料Id
        isOfferPersonalized: false //欧盟个性化定价披露
    }
    // Billing 9:订阅商品必须传offerToken;一次性商品选择特定优惠时也要传offerToken
    if (!isInapp.value) {
        // payPrams.offerToken = "AQ2SJC/E1M9cIzCkpjba57hkz81a4vr9sGx7BKy+agizCT2IVOBCdMsYO3xiozVv01+0XYNhbKfZpAGUJkys"
        const subscriptionOfferDetails = prdList.value[0].subscriptionOfferDetails as UTSJSONObject[]
        if (subscriptionOfferDetails.length == 0 || subscriptionOfferDetails[0].offerToken == null) {
            showToast('找不到offerToken');
            return
        }
        payPrams.offerToken = subscriptionOfferDetails[0].offerToken;
    }
    console.log('payPrams: ', payPrams);
    doPay(payPrams, result => {
        console.log('支付结果:', result);
        showToast('支付结果:' + JSON.stringify(result));
        if (result.code == 200 && result.data != null) {
            const payResult = result.data as UTSJSONObject[];
            // 支付成功,可以确认交易,必须purchaseState才调用
            if (payResult[0].purchaseState as Number == 1) {
                console.log("支付成功,可以确认交易");
                payList.value = payResult;
            } else { // purchaseState==2
                // 调用doQueryPurchases方法查询订单状态,purchaseState==1后才可以确认
                console.log("支付成功,订单状态为等待中,暂不可确认交易");
            }
        } else {
            // 支付失败
            console.log('支付失败');
        }
    });
}

/**
 * 发起支付
 * 支付参数为查询sku结果的某一项
 */
const handlePayAll = () => {
    console.log('do pay function.');
    console.log("that.prdList: ", prdList.value)
    if (prdList.value.length == 0 || prdList.value[0].productId == null) {
        showToast('产品不能为空,不可支付');
        return;
    }

    let payPrams = {
        productId: prdList.value[0].productId,
        accountId: "test-accountId-00000001", // 可选,用户Id
        profileId: "test-profileId-00000001", // 可选,个人资料Id
        isOfferPersonalized: false //欧盟个性化定价披露
    }
    // Billing 9:订阅商品必须传offerToken;一次性商品选择特定优惠时也要传offerToken
    if (!isInapp.value) {
        // payPrams.offerToken = "AQ2SJC/E1M9cIzCkpjba57hkz81a4vr9sGx7BKy+agizCT2IVOBCdMsYO3xiozVv01+0XYNhbKfZpAGUJkys"
        const subscriptionOfferDetails = prdList.value[0].subscriptionOfferDetails as UTSJSONObject[]
        if (subscriptionOfferDetails.length == 0 || subscriptionOfferDetails[0].offerToken == null) {
            showToast('找不到offerToken');
            return
        }
        payPrams.offerToken = subscriptionOfferDetails[0].offerToken;
    }
    console.log('payPrams: ', payPrams);
    doPayAll(payPrams, result => {
        console.log('支付结果:', result);
        showToast('支付结果:' + JSON.stringify(result));
        if (result.code == 200 && result.data != null) {
            const payResult = result.data as UTSJSONObject[];
            // 支付成功,可以确认交易,必须purchaseState才调用
            if (payResult[0].purchaseState as Number == 1) {
                console.log("支付成功,可以确认交易");
                payList.value = payResult;
            } else { // purchaseState==2
                // 调用doQueryPurchases方法查询订单状态,purchaseState==1后才可以确认
                console.log("支付成功,订单状态为等待中,暂不可确认交易");
            }
        } else {
            // 支付失败
            console.log('支付失败');
        }
    });
}

/**
 * 确认交易,消耗品
 */
const handleConsume = () => {
    console.log('do consume function.');
    if (payList.value.length == 0 || (payList.value[0].purchaseToken ?? payList.value[0].token) == null) {
        showToast('支付信息不能为空');
        return;
    }
    if (payList.value[0].purchaseState != 1) {
        showToast("订单状态未就绪")
        return
    }
    doConsume({
        purchaseToken: payList.value[0].purchaseToken ?? payList.value[0].token
    }, result => {
        console.log('Consume结果:', result);
        showToast('Consume结果:' + JSON.stringify(result));
        if (result.code == 200) {
            console.log('Consume成功');
        } else {
            console.log('Consume失败');
        }
    });
}

/**
 * 确认交易,非消耗品
 */
const handleAcknowledgePurchase = () => {
    console.log('do pay function.');

    if (payList.value.length == 0 || (payList.value[0].purchaseToken ?? payList.value[0].token) == null) {
        showToast('支付信息不能为空');
        return;
    }
    if (payList.value[0].purchaseState != 1) {
        showToast("订单状态未就绪")
        return
    }
    if (payList.value[0].acknowledged as boolean) {
        showToast("该订单已确认交易过了")
        return
    }
    // Billing 9 推荐直接使用 purchaseToken;授权商品前仍应先在服务端验签
    doAcknowledgePurchase({
        purchaseToken: payList.value[0].purchaseToken ?? payList.value[0].token
    }, result => {
        console.log('acknowledgePurchase结果:', result);
        showToast('acknowledgePurchase结果:' + JSON.stringify(result));
        if (result.code == 200) {
            console.log('acknowledgePurchase成功');
        } else {
            console.log('acknowledgePurchase失败');
        }
    });
}

/**
 * 查询购买交易
 * 返回您在应用内购买的当前拥有的商品的购买详情。
 * 仅返回有效订阅和未消费的一次性购买。
 * 
 * acknowledged  字段是是否已确认交易 true false
 * purchaseState 是订单状态  1--支付成功,可确认交易,2--等待中
 */
const handleQueryPurchases = () => {
    console.log("do doQueryPurchases function.");
    //参数inapp或subs
    doQueryPurchases({
        productType: isInapp.value ? "inapp" : "subs",
        includeSuspendedSubscriptions: false //订阅传true时包含暂停状态
    }, (result) => {
        console.log("queryPurchases结果:", result);
        showToast("queryPurchases结果:" + JSON.stringify(result));
        if (result.data != null) {
            // 支付成功后,如果没确认交易,doQueryPurchases查询到订单且订单purchaseState==1,再调用确认交易方法
            payList.value = result.data as UTSJSONObject[]
        }
    });
}

/**
 * 更新订阅,更新订阅后,也要确认交易才可以
 */
const handleUpdateSubs = () => {
    console.log('do updateSubs function.');
    if (isInapp.value) {
        showToast("应用内商品不支持更新订阅")
        return
    }
    if (prdList.value.length < 2 || prdList.value[1].productId == null || payList.value.length == 0) {
        showToast('产品不能为空,不可支付');
        return;
    }
    const oldProducts = payList.value[0].getArray<string>("products")
    if (oldProducts == null || oldProducts.length == 0) {
        showToast('找不到当前订阅商品ID');
        return
    }

    const subscriptionOfferDetails = prdList.value[1].getArray<UTSJSONObject>("subscriptionOfferDetails")
    if (subscriptionOfferDetails == null || subscriptionOfferDetails.length == 0 || subscriptionOfferDetails[0].offerToken == null) {
        showToast('找不到offerToken');
        return
    }
    // Billing 9 使用商品级 SubscriptionProductReplacementParams
    let params = {
        productId: prdList.value[1].productId,
        offerToken: subscriptionOfferDetails[0].offerToken,
        oldProductId: oldProducts[0],
        // 要更改的订阅的PurchaseToken
        oldPurchaseToken: payList.value[0].purchaseToken ?? payList.value[0].token,
        replacementMode: 3, //WITHOUT_PRORATION;Billing 9只允许1-6,0无效
        accountId: "test-accountId-00000001", // 可选,混淆后的用户Id
        profileId: "test-profileId-00000001", // 可选,混淆后的资料Id
        isOfferPersonalized: false, //欧盟个性化定价披露
        externalTransactionId:"" //可选,外部交易Id
    }
    console.log('doUpdateSubs params: ', params);
    doUpdateSubs(params, result => {
        console.log('更新订阅结果:', result);
        showToast('更新订阅结果:' + JSON.stringify(result));
        if (result.code == 200 && result.data != null) {
            // 支付成功
            console.log('更新订阅成功');
            payList.value = result.data as UTSJSONObject[];;
        } else {
            // 支付失败
            console.log('更新订阅失败');
        }
    });
}

/**
 * 显示应用内消息
 */
const handleShowInAppMessages = () => {
    console.log("do doShowInAppMessages function.");
    //参数inapp或subs
    doShowInAppMessages({
        inAppMessageCategoryId: 2, //0或2,详情参考谷歌文档
    },
        (result) => {
            console.log("doShowInAppMessages结果:", result);
            showToast("doShowInAppMessages结果:" + JSON.stringify(result));
            if (result.code == 200 && result.data != null) {
                const mResultData = result.data as UTSJSONObject
                // responseCode为谷歌返回的结果码,详情参考谷歌文档
                if (mResultData.responseCode == 0) {
                    // NO_ACTION_NEEDED
                    //  The flow has finished and there is no action needed from developers.
                } else if (mResultData.responseCode == 1) {
                    // SUBSCRIPTION_STATUS_UPDATED
                }
            }
        }
    );
}

隐私、权限声明

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

无

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

插件支付功能使用Google SDK,参考其官方网站 https://policies.google.com/privacy

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

无