2

I'm trying to implement the inApp billing service with IabHelper. I manage to go through the full purchase process without problems.

//-----------------------------------------------
public void billingServiceLaunchPurchase(String item) {
//-----------------------------------------------
    if (isNetworkAvailableSync(getBaseContext())) {
        currBuyItem=item;
        billingConsummeType=1;
        mHelper.launchPurchaseFlow(BaseActivity.this, item, 10001, mPurchaseFinishedListener, "");
    }
    else {
        onBillingServiceFailed();  
    }
}

    //-----------------------------------------------
    mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    //-----------------------------------------------
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
        {
           if (result.isFailure()) {
              // Handle error
              onBillingServiceFailed();                
              return;
         }      
         else if (purchase.getSku().equals(currBuyItem)) {
             billingServiceConsumeItem();
         }

        }
    }; 

@Override
//-----------------------------------------------------------------------    
protected void onActivityResult(int requestCode, int resultCode, Intent data)
//-----------------------------------------------------------------------
{
    if (billingServiceConnected) {
          if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {     
              super.onActivityResult(requestCode, resultCode, data);
          }
          else {
              // onActivityResult handled by IABUtil.
          }
    }
    else
           super.onActivityResult(requestCode, resultCode, data);

}    

However, I cannot detect the event when the user launches the purchase but then press the backspace on the Google confirmation screen with the button "BUY" to interrupt it.

It neither triggers a failure on onIabPurchaseFinished nor it triggers onActivityResult so my application stays in an intermediary status.

Please help me to solve my problem.

ppeterka
  • 20,583
  • 6
  • 63
  • 78
user1778857
  • 43
  • 1
  • 4

5 Answers5

5

According to what I have understood your question, you are searching for purchase cancel event in app billing.

You can trigger this via onActivityResult() method.Put below code in onActivityResult() method. There is simple RESEULT_CANCEL type that shows you user has been cancel purchasing.

    if (mHelper == null)
                return;

            if (requestCode == 10001) {

                int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
                String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
                Log.d("INAPP_PURCHASE_DATA", ">>>" + purchaseData);
                String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
                Log.d("INAPP_DATA_SIGNATURE", ">>>" + dataSignature);
                String continuationToken = data
                        .getStringExtra("INAPP_CONTINUATION_TOKEN");
                Log.d("INAPP_CONTINUATION_TOKEN", ">>>" + continuationToken);

                if (resultCode == RESULT_OK) {
                    try {
                        Log.d("purchaseData", ">>>"+purchaseData);
                        JSONObject jo = new JSONObject(purchaseData);
                        String sku = jo.getString("productId");
                        alert("You have bought the " + sku
                                + ". Excellent choice, adventurer!");
                    } catch (JSONException e) {
                        alert("Failed to parse purchase data.");
                        e.printStackTrace();
                    }
                } else if (resultCode == RESULT_CANCELED) {

                    Toast.makeText(AppMainTest.this,
                            "Sorry, you have canceled purchase Subscription.",
                            Toast.LENGTH_SHORT).show();

                } else if (resultCode == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
                    Toast.makeText(AppMainTest.this, "Item already owned",
                            Toast.LENGTH_SHORT).show();
                }

            }
}

or

you can also handle manually by using your business logic. check if user cancel purchase product then put flag user has been purchased or not if not then call launchPurchaseFlow() method again.

EDIT

onDestroy() method 

@Override
    public void onDestroy() {
        super.onDestroy();

        // very important:
        Log.d(TAG, "Destroying helper.");
        if (mHelper != null)
            mHelper.dispose();
        mHelper = null;
    }

if you have button then you can directly call launchPurchaseFlow() method into onClick event so that every time your mHelper created as new purchase.

or

if you are using it in onCreate method and you haven't any button click event to purchase product then you have to give value as null according to my knowledge.

Hope it will solve your problem.

Tad
  • 4,668
  • 34
  • 35
Maulik
  • 3,316
  • 20
  • 31
  • IT WORKED, THANKS A LOT !!!, now when I come back from this event, my purchase flow stays in an unfinished status and if the user wants to do another purchase I've got an exception "Can't start async operation (launchPurchaseFlow) because another asyn operation (launchPurchaseFlow) is in progress." Do you know how to destroy the previous purchaseFlow when the event Cancel is triggered? – user1778857 Oct 07 '13 at 10:11
  • you should ask another question for that or you can upvote my answer :P. please check my edited answer above. – Maulik Oct 07 '13 at 10:29
  • I'm not talking about destroying the mHelper when destroying the activity (this I know). What I want just to cancel the Purchase Flow after detecting the Cancel result so the mHelper can be reused with another purchase transaction if the user wants to try again (imagine he pressed backspace by error and then wants to launch de purchase again). In the current situation, the app will crash because the previous purchaseFlow still exists (even if the user has cancelled it). – user1778857 Oct 07 '13 at 10:52
  • 1
    Hi Malik, I finally managed to make it work. I had to completely destroy the mHelper and reinitialize it. It would be much better if there was a way to destroy only the Purchase Flow and keep the mHelper alive but at least it works :) – user1778857 Oct 07 '13 at 12:42
  • have you checked my edited answer above? how you call launchPurchaseFlow() method call? I mean on create or on button click? – Maulik Oct 07 '13 at 13:12
  • I've got a pop showing the cost (queryInventory) and two buttons: proceed and cancel. When the user clicks on Proceed the launchPurchaseFlow is triggered. And the Google Pop with the button BUY appears. If at that moment the user clicks the back button, the Google Pop disappears and I'm back again on the proceed Pop. If the users clicks again the proceed button and the purchaseFlow is launched again the app will crash because of the aforementioned exception. That's why I had to identify the cancel event and reinit the mHelper before letting the user click again on proceed. – user1778857 Oct 07 '13 at 13:22
  • I had the exact same problem: an exception in my activity that deals with the purchase flow anytime the user backed off from the purchase flow half way through. I had to dispose the mHelper and set it up again. Then it worked fine. Thanks. – Levon Feb 06 '15 at 11:53
2

When user press BACK or press outside the dialog, the purchase flow is still in process and if user press PURCHASE button again, there will be an exception "Can't start async operation because another async operation is in progress".

To fix this, I had manually create a flag to know if there is a purchase flow in progress. And since IABHelper don't provide a way to dispose a purchase flow, I have to dispose mHelper and recall initBilling()

boolean onPurchaseFlow = false; 
public void purchaseItem() {
        if (!onPurchaseFlow) {
            mHelper.launchPurchaseFlow(mActivity, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "");
            onPurchaseFlow = true;
        } else {
    //dispose mHelper       
    if (mHelper != null) {
            mHelper.dispose();
            mHelper = null;
            }

            initBilling(mActivity); // restart IABHelper, a code snippet will fire launchPurchaseFlow when onPurchaseFlow is true
        }
        }

The other important part is call launchPurchaseFlow in onQueryInventoryFinished() to ensure that it is called (in the second request of user) when all the init operation has completed:

public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        // YOUR CODE HERE
        if (onPurchaseFlow == true) {
        mHelper.launchPurchaseFlow(mActivity, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "");
        }
    }

Remember to reset the flag onPurchaseFlow = false when finish in onIabPurchaseFinished()

    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_PREMIUM)) {
            // bought the premium upgrade!
            // YOUR CODE HERE
            onPurchaseFlow = false;
        }
}
Quan Nguyen
  • 5,074
  • 3
  • 24
  • 26
2

You can access the result code through IabResult class, and compare it with the different result codes in IabHelper class, and use it in your OnIabPurchaseFinishedListener:

    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
        = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase)
    {            

        if (result.isFailure()) {

            if (result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED  || result.getResponse() == IabHelper.IABHELPER_USER_CANCELLED){

                // user cancelled purchase

            } else {

            // any oder reasult

            }
            return;
        }

        else if (purchase.getSku().equals(SKU_SPIRIT_LEVEL)) {

            // no error, purchase succeeded                

        }
    }
};
Gabor Mezo
  • 21
  • 3
  • In some purchase error cases the android system (IabHelper) sends the same result code as by the user cancel. – Gabor Mezo Mar 30 '17 at 13:27
0

you can detect the back press overriding by this method

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
//do what you want to avoid going back while during transaction
Log.d("bak button pressed", "ture");
}
return super.onKeyDown(keyCode, event);
}

even check this out, it might help you

Check if back key was pressed in android?

Community
  • 1
  • 1
Maulik Sheth
  • 584
  • 3
  • 10
  • 1
    Hi, thanks for the answer but it wasn't the problem. You cannot override the onKeyDown because you are positionned on the Google Billing Activity. – user1778857 Oct 07 '13 at 10:17
0

Inside IabHelper there's a method called handleActivityResult(...).

If you override onActivityResult (Fragment or Activity) and call that method inside (call it with the same parameters). In this way, the helper manage all the callbacks and can redo the purchase flow without launching any exceptions.