0

0

Android应用内购:在Java中实现用户订阅状态的准确检查

碧海醫心

碧海醫心

发布时间:2025-11-28 18:46:11

|

174人浏览过

|

来源于php中文网

原创

Android应用内购:在Java中实现用户订阅状态的准确检查

本教程详细阐述了如何在android应用中,使用google play billing library的`querypurchasesasync()`方法来检查用户的现有订阅状态,尤其是在应用启动时。文章将解释该方法与实时购买事件监听器(`purchasesupdatedlistener`)的区别,并提供完整的代码示例和最佳实践,确保用户订阅状态的准确性与安全性。

在Android应用中,管理用户订阅状态是实现内购功能的核心环节。当用户购买了订阅服务后,应用需要能够随时查询并验证该订阅的有效性,而不仅仅是在购买流程完成的当下。例如,当用户重新打开应用时,应用需要知道用户是否仍然拥有有效的订阅。

Google Play Billing Library 提供了两种主要机制来处理购买信息:

  1. PurchasesUpdatedListener: 用于监听实时发生的购买事件,例如用户完成新的购买流程或取消支付。
  2. queryPurchasesAsync(): 用于查询用户当前已拥有的所有有效购买(包括订阅)。这是在应用启动或需要验证用户现有订阅状态时应使用的主要方法。

核心方法:查询现有订阅 queryPurchasesAsync()

queryPurchasesAsync() 方法是专门设计用来检索与用户Google账户关联的所有有效购买(包括订阅和非消耗性商品)的。它是一个异步操作,通过回调 PurchasesResponseListener 返回查询结果。

实现步骤与代码示例

在使用 queryPurchasesAsync() 之前,请确保您的 BillingClient 实例已正确初始化并连接到 Google Play 服务。

立即学习Java免费学习笔记(深入)”;

Thiings
Thiings

免费的拟物化图标库

下载
import android.util.Log;
import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.QueryPurchasesParams;
import java.util.List;

public class SubscriptionManager {

    private BillingClient billingClient;
    private static final String TAG = "SubscriptionManager";

    // 构造函数或初始化方法中传入 BillingClient 实例
    public SubscriptionManager(BillingClient client) {
        this.billingClient = client;
    }

    /**
     * 检查用户当前的订阅状态。
     * 此方法应在 BillingClient 连接成功后调用。
     */
    public void checkSubscriptionStatus() {
        if (billingClient != null && billingClient.isReady()) {
            // 查询订阅类型商品
            billingClient.queryPurchasesAsync(
                QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(),
                (billingResult, purchases) -> {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        boolean hasActiveSubscription = false;
                        if (purchases != null && !purchases.isEmpty()) {
                            for (Purchase purchase : purchases) {
                                // 验证购买状态和商品ID
                                // 请替换 "your_subscription_product_id" 为您实际的订阅商品ID
                                if (purchase.getProducts().contains("your_subscription_product_id") &&
                                    purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {

                                    // 如果购买未被确认,则需要确认
                                    if (!purchase.isAcknowledged()) {
                                        acknowledgePurchase(purchase);
                                    }

                                    // 购买已确认且状态为已购买,则视为有效订阅
                                    if (purchase.isAcknowledged()) {
                                        hasActiveSubscription = true;
                                        Log.d(TAG, "用户拥有有效订阅: OrderId = " + purchase.getOrderId());
                                        // TODO: 根据您的应用逻辑更新UI或应用状态
                                        // 例如: Constant.subscription = true;
                                        break; // 找到一个有效订阅即可
                                    }
                                }
                            }
                        }

                        if (!hasActiveSubscription) {
                            Log.d(TAG, "用户没有有效的订阅。");
                            // TODO: 根据您的应用逻辑更新UI或应用状态
                            // 例如: Constant.subscription = false;
                        }
                    } else {
                        Log.e(TAG, "查询订阅失败: " + billingResult.getDebugMessage());
                    }
                }
            );
        } else {
            Log.e(TAG, "BillingClient 未准备好或为空,无法查询订阅。");
        }
    }

    /**
     * 辅助方法:确认购买。
     * Google Play 要求所有购买(包括订阅)必须在3天内确认。
     * 未确认的购买会在3天后自动退款。
     */
    private void acknowledgePurchase(Purchase purchase) {
        AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.getPurchaseToken())
                        .build();

        billingClient.acknowledgePurchase(acknowledgePurchaseParams, billingResult -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                Log.d(TAG, "购买已成功确认: OrderId = " + purchase.getOrderId());
                // 确认成功后,可以再次触发订阅状态检查或直接更新UI
                // checkSubscriptionStatus(); // 重新检查以更新状态
            } else {
                Log.e(TAG, "购买确认失败: " + billingResult.getDebugMessage());
            }
        });
    }

    // 您可能还需要一个方法来处理可消耗商品的消耗逻辑,如果您的应用有此类商品。
    // 订阅商品不需要消耗。
    /*
    void handleConsumablePurchase(Purchase purchase) {
        ConsumeParams consumeParams = ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();

        billingClient.consumeAsync(consumeParams, (billingResult, purchaseToken) -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                Log.d(TAG, "可消耗商品已成功消耗。");
                // TODO: 授予用户商品
            } else {
                Log.e(TAG, "可消耗商品消耗失败: " + billingResult.getDebugMessage());
            }
        });
    }
    */
}

代码解释:

  • QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(): 指定我们只查询订阅类型的商品。
  • PurchasesResponseListener: 这是 queryPurchasesAsync 的回调接口,它接收 BillingResult 和一个 Purchase 列表。
  • billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK: 检查查询操作本身是否成功。
  • purchase.getProducts().contains("your_subscription_product_id"): 这一步至关重要,它确保我们检查的是我们关心的特定订阅商品。务必将 "your_subscription_product_id" 替换为您的实际商品ID。
  • purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED: 验证购买的状态是否为“已购买”。
  • purchase.isAcknowledged(): 对于所有购买(包括订阅),Google Play 都要求进行确认。如果 isAcknowledged() 返回 false,则需要调用 acknowledgePurchase() 方法进行确认。未确认的购买将在3天后自动退款
  • acknowledgePurchase(purchase): 这是一个辅助方法,用于执行购买确认操作。

何时调用 checkSubscriptionStatus()?

为了确保用户订阅状态的及时性和准确性,您应该在以下关键时刻调用 checkSubscriptionStatus() 方法:

  1. 应用启动时: 在主 Activity 的 onResume() 方法中或应用初始化流程中调用,确保用户进入应用时,其订阅状态是最新的。
  2. 从后台返回前台时: 同样可以在 onResume() 中处理,以防用户在应用处于后台期间订阅状态发生变化(例如,订阅到期或在其他设备上取消)。
  3. 用户尝试访问受限内容时: 在用户尝试访问仅限订阅用户可用的高级功能之前进行检查。
  4. 网络恢复时: 如果应用在离线状态下启动,当网络连接恢复后,应重新检查订阅状态。
  5. BillingClient 连接成功后: 确保 BillingClient 已准备就绪且连接成功。

PurchasesUpdatedListener 的角色

虽然 queryPurchasesAsync() 用于查询现有订阅,但 PurchasesUpdatedListener 仍然是处理 新发生购买事件 的关键。当用户完成购买流程(无论是成功、取消还是其他错误)时,此监听器会被触发。

import android.util.Log;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import java.util.List;

public class MyBillingClientLifecycleManager implements PurchasesUpdatedListener {

    private SubscriptionManager subscriptionManager; // 假设您有SubscriptionManager实例

    public MyBillingClientLifecycleManager(SubscriptionManager manager) {
        this.subscriptionManager = manager;
    }

    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List purchases) {
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
            for (Purchase purchase : purchases) {
                // 对于新购买的订阅,需要进行确认
                if (purchase.getProducts().contains("your_subscription_product_id")) {
                    // 确认操作应在 SubscriptionManager 中处理
                    if (!purchase.isAcknowledged()) {
                        subscriptionManager.acknowledgePurchase(purchase);
                    }
                    Log.d("BillingFlow", "新订阅购买成功并处理: OrderId = " + purchase.getOrderId());
                    // TODO: 更新应用状态,例如 Constant.subscription = true;
                }
                // 如果是可消耗商品,则需要消耗
                // else if (purchase.getProducts().contains("your_consumable_product_id")) {
                //    subscriptionManager.handleConsumablePurchase(purchase);
                // }
            }
            // 无论新购买成功与否,都建议重新查询一次以同步最新状态
            subscriptionManager.checkSubscriptionStatus();

        } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED){
            Log.d("BillingFlow", "用户取消了购买流程。");
            // TODO: 更新应用状态,例如 Constant.subscription = false;
        } else {
            Log.e("BillingFlow", "购买流程出现错误: " + billingResult.getDebugMessage());
        }
    }
}

注意: 订阅商品不需要像可消耗商品那样进行“消耗”。订阅只需在购买后进行一次“确认”即可。原始问题中 handlePurchase 方法中包含的 consumeAsync 逻辑,通常用于处理可消耗商品,不适用于订阅。

重要注意事项与最佳实践

  1. 购买确认 (Acknowledgement): 这是Google Play Billing中最关键的环节之一。所有购买(包括订阅和非消耗性商品)都必须在购买成功后的3天内通过 acknowledgePurchase() 方法进行确认。如果未在规定时间内确认,Google Play 将自动退款给用户,并且购买会被撤销。
  2. 服务器端验证: 尽管客户端可以检查订阅状态,但为了防止作弊和提供更强大的安全性,强烈建议在您的后端服务器上

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

842

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

739

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

C# 教程
C# 教程

共94课时 | 7.2万人学习

Java 教程
Java 教程

共578课时 | 49万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号