Google Play Store’s In-app Billing API makes purchases and subscriptions easy. In just a few calls, your app can purchase items and subscriptions, verify previous purchases, etc. However, at The New York Times we ran into limitations while following Google’s recommended practices for testing our app’s use of the Billing API.
What Google offers on their Testing In-app Billing page:
Static responses allow a developer to handle different return codes on API calls (i.e. canceled, item_unavailable). This testing works by passing special product codes to the API to get the desired response. The solution is extremely limited. Since actual product codes cannot be passed, it cannot be used for integration testing. Also, it cannot be used to test any call other than REQUEST_PURCHASE on non-subscription items.
Test purchases are much more powerful. All API calls interact with the Google Play app exactly as with non-test purchases without anyone’s credit card being charged. Still, there are numerous limitations with test purchases:
- Since the app communicates with the actual Play Store app, there needs to be an actual app published in the Play Store in the alpha or beta channel. This can be a barrier in the case of new development.
- The testing app needs to be the same package and signed with the same key as the app in Google Play. This presents a problem when testing the debug version of the app, which is signed with a debug key, and might be a different package.
- The items being purchased must be published in Google Play. It is possible that items may not be ready to be published at development or testing time. Also, published items cannot be unpublished requiring some fields of the product to be locked in at testing time.
- Test purchases can only be done by testers whose Gmail addresses are registered with the developer account on Google Play Console. Those accounts must also be active on the testing device.
- Since the state of purchases is maintained within Google Play, it can be difficult to get into a desired state. For example, an app may present an option to purchase a subscription only if the user does not have an existing subscription. Once the tester purchases, she may need to repeat the test. This can only be done by cancelling the purchase. But when a purchase is canceled, it is still valid for the rest of the day. There is no quick way to return the tester to the state prior to the purchase.
- Test purchases only offer “happy path” testing. For example, there is no way to test how an integration handles a failed call to getProductDetails.
To get around these problems, we developed Register, an open-source library and companion app that allows seamless mocking of responses from Google Play’s In-app Billing.
Similar to a mock web server, you can point your app to use Register rather than the real Play Store In-app Billing implementation. Using Register, you will be able to validate in advance whether purchasing flows work correctly.
With minimal change to source, API calls are handled by Register’s service, rather than Google Play’s. In addition, the Register app allows testers to control the state of purchases in a mock store as well as control the response code of API calls. To understand how it all works, let’s take a look at a typical In-app Billing call. Let’s say we want to know which items a user has already purchased. Assuming we have followed Google’s initial instructions for implementing In-app Billing, we just need to create a service connection:
where mServiceCon is a service connection that make the actual call:
Register takes advantage of In-app Billing’s use of an .aidl file to call Google Play app’s service. The Register library contains an interface, GoogleServiceProvider, which mirrors the calls of the .aidl file. There are two implementations: GoogleServiceProviderImpl is a pass-through to the Google Play App service. GoogleServiceProviderTesting works against a second .aidl file which has the same interface, but a different package. These calls are handled by the Register service. This is all invisible to the client code. The only change required is to obtain the service intent using a helper method from the Register library.
The googleServiceProvider parameter can be either GoogleServiceProviderImpl or GoogleServiceProviderTesting. Typically, GoogleServiceProviderTesting would only be built into testing versions of the app. The Register sample app shows an example of a switch that toggles between the two implementations.
Beyond this small code change, Register needs information about products in order to mock the store. This is done by the configuration file register.json.
This file, taken from the Register sample app, contains two purchase items: an in-app purchase, and a subscription. It also includes two user emails, which will be included in the purchaseToken upon purchase. At this time, the file needs to be manually pushed to the device (adb push register.json sdcard/) but a future version will automatically push the file from the project and possibly install the Register app. Until it does this push, you will need build the app from the repository (./gradlew app:install). To use Register from your app, you will also need to include the library in build.gradle:
Once the code is properly calling Register’s bindings and the config file is in place, the Register app should be run once for the first time in order to grant permission for access to external storage (for API 23+). The app will also indicate if it could not read the config file. If all is well, testing can begin. Purchases can now be made. Upon purchase, a Register purchase dialog replaces the Google Play dialog.
Once purchases are made, they are reflected in subsequent calls to getPurchases. Additionally, they can be managed in the Register app. Purchases can be viewed and removed.
Register also allows testers to see how apps react to non-successful return codes from the API. In the example below, a tester changes the return code on a buy() call to RESULT_ITEM_UNAVAILABLE. Then, they return to the app and receive an “item not found” dialog on purchase.
Register has been used to test purchasing flows of our flagship Reader app for three years and counting. While we have found it very useful, there is still work to be done:
- Many features in the Play Store are not yet supported in Register, for example: support for multiple currencies, multiple languages, timely expiration of subscriptions, free trials, and introductory prices.
- If your integration includes receipt verification, you will need to account for the receipts returned by Register (at some point, we may open-source our own internal system).
- Google has just released Play Billing Library which, because it layers on top of In-app Billing, will interfere with Register.
We have no doubt that, with a little help from the community, we can make Register an all-in-one payment testing solution. In the meantime, we look forward to any feedback from you all.
Register: Better In App Billing Testing on Android was originally published in Times Open on Medium, where people are continuing the conversation by highlighting and responding to this story.