title: Consumable In-App Purchase in Android
body: <p>I have one game kind of application. I have gone through a lot of surf but I haven't get any satisfied solution. Here, user can buy coin packs more than once. I have some some problems in my code so user can buy only once. I have read <a href="http://developer.android.com/google/play/billing/api.html" rel="nofollow">documentation about consumable IAP</a>( In App Purchase ) and still the same problem. If I make consumablePurchase() call, it gives BILLING_RESPONSE_RESULT_DEVELOPER_ERROR (ResponseCode 5).</p>
  
  <p><em>Steps :</em></p>
  
  <p><strong>1) Call purchasePackage("android.test.iap.500coins")</strong></p>
  
  <pre><code>public void purchasePackage(String product_id) {
  
      try {
  
          Log.i(TAG, "product name : " + product_id);
          package_name = product_id;
  
          Bundle buyIntentBundle = mService
                  .getBuyIntent(3, getPackageName(), product_id, "inapp",
                          "C890B68423F8EA57F3ED38C3DCC816D7E389F4Cdc4961C23540dadC866B8CFFC5");
          Log.i(TAG,
                  "buy intent response :  "
                          + buyIntentBundle
                                  .getInt("BILLING_RESPONSE_RESULT_OK"));
          if (buyIntentBundle.getInt("BILLING_RESPONSE_RESULT_OK") == 0) {
              Log.i(TAG, "buyIntentBundle created");
              PendingIntent pendingIntent = buyIntentBundle
                      .getParcelable("BUY_INTENT");
              Log.i(TAG, "pendingIntent created");
              startIntentSenderForResult(pendingIntent.getIntentSender(),
                      1101, new Intent(), Integer.valueOf(0),
                      Integer.valueOf(0), Integer.valueOf(0));
              Log.i(TAG, "startIntentSenderForResult started");
          } else
              Log.i(TAG, "getBuyIntent response not ok");
      } catch (RemoteException e) {
          // TODO: handle exception
          Log.e(TAG, "RemoteException : " + e.getMessage());
      } catch (Exception e) {
          // TODO: handle exception
          Log.e(TAG, "Error in buyStructure : " + e.getMessage());
      }
  }
  </code></pre>
  
  <p><strong>2) get response in onActivityResult(int requestCode, int resultCode, Intent data) and make consumablePurchase() after getting response of purchasePackage()</strong></p>
  
  <pre><code>@Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      // TODO Auto-generated method stub
  
      Log.i(TAG, "requestCode : " + requestCode + " :resultCode : "
              + resultCode);
      if (data != null &amp;&amp; requestCode == 1101) {
  
          int responseCode = data.getIntExtra("RESPONSE_CODE", -1);
          String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
          String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
  
          Log.i(TAG, "responseCode : " + responseCode);
          if (resultCode == RESULT_OK) {
              try {
                  switch (responseCode) {
  
                  case 0:
                      /*new Thread() {
                          @Override
                          public void run() {
                              mHandler.sendEmptyMessage(purchaseStart);
                              StartupSync purchaseSync = new StartupSync(
                                      InAppActivity.this, mHandler);
                              purchaseSync.purchasePackage(package_name);
                              mHandler.sendEmptyMessage(purchaseComplete);
                          }
                      }.start(); */
                      JSONObject jo = new JSONObject(purchaseData);
                      String sku = jo.getString("productId");
                      String purchaseToken = jo.getString("purchaseToken");
  
                      Log.i(TAG, "You have bought the " + sku
                              + ". Excellent choice,adventurer!");
  
                      int coins = Integer.parseInt(db.selectSettingsValue("coins"));
                      Log.i(TAG, "coins " + coins);
                      coins = coins + intIncCoins;
                      Log.i(TAG, "coins " + coins);
  
                      db.updateSettings("coins", coins + "");
                      Toast.makeText(
                              InAppActivity.this,
                              "Thank You !",
                              Toast.LENGTH_SHORT).show();
                      finish();
  
                      int response =  mService.consumePurchase(3, sku, purchaseToken);
                      Toast.makeText(
                              InAppActivity.this,
                              "Response : " + response ,
                              Toast.LENGTH_LONG).show();
  
                      break;
  
                  case 1:
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_USER_CANCELED");
                      Toast.makeText(InAppActivity.this,
                              "User pressed back or canceled a dialog",
                              Toast.LENGTH_SHORT).show();
                      break;
                  case 3:
                      Log.i(TAG,
                              "BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE");
                      Toast.makeText(
                              InAppActivity.this,
                              "Billing API version is not supported for the type requested",
                              Toast.LENGTH_SHORT).show();
                      break;
                  case 4:
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE");
                      Toast.makeText(
                              InAppActivity.this,
                              "Requested product is not available for purchase",
                              Toast.LENGTH_SHORT).show();
                      break;
                  case 5:
  
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_DEVELOPER_ERROR");
                      Toast.makeText(
                              InAppActivity.this,
                              "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",
                              Toast.LENGTH_SHORT).show();
                      break;
                  case 6:
  
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_ERROR");
                      Toast.makeText(InAppActivity.this,
                              "Fatal error during the API action",
                              Toast.LENGTH_SHORT).show();
                      break;
                  case 7:
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED");
                      Toast.makeText(
                              InAppActivity.this,
                              "Failure to purchase since item is already owned",
                              Toast.LENGTH_SHORT).show();
                                              break;
                  case 8:
                      Log.i(TAG, "BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED");
                      Toast.makeText(InAppActivity.this,
                              "Failure to consume since item is not owned",
                              Toast.LENGTH_SHORT).show();
                      break;
                  }
  
              } catch (JSONException e) {
                  Log.i(TAG, "Failed to parse purchase data.");
                  e.printStackTrace();
              } catch (RemoteException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          } else if (resultCode == RESULT_CANCELED) {
              Toast.makeText(InAppActivity.this, "Purchase Failded",
                      Toast.LENGTH_SHORT).show();
          }
      } else if (resultCode == RESULT_CANCELED) {
          Toast.makeText(InAppActivity.this, "Purchase Canceled",
                  Toast.LENGTH_SHORT).show();
      }
      super.onActivityResult(requestCode, resultCode, data);
  }
  </code></pre>
  
  <p>Even I have tried another way like getting an array of all purchased IAP and make them consumable. I have written code in splash activity. Printed log is <a href="http://i.stack.imgur.com/T6Adb.png" rel="nofollow">here</a>.</p>
  
  <p><strong>Why does it give same error again and again ? That was a bug solved by google itself in March 2013.</strong></p>
  
  <p><strong>Any suggestions/advice acceptable!</strong></p>