应用内计费技术问询:订阅关联、跨端校验及签名验证等问题
作为刚接触应用内计费的开发者,你的这些疑问其实都是新手常会遇到的核心痛点,我来逐个帮你理清:
1. 无自有服务器下,订阅与应用账号关联的问题
首先明确:如果完全没有自有服务器,很难实现订阅权益与你的应用账号(而非Google账号)的稳定关联。
Google Play的订阅记录本质是绑定在用户的Google账号上的。如果用户购买后没关联你的应用账号,换设备登录你的应用账号时,你的应用根本无从知晓这个应用账号对应的哪个Google账号买过订阅,自然不会自动授予高级权益——这种情况是完全正常的,因为两者没有绑定关系。
如果不想搭建复杂服务器,有个折中方案:
- 在应用内引导用户完成「Google账号-应用账号」的绑定(比如登录应用账号后,让用户授权Google Play访问权限);
- 每次应用启动时,用Google Play Billing Library的
queryPurchasesAsync方法查询当前登录Google账号的订阅状态,同步到用户的应用账号本地存储;
但这个方案有缺陷:本地存储容易被篡改,且如果用户换设备用同一个应用账号但登录了不同的Google账号,还是无法获取权益,必须重新绑定之前购买的Google账号。
2. 跨渠道(非iOS购买,iOS端享权益)的实现
这个需求必须依赖服务器,因为iOS和Android的计费系统完全独立,两端应用无法直接验证对方渠道的购买记录。
具体思路:
- 搭建一个简单的后端服务,统一存储所有用户的购买记录(不管是Google Play、还是其他安卓渠道、甚至iOS的App Store);
- 用户在任何渠道购买后,将购买凭证(比如Google的purchase token、苹果的receipt)发送到服务器,服务器验证凭证有效性后,标记该应用账号的权益状态;
- iOS和安卓应用启动时,都向服务器请求当前应用账号的权益状态,根据返回结果开启高级功能。
3. & 4. 购买校验与签名信息验证的实现
首先纠正一个误区:完全不依赖Google服务器的校验是不可行也不安全的,你提到的tokenId、subscriptionId、purchaseId这些信息,最终还是需要和Google的服务器交互才能验证有效性。
对于新手来说,不建议自己手动处理OAuth token和签名验证,推荐两种简单方案:
方案一:客户端轻量校验(适合快速验证,但不安全)
用Google Play Billing Library提供的API直接在客户端查询订阅状态:
// 示例:Kotlin代码查询订阅 billingClient.queryPurchasesAsync(QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build()) { billingResult, purchases -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && !purchases.isNullOrEmpty()) { for (purchase in purchases) { // 获取订阅状态、过期时间 val isAutoRenewing = purchase.isAutoRenewing val expiryTime = purchase.purchaseTime + purchase.subscriptionPeriodMillis // 这里可以根据这些信息判断是否授予权益 } } }
但客户端校验的问题是容易被逆向破解,恶意用户可以篡改本地数据绕过校验,所以只适合临时测试,不适合上线。
方案二:服务器端校验(推荐,安全可靠)
你提到的分析服务器如果是托管的、无法运行自定义服务的话,建议用无服务器函数(比如Firebase Functions、Vercel Functions),这些平台对新手友好,免费额度足够支撑小型应用:
- 在Google Cloud控制台创建服务账号,获取JSON密钥,用于调用Google Play Developer API;
- 写一个简单的云函数,接收客户端发来的
purchaseToken和subscriptionId,调用Google Play的purchases.subscriptions.get接口验证:
// 示例:Node.js云函数验证订阅 const { google } = require('googleapis'); const playDeveloper = google.androidpublisher('v3'); exports.verifySubscription = async (req, res) => { const { packageName, subscriptionId, purchaseToken } = req.body; const auth = new google.auth.GoogleAuth({ keyFile: 'service-account-key.json', scopes: ['https://www.googleapis.com/auth/androidpublisher'], }); try { const response = await playDeveloper.purchases.subscriptions.get({ auth, packageName, subscriptionId, token: purchaseToken, }); const subscription = response.data; // 提取审批状态(比如subscription.status)、过期时间(subscription.expiryTimeMillis) res.status(200).json({ isValid: subscription.status === 'ACTIVE', expiryTime: subscription.expiryTimeMillis, }); } catch (err) { res.status(400).json({ error: err.message }); } };
- 客户端购买成功后,将
purchaseToken发送到这个云函数,根据返回的结果更新用户权益。
最后:权益应该绑定Google账号还是应用账号?
强烈建议绑定你的应用账号,原因有两个:
- 用户更习惯使用自己注册的应用账号,而非依赖Google账号(比如有些用户可能有多个Google账号,容易混淆);
- 跨设备、跨平台(安卓转iOS)的权益同步必须依赖应用账号,绑定Google账号的话,换设备或换平台就无法延续权益,用户体验极差。
当然,绑定应用账号意味着你需要一个简单的后端来存储用户的权益状态,但现在的无服务器工具已经非常容易上手,新手也能快速搭建起来。
内容的提问来源于stack exchange,提问作者Augodao Aika




