首页 > Java > java教程 > 正文

Android应用中查询和验证用户订阅状态的完整指南

碧海醫心
发布: 2025-11-28 18:00:11
原创
729人浏览过

Android应用中查询和验证用户订阅状态的完整指南

本教程旨在指导android开发者如何在应用启动时准确查询和验证用户的订阅状态。我们将详细介绍如何利用google play billing library的`querypurchasesasync()`方法来检索现有订阅,并结合`purchasesupdatedlistener`处理新的购买或退款事件。文章涵盖了初始化billingclient、处理查询结果、验证购买有效性以及管理订阅生命周期的关键步骤,确保您的应用能可靠地判断用户是否拥有有效的订阅。

在Android应用中,准确判断用户是否拥有有效的订阅是实现付费功能和内容访问控制的关键。Google Play Billing Library提供了强大的API来管理应用内商品和订阅。本文将详细阐述如何构建一个健壮的订阅状态检测机制,特别关注如何在应用启动时查询现有订阅。

核心概念:Google Play Billing Library

Google Play Billing Library是Android应用与Google Play商店计费系统交互的官方API。它简化了应用内商品(一次性购买)和订阅(周期性收费)的购买流程,并提供了查询用户购买历史和当前订阅状态的功能。

初始化 BillingClient

要与Google Play计费系统进行通信,首先需要初始化并连接BillingClient。BillingClient的构建器需要一个PurchasesUpdatedListener,用于接收新的购买和购买状态更新。

import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.QueryPurchasesParams;
import android.content.Context;
import android.util.Log;
import java.util.List;

public class SubscriptionManager {

    private static final String TAG = "SubscriptionManager";
    private BillingClient billingClient;
    private Context context;
    private SubscriptionStatusListener statusListener; // 用于通知外部订阅状态的接口

    public interface SubscriptionStatusListener {
        void onSubscriptionStatusChanged(boolean hasActiveSubscription);
        void onBillingServiceDisconnected();
    }

    public SubscriptionManager(Context context, SubscriptionStatusListener listener) {
        this.context = context;
        this.statusListener = listener;
        initializeBillingClient();
    }

    private void initializeBillingClient() {
        PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
                for (Purchase purchase : purchases) {
                    handleSubscriptionPurchase(purchase);
                }
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                Log.d(TAG, "用户取消购买流程");
                updateSubscriptionStatus(false);
            } else {
                Log.e(TAG, "购买更新错误: " + billingResult.getDebugMessage());
                updateSubscriptionStatus(false);
            }
        };

        billingClient = BillingClient.newBuilder(context)
                .setListener(purchasesUpdatedListener)
                .enablePendingPurchases() // 启用待处理购买,例如用户需要完成外部验证
                .build();

        startBillingConnection();
    }

    private void startBillingConnection() {
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.d(TAG, "BillingClient连接成功");
                    // 连接成功后,立即查询现有订阅
                    queryUserSubscriptions();
                } else {
                    Log.e(TAG, "BillingClient连接失败: " + billingResult.getDebugMessage());
                    updateSubscriptionStatus(false); // 连接失败,视为无订阅
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                Log.w(TAG, "Billing服务已断开,尝试重新连接...");
                if (statusListener != null) {
                    statusListener.onBillingServiceDisconnected();
                }
                // 实现重试逻辑,例如延迟几秒后再次调用startBillingConnection()
            }
        });
    }

    // 在应用生命周期结束时断开连接
    public void endConnection() {
        if (billingClient != null && billingClient.isReady()) {
            billingClient.endConnection();
            Log.d(TAG, "BillingClient连接已断开");
        }
    }
}
登录后复制

查询现有订阅:queryPurchasesAsync()

当应用启动或BillingClient成功连接后,我们需要主动查询用户当前拥有的所有非消耗性商品和订阅。这是通过queryPurchasesAsync()方法实现的。

Grok
Grok

马斯克发起的基于大语言模型(LLM)的AI聊天机器人TruthGPT,现用名Grok

Grok 437
查看详情 Grok
// 承接上面的SubscriptionManager类
public class SubscriptionManager {
    // ... (previous code) ...

    /**
     * 查询用户当前拥有的订阅。
     * 建议在BillingClient连接成功后立即调用。
     */
    public void queryUserSubscriptions() {
        if (billingClient.isReady()) {
            billingClient.queryPurchasesAsync(
                QueryPurchasesParams.newBuilder()
                    .setProductType(BillingClient.ProductType.SUBS) // 指定查询订阅商品类型
                    .build(),
                (billingResult, purchaseList) -> {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        boolean hasActiveSubscription = false;
                        if (purchaseList != null && !purchaseList.isEmpty()) {
                            for (Purchase purchase : purchaseList) {
                                // 检查购买状态和确认状态
                                if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
                                    if (!purchase.isAcknowledged()) {
                                        // 尚未确认的订阅,需要进行确认
                                        acknowledgePurchase(purchase);
                                        // 假定确认后会变为有效,暂时标记为有订阅
                                        hasActiveSubscription = true;
                                    } else {
                                        // 已确认且有效的订阅
                                        Log.d(TAG, "发现有效订阅: " + purchase.getProducts() + ", Token: " + purchase.getPurchaseToken());
                                        hasActiveSubscription = true;
                                        // 可以在此处进行服务器端验证
                                    }
                                } else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
                                    Log.d(TAG, "发现待处理订阅: " + purchase.getProducts());
                                    // 待处理状态,暂时不认为是有效订阅
                                }
                            }
                        }
                        updateSubscriptionStatus(hasActiveSubscription);
                    } else {
                        Log.e(TAG, "查询购买失败: " + billingResult.getDebugMessage());
                        updateSubscriptionStatus(false);
                    }
                });
        } else {
            Log.e(TAG, "BillingClient尚未准备好进行查询。");
            updateSubscriptionStatus(false);
        }
    }

    // ... (rest of the class) ...
}
登录后复制

处理购买更新与订阅确认:PurchasesUpdatedListener和acknowledgePurchase()

PurchasesUpdatedListener主要用于处理用户完成购买流程后的结果。当用户成功购买订阅后,Google Play会通过此监听器通知应用。对于订阅这类非消耗性商品,必须在收到购买信息后3天内进行确认(Acknowledge),否则Google Play将退款给用户。原问题中的handlePurchase方法使用了consumeAsync,这是针对消耗性商品的操作,对于订阅是错误的。订阅应该被确认而非消耗

// 承接上面的SubscriptionManager类
public class SubscriptionManager {
    // ... (previous code) ...

    /**
     * 处理订阅购买的逻辑。
     * 区分购买状态并进行确认。
     */
    private void handleSubscriptionPurchase(Purchase purchase) {
        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
            // 购买成功
            if (!purchase.isAcknowledged()) {
                // 订阅尚未确认,需要进行确认
                acknowledgePurchase(purchase);
            } else {
                // 订阅已确认,更新UI或内部状态
                Log.d(TAG, "订阅已确认并有效: " + purchase.getProducts());
                updateSubscriptionStatus(true);
                // 可以在此处进行服务器端验证
            }
        } else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
            // 购买处于待处理状态 (例如,需要用户进一步操作,如银行验证)
            Log.d(TAG, "购买处于待处理状态: " + purchase.getProducts());
            updateSubscriptionStatus(false); // 暂时认为无效
        } else {
            // 其他购买状态,例如FAILED
            Log.e(TAG, "购买失败或无效: " + purchase.getProducts());
            updateSubscriptionStatus(false);
        }
    }

    /**
     * 确认订阅购买。
     * 这是订阅生命周期中的关键一步。
     */
    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, "订阅确认成功: " + purchase.getProducts());
                updateSubscriptionStatus(true);
                // 可以在此处进行服务器端验证
            } else {
                Log.e(TAG, "订阅确认失败: " + billingResult.getDebugMessage());
                updateSubscriptionStatus(false);
            }
        });
    }

    /**
     * 辅助方法,用于更新应用内的订阅状态并通知监听器。
     */
    private void updateSubscriptionStatus(boolean hasSubscription) {
        Log.d(TAG, "当前订阅状态: " + (hasSubscription ? "有效" : "无效"));
        if (statusListener != null) {
            statusListener.onSubscriptionStatusChanged(hasSubscription);
        }
        // 您可以在此处更新一个全局变量(如Constant.subscription),
        // 或通过LiveData/RxJava等方式通知UI层更新。
    }

    // ... (rest of the class) ...
}
登录后复制

集成到应用启动流程

为了在应用启动时检查订阅状态,您可以在主Activity的onCreate()或onStart()方法中实例化SubscriptionManager并启动连接。

// 在您的Activity或Fragment中
public class MainActivity extends AppCompatActivity implements SubscriptionManager.SubscriptionStatusListener {

    private SubscriptionManager subscriptionManager;
    private TextView subscriptionStatusTextView; // 示例UI元素

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        subscriptionStatusTextView = findViewById(R.id.subscription_status_text);

        subscriptionManager = new SubscriptionManager(this, this);
        // BillingClient连接会在SubscriptionManager内部自动启动,并在连接成功后查询订阅
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (subscriptionManager != null) {
            subscriptionManager.endConnection(); // 断开BillingClient连接
        }
    }

    @Override
    public void onSubscriptionStatusChanged(boolean hasActiveSubscription) {
        // 在主线程更新UI
        runOnUiThread(() -> {
            if (hasActiveSubscription) {
                subscriptionStatusTextView.setText("订阅状态: 已订阅");
                // 允许访问付费内容
            } else {
                subscriptionStatusTextView.setText("订阅状态: 未订阅");
                // 引导用户购买订阅
            }
        });
    }

    @Override
    public void onBillingServiceDisconnected() {
        // 处理计费服务断开的情况,例如提示用户检查Google Play服务或重试
        runOnUiThread(() -> {
            subscriptionStatusTextView.setText("订阅状态: 计费服务断开");
            // 可以尝试重新连接或提示用户
        });
    }
}
登录后复制

订阅状态管理与服务器端验证

虽然客户端查询能提供即时反馈,但为了防止潜在的欺诈和确保数据一致性,强烈建议进行服务器端验证

  1. 客户端: 获取purchaseToken。
  2. 发送到服务器: 将purchaseToken连同用户ID发送到您的后端服务器。
  3. 服务器: 使用Google Play Developer API(特别是purchases.subscriptions.get方法)验证purchaseToken的有效性和订阅详情。
  4. 服务器响应: 根据验证结果,服务器通知客户端用户

以上就是Android应用中查询和验证用户订阅状态的完整指南的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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