应用内购买测试:android.test.purchased已经拥有

130

我目前正在测试一款未来应用的应用内购买功能,我成功“购买”了测试商品“android.test.purchased”,但每次尝试再次购买时,我都会收到响应代码7,这意味着我已经拥有该商品。

12-15 23:02:14.149: E/IabHelper(19829): 应用内购买错误:无法购买该商品,错误响应:7:商品已经拥有

据我所知,这个购买应该始终是可能的,对吗?以便开发人员可以测试他/她的应用程序?

如果不行,我该如何将其状态“重置”为未拥有? 我正在使用来自Google In-App Billing样例的util包。


1
我的招聘情况正好相反,每次都允许购买,但我需要已经购买的信息。 - Sandeep P
15个回答

110
将此代码添加到线程中以启动消费请求。

添加此代码到线程中以启动消费请求。

int response = mService.consumePurchase(3, getPackageName(), purchaseToken);

在购买测试中,购买令牌是

purchaseToken = "inapp:" + getPackageName() + ":android.test.purchased";

而且

if (response == 0)

然后消费就成功了。

另外别忘了在

IabHelper.Java

中将 mService 设置为 public,这样就可以像这样访问:

int response = mHelper.mService.consumePurchase(3, getPackageName(), purchaseToken);

2
传说这很有效,@psykhi 应该真的给你加分了。我尝试使用它,但无法弄清购买令牌。谢谢。 - Blundell
13
什么是mService?mHelper?但我找不到consumePurchase()方法。 - Bagusflyer
1
mService 是 IInAppBillingService.aidl 接口的对象,该接口绑定了用于应用内购买的购买服务。 - Aashutosh Sharma
1
我还想指出,如果你同时使用IABHelper和安全性,你可能需要更新verifyPurchase以返回true。在我的情况下,它失败了,只需默认返回true即可。 - Kalel Wade
1
@ZhouHao,mService 是 BillingClient,方法现在被称为 consumeAsync - Francisco Durdin Garcia
显示剩余2条评论

101

无需编写任何特殊的消费代码。只需使用adb命令清除Google Play商店数据:

adb shell pm clear com.android.vending

1
谢谢,它解决了问题,我只需要清除应用程序,所以我提供了完整的答案。这对我来说是正确的方法。 - sivi
这是许多人的正确做法。在您的应用程序中添加代码并重新部署会更耗时(有点玩笑意味)。 - Michael Labbé
9
这是一个好的测试设备解决方案,但要注意:这将重置您在Google Play商店应用程序中的所有设置。因此,我宁愿不在我的个人设备上进行此操作。 - racs
7
不行!这个想法不好,你会失去其他产品和应用程序的应用内购买,更好的做法是如果你想测试应用内购买,就必须消费产品。情况是创建两个东西,一个用于购买,另一个用于消费已购买的物品。 - Rahul Mandaliya
2
@RahulMandaliya,你的应用程序应该使用getPurchases() API来知道已经购买了什么,以便在缓存清除时具有弹性。 - mttmllns
显示剩余3条评论

69

事实证明,android.test.purchased物品的行为类似于普通ID。这意味着如果您想再次购买它,就必须在代码中某处将其消耗掉。我认为Google的文档在这个问题上是误导性的,他们应该添加另一个静态ID,供测试目的无限购买。


1
使用库存调用来获取购买对象,然后消耗它 - 一旦你理解了它,这就很容易了。 - slott
3
请查看@mttmlins的下面答案,或阅读http://www.vvse.com/blog/blog/2016/08/26/android-in-app-billing-clear-purchase-state-of-sku-android.test.purchased/。 - IgorGanapolsky
并不完全正确,我有一种情况,存在未消耗的测试项目,但是billingClient.queryPurchases()没有返回该未消耗的项目,所以现在我无法消耗该项目,因为我无法获取其purchaseToken - Jemshit Iskenderov

28

应用内版本 3:

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {

    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {

        .....................

        if (inventory.hasPurchase(SKU_CONTENT)) {

            mHelper.consumeAsync(inventory.getPurchase(SKU_CONTENT), null);
        }
    }
};

一张图片胜过千言万语,一个完整的裸代码片段胜过千次“将此代码添加到线程中”的尝试。 - Androidcoder

15

第三个版本 - 最快的解决方法:清除 Google Play 商店的缓存将会让 "android.test.purchased" 再次可用。


那不会影响系统上安装的其他应用程序吗? - IgorGanapolsky
1
@IgorGanapolsky,不会的。只有模拟购买会受到影响。"真实"购买存储在Google的服务器上,不会被修改。 - Greelings
1
我已经清除了Google Play应用程序数据,但它没有起作用。测试购买详细信息仍然存在。 - Srikar Reddy
你如何清除Google Play商店的缓存? - Richard
2
通过“设置/应用程序管理器/”或将Google Play商店图标从菜单拖到屏幕顶部的“信息”按钮(上),您可以通过Richard清除缓存。最后,只需点击“清除缓存”按钮即可。 - Greelings
1
对我来说,这是最好的答案。其他答案也很好,但这个肯定是解决问题最快的方法。 - Ignacio Roda

9
这是我们如何使用该项:
 consume.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    String purchaseToken = "inapp:" + getPackageName() + ":android.test.purchased";
                    try {
                        Log.d("","Running");
                        int response = mService.consumePurchase(3, getPackageName(), purchaseToken);
                        if(response==0)
                        {
                            Log.d("Consumed","Consumed");
                        }else {
                            Log.d("","No"+response);
                        }
                    }catch (RemoteException e)
                    {
                        Log.d("Errorr",""+e);
                    }

                }
            });
            t.start();
        }
    });

对我有用 - 曾经使用过 android.test.purchase,导致了一些问题 - 应用内购买的库存任务只是抛出了大量错误并且没有完成。 作为一次单独的运行,清除了这个问题,因此我可以继续使用我的实际 SKU,并像以前一样继续进行。谢谢 - Jon
1
非常好的答案。在启用应用内购买的apk上传到Google Play之前,可以与静态响应一起使用。 - infero
谢谢!对我有用。 - Viral Patel

6
前往Google Play开发者控制台,从左侧打开“订单管理”菜单项并选择要退款的订单。还要确保删除授权。


我同意这个观点。根据谷歌的说法,为了对同一个非消耗品执行多次测试购买,您可以使用Google Play控制台进行退款和撤销购买。 更多详细信息请参见https://developer.android.com/google/play/billing/test。 - Bink
1
我猜现在这应该是被接受的答案了。退款时,请确保还要删除测试买家的资格,否则该物品仍将归其所有。请参见 https://dev59.com/LZbfa4cB1Zd3GeqPtGUX#67633608。 - flauschtrud
如果我忘记删除权限,我该怎么办? - Hussien Fahmy

6
在我看来,如果您的程序没有设计为使用该项,则不需要调整代码以清除外部供应商的内存。这会使您的代码更加脆弱,并且您将不得不花费大量时间添加和删除不属于您软件的代码,因此,实施此类解决方案是一种糟糕的设计。对我而言清除 android.test.purchased 的最佳解决方案如下:
adb uninstall com.yourapp.name

然后。
adb shell pm clear com.android.vending

我不需要清除缓存或浏览我的应用程序设置或更改代码。我需要将adb添加到Windows系统的路径变量中,这非常直截了当。所以是的,您需要使用adb,这可能是您需要的,因此..

只需在环境变量中的Windows路径中添加C:\ .. \ android-sdk \ platform-tools; 我想在Mac和Linux操作系统中也很简单。希望它能帮助某些人在实施Android中应用账单方面少花费几天时间。


1
卸载不是清除 android.test.purchased sku 的必要步骤。 - IgorGanapolsky

4
主要问题是您需要消耗android.test.purchased商品。但是,此商品不会在您的查询清单中可用,因此您无法使用正常流程进行消耗。
因此,如果您正在使用IabHelper,在IabHelper类中,您可以临时将IInAppBillingService mService更改为public,以便从您的IabHelper中访问它。
然后在您的类中,可以像这样进行消耗:
int response = mHelper.mService.consumePurchase(3, getPackageName(), "inapp:"+getPackageName()+":android.test.purchased");

如果成功,响应将是0。 希望这可以帮助。

正切:您能否在托管项目上发出退款(使用测试帐户 - 显示不在仪表板中)? - Ed Lee
你可以在 android.test.purchased 上调用 consumeAsync(),这没有任何问题。 - IgorGanapolsky
对于简单的测试,如果使用IabHelper,则这是最好的方法。对我很有帮助。 - Rúben

3
为了测试目的,我建议您插入一段代码,在调用初始化 gp 购买流程的方法之前清除您以前购买的所有产品。特别是当您仅测试一个项目时,这非常方便。例如,像这样:
PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
    for (Purchase sourcePurchase : purchasesResult.getPurchasesList()) {
        if(sourcePurchase != null){

            ConsumeResponseListener listener = new ConsumeResponseListener() {
                @Override
                public void onConsumeResponse(String outToken, @BillingResponse int responseCode) {

                    System.out.println("all consumed");
                }
            };
            mBillingClient.consumeAsync(sourcePurchase.getPurchaseToken(), listener);
        }else{
            System.out.println("null");
        }
    }

// and then initiate whole process with clear "shoping basket"

BillingFlowParams.Builder builder = new BillingFlowParams.Builder()
        .setSku(itemName).setType(BillingClient.SkuType.INAPP);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接