Testing in-app purchases on Android

An addendum to the official documentation

Overview

Monetization is one of the most important aspects of distributing your product to the rest of the world. It can make or break a small freelance developer or an established startup.

Android, being so widespread, provides ways for users to purchase products from within your app: this is referred to as In-app Billing. Through this set of APIs, developers can offer two types of products:

  • Managed in-app products
    As the name suggests, these products are managed by the developer, and they branch into consumable and non-consumable. A consumable product is usually temporary and, once consumed, can be bought again, whereas a non-consumable product is a one-off benefit that the user will continue having in your app.
  • Subscriptions
    These products come with a validity period (days, months, weeks, years) and are automatically renewed at the end of each billing cycle. When a subscription is not renewed, the product is no longer active for the user.

The official documentation is very helpful when it comes to the first steps for adding in-app products to your application. In particular, the “Selling In-App Products” training is well structured and walks you through each required step:

  1. Adding the Play Billing Library to your app (also check out this article by Joe Birch)
  2. Configuring the products in the Google Play Console
  3. Testing the in-app products in your app

Let’s focus on the third point.

Testing, according to the documentation

According to the documentation for testing, we have two ways of testing purchases:

  • Static responses from Google Play
    By using a restricted set of product IDs, you can trigger static responses from Google Play, so you can test that your app correctly handles all the possible states. You should use this when integrating the Play Billing Library into our app, or for instrumentation testing.
  • Test purchases
    A Google account whitelisted as license-test in the Play Console will be able to make purchases without being actually charged. You can use this when the app goes to QA, or for general testing.

Static responses

Using static responses sounds easy enough, right? You just use one on the following product IDs during a purchase operation:

  • android.test.purchased
  • android.test.canceled
  • android.test.refunded
  • android.test.item_unavailable

and the Play Store will reply accordingly. The code looks roughly like this:

mService.getBuyIntent(3, "com.example.myapp", "android.test.purchased", PRODUCT_TYPE, developerPayload);

However, if you are testing subscriptions, you’re out of luck:

Note: If you’re testing subscription purchases, you must use the product ID of an actual subscription, not a reserved product ID. [Reference]

This means that we cannot rely on static responses to test subscriptions; instead, we need to resort to test purchases.

Test purchases

By using the so-called In-app Billing Sandbox, we can enable access to test purchases. These are the closest thing we have to actual purchases, with a few notable exceptions:

  • You are not being charged any amount for the product you buy
  • If you’re buying a subscription, the billing period recurs on a daily basis, regardless of the duration configured in the Play Console
  • You have manual control over the response for each purchase

The last point is particularly interesting, because we have two ways of customizing the test purchase behavior.

The first method allows for fine control over the licensing behavior for all the testers: for example, by leaving it to RESPOND_NORMALLY we have a behavior similar to the real one.

The second method, on the other hand, allows for coarse control over the response of the fake credit card: you can decide whether the card will always approve the purchase or always decline it. Intuitively enough, this second method can be customized by each tester.

On the left, a screenshot of the License Testing section on the Play Console; on the right, the on-device configuration dialog for test purchases.

In order to be eligible for test purchases, there are a few steps to go through:

  1. Your APK must be uploaded to the Play Console (drafts are no longer supported)
  2. Add license testers in the Play Console
  3. Have testers join the alpha/beta testing group (if available)
  4. Wait 15 minutes, then start testing

Sounds easy enough, right? The documentation is also very encouraging

It’s easy to set up test purchases [1]

…Sure, let’s see about that. 😒

Testing, according to real life

You religiously follow the documentation, wait 15 minutes (make it 30, just in case), you start testing and…an error occurs. What now?

It turns out that the documentation is fairly optimistic when explaining the required steps for testing in-app purchases. According to this StackOverflow answer, which in turn is a collection of various trial-and-errors by other users, plus my personal experience, there are actually 10+ conditions that you need to meet or account for before being able to properly use test products!

Let’s recap them here:

  1. Your app is published (i.e., not in draft).
  2. The APK must be published (either production, alpha or beta channels).
  3. The APK you uploaded matches the one you’re testing with when it comes to version code, version name, and keystore signature.
  4. When testing on the device, you’re using a different account than the one tied to the Play Console (i.e., not your developer account).
  5. The instructions to wait 15 minutes are a bit too optimistic, as it can take up to 2 hours for changes to propagate from the Play Console.
  6. Double check that the SKU you’re using in the app matches the one for the product that was configured in the Play Console.
  7. Double check that you’re not trying to purchase an already owned product or an already active subscription.
  8. Double check that you have activated your products in the Play Console: by default, products in the console are deactivated and you need to manually activate them.
  9. If you’re using the alpha/beta channels, make sure that the account you’re testing with is part of the testing group (i.e., has clicked on “Become a tester” after following the opt-in URL).
  10. If you use ABI flavors, like arm-v7, arm-v8, etc., make sure the APK you’re using for testing contains all the ABI libraries.
  11. Make sure that, when retrieving the Intent using getBuyIntent, you’re passing the correct product type, i.e., inapp if you’re purchasing managed in-app products or subs if you’re purchasing subscriptions.
  12. If you’re using the public key for enhanced security, make sure it matches the one on the Play Console because it might change over time (see here).
  13. Check that Google Play Services are updated on the test device by going to the Google Play Services page on the Play Store.

As you can see, the sandbox is far from straightforward when it comes to real usage, but at least now we have a few extra hints to start looking for a solution!

Bonus tip: faster testing for test subscriptions

We mentioned earlier that test subscriptions have a 1-day expiration period, regardless of their original duration. This means that, even if we cancel the subscription, it will still be considered active for that day. In turn, when the app retrieves the list of purchased products, it will receive the cancelled subscription and present it as an active subscription (because it technically still is). Your UI will then react accordingly and display premium features, instead of the purchase button: again, technically correct, but very inconvenient for us to do quick testing.

However, there’s a nice way of checking whether a subscription’s auto-renewal has been cancelled: the autoRenewing field inside the INAPP_PURCHASE_DATA JSON payload that comes from the billing service (see documentation). When checking the validity of a subscription in a debug environment, you can assume that when autoRenewing is false the subscription is cancelled and you can purchase another one.

⚠️ Be careful though! This check should only be done in debug/staging environments and not in production, so make sure you limit this “strict” check to debug/staging builds only. ⚠️

Update! 17/01/2018

While this post was still being drafted, Google announced that test subscriptions will no longer be renewed daily. Instead, the renewal period will vary from 5 to 30 minutes, depending on the original billing period.

In addition to this, they are limiting the amount of times a test subscription can be renewed to 6 (it is currently unlimited).

All these modifications will be applied starting February 20th 2018.

Conclusion

While far from perfect, the in-app billing support is critical for the Android ecosystem to grow and to being able to generate revenue.

When implementing purchases in your app, it’s worth paying attention to the checklist we reported before to avoid headaches, as well as provide a reliable sandbox environment for you to develop with and for your testers to test the app.

It’s also nice to see that Google is committed for the long run, as they just released the all new Play Billing Library (compared to the old 2013 version!).

A final shout out to the Android community, for being as open and as supportive as ever!