In-App Billing test: android.test.purchased already owned
Asked Answered
D

15

130

I am currently testing In-App Billing for a future app, and after I successfully "bought" the test item "android.test.purchased" the first time, I now receive the response code 7 every time I try to buy it again, which means that I already own this item.

12-15 23:02:14.149: E/IabHelper(19829): In-app billing error: Unable to buy item, Error response: 7:Item Already Owned

From what I understand, this purchase is supposed to always be possible, right? So that the developer can test his/her app?

If not, how can I "reset" its state to not owned? I am using the util package from the Google In-App Billing Sample.

Dethrone answered 15/12, 2012 at 22:9 Comment(1)
my recruitment is vise verses ..i need the message already owned but every time its allowing to buyFrig
D
69

It turns out that the android.test.purchased item behaves like a regular ID. It means that if you want be able to buy it again, you have to consume it somewhere in your code. I think that the Google documentation is misleading on this matter, and that they should add another static ID that you can buy endlessly for test purposes.

Dethrone answered 16/12, 2012 at 0:30 Comment(3)
Use the inventory call to fetch the Purchase object and then consume it - its pretty easy once you get your head around it.Skewness
Look at below answer from @mttmlins, or read vvse.com/blog/blog/2016/08/26/…Vale
Not completely true, i have a case where there is unconsumed test item, but billingClient.queryPurchases() does not return that unconsumed item, so now i can not consume item because i can not get purchaseToken of itUncoil
T
110

Add this code to a thread to initiate consume request.

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

Here for the purchase test, purchaseToken is

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

And

if (response == 0)

then the consumption is successful.

also don't forget to make mService public in

IabHelper.Java

then it would be possible to access like this:

int response = mHelper.mService.consumePurchase(3, getPackageName(), purchaseToken);
Tenderfoot answered 20/12, 2012 at 8:27 Comment(7)
Legend this works, @Dethrone should really have given you the points. I was trying to consume it but couldn't work out the purchase token. ThanksAmarillas
@pks: you can place this code wherever you need to consume your purchase. On any click action or immediate after purchase, any where as your app needsTenderfoot
What is mService? mHelper? But I can't find the consumePurchase()Curson
mService is object of IInAppBillingService.aidl interface which is binded with purchase service initiated for inapp purchase.Tenderfoot
I would also like to point out if your using the IABHelper with the security as well, you may need to update verifyPurchase to return true. In my case, it was failing and just had to return true by default.Dopp
@Bhavesh: the purchase token mentioned here is of test purchases make sure you are not using these with real purchases. If you are having trouble with test purchase please explain what exactly the problem you are facing and what response you are getting from google at the time of consumption. Post your JSON response here.Tenderfoot
@ZhouHao mService is the BillingClient, and the method is now called consumeAsyncQuevedo
M
101

No need to write any special consumption code. Just use the adb command for clearing the Google Play Store data:

adb shell pm clear com.android.vending
Mccartney answered 11/5, 2015 at 22:46 Comment(8)
thanks it did the trick i just had to clear the app also so I made a full answer available. This is the right approach for me.Vannessavanni
This is the right approach for a lot of people. Adding code to your app and redeploying is more time consuming (pun intended).Ardeb
I confirm this is the best and easiest way to reset your test purchase ! thanks so much.Sociolinguistics
For me - I ran this command, then cleared the my app's cache (also cleared cache for Google Play Store, tho not sure if that made a difference), and then redeployed from Eclipse .. and the payment prompt appeared again. Thanks!Darrick
This is a good solution for a test device, but be careful: it will reset all your settings in Google Play Store app. So, I would rather not doing it on my personal device.Miramirabeau
No! this is bad idea, you lost in-app purchase of your other product and other apps, good one is you have to consume product if you want test your in app purchase, scenario is just create two things one for purchase and another for consume purchased item.Ked
@RahulMandaliya your app should be resilient against cache clears by using the getPurchases() API to know what has already been purchased.Mccartney
Not working for me. I think the purchase information should be managed by Google.Alberich
D
69

It turns out that the android.test.purchased item behaves like a regular ID. It means that if you want be able to buy it again, you have to consume it somewhere in your code. I think that the Google documentation is misleading on this matter, and that they should add another static ID that you can buy endlessly for test purposes.

Dethrone answered 16/12, 2012 at 0:30 Comment(3)
Use the inventory call to fetch the Purchase object and then consume it - its pretty easy once you get your head around it.Skewness
Look at below answer from @mttmlins, or read vvse.com/blog/blog/2016/08/26/…Vale
Not completely true, i have a case where there is unconsumed test item, but billingClient.queryPurchases() does not return that unconsumed item, so now i can not consume item because i can not get purchaseToken of itUncoil
T
28

In-app version 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);
        }
    }
};
Trout answered 16/3, 2013 at 18:39 Comment(1)
As a picture is worth a thousand words, a complete working bare bones code piece is worth a thousand 'Add this code to a thread . . .'.Derekderelict
S
15

Version 3 - Fastest way to solve : Clearing the cache of Google Play Store will let "android.test.purchased" available again.

Seethe answered 7/4, 2016 at 8:25 Comment(6)
Wouldn't that affect other apps installed on the system too?Vale
@IgorGanapolsky, no. Only the simulated purchases will be affected. The "real" puchases are stored on the Google's server, and won't be modified.Seethe
I've cleared Google Play the app data but it didn't work. Test purchase details still exists.Goldsmith
How do you Clear the cache of Google Play Store?Aguayo
@Aguayo via "settings/applications manager/" or dragging the Google play store icon from the menu to the "information" button (on the top of the screen). Finally, just click the "clear cache" button.Seethe
Best answer for me. All others are good, but this one definitely is the fastest way to solveAbbe
F
9

This is how we can consume the Item

 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();
        }
    });
Falcongentle answered 13/9, 2014 at 14:47 Comment(2)
Worked for me - had used the android.test.purchase at some point which was causing issues - inventory task in in-app billing just threw a load of errors and never finished. Running this as a one-off cleared it so I could resume using my to my actual SKU and carry on as I was before. ThanksAshlieashlin
Excellent answer. Works with Static Responses = before the apk with In-App Billing enabled is uploaded to Google Play.Garrettgarrick
V
6

In my opinion if your program is not designed to consume the item you do not need to tweak the code in order to clear the memory of an outside vendor. This will make your code more fragile and you will have then to spend a lot of time to add and remove code that does not belong to your software so it is a bad design to implement a solution like that.

The best solution that worked for me to clear android.test.purchased was

adb uninstall com.yourapp.name

and then

adb shell pm clear com.android.vending

I did not need to clear cash and to browse my apps setting or to change code for that. I did need to add the adb to path variables of windows system which was pretty straight forward. So yes you need to use adb which you probably need anyway so..

You just add your C:\ ...\android-sdk\platform-tools; in windows path in environment variables, and I imagine that it is pretty simple in mac and linux os as well. Hope it helps someone to spend few days less with implementing android in app billings.

Vannessavanni answered 1/7, 2015 at 12:2 Comment(1)
uninstall is not necessary to clear android.test.purchased sku.Vale
H
6

Go to the Google Play Developer Console, open Order Management menu item from the left side and select the order you want to refund. Also make sure to remove the entitlement.

Householder answered 6/9, 2017 at 13:13 Comment(3)
I concur with this. Per Google, To perform multiple test purchases for the same non-consumable product, you can refund and revoke purchases using Google Play Console. More detail at developer.android.com/google/play/billing/test.Eurhythmy
I guess this should be the accepted answer nowadays. When refunding make sure to also remove the entitlement of the test buyer, otherwise the item will still be owned. See https://mcmap.net/q/175261/-quot-you-already-own-this-item-quot-google-play-inapp-error.Mariano
if i forgot to remove entitlement , what should i doTibold
J
4

The main issue is you have to consume the android.test.purchased item. But this item won't be available in your query inventory, so you can't consume using the normal flow.

So, if you are using IabHelper, in IabHelper class, you can temporarily change the IInAppBillingService mService to public so that it is accessible from your IabHelper.

Then in your class, you can consume like this,

int response = mHelper.mService.consumePurchase(3, getPackageName(), "inapp:"+getPackageName()+":android.test.purchased");

If success, the response is going to be 0.

Hope this helps.

Jelene answered 14/9, 2016 at 9:28 Comment(3)
Tangent: can you issue a refund on a managed item (with a test account - it doesn't seem to show in the dashboard)?Anhedral
You can call consumeAsync() on android.test.purchased. There is no problem with that.Vale
For simple test this is best way possible if using IabHelper. Helped me.Vicarage
A
3

For testing purposes I also suggest you to insert a piece of code that will be clearing all the products that you've bought before calling a method that initializes gp purchase flow. That is especially comfortable, when you test just one item at the moment. E.g. like this:

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);
Azar answered 8/7, 2017 at 18:30 Comment(0)
D
1

If you are in test environment

1) In the case of android.test.purchased, I can reset the fake payment by restarting android device(consumed the inventory).

2) In InApp util there is a file called Security.java make it as following, for temporary. Since the testing payment(fake) always return false due to security exception.

public static boolean verifyPurchase(String base64PublicKey,
                                     String signedData, String signature) {
    return true; }

Then in your OnIabPurchaseFinishedListener call fechInvForconsumeItem()

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
            = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result,
                                          Purchase purchase)
        {
            if (result.isFailure()) {
                // Handle error
                Log.e("123","Failure");

                return;
            }
            else if (purchase.getSku().equals(ITEM_SKU)) {
                Log.e("123","PURCAsed");
                fechInvForconsumeItem(); // Restart device if not consume

            }

        }
    };

The fechInvForconsumeItem() is

    public void fechInvForconsumeItem() {
    mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
        = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result,
                                         Inventory inventory) {


        if (result.isFailure()) {
            // Handle failure
            Log.e("11","Failure");



        } else {
            Log.e("11","suc");
            mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),
                    mConsumeFinishedListener);
        }


    }
};

Consume Listener is

    IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
        new IabHelper.OnConsumeFinishedListener() {
            public void onConsumeFinished(Purchase purchase,
                                          IabResult result) {

                if (result.isSuccess()) {
                } else {
                    // handle error
                    Log.e("11","sucConsume");
                }
            }
        };
Dendriform answered 27/12, 2016 at 17:58 Comment(0)
K
0
IabHelper.QueryInventoryFinishedListener 
       mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
       public void onQueryInventoryFinished(IabResult result, Inventory inventory)   
       {
          if (result.isFailure()) {
             return;
           }          
          try {

                if(inventory.hasPurchase("product_sku_id"))
                {   
                     isItemEnable= true;
                     mHelper.consumeAsync(inventory.getPurchase("product_sku_id"),null);            
                }
                else
                {
                       isItemEnable = false;
                }           

            } catch (Exception e) {
                e.printStackTrace();
            }
       }

    };
Killifish answered 6/2, 2015 at 10:29 Comment(0)
C
-1

In my case, it appears that Google does not record a purchase for the item. Rather, the local copy of Google Play Services caches the purchase. That way, when a second request is made on the same device, android.test.purchased already owned appears. However, using another device or resetting the device clears the cache, and allows the purchase to be repeated.

Cleopatracleopatre answered 22/4, 2015 at 8:23 Comment(0)
W
-1

In my case, I just needed to clear the apps cache. After clearing the cache, I was able to initiate the purchase flow again.

From my device (4.4.2), I navigated to "Settings->Application manager". Next, I selected the app from the "DOWNLOADED" tab, and then "Clear cache".

Whin answered 20/3, 2016 at 15:34 Comment(0)
T
-2

This is the difference between consumable and non-consumable items; non-consumable items (what you seem to be dealing with here) have their state tracked persistently, while consumable items can be purchased multiple times. You'll have to go into your Play management console and cancel/refund the sale to test it again.

Thuja answered 15/12, 2012 at 22:52 Comment(6)
But the thing is that I don't have any "power" on these "fake" items, and I don't see where I could change the sale status, since they are not real purchases. The google doc says about the android.test.purchased : When you make an In-app Billing request with this product ID, Google Play responds as though you successfully purchased an item. So I'm pretty confused :/Dethrone
Are you doing static response testing still? If so, you just need to change what static response is sent. It sounds like you're up to "Testing In-app Purchases Using Your Own Product IDs", though, which does real purchases with cancelable orders.Thuja
I'm still stuck with the static IDs. What I would like is to be able to test an in-app purchase with the android.test.purchased ID. It just worked the first time when it should always work from what I understand from the Google doc. Sorry if I was not clear!Dethrone
My misinterpretation, sorry. In that case... I don't know. It's definitely supposed to return success each time, I haven't seen the case you're seeing. Sorry.Thuja
Will try to look again at the problem after a good night of sleep :) Thanks for your help !Dethrone
OP was asking specifically about android.test.purchased, which has nothing to do with Play Management console.Vale

© 2022 - 2024 — McMap. All rights reserved.