We continue our series of articles about the implementation of in-app purchases in a Google Play app. For more posts visit these links:
- Android in-app purchases, part 1: configuration and adding to the project
- Android in-app purchases, part 2: processing purchases with the Google Play Billing Library.
- Android in-app purchases, part 3: retrieving active purchases and subscription change.
- Android in-app purchases, part 4: error codes from the Billing Library and testing.
- Android in-app purchases, part 5: server-side purchase validation.
An example of how we passed errors to our callbacks can be found in this article. We’ve already considered one of the errors in previous articles — this is USER_CANCELED when a user closes the purchase dialog without buying anything. Let’s get to know the others.
ERROR and DEVELOPER_ERROR for Android billing
Let’s start with the errors called ERROR (responseCode 6) and DEVELOPER_ERROR (responseCode 5). For the first case, Google writes in the documentation “Fatal error during the API action”, for the second one – “Invalid arguments provided to the API”. For example, I was able to get a DEVELOPER_ERROR when I passed an empty string to the builder in setType() for the querySkuDetailsAsync() request.
But it is not that simple. I went further and in the launchBillingFlow() method I used the modified SkuDetails (pulled the json from the SkuDetails of the real product, changed the productID in it and passed it to the new SkuDetails constructor). In fact, this is an invalid argument, and I expected to get a DEVELOPER_ERROR, but … I got an ERROR.
This, of course, was an artificial example. The case when Google rejected the payment is much closer to reality. If you select “test card, always declined” in the purchase dialog when testing purchases from a test card, what we will tell you about at the end of the article, the ERROR will also return, but with a proper text.
In the third article, where the subscription change was described, we increased the price of an annual subscription almost 3 times for one of the proration modes, but did not say what mistake should have been there if we had not done that. We are making amends.
Since it turns out that the wrong proration mode is specified there, we should logically get the same DEVELOPER_ERROR. Instead, we get SERVICE_UNAVAILABLE (responseCode 2). We also get it if we enter any inappropriate number as proration mode (this is int, not enum, no one will stop us), and if we specify the wrong purchaseToken. We look at the documentation about SERVICE_UNAVAILABLE and … wait, what?! We see that the “Network connection is down.”.
At the same time, we also see a weird dialog:
Another interesting thing in the case of ERROR is that when closing the dialog NOT through the “OK” button (namely, by means that are interpreted as “navigate back”), the ERROR comes to onPurchasesUpdated(), and in the case of SERVICE_UNAVAILABLE in a similar situation comes USER_CANCELED (but if you click “OK” in the dialog, we will receive SERVICE_UNAVAILABLE, as expected).
And in the case of lost internet connection there indeed comes SERVICE_UNAVAILABLE.
Get an ebook with insights
and advice from top experts
Google Play Billing error codes
Here are some more error codes with small comments, so to speak, honorable mentions.
- BILLING_UNAVAILABLE (responseCode 3). Google explains that “The Billing API version is not supported for the type requested”. I was able to reproduce this error when logged out of my Google account, as well as when used a Huawei device without Google Play Services. It may also be reproduced on older phones where Google Play has not been updated.
- SERVICE_DISCONNECTED (responseCode -1). The application is sometimes disconnected from the Google Play service. This can happen if the Play Store unexpectedly updates. Therefore, I advise you to play it safe and connect before each call of the Billing Library methods, as in the previous articles. Also, both Google and we advise you to add some retry policy if this error still comes in the response.
- SERVICE_TIMEOUT (responseCode -3). The name speaks for itself — we have been waiting for a response from Google Play for too long.
- FEATURE NOT SUPPORTED. There are five FeatureType constants in the BillingClient class. Their availability on this device can be checked using the method of BillingClient.isFeatureSupported(BillingClient.FeatureType.A_NECESSARY_FEATURE). On my phone (Xiaomi Mi A2 Lite), FEATURE_NOT_SUPPORTED has returned only for SUBSCRIPTIONS_ON_VR. At the same time, for IN_APP_ITEMS_ON_VR, as well as for all other features, OK has returned.
- ITEM_NOT_OWNED (responseCode 8). It occurs while trying to consume a purchase that we do not have. Possibly, even repeatedly after a successful consumption.
- ITEM_ALREADY_OWNED (responseCode 7). And here, on the contrary, the error occurs while trying to buy a product that we already have. In such a case, you just need to update the UI and make the purchase button non-clickable.
ITEM_UNAVAILABLE billing response code
The last and probably the most popular error at the beginning of the in-app purchases implementation path is ITEM_UNAVAILABLE (responseCode 4). It says that the product is not available for purchase, but does not explain why. The reasons can be very different: from testing on the wrong account or device to buying an inactive product.
Here is a checklist of what you need to do to avoid this error while testing:
1. Send an app with the Billing Library to your test track. This is a mandatory condition; at the same time, you can also test on debug builds with the same applicationId, but it is important that the app with the Billing Library is uploaded to the Play Console at least once.
2. Add Google accounts of testers to this test track, which is especially important for internal testing or closed alpha/beta. There will also be a link in the How testers join your test section, where the testers should accept the invitation.
3. You can buy only an activated product. After creating a product, there is an activate button in the Play Console. We described the process of creating a product in more detail in the first article.
4. Make sure that the testing on the device takes place from a tester’s Google account (which means that it must be enrolled as a tester in this test track and must have all the necessary technical access). This point seems to be obvious but things happen, and you also need to check this if you have received such an error.
5. The applicationId of the build which is used for purchase testing must completely match the applicationId from the Play Console. This is especially important for those who have a suffix added in their debug builds.
6. Add the email addresses of testers to the Setup → License Testing section in the left menu of the account (not the application), so that they buy products for free from a test card, not from a real one. Another advantage is that subscriptions in this case will have a test duration. It is not related to this error, but it is also useful knowledge.
Errors can greatly complicate the work, so it is always important to understand how they can occur. Considering how many steps you need to go through to get access to products, the easiest way is to catch ITEM_UNAVAILABLE. Therefore, I hope that my checklist will help you.
The Adapty SDK will facilitate error handling and provide a lot of other advantages: basic subscription analytics, cohort analysis, simple testing of paywalls, integration with analytics services, server error validation. Try it now!