android: Inapp billing: Error response: 7:Item Already Owned
Asked Answered
P

5

35

I am learning to implement an in-app billing for my app such that people can for example, donate $ when press the donate button.

The user is allowed to donate more than one time, i.e. the purchase is consumable.

The codes below are sourced from the TrivalDrive sample and some tutorials from the web:

Code:

IabHelper mHelper;
static final String ITEM_SKU = "android.test.purchased"; 

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_in_app_billing);

    buy10Button = (Button) findViewById(R.id.buy10Button); 
    buy15Button = (Button) findViewById(R.id.buy15Button); 
    buy20Button = (Button) findViewById(R.id.buy20Button);      

    String base64EncodedPublicKey = "keykeykey";

    mHelper = new IabHelper(this, base64EncodedPublicKey);


    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() 
    {
          public void onIabSetupFinished(IabResult result) 
          {
            if (!result.isSuccess()) 
            {
               Log.d(TAG, "In-app Billing setup failed: " + result);
               return;
            } 
            if (mHelper == null) 
            {
                return;
            }          
            Log.d(TAG, "In-app Billing is set up OK");
          }
    });     
}

public void buy10Click(View view) 
{
    mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,  mPurchaseFinishedListener, "");
}

public void buy15Click(View view) 
{

}

public void buy20Click(View view) 
{

}   

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    if (mHelper == null) return;  
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) 
    {     
        super.onActivityResult(requestCode, resultCode, data);
    }
}

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() 
{
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
           // Handle error
               return;
        }      
        else if ((purchase.getSku().equals(ITEM_SKU)))   
        {
           consumeItem();
        }              
    }
};

public void consumeItem() 
{
    mHelper.queryInventoryAsync(mReceivedInventoryListener);
}

IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() 
{
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
            // Handle failure
        } 
        else 
        {
            mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU), mConsumeFinishedListener);
        }
    }
};

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() 
{
    public void onConsumeFinished(Purchase purchase, IabResult result) 
    {
        if (mHelper == null) return;
        if (result.isSuccess()) 
        {
            Toast.makeText(InAppBillingActivity.this, "Thank you for your donation!!", Toast.LENGTH_LONG).show();   
        } 
        else 
        {
            // handle error
        }
    }
};

Question:

Yet I keep on receiving E/IabHelper(13392): In-app billing error: Unable to buy item, Error response: 7:Item Already Owned error and that the payment dialog of the Google Play just does not popup.

I have researched and found out many similar situations, some suggested to wait for a few minute and then the purchase will be reset by itself, but I have waited for almost an hour but it still sucks.

I have also found that someone suggest to change the IabResult public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } to return also the BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED as isSuccess = true, yet i dont know how to amend such...

How could the problem be fixed? Thanks!!

Pernik answered 5/10, 2013 at 8:55 Comment(2)
Make sure in start up get the inventory to check if any item is owned, if so then consume it.Stallard
A working Answer is enter link description hereOrdinate
C
22

Check my below code here:

I don't understand in your code why have you used query inventory in purchase finish listener. ConsumeAsync() method should be call while you getting the sku same as your requested sku.

// Callback for when a purchase is finished
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: "
                    + purchase);
            if (result.isFailure()) {
                complain("Error purchasing: " + result);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                return;
            }

            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_GAS)) {

                 // remove query inventory method from here and put consumeAsync() directly
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);

            }

        }
    };

startSetup method

// you have forgot to call query inventory method in startSetup method.

 mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                public void onIabSetupFinished(IabResult result) {
                    Log.d(TAG, "Setup finished.");

                    if (!result.isSuccess()) {
                        // Oh noes, there was a problem.
                        complain("Problem setting up in-app billing: " + result);
                        return;
                    }

                    // Hooray, IAB is fully set up. Now, let's get an inventory of
                    // stuff we own.
                    Log.d(TAG, "Setup successful. Querying inventory.");
                    mHelper.queryInventoryAsync(mGotInventoryListener);
                }
            });

QueryInventoryFinishedListener

And also check if condition purchase is same as you are requested is not equals to null and developer payload is also same in your query inventory finish listener.

if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)){
    //code
}
// Listener that's called when we finish querying the items and
        // subscriptions we own
        IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
            public void onQueryInventoryFinished(IabResult result,
                    Inventory inventory) {
                Log.d(TAG, "Query inventory finished.");
                if (result.isFailure()) {
                    complain("Failed to query inventory: " + result);
                    return;
                }

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

                /*
                 * Check for items we own. Notice that for each purchase, we check
                 * the developer payload to see if it's correct! See
                 * verifyDeveloperPayload().
                 */

                // // Check for gas delivery -- if we own gas, we should fill up the
                // tank immediately
                Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
                if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                    Log.d(TAG, "We have gas. Consuming it.");
                    mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
                            mConsumeFinishedListener);
                    return;
                }
            }
        };

Explaination why it happends:

Whenever you purchased consumable item google play store will not be managed it's product purchased detail and other things in the Google play console. That's why we have to call consumeAsync() method. when we purchased item, Google play store keep record item has been purchased for the one time and allow you to purchased second time.

Hope it will solve your problem.

Cnossus answered 7/10, 2013 at 6:9 Comment(7)
u are right! i have rearranged the codes from beginning to end and it works now by referencing to the Google TrivalDrive sample and it works smooth now! Now will further amend so as trying to implement more products to be selected...thanks!Pernik
verifyDeveloperPayload(gasPurchase) this is witch class method? I can't find out this.Flowerage
@ZalaJanaksinh this is in the sample code downloaded from google documentation in MainActivity.java class.Tafia
It crash on start after I add that "mHelper.queryInventoryAsync(mGotInventoryListener);"Rilke
@Rilke Create new question on the stackoverflow and please include your crash log to conclude the issue, I think it may be because of some other reasons. And you can provide question link over here.Cnossus
@Cnossus can you please check my question in #29423442Rheims
Got same issue with non-consumable item. This is just ad disabling. I am able successfully disable ad on one device, but whet I switch to another device I see ad again. invanory.getPurchase return null. I tried to make additional mHelper.queryInventory(boolean querySkuDetails, List<String> moreSkus) call but getting null again. How I can recognize that item was purchased on different device? I suppose I don't need call consumeAsync since item non-consumable, do I?Ethyne
D
41

You purchased "android.test.purchased" but did not consume it. However, if you forgot to consume it immediately, it is not easy to consume it again. We can wait for 14 days. The fake purchase will be cleared automatically. But it is not acceptable.

I spent a lot of time finding the solution:

Add this line to get debug info.

_iabHelper.enableDebugLogging(true, "TAG");

Run the app. In LogCat, you will see a json string like

{"packageName":"com.example","orderId":"transactionId.android.test.purchased","productId":"android.test.purchased","developerPayload":"123","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:com.example:android.test.purchased"}

Consume it manually (Replace THAT_JSON_STRING with your json string)

    Purchase purchase;
    try {
        purchase = new Purchase("inapp", THAT_JSON_STRING, "");
        _iabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {

            @Override
            public void onConsumeFinished(Purchase purchase, IabResult result) {
                Log.d("TAG", "Result: " + result);
            }
        });
    } catch (JSONException e) {
        e.printStackTrace();
    }

_iabHelper is mHelper.

Darius answered 15/1, 2014 at 19:16 Comment(8)
Thanks a lot for your answer! I will have a try!Pernik
I enabled debugging but can't see JSON string in logs . Can you give me an idea from where or how it gets values so i can create my own string ?Wyler
To get the JSON, you have to query the inventory.Archespore
Really working but it can work on live sku which is generatd from play store?Helpmate
I had an issue while query inventory, it doesn't, return any purchase details, but when it tried to purchase the item its shows the item is already purchased.The issue got solved after i restarted the device it began to return the purchased item in query Inventory. Seems helpful to some one :) .Kironde
Works perfect!!Decennial
Is this specific to test product ? Because i can not see unconsumed item in billingClient.queryPurchases()Formerly
You don't have to query anything these days, the ConsumeParams builder accepts the purchase token directly. The token for your test purchase will be inapp:YOUR_APP_PACKAGE_ID:android.test.purchased. Just consume this.Everyone
C
22

Check my below code here:

I don't understand in your code why have you used query inventory in purchase finish listener. ConsumeAsync() method should be call while you getting the sku same as your requested sku.

// Callback for when a purchase is finished
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: "
                    + purchase);
            if (result.isFailure()) {
                complain("Error purchasing: " + result);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                return;
            }

            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_GAS)) {

                 // remove query inventory method from here and put consumeAsync() directly
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);

            }

        }
    };

startSetup method

// you have forgot to call query inventory method in startSetup method.

 mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                public void onIabSetupFinished(IabResult result) {
                    Log.d(TAG, "Setup finished.");

                    if (!result.isSuccess()) {
                        // Oh noes, there was a problem.
                        complain("Problem setting up in-app billing: " + result);
                        return;
                    }

                    // Hooray, IAB is fully set up. Now, let's get an inventory of
                    // stuff we own.
                    Log.d(TAG, "Setup successful. Querying inventory.");
                    mHelper.queryInventoryAsync(mGotInventoryListener);
                }
            });

QueryInventoryFinishedListener

And also check if condition purchase is same as you are requested is not equals to null and developer payload is also same in your query inventory finish listener.

if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)){
    //code
}
// Listener that's called when we finish querying the items and
        // subscriptions we own
        IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
            public void onQueryInventoryFinished(IabResult result,
                    Inventory inventory) {
                Log.d(TAG, "Query inventory finished.");
                if (result.isFailure()) {
                    complain("Failed to query inventory: " + result);
                    return;
                }

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

                /*
                 * Check for items we own. Notice that for each purchase, we check
                 * the developer payload to see if it's correct! See
                 * verifyDeveloperPayload().
                 */

                // // Check for gas delivery -- if we own gas, we should fill up the
                // tank immediately
                Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
                if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                    Log.d(TAG, "We have gas. Consuming it.");
                    mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
                            mConsumeFinishedListener);
                    return;
                }
            }
        };

Explaination why it happends:

Whenever you purchased consumable item google play store will not be managed it's product purchased detail and other things in the Google play console. That's why we have to call consumeAsync() method. when we purchased item, Google play store keep record item has been purchased for the one time and allow you to purchased second time.

Hope it will solve your problem.

Cnossus answered 7/10, 2013 at 6:9 Comment(7)
u are right! i have rearranged the codes from beginning to end and it works now by referencing to the Google TrivalDrive sample and it works smooth now! Now will further amend so as trying to implement more products to be selected...thanks!Pernik
verifyDeveloperPayload(gasPurchase) this is witch class method? I can't find out this.Flowerage
@ZalaJanaksinh this is in the sample code downloaded from google documentation in MainActivity.java class.Tafia
It crash on start after I add that "mHelper.queryInventoryAsync(mGotInventoryListener);"Rilke
@Rilke Create new question on the stackoverflow and please include your crash log to conclude the issue, I think it may be because of some other reasons. And you can provide question link over here.Cnossus
@Cnossus can you please check my question in #29423442Rheims
Got same issue with non-consumable item. This is just ad disabling. I am able successfully disable ad on one device, but whet I switch to another device I see ad again. invanory.getPurchase return null. I tried to make additional mHelper.queryInventory(boolean querySkuDetails, List<String> moreSkus) call but getting null again. How I can recognize that item was purchased on different device? I suppose I don't need call consumeAsync since item non-consumable, do I?Ethyne
C
6

You can use Google Play "FINANCIAL REPORTS"->"Visit your merchant account for more details"->"Orders" to View and Cancel any order to "consume it". Then you need to restart your device. =)

Cribble answered 30/10, 2014 at 17:20 Comment(2)
this should be higherLyns
restarting device didn't work for me, but clearing cache on Google Play app did work :)Sayers
S
6

I managed to "consume the purchase" simply by restarting the device.

Suzysuzzy answered 22/9, 2015 at 2:8 Comment(0)
F
1

I'm using: implementation 'com.android.billingclient:billing:2.0.0' and had the same error on purchase process.

  • the point is: before of launching the purchase we should consume the pending purchases.
  • please see the code snippet below:

        List<String> skuList = new ArrayList();
        skuList.add(THE_IAP_ID);
        BillingClient.Builder billingClientBuilder = BillingClient.newBuilder(context).setListener(new PurchasesUpdatedListener() {
            @Override
            public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
                 // here we have to check the result ...
    
            });
    
            billingClientBuilder.enablePendingPurchases();
            billingClient = billingClientBuilder.build();
            billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready - query purchases.
    
                    Purchase.PurchasesResult pr = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
                    List<Purchase> pList = pr.getPurchasesList();
                    for (Purchase iitem : pList) {
                        ConsumeParams consumeParams = ConsumeParams.newBuilder()
                                .setPurchaseToken(iitem.getPurchaseToken())
                                .build();
                        billingClient.consumeAsync(consumeParams, consumeResponseListener);
                    }
    
                    // process the purchase
                } else {
                    // cancelled or s.e. 
                    ...
                }
            }
    

Best Regards, Have you fun :)

Fiume answered 2/10, 2019 at 13:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.