共计 2100 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
在开发 ChatGPT Plus 订阅功能时,Google Play 内购面临几个核心挑战:

- 支付验证延迟:Google Play 服务器通知可能存在 15-20 分钟的延迟,导致用户支付成功后客户端未能及时更新状态
- 跨设备同步:用户在不同设备购买 / 取消订阅时,如何保证状态一致性
- 退款争议处理:Google Play 允许用户在 48 小时内无条件退款,需特殊处理
- 区域限制:某些地区无法使用 Google Play 支付,需要备用方案
技术方案对比
纯客户端方案
- 优点:实现简单,无需后端支持
- 缺点:无法防范伪造收据,状态同步不可靠
服务器验证方案
- 优点:支付凭证可验证,状态可集中管理
- 缺点:需要后端开发资源
结论:生产环境必须采用服务器验证方案
核心实现
1. Google Play Billing Library 集成
在 build.gradle 中添加依赖:
implementation "com.android.billingclient:billing-ktx:5.1.0"
初始化 BillingClient:
private val billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
2. 订阅流程实现
关键代码示例:
suspend fun launchSubscriptionFlow(activity: Activity, productId: String) {val productDetails = getProductDetails(productId) ?: return
val params = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build())
)
.build()
billingClient.launchBillingFlow(activity, params)
}
3. 服务器验证接口设计
推荐验证流程:
- 客户端获取 purchaseToken 后发送到业务服务器
- 业务服务器调用 Google Play Developer API 验证
- 根据验证结果更新用户订阅状态
验证接口示例:
@POST("/api/subscription/verify")
suspend fun verifyPurchase(@Body request: VerifyRequest): Response<SubscriptionStatus>
// 请求体
data class VerifyRequest(
val purchaseToken: String,
val productId: String
)
避坑指南
处理延迟通知
解决方案:
- 实现本地购买记录缓存
- 定时 (每 5 分钟) 主动查询未确认的订单
- 重要操作前强制刷新状态
代码示例:
fun queryPendingPurchases() {
billingClient.queryPurchasesAsync(QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()) { _, purchases ->
purchases?.forEach {processPurchase(it) }
}
}
订阅状态同步
推荐方案:
- 客户端启动时从服务器拉取最新状态
- 使用 WorkManager 定期同步
- 关键操作前进行状态校验
安全考量
防重复消费
实现方案:
- 服务器维护 purchaseToken 白名单
- 相同 productId 的订阅只处理最新 token
- 记录所有验证请求日志
数据加密
敏感数据传输建议:
- 使用 HTTPS + 双向证书验证
- 对 purchaseToken 进行 AES 加密
- 关键接口添加签名校验
性能优化
本地缓存策略
实现多级缓存:
- MemoryCache:使用 LruCache 存储活跃用户状态
- DiskCache:Room 数据库持久化存储
- 缓存过期策略:普通用户 1 小时,订阅用户 15 分钟
网络请求优化
建议措施:
- 合并验证请求(批量接口)
- 使用 gzip 压缩
- 实现请求重试机制
扩展思考:多平台适配
设计可扩展架构:
-
定义统一订阅接口
interface SubscriptionService {suspend fun purchase(productId: String): PurchaseResult suspend fun getSubscriptions(): List<Subscription>} -
实现平台适配层
- 使用策略模式动态选择实现
总结
实现 Google Play 内购订阅需要特别注意状态同步和验证机制。建议:
- 务必实现服务器端验证
- 处理好 Google Play 的各类边界情况
- 建立完善的监控系统跟踪订阅状态
- 提前规划多平台兼容方案
完整示例项目已开源在 GitHub(伪代码,实际项目需替换真实地址),包含所有关键实现细节和单元测试。
正文完
发表至: 移动开发
近一天内
