Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Google Play In-App Billing Demo App

4.76/5 (16 votes)
3 Feb 2014Eclipse12 min read 58.3K   2K  
This article is an example of Google Play In-App Billing Version 3. The attached TestInAppBilling source code is a complete demo application.

 

1. Introduction

This article and the associated source code is an example of Google Play In-App Billing Version 3. The attached TestInAppBilling source code is a complete demo application. In the case of In-App Billing it is not possible to provide ready to run application (APK file). Each In-App Billing must have a unique package code and unique Google Play provided key. To obtain a license key you must have access to Google Play Developer Console. If you do not have access to the developer console you must register as a developer. Section 2. Installation goes through all the required steps to get the demo application up and running.

The TestInAppBilling source code is part of Android Color Selector for Programmers application available on Google Play. The application is free. However, the user can purchase the source code of the application by using In-App Billing. If you download Android Color Selector go to the About screen to see the In-App buttons. A brief description of the Android Color Selector is given below.

The Android Color Selector for Programmers application allows you to select a color from a color chart. The result of the selection is a 24 bit RGB color value. The application was designed to be called from another application and return the result to the calling application. If you develop an application that requires a user color selection, Android Color Selector is for you. You can also start the Android Color Selector manually by clicking the icon on your tablet. The result will be displayed on the screen.

2. TestInAppBilling Source Code Installation

If you are an experience Android programmer you can skip this section. The only things you must do is change the package name from "com.granotech.testinginappbilling" to your own name, define on Google Play one in-app product with product0001 id, and copy the application key from Google Play to TestInAppBilling.java.

2.1. Prerequisites

Testing the TestInAppBilling application requires the following prerequisites:

  • You are registered as a developer and have access to Google Play Developer Console.
  • You have installed Android Developer Tools (ADT) on your computer.
  • You have an Android tablet with Google Play Store software installed.
  • You have enabled developer mode and USB access on your tablet.
  • You have enabled installation from unknown sources on your tablet.
  • Your tablet has file explorer type software. If not, get one.

2.2. Importing the source code to Android Developer Tools (ADT)

If you know how to import existing Android code you can skip this section except for the package rename step. After the import you must change the package name from "com.granotech.testinappbilling" to a globally unique name such as "com.yourcompany.yourappname".

  • Download the source code.
  • Extract the files from the Zip file to a temporary folder. Please note: the ADT import function will not work directly with the downloaded Zip file.
  • Start ADT program.
  • If you want to load the project into empty workspace, you must add a new empty project to it. You cannot import a project to an empty workspace. This empty project can be deleted after you import the source code.
  • Select Import from the File menu.
  • Select Android --> Existing Android Code into Workspace. Click Next.
  • Click Browse Root Directory button. Navigate to the location you extracted the project files. Click OK.
  • You will see TestInAppBilling project to import.
  • Make sure to CHECK the "Copy projects into workspace" checkbox.
  • Click Finish.
  • TestInAppBillingActivity package was added to your Package Explorer.
  • Rename (Alt-Shift-R) this name to a name of your choice.
  • In the Package Explorer pane under src rename the com.granotech.testinappbilling to "com.yourcompany.yourappname". You must change the package name to a globally unique name in order for Google Play to accept it.
  • If Empty project was created, it can be deleted now.
  • You can perform very limited testing in debug mode. You must go through the next two steps before being able to test the in-app billing. When you run it the first time make sure it is set as Android Application.

2.3. Adding TestInAppBilling application to Google Play Developer Console

  • Log into your Google Play Developer Console Account.
  • Add a new Application.
  • Give it a title of your choice. For the purpose of this article we will use Test In-App Billing.
  • Define one in-app product. For the purpose of this test, it must be a Managed Product with "product0001" product ID. All other information is not relevant to the test.

2.4. Uploading TestInAppBilling APK to Alpha

  • Log into your Google Play Developer Console Account.
  • Select Test In-App Billing application.
  • Select Services & APIs.
  • Go to "Your license key for this application" and copy the base64 encoded RSA public key to the clipboard.
  • Go to ADT and Edit TestInAppBillingActivity.java source module.
  • Paste the base64 encoded RSA public key from the clipboard into applicationPublicKey string replacing the existing dummy key.
  • Click File-->Export
  • Select Android-->Export Android Application. Click Next.
  • Select TestInAppBilling project. Click next.
  • If this is the first time, create keystore, otherwise enter the password.
  • Create a new key or enter the password for existing key.
  • Save your APK file in a production directory on your development computer.
  • Go back to the Developer Console and select TestInAppBilling application.
  • Select APK Alpha Testing.
  • Click Upload new APK to Alpha.
  • Drop your production APK file for upload to Google Play. When upload is done click save.

2.5. Testing TestInAppBilling on your tablet

  • Connect your tablet to your development computer via USB cable.
  • Copy the production APK file that was created in step 2.4 above from your production directory on your development computer to a download directory of the tablet.
  • Go to your tablet and look for the APK file. Click on it and install it on your tablet.
  • Open the application.
  • You have two choices: buy a product or consume a product. First press the Buy button. The program will initiate buying process for the product that was defined in the Developer Console. Once you buy it you cannot buy it again unless it is consumed. To consume the product press the second button.
  • If there is an error, you will get a message and error location. Error location is source module name, line number and method.

3. Application Source Code Overview

The attached source code has three java modules:

  • InAppBilling.java
    The InAppBilling class does all the communication work with Google Play. This is the class that you will incorporate into your application to perform In-App Billing.

  • TestInAppBillingActivity.java
    The TestInAppBillingActivity is simulation of your own activity class for In-App Billing.

  • IInAppBillingService.aidl
    The IInAppBillingService.aidl is Android Interface Definition Language (AIDL) file defines the interface to the Google Play service. The source code is provided by Google. Please note: you cannot change the name or package name assoicaited with this module. The package name must be "com.android.vending.billing". Go to the following link to get all the information about it. Preparing Your In-app Billing Application.

4. InAppBilling Class Code Overview

The InAppBilling class handles the in-app billing flow for purchasing, or consuming one item. To purchase an item you instantiate the class and call the startServiceConnection method. The purchase process is asynchronous. The startServiceConnection method initiates the process and returns immediately. When the process terminates, sometime later, one of the callback methods of the InAppBillingListener class will be called. The InAppBilling class is active from the moment startServiceConnection is called until any of the callback methods of InAppBillingListener is called. You can call startServiceConnection multiple times except when the class is busy. A call to startServiceConnection while busy will be ignored.

4.1. InAppBilling Constructor

public InAppBilling
	(
	Activity parentActivity,
	final InAppBillingListener inAppBillingListener,
	String appPublicKeyStr,
	int purchaseRequestCode
	)

Calling arguments are:

  • parentActivity: the parent activity context for InAppBilling. In this demo project it isTestInAppBillingActivity.
  • inAppBillingListener: a class implementing the callback methods to deliver the final result. The InAppBillingListener interface is described in the next section. In this demo project it is TestInAppBillingActivity.
  • appPublicKeyStr: the public key assigned by Google Play Store tothis application in base64 format.
  • purchaseRequestCode: request code for onActivityResult.

4.2. InAppBillingListener Interface

public interface InAppBillingListener
	{
	public void inAppBillingBuySuccsess();
	public void inAppBillingItemAlreadyOwned();
	public void inAppBillingCanceled();
	public void inAppBillingConsumeSuccsess();
	public void inAppBillingItemNotOwned();
	public void inAppBillingFailure(String errorMessage);
	}

The listener class must implement 6 methods.

  • inAppBillingBuySuccsess(): The purchase process was successful. Add code to provide the customer with the product purchased.
  • inAppBillingItemAlreadyOwned(): The customer already owns this product. No purchase request was made to Google Play. Do whatever is appropriate in this case.
  • inAppBillingCanceled(): The purchase process was canceled. You can leave this method empty or provide some feedback to the user.
  • inAppBillingConsumeSuccess(): The product was consumed successfully. In other words, the user can purchase it one more time.
  • inAppBillingItemNotOwned(): The item is not owned. Therefore it cannot be consumed.
  • inAppBillingFailure(String errorMessage): Unexpected error occurred. The error message has an appropriate text message plus the source code file name and line number of the error. In addition the method name is given.

Please note: when any of the listening methods is called the InAppBilling class is no longer active. In other words, you can initiate another purchase or consume using the same object.

4.3. Calling startServiceConnection method

public void startServiceConnection
	(
	String itemType,
	String itemSku,
	boolean consumeItem
	)
  • itemType: The item can be ITEM_TYPE_ONE_TIME_PURCHASE = "inapp" for one-time purchases or ITEM_TYPE_SUBSCRIPTION = "subs" for subscription.
  • itemSku: Item product ID exactly as defined in Google Play Developer Console. In this example it is product0001.
  • consumeItem: For purchase it should be ACTIVITY_TYPE_PURCHASE = false. For consume it should be ACTIVITY_TYPE_CONSUME = true.

4.4. InAppBilling Logic Flow

  • Instantiate InAppBilling object by calling the constructor. You can reuse the object as long as it is not active.
  • Call startServiceConnection method. This method will create a serviceConnection and bind it to Google Play Store service on the device. The binding process is asynchronous. The startServiceConnection will return immediately before the connection is activated.
  • When the binding process is done the system will call the serviceConnected method.
  • The serviceConnected method will test if in-app billing service is available on this device. Note: IInAppBillingService.isBillingSupported.
  • If in-app billing is supported, the method will check if the item is available for sale. Note: IInAppBillingService.getSkuDetails.
  • If the item is available for sale, the method will check if it is already owned by the customer. Note: IInAppBillingService.getPurchases.
  • If the startServiceConnection was called for consume and the item is owned, the item will be consumed. Note: inAppBillingService.consumePurchase. If the item is not owned, the InAppBilling will terminate with a call to inAppBillingItemNotOwned().
  • If the startServiceConnection was called for purchase and the item is owned by the customer, the InAppBilling will terminate with a call to inAppBillingItemAlreadyOwned().
  • If the startServiceConnection was called for purchase and the item is not owned by the customer the program will send an asynchronous request to purchase the item. Note: inAppBillingService.getBuyIntent and startIntentSenderForResult.
  • Please note: this is the time the customer will see the Google dialog asking the customer to approve the purchase.
  • When the request was processed by Google Play Store The system calls onActivityResult method in that parent Activity class This call is transferred to onActivityResult method in this class.
  • The onActivityResult method checks the result. There are four possible outcomes: (1) User canceled (2) Error (3) Result is ok but returned data is invalid (4) Purchase is successful. The appropriate callback method will be called.
  • Whenever a callback methods is called, the InAppBilling class un-binds itself from the service, resets the active flag and then calls the callback method.

4.5. InAppBilling Programming Notes

The calls to IInAppBillingService.getSkuDetails(), IInAppBillingService.getBuyIntent() and IINAppBillingService.getPurchases() return more information than used in this demo. The information is in JSON key-value pairs. The InAppBilling.java source code identifies the extra information available to you if you need it. For more information got to In-app Billing Reference (IAB Version 3).

The InAppBilling class contains two methods related to signature verification: verifySignature and decodeBase64. These methods are based on the sample project given by Google. However, both are much simpler.

At the end of InAppBilling source there are a number of methods related to creation of error message. The getErrorLocation is of particular interest to C and c++ programmers because the method shows how to get the source module name __FILE__ and the line number __LINE__ of the error.

5. TestInAppBillingActivity Programming Notes

The TestInAppBillingActivity class is a simulation of your own activity class. This is the activity that In-App Billing will be called from. Below you will find all methods defined in this class. For other members of the class please look at the source module.

5.1. onCreate

This activity creates the layout programmatically. We use RelativeLayout. Four views are added to the layout: title, buy button, consume button, and text message area.

@Override
protected void onCreate(Bundle savedInstanceState)
	{
	// call super class
	super.onCreate(savedInstanceState);

	// get display metrics
	DisplayMetrics displayMetrics = new DisplayMetrics();
	getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
	
	// conversion factor mm to pixels
	mmToPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1.0F, displayMetrics);
	
	// text size and margin
	float textSize = 7.0F;
	float msgTextSize = 5.0F;
	int margin = (int) (4.0F * mmToPixels);
	
	// create layout
	layout = new RelativeLayout(this);
	
	// take all screen area
	layout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));

	// same background color
	layout.setBackgroundColor(Color.rgb(163, 176, 255));

	// center view at the center of the screen
	layout.setGravity(Gravity.CENTER);

	// create title 1
	createTextView(TITLE1_ID, "Google Play", textSize, 0);

	// create title 2
	createTextView(TITLE2_ID, "In-App Billing Demo", textSize, 0);
	
	// create title 3
	createTextView(TITLE3_ID, "Granotech Limited", msgTextSize, margin);

	// create buy button
	createButton(BUY_BUTTON_ID, "Buy Product", textSize, margin).setOnClickListener(new OnClickListener()
    		{
    		// define on click listener to button
		public void onClick(View view)
			{
			buyProduct();
			return;
			}});
    
	// create consume button
	createButton(CONSUME_BUTTON_ID, "Consume Product", textSize, margin).setOnClickListener(new OnClickListener()
    		{
    		// define on click listener to button
		public void onClick(View view)
			{
			consumeProduct();
			return;
			}});
    
	// create message box
	msgBox = createTextView(MESSAGE_BOX_ID, "Click either\nBuy Product button or\nConsume Product button", msgTextSize,  margin);

	// add layout to activity
	setContentView(layout);
	return;
	}

5.2. createTextView

Create TextView during onCreate.

private TextView createTextView(int id, String text, float textSize, int margin)
	{
	TextView textView = new TextView(this);
	textView.setId(id);
   	textView.setText(text);
   	textView.setTextSize(TypedValue.COMPLEX_UNIT_MM, textSize);
	RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
		RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
	lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
	if(id > 0) lp.addRule(RelativeLayout.BELOW, id - 1);
	lp.setMargins(0, 0, 0, margin);
	layout.addView(textView, lp);
	return(textView);
	}

5.3. createButton

Create Button during onCreate.

private Button createButton(int id, String text, float textSize, int margin)
	{
	// create buy button
	Button button = new Button(this);
	button.setId(id);
	button.setText(text);
	button.setTextSize(TypedValue.COMPLEX_UNIT_MM, textSize);
	int padding = (int) (4.0F * mmToPixels);
	button.setPadding(padding, 0, padding, 0);
	RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
		RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
	lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
	if(id > 0) lp.addRule(RelativeLayout.BELOW, id - 1);
	lp.setMargins(0, 0, 0, margin);
	layout.addView(button, lp);
	return(button);
	}

5.4. buyProduct

It is the Buy Product button on-click listener. It is the method that will initiate the purchase process.

// user clicked the buy button
private void buyProduct()
	{
	// first time
	if(inAppBilling == null)
		{
		// create in-app billing object
		inAppBilling = new InAppBilling(this, this, applicationPublicKey, PURCHASE_REQUEST_CODE);
		}
	
	// InAppBilling initialization
	// NOTE: if inAppBilling is already active, the call is ignored
	inAppBilling.startServiceConnection(InAppBilling.ITEM_TYPE_ONE_TIME_PURCHASE,
				PRODUCT_SKU, InAppBilling.ACTIVITY_TYPE_PURCHASE);
	
	// exit
	return;
	}

5.5. consumeProduct

It is the Consume Product button on-click listener. It is the method that will initiate the consume product process.

// user clicked the consume button
private void consumeProduct()
	{
	// first time
	if(inAppBilling == null)
		{
		// create in-app billing object
		inAppBilling = new InAppBilling(this, this, applicationPublicKey, PURCHASE_REQUEST_CODE);
		}
	
	// InAppBilling initialization
	// NOTE: if inAppBilling is already active, the call is ignored
	inAppBilling.startServiceConnection(InAppBilling.ITEM_TYPE_ONE_TIME_PURCHASE,
			PRODUCT_SKU, InAppBilling.ACTIVITY_TYPE_CONSUME);

	// exit
	return;
	}

5.6. inAppBillingBuySuccsess

Listener callback method. Buy process was successful.

@Override
public void inAppBillingBuySuccsess()
	{
	msgBox.setText("In App purchase successful");
	return;
	}

5.7. inAppBillingItemAlreadyOwned

Listener callback method. Buy process did not go through. The customer already owns this product.

@Override
public void inAppBillingItemAlreadyOwned()
	{
	msgBox.setText("Product is already owned.\nPurchase was not initiated.");
	return;
	}

5.8. inAppBillingCanceled

Listener callback method. Buy process was canceled.

@Override
public void inAppBillingCanceled()
	{
	msgBox.setText("Purchase was canceled by user");
	return;
	}

5.9. inAppBillingConsumeSuccess

Listener callback method. Customer owned this product and now it was consumed. He/she can buy it again.

@Override
public void inAppBillingConsumeSuccsess()
	{
	msgBox.setText("In App consume product successful");
	return;
	}

5.10. inAppBillingItemNotOwned

Listener callback method. Consume process did not go through. The item was not owned.

@Override
public void inAppBillingItemNotOwned()
	{
	msgBox.setText("Product is not owned.\nConsume failed.");
	return;
	}

5.11. inAppBillingBillingFailure

Listener callback method. Unexpected error occurred during the purchase or consume process.

@Override
public void inAppBillingFailure(String errorMessage)
	{
	msgBox.setText("Purchase or consume process failed.\n" + errorMessage);
	return;
	}

5.12. onActivityResult

This callback will be activated when Google Play received customer authorization to go ahead with the purchase.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
	// purchase request code
	if(inAppBilling != null && requestCode == PURCHASE_REQUEST_CODE)
		{
	    	// Pass on the activity result to inAppBilling for handling
		inAppBilling.onActivityResult(resultCode, data);
		}
	else
		{
		// default onActivityResult
		super.onActivityResult(requestCode, resultCode, data);
		}
	return;
	}

5.13. onBackPressed

User pressed on back button. Terminate the application

@Override
public void onBackPressed()
	{
	// terminate activity and return RESULT_CANCELED
	if(inAppBilling != null) inAppBilling.dispose();
	finish();
	return;
	}

5.14. onDestroy

Application is about to be destroyed. Make sure InAppBilling is disposed of.

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

6. References

Google has extensive website for software developers. The following link is specific for in-app billing: Preparing Your In-app Billing Application.

7. Other open source software published by the author:

8. History

2014/01/16: Version 1.0 Original revision

License

This article, along with any associated source code and files, is licensed under The Eclipse Public License 1.0