Android In-app billing upgrade does not work

Customers sometimes report that they don’t get the In-app billing (IAB) upgrade to full version, eventhough the apps are carefully tested on our test devices. We use managed IAB in 3 apps. We test through alpha testing and we have not found any problems with upgrading. 2 of the apps have IAB version 3 and 1 have IAB version 5. It is the same problem. The apps are developed according to IAB guidelines.

Why does this happen? Is there some problem with Google’s servers, which causes these IAB errors, which prevents some customers from getting the upgrade? Has anyone else encountered the same issue?

This is a very serious issue for my company, since it results in bad reviews and lower rankings for the IAB apps. We can not use IAB in the upcoming apps, since it seems to be too unreliable. It is also very annoying that IAB seems to be much more reliable on iOS. These are 2 other issues with IAB, which we still haven’t found any solution to:

Consume purchase for In-app billing does not work on Android 6

In-app purchases are lost after uninstall on “managed” purchase type

Here are the statistics about this issue:

app 1: 1215 sold upgrades, 2 complaints from customers who did not get the upgrade

app 2: 94 sold upgrades, 2 complaints from customers who did not get the upgrade

app 3: 122 sold upgrades, 4 complaints from customers who did not get the upgrade

There could be more customers, who did not get the upgrade. The statistics are only for those who complained.

That’s actually expected (for lack of better word), I have more or less same amount of issues with IAB (around ~300 reported issues out of +50k IAB transactions which is around .5% and it matching your stats, I’ve also confirmed this with some other developers), what I noticed:

  1. there are sometimes issues w GPS caching of Play servers response which causes IAB to return invalid (outdated) response, this can usually be fixed by querying IAB afer some time (restore purchases button or query purchases on startup come in handy)
  2. there are definitely issues (or maybe by design limitation) with multi-account, if user has more than one account configured in Google Play account used for purchase has to be set as default (primary) at least once (this is probably most common cause of issues with IAB when user is reinstalling app or installing app on secondary/new device)
  3. some other random stuff I have no idea what’s root cause but indeed in some cases IAB simply refuses to work (I was even able to confirm that with one user who installed APK with some additional debugs and collected logs: managed in-app item wasn’t in list returned by getPurchases although he clearly owned it (which I could confirm in payments.google.com console)

I’ve deal with this issues by managing backup licensing provider which uses simple GAE (Google App Engine) server that issues and manages unlock codes, in case of any issues with IAB I simply send unlock code.

Thanks for the info! I have asked about this on Stackoverflow and and many other places, but no one else has answered. Have managed to decrease the ratio of lost purchases through the backup licensing provider and through querying IAB after some time?

You mention querying purchases on startup. I think I already have done that. Do you mean anything more than what I have done in the code below:

    buyHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        @Override
        public void onIabSetupFinished(IabResult result) {//$$ this should not be done after upgrading to full version
            if (!result.isSuccess()) {
                Log.v("**Billing ERROR**: ",
                        "" + result);
            } else {
                Log.v("PURCHASE SUCCESSFULLY: ",
                        "" + result);
                setUpInventory();
            }


        }

private void setUpInventory() {
ArrayList<String> moreSkus = new ArrayList<String>();
moreSkus.add(SKU);
buyHelper.queryInventoryAsync(true, moreSkus,
new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result,
Inventory inv) {

                    if (result.isSuccess()) {
                        SkuDetails details = inv.getSkuDetails(SKU);
                        purchase = inv.getPurchase(SKU);

                    } else {
                    }
                }
            });
}

You also mention a backup licensing provider which uses simple GAE server that issues and manages unlock codes. Does that require setting up and running my own server? Isn’t that time consuming and expensive?

Shouldn’t Google have informed us about these issues? There is no info about any of this in the IAB documentation. I would never have started with IAB if I knew about these issues in the first place.

Customers who don’t get the upgrade have accused me of scam and fraud in their reviews. How should I deal with this? Is it really worth it to use IAB, when it is so unreliable? None of these issues have ever occurred in my apps, with a separate free and full version. It seems like any app with IAB will get lower reviews and rankings, due to some customers getting angry when they are not getting their upgrades. So it seems like an app with the standard solution of a separate free and full version will reach higher on the ranking charts.

Looks, OK, the only thing I’d change is to store purchase locally and only re-check once a week or so, there’s really no need to re-check license every time user run your app.

Well it’s GAE, with GAE you are hosting your webapp on google servers, they are charging for traffic and storage used but free tier is more than enough to handle this workload.

That’s seems like an overkill, I mean we are talking about less than half percent of users affected, you can always issue refund for them. Also reinstalling application sometimes help (dunno why).

I always (and I mean it) reply to such review asking user to contact me directly via FB or e-mail (and include link to my FB profile and my office e-mail. Most of users reply and contact me directly and I can try to resolve this issue somehow (worst case scenario is to issue refund but I rarely have to resort to this thanks to my GAE backup).

Do you use the backup licensing provider only to generate license codes for those customers, who don’t get the upgrade through IAB? You then mail them this license code after you have seen their complaints in the reviews?

I have solved this by sending those customers a special built version, where the upgrade is enabled. It still takes time and energy to respond the reviews and then send them the link to the special built version. Most of the customers don’t reply, but I will try your approach with sending them my public FB profile and office e-mail.

Yes, basically, my GAE webapp is really trivial, it uses standard endpoints architecture (see here: https://cloud.google.com/appengine/docs/java/endpoints/helloendpoints-android-studio, the only thing not covered by this tutorial is authentication) and there is basically two calls: redeem and check, redeem validates code and stores user id (which is available since endpoint is marked as available for logged users) and check test logged user against datastore. I use this for promotions (I can run promotion via FB and offer free licenses that way) and as a backup when there’s something wrong with IAB.

Yeah, I’ve noticed that when I used standard canned reply like “Please contact us directly via e-mail” it really doesn’t work but changing this to “Please contact us directly via e-mail: <email_here> or our FB: <fb_here> and we will fix this” effect in much better response ratio. Additionally response phrased that way negates any potential damage as new users which can otherwise be reluctant due to comments with “scam, thieves” and such see that developer is taking active approach to such issues (hence no scamming :)).

Interesting solution with the GAE webapp! The problem must however be that you can only send the promo codes to those customers, who mail you or write a review with text when the upgrade does not work. The risk is that the customer will write an anonymous 1 star review without any text. Then there is no possibility of contacting the customer.

You wrote that it would be better to query IAB after some time. I assume that you mean calling queryInventoryAsync() once a week or so?

You gave me the advice to to store the purchases locally. Do you mean storing the purchase object in an Internal Storage, as described here?

How to save data in an android app - Stack Overflow

That’s how it is and the simplest solution for that is not to care - I mean I try to help users with issues but if someone leaves 1 star rating without comment and/or stupid review he/she may GTFO.

Yes, I use SharedPreferences to save license state and recheck only once a week.

You have useful technical solutions to this problem. I think this is how far we as developers can reach with this issue now.

I think that the issue is serious and Google needs to investigate it. Imagine a bank which uses a financial software where 0,5% of all transactions disappear and those customers loose their money. The same thing happens here. If customers know that they can loose their money when buying In-app upgrades, they are less likely to buy apps on Google play in the future. The buying ratio is already higher on iOS and this issue is not favorable for Android in the competition with iOS.

I think that this is a very serious issue. Imagine a bank where 0,5% of all transactions get lost and those customers loose their money. That is how serious this problem is. It makes customers not want to buy apps on Google play, because the purchasing process is not reliable. This is very damaging to Android developers and to Google Play, especially since the purchasing rate is already higher on iOS Appstore. This issue makes me not want to use
IAB until it is solved. I am so tired of getting blamed by customers for an error that I am not responsible for.

We know how to implement In app billing. You don’t have to explain this for us. The issue is that Google’s servers don’t work in 0,5% of the cases, so these customers loose their purchases. We developers can not do anything about this, except to try to inform Google about it, so they fix the problem.

I totally agree, Adam. This bug is so serious and I have tried to contact Google and inform them about it. I have got no answer so far. I really appreciate Google for their invention of Android , but I don’t understand why there is no interest from them in fixing this bug.
It has so serious consequences for my company, that we are considering to stop all Android development and focus on iOS instead. It just does not pay off to produce Android apps, when there is no reliable way to sell upgrades through In-app billing. My shareholders say that they do not want to invest more in Android app development until In-app billing can be used in a reliable way.

Does anyone know how to contact Google about this issue?

I have exactly the same problem, Adam. It seems like ALL developers and customers on Google play are affected by this.

oswaldmandus, does your code fix with querying purchases on startup less frequently improve this problem? I mean does it make In-app billing work more often? I understand that you also have the same problem with 0,5% of the customers not getting their upgrade and their paid app. But will querying purchases on startup less often decrease this error rate in some way?

I mean would querying purchases on startup more frequently (like we have in our apps) increase this error rate?

I tried to implement your solution with re-checking license and calling queryInventoryAsync() once a week. Do you mean storing a timestamp when the app was installed? Then checking the current time every time the app starts and compare if a week has passed since then?

What is the purpose of storing the purchases locally in an Internal Storage?

I actually do not think the solution with re-checking license only once a week will decrease the IAB errors in my case. The reason is that queryInventoryAsync() in my setUpInventory method is only called if the user has not upgraded to full version in my apps. These methods are not called after the user has upgraded, so they can not cause any loss of purchases.

Oh, sorry I was sure you check inventory regardless of current purchase status. In such case there’s no need for this. I re-check purchases every week to handle refunds (I issue refund quite if user ask me to do so even to avoid negative review and in such case I’d like to revoke purchase).

Well, we already know all of this.

oswaldmandus , do you know any way of reporting this bug to Google or how to contact them? You are so far the only one, who has answered and supplied useful information about this issue.

It is possible to report bugs about open source Android code on https://code.google.com/p/android/issues. In-app billing is included in Play services, which is not open source, so the In-app billing bugs here can not be reported to https://code.google.com/p/android/issues.

I can not find any developer forum or Google+ community about In-app billing. There are Google+ communities for Chromecast and lots of other Android technologies, but none for In-app billing. I often get complains about In-app billing not working and this is very frustrating. It decreases my motivation for working with this, especially since the errors are not caused by my apps and I can not do anything to fix the bugs!