Google Play In-App Billing Demo App






4.76/5 (14 votes)
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. TheInAppBillingListener
interface
is described in the next section. In this demo project it isTestInAppBillingActivity
.appPublicKeyStr
: the public key assigned by Google Play Store tothis application in base64 format.purchaseRequestCode
: request code foronActivityResult
.
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 beITEM_TYPE_ONE_TIME_PURCHASE
= "inapp" for one-time purchases orITEM_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 beACTIVITY_TYPE_PURCHASE
= false. For consume it should beACTIVITY_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 aserviceConnection
and bind it to Google Play Store service on the device. The binding process is asynchronous. ThestartServiceConnection
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 toinAppBillingItemNotOwned
(). - If the
startServiceConnection
was called for purchase and the item is owned by the customer, the InAppBilling will terminate with a call toinAppBillingItemAlreadyOwned
(). - 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
andstartIntentSenderForResult
. - 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 parentActivity
class This call is transferred toonActivityResult
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:
Android Color Selector for Programmers is a free App available on Google Play.
PDF File Writer C# Class Library
Prize winner in Competition "Best overall article of April 2013"
Prize winner in Competition "Best C# article of April 2013"
PDF File Writer is a C# class library allowing .NET applications to create PDF files.PDF File Analyzer With C# Parsing Classes
PDF File Analyzer is designed to read, parse, and display the internal structure of PDF files.Processing Standard Zip Files with C# compression/decompression classes
This project will give you the tools needed to compress and decompress files using the Deflate method of compression, and to read and write standard Zip files.
8. History
2014/01/16: Version 1.0 Original revision