LibGdx + Google In App Billing v3

Here are some of my notes and how to implement Google Play Services In App Billing v3  with a LibGdx game. I just implemented removing ads for my game, so that is all I am covering. I’m going to focus on how to integrate with LibGdx and not very much on how to setup In-App Billing in the play console, or how to create in app purchase.

Step 1 has a link to the documentation that shows you how to setup an account, create a public key, etc…

As always the documentation pages are very useful

Step 1

First thing first, follow the documentation at Preparing Your In-App Billing Application to download the sample app and get it running. You’ll need some files from the sample app to make your life easier. After you follow the instructions on that page you will have all of the libraries and helper classes you need to connect to the billing service.

All of the code mentioned on that page will go into your LibGdx-android project in the MainActivity.java file. Your onCreate function should look something like this

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...
    String base64EncodedPublicKey = "{YOUR_KEY_HERE}";

    // compute your public key and store it in base64EncodedPublicKey
    mHelper = new IabHelper(this, base64EncodedPublicKey);

    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
           public void onIabSetupFinished(IabResult result) {
              if (!result.isSuccess()) {
                 // Oh noes, there was a problem.
                 Log.d("IAB", "Problem setting up In-app Billing: " + result);
              }            
              // Hooray, IAB is fully set up!
              Log.d("IAB", "Billing Success: " + result);
           }
        });

    AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
    cfg.useGL20 = true;
    game = new WordHive(this);
    initialize(game , cfg);
}

And don’t forget to update the Destroy function

@Override
public void onDestroy() {
   super.onDestroy();
   if (mHelper != null) mHelper.dispose();
   mHelper = null;
}

If you get step 1 done you should be able to create a connection to the billing service.  Start your app and check out logcat to see if any error message was printed out. You want to see “Billing Success” output to logcat.

At this point you are ready to begin interacting with in app products. You’ll probably want to setup your account to be a billing tester Testing In-App Billing for details on how to add your gmail account (or any others)

Step 2

Now that your MainActivity has a basic binding to the billing service and you will be able to interact with it from the MainActivity.  You’ll want to be able to call this code from your game somewhere.

Read Integrating Google Play Services with LibGdx for a basic guide to interacting with native Android code. Basically you define an ‘interface’ which your MainActivity will implement.  You can pass a reference to this interface into your LibGdx game, and then use that reference to call your desired service.

For instance I will create an interface on my MainActivity with a function called removeAds. If the user clicks my “Remove Ads” Textbutton, then I will call the removeAds() function to start the purchase flow…

public interface IabInterface {

     public String SKU_REMOVE_ADS = "remove_ads";

     // (arbitrary) request code for the purchase flow     
     static final int RC_REQUEST = 10001;
     public void removeAds();
}

SKU_REMOVE_ADS is the product sku provided in the google play console

RC_REQUEST is just an arbitrary number. I used the one from the google sample, but I’m not sure if there is more to it

Step 3

Next step is to implement your IabInterface.

Make sure you extend your MainActivity to Implement IabInterface, and then implement the removeAds function.

public class MainActivity extends AndroidApplication implements GameHelperListener, IabInterface {

Here is my removeAds function:

public void removeAds() {
     mHelper.launchPurchaseFlow(this, SKU_REMOVE_ADS, RC_REQUEST,
     mPurchaseFinishedListener, "HANDLE_PAYLOADS");
 }

mHelper.launchPurchaseFlow is a function provided in the IAB helper provided by Google. Again I copied the mPurchaseFinishedListener from the Google sample app (I told you the documentation and sample apps would be useful!)

    // Callback for when a purchase is finished
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            if ( purchase == null) return;
            Log.d("IAB", "Purchase finished: " + result + ", purchase: " + purchase);

            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            if (result.isFailure()) {
                //complain("Error purchasing: " + result);
                //setWaitScreen(false);
                return;
            }
//            if (!verifyDeveloperPayload(purchase)) {
//                //complain("Error purchasing. Authenticity verification failed.");
//                //setWaitScreen(false);
//                return;
//            }

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

            if (purchase.getSku().equals(SKU_REMOVE_ADS)) {
                // bought the premium upgrade!
                Log.d("IAB", "Purchase is premium upgrade. Congratulating user.");

                // Do what you want here maybe call your game to do some update
                //
            	// Maybe set a flag to indicate that ads shouldn't show anymore
                mAdsRemoved = true;

            }
        }
    };

Step 4 (testing break)

Now you want to test to see if you can follow through the whole purchase flow. For testing purposes I just added a call to removeAds() immediately after the IabHelper OnIabSetupFinishedListener was called. You’ll probably want to move all of the logic behind some user interface like a TextButton, but for testing this is good to start. Make sure you’ve added yourself as a tester to make sure you don’t pay.

I also have read that you can’t test with the same account that your marketplace account is on.  The marketplace doesn’t allow you to pay yourself! So you may need to create a new google account just for testing.

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
  public void onIabSetupFinished(IabResult result) {
     if (!result.isSuccess()) {
        // Oh noes, there was a problem.
        Log.d("IAB", "Problem setting up In-app Billing: " + result);
     }            
     // Hooray, IAB is fully set up!
     Log.d("IAB", "Billing Success: " + result);

     removeAds();
  }
});

Now you just need to run your app and see the purchase flow pop-up!

Step 5

You’ve got the purchase flow working now (hopefully) and now you need to handle ‘querying’ purchase  history. Start by adding a new function to your IabInterface

public interface IabInterface {
    public void processPurchases();    
}

Implement your processPurchases function so so:

public void processPurchases() {
    mHelper.queryInventoryAsync(mGotInventoryListener);
}

 

Once again, look to the sample app to understand how queryInventoryAsync is implemented. my mGotInventoryListener looks like this

// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        Log.d("IAB", "Query inventory finished.");

        // Have we been disposed of in the meantime? If so, quit.
        if (mHelper == null) return;

        // Is it a failure?
        if (result.isFailure()) {
            // handle failure here
            return;
        }

        // Do we have the premium upgrade?
        Purchase removeAdPurchase = inventory.getPurchase(SKU_REMOVE_ADS);
        mAdsRemoved = (removeAdPurchase != null);
    }
};

You’ll also need to update the OnActivityResult function of your MainActivity to handle the iab results

@Override
public void onActivityResult(int request, int response, Intent data) {
    super.onActivityResult(request, response, data);

    if (mHelper != null) {
        // Pass on the activity result to the helper for handling
        if (mHelper.handleActivityResult(request, response, data)) {
            Log.d("IAB", "onActivityResult handled by IABUtil.");
        }
    }
}

 

Now, instead of calling removeAds() when the IabHelper setup is completed, you can call processPurchases

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
  public void onIabSetupFinished(IabResult result) {
     if (!result.isSuccess()) {
        // Oh noes, there was a problem.
        Log.d("IAB", "Problem setting up In-app Billing: " + result);
     }            
     // Hooray, IAB is fully set up!
     Log.d("IAB", "Billing Success: " + result);

     processPurchases();
  }
});

Step 6

Now you should have all the pieces you need to connect and handle purchases. I’m still not sure how the developer payloads are supposed to work, so I will try to update this post when I figure that out.

Appendix

Testing Error

The first time I tried out a purchase I ended up with an error: “This version of the application is not configured for billing through Google Play. Check the help center for more information.”

Screenshot_2014-02-13-10-16-28

This error shows up because you have to use an apk that is signed (not a debug version) and the app that you are testing needs to have the same versionCode as the one most recently uploaded to Google Play. You already had to upload an apk with the billing persmissions to google play just to setup the billing account. So make sure you don’t update the versionCode on your app until you are done testing and ready to push.