Monday, June 20, 2011

Some Notes on Implementing In-App Billing on Android

About a year ago, I implemented in-app payments for iOS, and it was painful. Technically, it wasn't too hard but there were a number of hoops to jump though to get everything working. Lack of clear documentation and undocumented error responses seemed to be the primary problem - I think a hold up with verifying tax information on the developer account resulted in strange, undocumented failure codes in my case. There were several dozen blog posts complaining about many such "magic" issues back then. It was almost comical, to be honest.

This last weekend, I decided to add in-app billing to an app that's already shipping for Android, and I ran into a couple of hiccups that I think are worth documenting here to save others some time/pain. Overall, I think the Android folks learnt from some of the pitfalls that early developers hit with in-app payments on iOS and worked to fix those. The documentation is a lot clearer and precise compared to where iOS was a year ago. Android Market also has a nifty static test/response facility which makes it easy to test code without worrying about external influences. iOS was desperately lacking something of this sort (not sure if it's better now) - I even wrote a dummy implementation of the iOS in-app payment API and the different response conditions to let me test my code in an emulator, which was really helpful. I'm glad Android got this right (still no testing in an emulator though, which is a bummer because of the inability to test with different Android OS versions).

At a high level, the in-app billing protocol on Android is extremely chatty (see the diagrams on this page). It seems particularly so when compared to PayPal's rival offering. PayPal's in-app billing module handles payment using startActivityForResult and onActivityResult methods which can be used for communicating between child/parent activities. This is very similar to how Facebook's Android SDK handles single sign on. That model is extremely simple to understand and implement for developers - you could probably have it coded up and tested faster than you can finish reading this blog post. I think part of this chattiness is to ensure a higher level of security and also to handle managed purchases, which are both likely harder to do using PayPal's approach. I assume they had good reason to make it as chatty as it is now, but my gut says that Google loses out on a lot of developers because of this apparent complexity. Having said that, the sample application Google provides does a really good job of illustrating the various communications that need to occur, so studying that code makes the chattiness easier to understand (for some reason, code registers more easily in my brain than verbose documentation - good code and good documentation together are very rare, and Android seems to have gotten this right - kudos!)

One of the biggest red flags with using Android's in-app billing, however, is the development teams non-responsiveness to critical bugs. This issue, marked critical and accepted, talks about transactions failing 4-6% of the time, and has been active for over two months without an owner being assigned. An equally disturbing issue with unacceptably long latency when handling authorization has been open for almost two months without any comment from Google or even an owner being assigned. In fact, browsing the issues list certainly doesn't inspire confidence in the platform. It would be a big mistake by Google to not take these issues seriously. In my case, I'm certainly debating the merit of jumping into this as an early adopter.

Some other notes about implementing in-app billing on Android:
  • It sounds obvious in hindsight, but you need to make sure that your application is signed with the release key before testing in-app billing, even the static response stuff. Developer's aren't used to doing this while debugging, so it's worth calling out in the documentation.
  • If you want valid signatures for the JSON responses as suggested by the table here, the "debuggable" attribute in the app manifest should be set to false, even if you're using a release-key-signed APK and static responses. I was a little annoyed by this un-documented restriction because it basically meant that I had to resort to using logging as the only real means of debugging my code. If there is indeed some way to debug this using a debugger (and yet receive valid signatures), it’s definitely not obvious to me. Again, not a terribly big deal once you realize that this attribute actually seems to affect signatures, but that isn't documented anywhere.
  • If you're using a server to validate signatures (as you should), be aware that the public key you obtain from the Android Market profile page is a base-64 encoded string. You will almost certainly need to convert it to PEM/DER format or use the x509 certificate for it to be usable with most server openssl implementations. For instance, this command can be used to convert the base-64 encoded key into a key usable by PHP:
    openssl enc -base64 -d -in publickey.base64 -A | openssl rsa -inform DER -pubin > publickey.pem
    Also, if you're using the PHP function openssl_verify, remember that $signature needs to be the binary signature, not the base-64 encoded signature string that the Android Market app sends (I found out the long way).
  • Unavailability of subscriptions/auto-billing is a big gap in the offering currently, but I assume support for it is coming soon, since iOS already supports it.
  • I hit an issue when testing payments using carrier billing, where accepting the carrier ToS basically causes the transaction to get lost somewhere between the carrier and Android Market. I logged this bug to track the issue.
Well, I hope that the web-search spiders do their job and this post helps others out there looking at implementing in-app billing on Android. And making Google get a move on those bugs.

4 comments:

Jim Strange said...

Great post!

One other thing to call out:
You can test your implementation of in-app billing (including verifying the signature) without publishing your app.
1. Upload the app to the market (the apk you upload needs to have the billing permission). Hit SAVE, NOT the PUBLISH button!
2. Create products
3. Add test google accounts (these google accounts can make purchases against your products)
4. Now, you can adb install your apk and iterate until it works :) (it needs to be signed with the same key as the one you uploaded, and have the same version number!)

Also, I was able to test the static responses without signing with my release key (using a debug key) on my HTC Incredible.

MCruiseOn said...

I have a blog written on in app billing for android. Hope it helps you out.
http://mcondev.wordpress.com/2011/06/26/integrate-in-app-billing-just-3-lines-of-code-in-your-app/

Chris Maddern said...

Hey Viraj,

You mentioned that you wrote a service interface to dummy the iPhone AppStore service... is there any chance you wouldn't mind sharing that?

Great post, thanks!

n8n8baby said...

Thanks for the post,
I too, am very interested in the test interface for the iPhone store.