Using Google’s In-app billing to add a ‘donate’ feature in your android app

App monetization is an important driver when you are developing an app. A lot of fantastic apps on the Google Play store choose to go the freemium route, relying on in-app purchases, game currency and so forth. A lot of the apps do rely on advertisements as well, but reactions to ads are not always favourable and unless you can rack up a huge number of downloads and position your ad smartly, it is also going to be an exercise in futility. Not to mention the ad-blockers which even come pre-installed on some custom ROMS.

So just this last month, we decided to implement Google’s handy In-app billing v3 into our app on the play store and added a donate feature to reach out to all those in the mood for some philanthropy. Implementing it was quite easy, though it did take a bit of googling around(The documentation is exhaustive) but I didn’t find any direct tutorials. So I’m just adding a quick tutorial in case budding developers need to add this feature in their app and do not want to go the PayPal way(I am told that Google has started pulling down such apps because it violates some of their policies) and need a quick fire way to implement this feature.

Before we start, it’s a fantastic idea to download the sample app that comes with the Google SDK. It’s called Trivial Drive, and contains the libraries which you would need to use. Also, if you have time on your hands, do go through the documentation on In-app billing to learn about the nitty gritties.

Here are a couple of important things you would need to follow in order to test the API :

  • Go to the google dev console and make sure you have added an application. You needn’t upload an apk immediately, but you have to add the application listing so that google play can generate your license key. The license key can be found in the “Services and APIs” section.
  • Make a list of all the product IDs that you are going to use. The product IDs are important here; since once you put that in the dev console there is no way to reuse it, even after deleting it. A sample product ID could be “donate_large”, signifying a large donation amount connected to it.
  • Most importantly, make sure you are using your product release key to sign your app(and upload to the dev console). When you install an application from Eclipse, it gets signed by the debug key. THIS WILL NOT WORK. You have to export your android app with the release keystore and then test/deploy.
  • Remember you can only define the in-app Product ID’s once you add the relevant vending permissions to your Manifest file. If this is the first time you are adding in-app billing to your app, you would need to upload a test version to Google play first. Always remember to use the release keystore and not the default debug keystore which Eclipse signs your apk with. We uploaded a test version to the google dev console, added all the product ID’s for the donation amounts and then finally uploaded a release apk (with a higher version number) to be published. Add new product ID’s if you don’t want to test with the reserved test product IDs and it should all fit in.

Now, on to implementation. If you have a look at the Trivial drive sample app, you will see a more or less full set of features. Now to implement a donate button, we wouldn’t need to do all of that, for example, querying the inventory to see which items we “own” or checking for “premium” subscriptions. One limitation which might bother you is that the user can no longer donate whatever amount they choose, i.e, it cannot be a whimsical decision. You need to carefully guide the user through a selection of donation amounts and let them choose. For our app, we chose 5 tiers of payment, from a smaller amount to a bigger amount, however, this is completely upto you.

First off, we need to initialise a IabHelper type object(this could be a global reference) and initiate the setup process in onCreate().

// the helper object

IabHelper mHelper;

//other global variables

@Override

protected void onCreate(Bundle savedInstanceState) {

//code

String base64EncodedPublicKey = getString(R.string.app_license);

Log.d(TAG, "Creating IAB helper.");

mHelper = new IabHelper(this, base64EncodedPublicKey);

// enable debug logging (for a production application, you should set

// this to false).

mHelper.enableDebugLogging(false);

// Start setup. This is asynchronous and the specified listener

// will be called once setup completes.

Log.d(TAG, "Starting setup.");

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {

public void onIabSetupFinished(IabResult result) {

Log.d(TAG, "Setup finished.");

if (!result.isSuccess()) {

// Oh noes, there was a problem.

toast(getString(R.string.in_app_bill_error) + result);

return;

}

// Have we been disposed of in the meantime? If so, quit.

if (mHelper == null)

return;

// IAB is fully set up. Now, let's get an inventory of stuff we own.

//   --commented out here as we didn't need it for donation purposes.

// Log.d(TAG, "Setup successful. Querying inventory.");

// mHelper.queryInventoryAsync(mGotInventoryListener);

}

});
}

Remember that you cannot initiate multiple transactions at the same time. The IabHelper launches an asynchronous process and it can handle only one transaction at a time. This is fairly obvious, because if you didn’t have the shopping cart system in a supermarket, you would be able to buy a product at a time.

Remember to put this method in your donation activity as well :

//DO NOT SKIP THIS METHOD

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + ","

+ data);

if (mHelper == null)

return;

// Pass on the activity result to the helper for handling

if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {

// not handled, so handle it ourselves (here's where you'd

// perform any handling of activity results not related to in-app

// billing...

super.onActivityResult(requestCode, resultCode, data);

} else {

Log.d(TAG, "onActivityResult handled by IABUtil.");

}

}

Important : Always remember to store your sensitive app information in a secure place, especially if you are putting your code on GitHub. use an xml file to store the license key as a string, and add that xml to .gitignore. Once you accidentally publish a sensitive piece of information on the internet, remember you cannot reverse it back without taking extreme measures.

Once a user is presented with the donation amounts and they select it, IabHelper initiates the transaction flow. The purchase must be “consumed” for it to be reused again inside the app. So for the actual microtransactions to take place, we enlist the help of a few ‘listeners’.

Firstly, we need to launch the actual purchase flow when a donation amount is selected :

mHelper.launchPurchaseFlow(this, SKU_SMALL, RC_REQUEST,
mPurchaseFinishedListener, payload);

SKU_SMALL is a final static string type which has the product id stored in it. This could be anything, as long as it’s exactly mentioned in the Google dev console. You cannot replace the final static string with a non-final variable, so define the string values in the beginning of your code. RC_REQUEST is a random int. The ‘payload’ becomes extremely important in the case of in-app items and you could use your own server to verify if the user is actually making a correct purchase or if something is afoul, but in our case, it could be defined as a null string because it is not that important. Again, this is very contextual.

Once the purchase flow has been launched, IabHelper needs to check when the purchase has been finished, so let’s define that listener here :

// Callback for when a purchase is finished

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {

public void onIabPurchaseFinished(IabResult result, Purchase purchase) {

Log.d(TAG, "Purchase finished: " + result + ", purchase: "

+ purchase);

// if we were disposed of in the meantime, quit.

if (mHelper == null)

return;

if (result.isFailure()) {

toast(getString(R.string.purchase_error) + result);

// setWaitScreen(false);

return;

}

if (!verifyDeveloperPayload(purchase)) {

toast(getString(R.string.error_verification));

// setWaitScreen(false);

return;

}

Log.d(TAG, "Purchase successful.");

if (purchase.getSku().equals(SKU_SMALL)

|| purchase.getSku().equals(SKU_MEDIUM)

|| purchase.getSku().equals(SKU_LARGE)

|| purchase.getSku().equals(SKU_XL)

|| purchase.getSku().equals(SKU_XXL)) {

//check if any item is consumed 

mHelper.consumeAsync(purchase, mConsumeFinishedListener);

}

}

};

And finally, once this transaction is over, you need to initiate the finishing process.

// Called when consumption is complete

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {

public void onConsumeFinished(Purchase purchase, IabResult result) {

Log.d(TAG, "Consumption finished. Purchase: " + purchase

+ ", result: " + result);

// if we were disposed of in the meantime, quit.

if (mHelper == null)

return;

//check which SKU is consumed here and then proceed.
if (result.isSuccess()) {
Log.d(TAG, "Consumption successful. Provisioning.");
toast(getString(R.string.thank_you));

} else {

toast(getString(R.string.error_consume) + result);

}

Log.d(TAG, "End consumption flow.");

}};

That’s it. As easy as that. Before you start testing, remember to add the in-app items in google dev console, complete with a description, amount, switch auto-convert on or define separate amounts for other currencies.

Export your apk and sign it with the release keystore, install it onto your test device and Voila!

Important : Remember to add your tester email id’s to Google dev console. Else you will get an error when you try to test your app. The tester id’s will not get charged but you can simulate the actual purchase flow as well. Other Google id’s can only access the in-app billing features once the app is published on the Google play store

Go ahead guys. Implement a smarter way to get rewarded for your efforts. Best of luck!

If you want to see the full source code to see all of this in action, check out our GitHub repo here.

You can follow me on twitter.

Leave a comment