Jonathan Peppers


Xamarin MVP and C# nerd

Securing Google Play In-App Purchases for Xamarin with Azure Functions

If you missed part 1 of this series, read about the basics and how to secure iOS in-app purchases here. In this post, I will build on the existing PCL from the iOS sample. Make sure to take a look at the full source code here if you need the full picture.

Implementing secure in-app purchases for Google Play is a bit trickier than iOS for several reasons. The Android APIs are a bit more involved, and Android's openness lends itself to more hacking, in general.

For example, here are the kinds of things Android users can do (a bit more easily than iOS):

  • Modify the device's filesystem: at the system, or app level
  • Install a mobile version of Wireshark
  • An app exists called Freedom, targeted directly at unlocking free in-app purchases on Android

NOTE: I would not recommmend installing any of these things without doing your own research

Implementing In-App Purchases for Google Play

So for getting started with this, I followed Google's Java sample and ported it to C# (using C# idioms). The very first thing you need will be an Android aidl file, along with a custom one needed for Xamarin apps. You can think of AIDL as Android's version of a contract for interprocess communication. Read more on the topic here.

To set them up in your project, download IInAppBillingService.aidl and Bundle.aidl from my sample here, and set the build action to AndroidInterfaceDescription in Visual Studio/Xamarin Studio. Doing this will generate several C# classes for interacting with the Android billing service from within your application.

Proguard

If you are using proguard, which I highly recommend for apps using Java libraries (Google Play Services, etc.). You will need to add the following to your Proguard.cfg file:

Using proguard is a good idea in general, as it obfuscates and strips unused code from any Java libraries your application is using. Since we have an aidl file that doesn't map to a Java class included in our app, it will get stripped by proguard without adding an exemption.

Android Permissions

Before we start anything, go ahead and add permissions for android.permission.INTERNET and com.android.vending.BILLING to your Android application, as they are needed for in-app billing to work.

Next, let's define several constants that we will need to use throughout our GooglePlayPurchaseService. These were ported from the Java sample, and we may not need to use them all.

With that out of the way, we need to implement an IServiceConnection for billing. Its job is to connect to the Android billing service and instantiate a IInAppBillingService instance our app can use for interprocess communication.

We really only need two methods here: Connect and Disconnect, and also need a way to retrieve the interface to the billing service. We are using TaskCompletionSource to wrap the callback in a Task for async/await.

Our GooglePlayPurchaseService

To actually get to the point of making IAPs, let's implement how prices are retrieved. We will be calling IInAppBillingService.GetSkuDetails, which returns JSON that we will need to parse ourselves. I used JSON.NET and a strongly-typed private class to do this work:

To move forward from here, I would recommend doing the setup on the Google Play console side at this point. Go ahead and setup the following for your app:

  • Make a build of your app with the correct package name, android.permission.INTERNET and com.android.vending.BILLING declared, and signed with the keystore you are wanting to use for Google Play
  • Upload your apk to the Alpha channel
  • Fill out your in-app purchase information

One other thing to keep in mind for debugging IAPs, is that all of the following have to match a build uploaded to Google Play: android:versionCode, android:versionName, your package name, and the keystore.

So if the keystore has to match, how in the world can you debug?

There is a trick here, that I think should help alot of people with this. There is a special debug keystore file used for debug builds of Xamarin apps with an alias of androiddebugkey and a password of android. It is generated when you install Xamarin, but it generally located in either:

  • %localappdata%\Xamarin\Mono for Android\debug.keystore on Windows
  • ~/Xamarin/Mono for Android/debug.keystore on Mac

What you can do to debug IAPs in an emulator, real device, etc. is to modify your official Google Play keystore file so that the alias and passwords match the debug key. Then you simply have to copy over top of the Xamarin-generated key, and the next time you debug it will be signed with your Google Play keystore.

It is a bit tricky to modify your key in this manner via command line, so I setup a quick shell script to do it:

I have a windows version here as well. From here all you need to do is copy over Xamarin's key (back it up, of course), and you will be able to debug with your official Android keystore.

Implementing our Google Play purchase service

For completing a purchase, there are quit a bit of hoops to jump through. In general:

  • Create a developer payload to identify the order. We'll just use a Guid.
  • Invoke the IInAppBillingService to start a payment, which returns an Android Intent
  • Invoke the intent and wait for the result via OnActivityResult
  • Validate everything along the way...

So our BuyNative implementation looks like:

That is only half of the implementation, and I'm leaving out my handling of ResultItemAlreadyOwned. Next, we need a HandleActivityResult method for Android to notify us the result of the purchase. I recommend using an IoC/DI container of some kind to manage your purchase service so your Activity can call it.

From here, make sure to pass the results of OnActivityResult from your activity to this service. You will also need to fill out the PublicKey property with the key found in the Google Play dev console. NOTE: it is also a good idea to obsfuscate the key in your app in some way. I recommend using James Montemagno's method here

Testing a purchase

Just like on iOS, alot of things have to be in order on the Google Play developer console before you can actually complete a real purchase.

  • Publish an APK to at least the alpha channel. This means you have to fill out things like app icon, screenshots, etc. with at least sample images.
  • Make sure you are not attempting to purchase with the same Google account that is the owner of your Google Play account
  • Google is aggressive with caching (for good reason), sometimes in various steps throughout the process you will just need to wait a couple hours and try again

You probably should go ahead and verify everything is working at this point and make a real purchase, before adding server-side validation. One thing to keep in mind, is that there is actually an option for giving users free purchases from your Google Play account. If you go to Settings | Developer Account | Account Details | License Testing, you can add as many users are you like that will not be charged for IAPs.

Verifying IAPs with an Azure Function

Now that we've gone through all that work, we can actually validate the purchase on a server to prevent any tomfoolery. I recommend using something like Azure Functions if your app does not have a backend.

For our function, we will need the NuGet package for Google.Apis.AndroidPublisher.v2, which gives you the ability to validate Google Play receipts from C#. Without further ado, here is the Azure function I came up with:

See the full source code for the Azure function here.

GooglePlayAccount and GooglePlayKey will need to be filled out as application settings for your Azure function. Generating these keys is somewhat of a pain to setup, it is the same process that you would use for authentication when pushing automated builds to Google Play.

This process can be summed up by:

  • Tie a Google Cloud project to your Google Play account (if your account is fairly new, it does it for you by default)
  • Create a Service Account Key tied to a user in your Google Cloud account
  • Download the json file and keep it in a safe place, you will need to know the email and Base64 key included
  • Grant your Google Cloud account access in your Google Play console as if it were a normal user
There are some reasonable instructions here on the VSTS site.

Finally, we have make one small change to our PurchaseService base class. In our Buy method, we need to add:

Conclusion

I'm sorry it took so long to get the Google Play version of this post completed, but you can see how it was somewhat painful to set up. However, I think it is very important to add extra levels of protection for IAPs on Android. Implementing server-side checking, should prevent users from getting free IAPs in your app from software like Freedom, etc. If you get stuck with anything in this post, feel free to leave a comment below and I'll help you out.


comments powered by Disqus