Android In-App Billing:订单取消后购买状态保持“已购买”

Posted

技术标签:

【中文标题】Android In-App Billing:订单取消后购买状态保持“已购买”【英文标题】:Android In-App Billing: Purchase state stays "purchased" after order cancelation 【发布时间】:2012-12-27 13:14:03 【问题描述】:

我目前正在测试我的 InApp 计费机制(使用 InApp Billing 版本 3 API,因此以 TrivialDrive 示例作为参考)。

我有一个托管项目,升级到高级版本。

现在,使用我的测试帐户购买该商品是可行的,但是当我之后在 Google 结帐中取消整个订单时,我的代码仍然告诉我该商品已被购买并因此授予高级功能。

这是我在 MainActivity 中检查购买的方式。我不会将购买状态保存在本地某处,据我了解,使用计费 API v3,您可以根据需要临时查询购买。

@Override
    protected void onStart() 
        // TODO Auto-generated method stub
        super.onStart();

        iabHelper = new IabHelper(this, Helper.getPKey());
        iabHelper.enableDebugLogging(true);

        iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() 

            @Override
            public void onIabSetupFinished(IabResult result) 
                Log.d("IAB", "SETUP FINISHED");

                if(!result.isSuccess())
                
                    Log.d("IAB", "SETUP NOT OK");
                    return;
                
                else
                    Log.d("IAB", "SETUP OK");

                iabHelper.queryInventoryAsync(
                    new QueryInventoryFinishedListener() 

                        @Override
                        public void onQueryInventoryFinished(IabResult result, Inventory inv) 
                            Log.d("IAB", "Query inventory finished.");
                            if (result.isFailure()) 
                                Log.d("IAB","Failed to query inventory: " + result);
                                return;
                            

                            Log.d("IAB", "Query inventory was successful.");

                            // Do we have the premium upgrade?
                            boolean mIsPremium = inv.hasPurchase(Helper.premiumSku);
                            Purchase p = inv.getPurchase(Helper.premiumSku);
                            if(p != null)
                                Log.d("IAB PURCHASE STATE", IabHelper.getResponseDesc(p.getPurchaseState()));
                            else
                                Log.d("IAB PURCHASE STATE", "Purchase is null");

                            Log.d("IAB", "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));


                        
                                           

                );              
            
        );       
    

我不断得到 getPurchaseState = 0,这意味着已购买,即使在我取消订单一小时后也是如此。为什么?

【问题讨论】:

我没有答案,但可以确认我也遇到了这个问题。 1. 购买 2. 进入谷歌结帐并取消订单 3. 观察已发送确认订单取消的电子邮件 4. 后续请求给出 PurchasedState = 0(即已购买) 也许需要一段时间才能过滤更改?另请参阅本页 cmets 部分中的以下讨论,其他人报告了同样的问题。 plus.google.com/u/1/+androidDevelopers/posts/R8DKwZDsz5m 我看到了类似的问题。我使用 IAP v3 API 并购买了一件商品,然后在谷歌结帐中取消了订单。但是当我再次尝试购买时,IAP v3 会返回“7:Item Already Owned”。我也在使用 trivialdrive 示例。库存显示该项目的 purchasestate 为“0”,并且 purchaseate 为 1358439773 是格林威治标准时间 2013 年 1 月 17 日下午 4:22。这正是 google checkout 显示为取消日期的日期和时间。 这与您的问题无关,但我只想指出,使用 IabHelper.getResponseDesc(p.getPurchaseState()) 获取购买状态的描述是不合适的,因为 getResponseDesc()用于检索响应代码的描述,而不是购买状态指示器的描述。对于后者,根据developer.android.com/google/play/billing/…,“可能的值为 0(已购买)、1(已取消)或 2(已退款)。”这些常量似乎没有在原始的 TrivialDrive 示例代码中定义甚至使用。 @Toni:我现在已经使用 TrivialDrive 示例应用的修改版本测试了取消订单,并且遇到了同样的问题。我进入 Checkout 并取消了我从我自己的测试帐户(不是开发者帐户)下的订单,十五小时后,该应用程序仍然报告购买类型为 0(已购买)。这是在带有 Google Play 商店版本 3.10.10 的 Nexus 7 上。在此之后,我清除了 Google Play 应用程序的缓存,停止了 TrivialDrive 应用程序并重新启动它。还是没有变化。然后,第一次在我的 Nexus One 手机上安装了 TrivialDrive;仍然购买。 好的,答案是最多可能需要 72 小时。请参阅此其他 SO 页面的已接受答案:***.com/questions/13861625/… 【参考方案1】:

我注意到了同样的事情:

使用测试帐户进行应用内购买 -> 取消访问并退款购买 -> getPurchaseState 仍然返回已购买(即使在重新登录并重新启动后),因此我的应用中不会删除对高级功能的访问。

但是当我用真实的购买测试同样的东西时:

客户进行了真正的购买 -> 几周后我退款了 -> 客户无法再使用我的应用的高级功能。

难道这只是测试购买的问题吗?

【讨论】:

【参考方案2】:

您可以使用的是购买数据的autoRenewing字段。根据documentation:

autoRenewing:指示订阅是否自动续订。如果为 true,则订阅处于活动状态,并将在下一个计费日期自动续订。 如果为 false,则表示用户已取消订阅

并且此字段在取消后立即更新。

【讨论】:

如果不是订阅怎么办? 在提交取消后立即返回false,但这并不意味着您应该将用户降级为未付费版本,因为在到期日期结束之前,您仍应为取消的订阅提供完整版本。问题是,您不知道到期日期,因为 google api 没有返回。 哦,非常感谢 :) 我正在使用订阅并试一试??【参考方案3】:

Google 官方文档中已经给出了很好的回答。复制这里的文字。

当用户取消订阅时,Google Play 不会为当前的结算周期提供退款。相反,它允许用户在当前计费周期结束之前访问已取消的订阅,届时它会终止订阅。例如,如果用户购买了每月订阅并在周期的第 15 天取消它,Google Play 将认为该订阅在第 30 天(或其他日期,具体取决于月份)结束之前有效。

这应该可以解释一切。 getPurchase() 仍将返回购买数据,直到当前订阅周期结束。

【讨论】:

我相信 OP 是在谈论托管购买,而不是订阅。 没有人说他在谈论订阅。我对托管购买也有同样的问题。这些根本没有“订阅周期”。 不一定。我的(测试)订阅很久以前就被取消了,并且在 IabHelper 中仍然被标记为“拥有”,尽管已经超过了有效期。 是的,订阅也有问题。谷歌文档有虚假信息。如果您查询 getPurchase 取消的订阅即使在过期日期之后也会返回。【参考方案4】:

在等待了大约 12 个小时并尝试了此处建议的所有内容后,我仍然面临同样的问题。对我有用的是以下 adb 命令:

adb shell pm clear com.android.vending

【讨论】:

谢谢!这结合 Jannon 的回答 (***.com/questions/14303850/…) 引导我解决问题。 如果您的应用用户可以重现问题,我不会将其称为“解决方案”,即使他们知道如何操作,他们也不会使用 adb 以这种方式清除他们的购买。在获得退款后享受应用内购买的能力从用户的角度来看不是问题...... 解决了开发过程中的缓存权限问题。用户端的解决方案是使用 Google Play Developer API & service account 直接查看产品的购买状态,并将其作为启用权利的基础。这应该为您指明正确的方向:***.com/questions/11115381/…【参考方案5】:

在使用同一帐户的另一台设备上使用该应用时也会出现此问题。在设备重新启动之前,即使在数小时后,也不会收到购买的商品。如果再次尝试购买,谷歌钱包对话框会显示“项目已拥有”。 iabHelper 的返回码仍然是“用户取消”,因为购买活动的真实响应没有返回,只是写在调试日志中。

else if (resultCode == Activity.RESULT_CANCELED) 
        logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
        result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");
        if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
    

所以它不可能对这个谷歌计费对话框做出反应,因为我们总是得到相同的结果 IABHELPER_USER_CANCELED,即使对话框说“项目已经拥有”。

编辑:

我用这个来修复它:

else if (resultCode == Activity.RESULT_CANCELED) 
        logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
        if(responseCode == 7) 
            result = new IabResult(BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED, "Item already owned.");
        else                                                                                             
            result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");
        if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
    

所以现在如果计费对话框的响应是 7 作为“项目已拥有”,我会将其报告给我的听众。

【讨论】:

【参考方案6】:

我在文档 (IAB API v2) 中找到了以下部分,但我不确定这是否可用于 IAB API v3。但广播可能仍会发送。

"... 当 Google Play 收到来自 Google Wallet 的退款通知时,您的应用程序可以收到 IN_APP_NOTIFY 广播意图。在这种情况下,Google Play 会向您的应用程序发送 IN_APP_NOTIFY 消息。您的应用程序可以以相同的方式处理此消息处理来自应用程序发起的 REQUEST_PURCHASE 消息的响应,以便最终您的应用程序收到 PURCHASE_STATE_CHANGED 消息,其中包含有关已退款项目的信息。退款信息包含在伴随 PURCHASE_STATE_CHANGED 广播意图的 JSON 字符串中。此外,purchaseState 字段在 JSON 字符串中设置为 2。”

来自:http://developer.android.com/google/play/billing/v2/api.html#billing-action-notify

【讨论】:

【参考方案7】:

我知道这已经一岁了,但所提供的答案/提示都没有帮助我,所以我想我会添加我的解决方案。

首先,我遇到了同样的问题。即,进行了一次测试购买,取消了它,仍然收到表明购买有效的购买状态。

我忘记的是,我最近将 Google Play 开发者控制台设置面板上的“许可测试响应”字段从“响应正常”切换为“许可”

将其切换回“RESPOND_NORMALLY”后,已取消购买的购买状态已正确返回。

所以,在尝试等待几天之前,您可能需要检查一下

【讨论】:

不就是 LVL,而不是应用内计费吗?【参考方案8】:

步骤 1. 等待大约 10 分钟;直到您看到“已取消的订单”已交付。在你的谷歌钱包里。

9 月 15 日上午 11:28 已取消订单已送达。

9 月 15 日上午 11:18 已取消 您取消了此订单。原因:客户要求取消。

第 2 步。在设备上注销您的测试 google 帐户,然后重新登录。

至少这解决了我的问题。

【讨论】:

Reboot 也为我做了。但似乎用户只要不重新启动设备就可以继续购买,这对很多人来说是从来没有的。 我尝试重新登录、重启、清除 playstore 应用和服务、重新安装应用并在新设备上使用相同帐户下载应用。但仍然显示购买统计信息为 0(已购买)。 不管怎样,唯一对我有用的是清除 Google Play Store 应用程序上的数据(不仅仅是缓存)。我使用的是 nexus 7 android 版本 6.0.1 认真的吗?当您分发应用程序时,我们谈论的是现实生活中的问题,您可以要求用户按照这些步骤操作!【参考方案9】:

如果我没看错,那么 trivialdrivesample 中的参考代码是错误的,这对于应用内计费的官方参考项目来说将是一种耻辱。

如果 purchase == null 仅表示从未购买过。要获取真实信息,您必须致电

purchase.getPurchaseState()

根据here

purchaseState 订单的购买状态。可能的值为 0(已购买)、1(已取消)、2(已退款)或 3(已过期,仅限订阅购买)。

【讨论】:

根据 v3 文档,没有 3(到期)。这是否意味着我们不能再通过这种方法检查订阅状态? developer.android.com/google/play/billing/… 目前,此信息位于:developers.google.com/android-publisher/api-ref/purchases/…

以上是关于Android In-App Billing:订单取消后购买状态保持“已购买”的主要内容,如果未能解决你的问题,请参考以下文章

Android In-App Billing v3:“无法执行操作:queryInventory”

SDK接入之Android Google Play内支付(in-app Billing)接入

android in-app billing v3 api中的开发者有效负载应该是多少?

如何使用 Java (Servlet) 验证来自 In-App Billing Android Market 的签名数据

android in-app billing v3 api中的开发人员有效负载应该是啥?

In-App Billing v3 - 不检测退款