How to best save InApp purchase status locally? How to best save InApp purchase status locally? android android

How to best save InApp purchase status locally?


I too was making the same investigations, but during my testing I figured out that you do not need to store it, as Google do all the caching you need and I suspect (though I have not investigated it) that they are doing so as securely as possible (seeing as it in their interest too!)

So here is what i do

// Done in onCreatemHelper = new IabHelper(this, getPublicKey());mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {public void onIabSetupFinished(IabResult result) {      if (!result.isSuccess()) {         // Oh noes, there was a problem.         Log("Problem setting up In-app Billing: " + result);      } else {         Log("onIabSetupFinished " + result.getResponse());         mHelper.queryInventoryAsync(mGotInventoryListener);     }    }});// Called by button pressprivate void buyProUpgrade() {    mHelper.launchPurchaseFlow(this, "android.test.purchased", 10001,              mPurchaseFinishedListener, ((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId());}// Get purchase responseprivate IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {    public void onIabPurchaseFinished(IabResult result, Purchase purchase)     {       if (result.isFailure()) {          Log("Error purchasing: " + result);          return;       }             else if (purchase.getSku().equals("android.test.purchased")) {      Log("onIabPurchaseFinished GOT A RESPONSE.");              mHelper.queryInventoryAsync(mGotInventoryListener);      }    }};// Get already purchased responseprivate IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {    public void onQueryInventoryFinished(IabResult result,       Inventory inventory) {       if (result.isFailure()) {         // handle error here           Log("Error checking inventory: " + result);        }       else {         // does the user have the premium upgrade?                 mIsPremium = inventory.hasPurchase("android.test.purchased");                 setTheme();         Log("onQueryInventoryFinished GOT A RESPONSE (" + mIsPremium + ").");       }    }};

So what happens here?

The IAB is set up and calls startSetup, on a successful completion (as long as it has been run once with an internet connection and is set up correctly it will always succeed) we call queryInventoryAsync to find out what is already purchased (again if this has been called while online it always works while offline).

So if a purchase is completed successfully (as can only be done while online) we call queryInventoryAsync to ensure that it has been called while online.

Now there is no need to store anything to do with purchases and makes your app a lot less hackable.

I have tested this many ways, flight mode, turning the devices off an on again and the only thing that messes it up is clearing data in some of the Google apps on the phone (Not likely to happen!).

Please contribute to this if you have different experiences, my app is still in early testing stage.


I refactored ne0's answer into a static method, including the comments from snark.

I call this method when my app starts - you'll need to enable your features at the TODO

/** * This is how you check with Google if the user previously purchased a non-consumable IAP * @param context App Context */public static void queryPlayStoreForPurchases(Context context){    final IabHelper helper = new IabHelper(context, getPublicKey());    helper.startSetup(new IabHelper.OnIabSetupFinishedListener()     {         public void onIabSetupFinished(IabResult result)          {               if (!result.isSuccess())                {                   Log.d("InApp", "In-app Billing setup failed: " + result);               }                else                {                      helper.queryInventoryAsync(false, new IabHelper.QueryInventoryFinishedListener()                    {                        public void onQueryInventoryFinished(IabResult result, Inventory inventory)                        {                            // If the user has IAP'd the Pro version, let 'em have it.                            if (inventory.hasPurchase(PRO_VERSION_SKU))                            {                                //TODO: ENABLE YOUR PRO FEATURES!!                                 Log.d("IAP Check", "IAP Feature enabled!");                            }                            else                            {                                Log.d("IAP Check", "User has not purchased Pro version, not enabling features.");                            }                        }                    });               }         }    });}

This will work across reboots and without a network connection, provided the user purchased the item.


Since you already know that it's impossible to make it unhackable using this system, I would recommend not attempting to prevent hacking. What you are proposing is known as "Security through obscurity" and is usually a bad idea.

My advice would be to try queryInventoryAsync() first, and only check your 'isPremium' flag if there is no internet connection.

There are also a few potential other ways of going about this, such as having separate free and premium apps, instead of an in app purchase. How other people handle this and the tools Google makes available might warrant an investigation.

queryInventoryAsync will automatically take into account uninstall and reinstalls, as it tracks purchases for the logged in user.