PAYATHON 2026

Android In-App Billing (Subscription) 如何设置免付费用户?

支付小周

明确结论

Google Play Billing API 不支持将正常订阅订单的价格针对指定用户改为 0,用户完成订阅后也不能跳过 Google Play 收款。

如果想让自己、家人或朋友永久免费使用 premium 功能,可以通过应用自身的账号和权限系统,为这些用户直接授予 premium 权限,无需创建 Google Play 订阅。

推荐实现方式

将“是否为 premium 用户”与“是否拥有有效订阅”分开判断。用户满足以下任一条件,就可以获得 premium 权限:

  • 拥有经过验证且仍然有效的 Google Play 订阅;
  • 由后台管理员手动授予 premium 权限;
  • 获得设有明确到期时间的赠送权限。

服务端可以保存权限来源:

user_id
premium_source     // google_play、manual_grant、promotion
premium_expires_at // 永久授权可为空
revoked_at
note

判断逻辑可以写成:

premium =
    hasActiveGooglePlaySubscription(userId)
    OR hasValidManualGrant(userId)

正常用户仍通过 Google Play 购买订阅,指定用户则由应用后台直接授权。

Kotlin 示例

客户端不应只根据本地名单判断权限。用户登录后,客户端应向服务端查询:

data class Entitlement(
    val premium: Boolean,
    val source: String?,
    val expiresAt: String?
)

suspend fun refreshPremiumStatus(userId: String): Boolean {
    val entitlement = entitlementApi.getEntitlement(userId)
    return entitlement.premium
}

服务端可以采用类似的判断方式:

fun hasPremiumAccess(userId: String): Boolean {
    val manualGrant = premiumGrantRepository.findActiveGrant(userId)

    if (manualGrant != null &&
        manualGrant.revokedAt == null &&
        (manualGrant.expiresAt == null || manualGrant.expiresAt.isAfter(Instant.now()))
    ) {
        return true
    }

    return subscriptionRepository.hasActiveGooglePlaySubscription(userId)
}

管理员为指定账号授权时,可以同时记录权限来源和授权原因:

fun grantPremium(
    userId: String,
    expiresAt: Instant? = null,
    note: String? = null
) {
    premiumGrantRepository.save(
        PremiumGrant(
            userId = userId,
            source = "manual_grant",
            expiresAt = expiresAt,
            revokedAt = null,
            note = note
        )
    )
}

在生产环境中,这类管理接口必须验证管理员身份,并记录审计日志。

Google Play 订阅仍需正常验证

对于付费用户,服务端应验证其购买凭据。客户端通过 Google Play Billing Library 获取 purchaseToken,将它发送给服务端,再由服务端调用 Google Play Developer API 查询订阅状态。

不要只依赖客户端的以下判断:

purchase.purchaseState == Purchase.PurchaseState.PURCHASED

客户端状态可能被篡改,订阅也可能续订失败、被退款、被撤销或已经过期。因此,最终的 premium 权限应由服务端结合订阅状态和手动授权状态统一判断。

其他可选方案

促销代码或优惠

在部分产品配置、地区和政策条件下,Google Play Console 可能提供订阅促销代码、免费试用或优惠方案。这些功能适合营销活动,但通常会受到数量、有效期、用户资格和兑换规则等限制。

这类优惠并不等于“让任意指定账号永久免除订阅费”。具体能否使用,应以 Play Console 当前为该订阅产品提供的选项及 Google Play 官方政策为准。

License testers

License testers 适合测试购买流程,不适合用作亲友长期免费使用 premium 的正式方案。测试购买可能采用特殊的测试周期和结算方式,不应作为生产环境长期授予权限的依据。

本地白名单

从技术上看,可以在客户端加入白名单:

val freePremiumUsers = setOf(
    "friend@example.com",
    "family@example.com"
)

但不建议这样处理。名单会被打包进安装包,存在被反编译的风险;每次修改名单还要重新发布应用,仅检查邮箱字符串也容易被绕过。更稳妥的做法是把白名单保存在受控的服务端,并使用稳定的内部 userId 建立关联,不要依赖昵称或可能变更的邮箱。

注意事项

  • 应用需要有自己的登录账号体系,才能可靠识别获得赠送权限的用户。
  • 不要用设备 ID 识别亲友。换机、重装和隐私限制都会影响识别结果。
  • 手动授权最好支持设置到期时间、撤销权限和审计记录,避免将永久权限写死。
  • 购买、退款或订阅状态发生变化时,应及时同步服务端权限。
  • 如果 premium 数字功能通常必须通过 Google Play Billing 销售,就不能把手动赠送权限包装成绕过 Google Play 支付的公开销售渠道。
  • 少量内部测试人员可以使用测试机制;需要长期免付费使用时,应采用服务端授权。

备注:内容仅供参考。