[TUT] Simple InApp Billing / Payment V3

Google have released a new version of their In App Billing (Version 3). So I’ve followed suit and updated my tutorial!

Eclipse Project Source Code Download

GitHub Source Code Repo Link

There are a fair few changes, too many to fit into one tutorial but the overview is:

All purchases are now managed purchases
This means that once a user buys your item they own it, you have to ‘consume the purchase’ if you want to allow it to be purchased again. This is used in this tutorial but not explained or coded in a clean way so it is left for my next tut.
The benefits of this are that all purchases will be stored by Google Play and it will be easy to retrieve the status of whether a user has already purchased an item.

The Android team have released a ‘helper’ class
This is to make in app purchases that much simpler. It has a vague resemblance to my original ‘BillingHelper’ class only it is called ‘IabHelper’ .. very interesting. This helper class makes things a hell of a lot easier.

Let’s get straight into it.

To start with I’d like to say, this isn’t the only way to do this. I myself have followed the developer tutorials on developer.android and managed to create this test project which I can now use as a reference when making other projects. Feel free to comment on the style and ask any questions. I would recommend you download the test project I have attached rather than copy and paste, this will stop minor syntax errors.

Developer.Android Links:
Google Billing Overview
Google Billing Integration

Ok first the outline.

Were going to create and app that has one button, when you click this button it informs the android market you want to make a purchase. On confirmation of this purchase you have bought a picture of a passport and it is shown to you in the app.

Simple setup stuff will be skipped (it is all in the download source).

First you create your project with the IInAppBillingService.aidl file included. You then need to add the Billing Helper classes, look at the source code, I have added these in the android.vending.billing.util package. They are the same as the Android example except the IabHelper which I have modified when I found some slight bugs.

Create 3 Activities, the first is the start up activity. This checks if In App Purchase is available on the device. If it is we load the main activity if it isn’t we don’t let them go any further:

StartUpActivity.java

package com.blundell.tutorial.simpleinappbillingv3.ui;

import android.os.Bundle;

import com.android.vending.billing.util.IabHelper.OnIabSetupFinishedListener;
import com.android.vending.billing.util.IabResult;
import com.blundell.tutorial.simpleinappbillingv3.ui.base.PurchaseActivity;
import com.blundell.tutorial.simpleinappbillingv3.util.Log;

/**
 * Checks that In App Purchasing is available on this device
 * 
 * @author Blundell
 * 
 */
public class StartUpActivity extends PurchaseActivity implements OnIabSetupFinishedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("App started");
    }

    @Override
    public void onIabSetupFinished(IabResult result) {
        if (result.isSuccess()) {
            Log.d("In-app Billing set up" + result);
            dealWithIabSetupSuccess();
        } else {
            Log.d("Problem setting up In-app Billing: " + result);
            dealWithIabSetupFailure();
        }
    }

    @Override
    protected void dealWithIabSetupSuccess() {
        navigate().toMainActivity();
        finish();
    }

    @Override
    protected void dealWithIabSetupFailure() {
        popBurntToast("Sorry In App Billing isn't available on your device");
    }
}

This StartUpActivity extends the PurchaseActivity. The PurchaseActivity is what talks to the Android helper service. Whenever the activity is created it will check that in app billing is still supported at this time.

PurchaseActivity.java

package com.blundell.tutorial.simpleinappbillingv3.ui.base;

import android.content.Intent;
import android.os.Bundle;

import com.android.vending.billing.util.*;
import com.android.vending.billing.util.IabHelper.OnIabPurchaseFinishedListener;
import com.android.vending.billing.util.IabHelper.OnIabSetupFinishedListener;
import com.blundell.tutorial.simpleinappbillingv3.AppProperties;
import com.blundell.tutorial.simpleinappbillingv3.R;
import com.blundell.tutorial.simpleinappbillingv3.domain.items.Passport;
import com.blundell.tutorial.simpleinappbillingv3.util.Log;

/**
 * This class should be the parent of any Activity that wants to do in app purchases
 * it makes our life easier by wrapping up the talking to the IabHelper and just exposing what is needed.
 * 
 * When this activity starts it will bind to the Google Play IAB service and check for its availability
 * 
 * After that you can purchase items using purchaseItem(String sku) and listening for the result
 * by overriding dealWithPurchaseFailed(IabResult result) and dealWithPurchaseSuccess(IabResult result, Purchase info)
 * 
 * @author Blundell
 * 
 */
public abstract class PurchaseActivity extends BlundellActivity implements OnIabSetupFinishedListener, OnIabPurchaseFinishedListener {

    private IabHelper billingHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_purchase);
        setResult(RESULT_CANCELED);

        billingHelper = new IabHelper(this, AppProperties.BASE_64_KEY);
        billingHelper.startSetup(this);
    }

    @Override
    public void onIabSetupFinished(IabResult result) {
        if (result.isSuccess()) {
            Log.d("In-app Billing set up" + result);
            dealWithIabSetupSuccess();
        } else {
            Log.d("Problem setting up In-app Billing: " + result);
            dealWithIabSetupFailure();
        }
    }

    protected abstract void dealWithIabSetupSuccess();

    protected abstract void dealWithIabSetupFailure();

    protected void purchaseItem(String sku) {
        billingHelper.launchPurchaseFlow(this, sku, 123, this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        billingHelper.handleActivityResult(requestCode, resultCode, data);
    }

    /**
     * Security Recommendation: When you receive the purchase response from Google Play, make sure to check the returned data
     * signature, the orderId, and the developerPayload string in the Purchase object to make sure that you are getting the
     * expected values. You should verify that the orderId is a unique value that you have not previously processed, and the
     * developerPayload string matches the token that you sent previously with the purchase request. As a further security
     * precaution, you should perform the verification on your own secure server.
     */
    @Override
    public void onIabPurchaseFinished(IabResult result, Purchase info) {
        if (result.isFailure()) {
            dealWithPurchaseFailed(result);
        } else if (Passport.SKU.equals(info.getSku())) {
            dealWithPurchaseSuccess(result, info);
        }
        finish();
    }

    protected void dealWithPurchaseFailed(IabResult result) {
        Log.d("Error purchasing: " + result);
    }

    protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
        Log.d("Item purchased: " + result);
        // DEBUG XXX
        // We consume the item straight away so we can test multiple purchases
        billingHelper.consumeAsync(info, null);
        // END DEBUG
    }

    @Override
    protected void onDestroy() {
        disposeBillingHelper();
        super.onDestroy();
    }

    private void disposeBillingHelper() {
        if (billingHelper != null) {
            billingHelper.dispose();
        }
        billingHelper = null;
    }
}

Then our MainActivity, this just has the button for purchasing a passport. It will also receive a callback saying if the passport was bought or not.

The MainActivity starts the PurchasePassportActivity using startActivityForResult, this is how we inform it of a purchase. As with the StartUpActivity the PurchasePassportActivity extends our PurchaseActivity allowing it to talk to the Google Play Billing Service (and it’s helper class).

When the PurchasePassportActivity is started, it will get a callback saying billing is available and go onto to purchase the passport. The only thing special in this Activity is that is passed the SKU (the items ID from Google Play) to the Helper service. In this tutorial we use the “android.test.purchased” SKU meaning purchases will always be successful.

PurchasePassportActivity

package com.blundell.tutorial.simpleinappbillingv3.ui;

import android.os.Bundle;

import com.android.vending.billing.util.IabResult;
import com.android.vending.billing.util.Purchase;
import com.blundell.tutorial.simpleinappbillingv3.domain.items.Passport;
import com.blundell.tutorial.simpleinappbillingv3.ui.base.PurchaseActivity;

/**
 * This activity will purchase a Passport from Google Play.
 * 
 * If you wanted to change to purchase something else all you have to change is the SKU (item id) that is used
 * you could even pass this in as an Intent EXTRA to avoid duplication for multiple items to purchase
 * 
 * N.B that we extend PurchaseActivity if you don't understand something look up to this class
 * 
 * @author Blundell
 * 
 */
public class PurchasePassportActivity extends PurchaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set the result as cancelled in case anything fails before we purchase the item
        setResult(RESULT_CANCELED);
        // Then wait for the callback if we have successfully setup in app billing or not (because we extend PurchaseActivity)
    }

    @Override
    protected void dealWithIabSetupFailure() {
        popBurntToast("Sorry buying a passport is not available at this current time");
        finish();
    }

    @Override
    protected void dealWithIabSetupSuccess() {
        purchaseItem(Passport.SKU);
    }

    @Override
    protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
        super.dealWithPurchaseSuccess(result, info);
        setResult(RESULT_OK);
        finish();
    }

    @Override
    protected void dealWithPurchaseFailed(IabResult result) {
        super.dealWithPurchaseFailed(result);
        setResult(RESULT_CANCELED);
        finish();
    }

}

Note- The code below in the PurchaseActivity.java would be removed if you wanted to use this. It is there because the new version of in app billing treats all purchases as “managed items” i.e. when you buy them once you own them until you use them or they are refunded. So this code automatically ‘refunds’ the purchase so the demo doesn’t just work first time!

// DEBUG XXX
// We consume the item straight away so we can test multiple purchases
billingHelper.consumeAsync(info, null);
// END DEBUG

That’s it, the rest of the code are just helpers and comments to try and explain what is going on and making it easier to extend.

As I always say This code tutorial isn’t foolproof and I feel I may of swept over a few things. But I really want to just give an alternative to the tutorial that is on the developer.android site. You could read this and understand the smaller concepts then go on to make a better implementation.

If you want to productionise this code you will want to go away and look at some of the security tutorials, obfuscation etc.

Remember to say thanks and enjoy!

Here are the code links again:

Eclipse Project Source Code Download

GitHub Source Code Repo Link

93 thoughts on “[TUT] Simple InApp Billing / Payment V3

  1. i have implemented in-app billing in my project but How to implement Restore Transactions Functionality in this sample project please help me

  2. Hi,

    code works fine…so what line of code do i need to change if i want to purchase the item only once?
    I tried deleting

    billingHelper.consumeAsync(info, null);

    Like you said in your tutorial..and it allows me to purchase the item once, but then when i close the app down and then go back into the app and hoping that when i go to click the button again…it says i cant purchase it/access the app(something along those lines )… thoughts? is there a line of code i need to add so the app remembers i brought it and that i can now use it at will when ever i close the app and open it again? many thanks =)

    1. Unfortunately not,you’d have to query and ask if this has already been purchased or consumed.

  3. Excellent Tutorial, But Stuck at some place. The sample code itself worked fine, But when I tried with my own public key and my own package I get “ITEM NOT FOUND” error, So I did as you suggested used my own key but used TEST SKU, It works fine… So what is the problem with my package name,? I have uploaded my app in google play and it is in draft state … Pls advise

    1. You might just have to wait a little (for your draft to propagate to the google servers). Not sure on your project specifics

Comments are closed.