Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android: Inapp billing: Error response: 7:Item Already Owned

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!!

like image 392
pearmak Avatar asked Oct 05 '13 08:10

pearmak


People also ask

What are the possible reasons for in-app billing errors?

This error can also indicate that the application was not correctly signed or properly set up for In-app Billing in Google Play, or does not have the necessary permissions in its manifest. Fatal error during the API action. Requested feature is not supported by Play Store on the current device. Failure to purchase since item is already owned.

Why am I getting an API version error when billing?

User pressed back or canceled a dialog. Billing API version is not supported for the type requested. Invalid arguments provided to the API. This error can also indicate that the application was not correctly signed or properly set up for In-app Billing in Google Play, or does not have the necessary permissions in its manifest.

How to retrieve in-app products owned by the user?

StringArrayList containing the signatures of purchases from this app. String containing a continuation token to retrieve the next set of in-app products owned by the user. This is only set by the Google Play service if the number of products owned by the user is very large.

What are the errors during the API action?

Fatal error during the API action. Requested feature is not supported by Play Store on the current device. Failure to purchase since item is already owned. Failure to consume since item is not owned. Requested product is not available for purchase. Success. Play Store service is not connected now - potentially transient state.


Video Answer


2 Answers

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.

like image 133
Vince Yuan Avatar answered Sep 19 '22 09:09

Vince Yuan


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.

like image 38
Maulik Avatar answered Sep 18 '22 09:09

Maulik