A few months ago I was tasked with rewriting the subscription code for Pigment, the coloring book app I work on at Pixite. Our users were suffering from a bug causing paying users to sometimes lose access to premium content, and the original implementation was somewhat hastily written, so I decided it made sense to invest the time to rewrite the billing code from scratch.

Play Billing Library

Google makes interacting with the Play Store’s in app billing infrastructure pretty easy with the Play Billing Library. This is a small library that manages a service connection with Google Play and takes care of some of the binding and threading concerns for you.

As of Google I/O 2018, the Play Billing Library is also the official way to interact with the Google Play in app billing service. No more manually managing AIDL files and service connections.

Billing Code is Hard

My biggest challenge with billing code has always been manual testing. Google’s two approaches to testing are static responses and test users. Static responses mean that you can use static test SKUs in place of your actual product SKUs to get predefined responses. While this is a great start, it means that you have to bypass some of your production code in order to replace these SKUs.

Testing with test users requires that you have an app uploaded to the Play Store, though it doesn’t have to be released. That means there is no way to test your app until you’ve at least released an alpha version (even just a placeholder). That also means that each new build you want to test must be built with release keys (not debuggable) and uploaded to the Play Store (slow).

In addition to that, once you make a purchase with a test user it’s quite difficult to test things like cancellations, subscription renewals, and returns. In Pigment I deal with subscriptions, which renew each day. That means that I can test the subscription flow with a test user only once per day. I’ve heard that time is being reduced, but it’s still not instant, so it’s too slow.

Alternatives

The fine folks at The New York Times came up with a really interesting solution to this problem, called Register. Register is a library and companion app to solve this exact problem on Android. It works by duplicating the service that the Play Store implements, and allowing you to instead interact with their alternative service, which serves local results managed with a companion app.

This makes for a fine solution, especially if you’re billing implementation interacts directly with the Play Store at the service level. For anyone using the Play Billing Library, however, this is more complex than is needed (and doesn’t yet work). In it’s current state, Register reimplements more of the billing stack than is required in order to provide an alternative, easily testable replacement.

In addition, Register adds code to your release variant. Normally this is fine for a library, but in the same way I avoid allowing test code into my release variant, I don’t want tools intended for internal and test variants to be included in my production builds, largely because that adds more surface area to my final APK, and more chances for exploitation.

Solution

After investigating and testing an implementation of Register in Pigment, I decided it was a bit too heavyweight for my needs. I wanted a solution that would be able to be a drop in replacement for the Play Billing Library (whose main interface is an abstract class), wouldn’t require any test code in my production APK, and would allow me to easily, and instantly, alter my subscription status and make purchases.

With those ideals in mind, I created BillingX, a simple library that makes it easy to make mock purchases in internal builds with almost no code changes.

BillingX implements the BillingClient class from the Play Billing Library, allowing you to use it instead of the official one in your debug builds with no code changes. By only including BillingX as a debug dependency you can be sure that none of it’s code will add to the final size of your APK, or be included in your production build.

How To Get Started

BillingX is meant to be included in your internal builds, which is the debug variant, for me. Since it’s a drop in replacent, that means that you can only include it in the debug variant, and use the normal Play Billing library in the release variant.

debugImplementation 'com.pixiteapps.billingx:billingx:0.8.0'
releaseImplementation 'com.android.billingclient:billing:1.1'

Now you simply need to provide the correct billing client for each variant. If you’re using a dependency injection framework, like Dagger, I’ll assume you know how to provide different dependencies for different environments. If you’re not, you can easily create a simple factory in each variant to provide the correct client.

To do this, first create a BillingClientFactory singleton somewhere under src/release/java with a single function that returns your normal BillingClient.

object BillingClientFactory {
  fun createBillingClient(activity: Activity, updateListener: PurchasesUpdatedListener): BillingClient {
    return BillingClient
        .newBuilder(activity)
        .setListener(updateListener)
        .build()
  }
}

Then, create the same file under src/debug/java and return a DebugBillingClient instead of the Play Billing BillingClient.

object BillingClientFactory {
  fun createBillingClient(activity: Activity, updateListener: PurchasesUpdatedListener): BillingClient {
    return DebugBillingClient(
        activity = activity,
        purchasesUpdatedListener = updateListener
    )
  }
}

Once you have this, you can use this factory wherever you need to fetch your BillingClient and you’ll get the right one for the variant you are on.

Adding Inventory

The last change that’s needed to set up BillingX for use is to add items to the inventory. BillingX comes with a built in BillingStore that you can use to manage the inventory. This can be as simple as reinitializing the products in the default store any time you create a new DebugBillingClient.

BillingStore.defaultStore(activity)
    .clearProducts()
    .addProduct(
      SkuDetailsBuilder(
        sku = BillingManager.SKU_SUBS,
        type = BillingClient.SkuType.SUBS,
        price = "$9.99",
        priceAmountMicros = 9990000,
        priceCurrencyCode = "USD",
        title = "Premium Access",
        description = "Get all the super cool features.",
        subscriptionPeriod = "p1m",
        freeTrialPeriod = "p1w"
      ).build()
    )

That concludes the code changes required to use BillingX in your debug builds. Now, any time you call launchPurchaseFlow in your app code, the user will see a BillingX purchase dialog, instead of the Google Play dialog, and will be able to purchase items without real transactions taking place.

Managing Purchase State

Using this BillingStore also allows you to hide things in your debug builds to allow users to change their purchase state when needed. In Pigment, I have a debug drawer (similar to the BillingX sample app) which contains a switch allowing the user to toggle their subscription status.

This is simple using the same BillingStore that you used before to populate your inventory. You can manage purchases in much the same way you manage products, allowing you to put the user in whatever state is needed.

subscriptionSwitch.setOnClickListener {
  val ctx = context ?: return@setOnClickListener
  if (subscriptionSwitch.isChecked) {
    BillingStore.defaultStore(ctx)
        .addPurchase(
          PurchaseBuilder(
            orderId = "abcd123",
            packageName = ctx.packageName,
            sku = BillingManager.SKU_SUBS,
            purchaseTime = Date().time,
            purchaseToken = "token-123",
            signature = "foo",
            isAutoRenewing = true
          ).build()
        )
    subsRepo.setSubscribed(true)
  } else {
    BillingStore.defaultStore(ctx).clearPurchases()
    subsRepo.setSubscribed(false)
  }
}

Extensible

The default BillingStore is quite simple, and uses SharedPreferences to store the inventory and purchase information (for now), but you’re not restricted to this. You can implement your own billing store, which can store your inventory in your own database, a companion app, or even your own web service.

Register

While working on BillingX, and before releasing it publicly, I made sure to share it with the folks that manage Register. We’ve had some long discussion about whether BillingX fits inside Register, or on it’s own. Ultimately, since Register has been and remains super userful to anyone who doesn’t use the Play Billing Library, and BillingX is only intended for those users, we decided it would be best to release separately.

If you’re not using the Play Billing Library, and don’t want to update to it, then I highly recomment checking out Register.

Conclusion

At Pixite, we’ve been using BillingX for the past several months. It makes using our internal builds much easier, and allows testers to easily test all of the states without needing to wait for the long refresh times of the Play Store.