Implementing In App purchases for Amazon in Android.

by / Tuesday, 29 October 2013 / Published in Android

The In-App Purchasing API lets you sell digital content and subscriptions–such as in-game currency, expansion packs, upgrades, magazine issues and more–from within your apps.

Here is an example to implement In App purchases for Amazon in Android.

We have to add the following in AndroidMainfest.xml:

<receiver android:name="com.amazon.inapp.purchasing.ResponseReceiver" >
<intent-filter>
<action
android:name="com.amazon.inapp.purchasing.NOTIFY"
android:permission="com.amazon.inapp.purchasing.Permission.NOTIFY" />
</intent-filter>
</receiver>

Wehave use the following java classes to Implement this.

1.AmazonPurchaseInfo.java

2.ButtonClickerObserver.java

3.DeveloperConsole.java

4.MainActivity.java

5.NextActivity.java

Code for AmazonPurchaseInfo.java

public class AmazonPurchaseInfo {

final String purchaseStatusAmazon = "hasPurchase";
String currentUser;
Map<String, String> requestIds = new HashMap<String, String>();
Context context;
final String amazonProductId="product Id";
public AmazonPurchaseInfo(Context context) {
this.context=context;
}

public void storeRequestId(String requestId, String key) {
requestIds.put(requestId, key);
}

public SharedPreferences getSharedPreferencesForCurrentUser() {
final SharedPreferences settings = context.getSharedPreferences(currentUser, Context.MODE_PRIVATE);
return settings;
}

public SharedPreferences.Editor getSharedPreferencesEditor(){
return getSharedPreferencesForCurrentUser().edit();
}

public String getCurrentUser(){
return currentUser;
}

public void setCurrentUser(final String currentUser){
this.currentUser = currentUser;
}
}

Code for ButtonClickerObserver.java:

public class ButtonClickerObserver extends BasePurchasingObserver {

private static final String OFFSET = "offset";
private static final String START_DATE = "startDate";
private static final String TAG = "Amazon-IAP";
//private final ButtonClickerActivity baseActivity;
Context context;
AmazonPurchaseInfo pinfo=MainActivity.pinfo;

/**
* Creates new instance of the ButtonClickerObserver class.
*
* @param buttonClickerActivity Activity context
*/
public ButtonClickerObserver(Context context) {
super(context);
//this.baseActivity = buttonClickerActivity;
}

/**
* Invoked once the observer is registered with the Puchasing Manager If the boolean is false, the application is
* receiving responses from the SDK Tester. If the boolean is true, the application is live in production.
*
* @param isSandboxMode
*            Boolean value that shows if the app is live or not.
*/
@Override
public void onSdkAvailable(final boolean isSandboxMode) {
Log.v(TAG, "onSdkAvailable recieved: Response -" + isSandboxMode);
PurchasingManager.initiateGetUserIdRequest();
}

/**
* Invoked once the call from initiateGetUserIdRequest is completed.
* On a successful response, a response object is passed which contains the request id, request status, and the
* userid generated for your application.
*
* @param getUserIdResponse
*            Response object containing the UserID
*/
@Override
public void onGetUserIdResponse(final GetUserIdResponse getUserIdResponse) {
Log.v(TAG, "onGetUserIdResponse recieved: Response -" + getUserIdResponse);
Log.v(TAG, "RequestId:" + getUserIdResponse.getRequestId());
Log.v(TAG, "IdRequestStatus:" + getUserIdResponse.getUserIdRequestStatus());
new GetUserIdAsyncTask().execute(getUserIdResponse);
}

/**
* Invoked once the call from initiateItemDataRequest is completed.
* On a successful response, a response object is passed which contains the request id, request status, and a set of
* item data for the requested skus. Items that have been suppressed or are unavailable will be returned in a
* set of unavailable skus.
*
* @param itemDataResponse
*            Response object containing a set of purchasable/non-purchasable items
*/
@Override
public void onItemDataResponse(final ItemDataResponse itemDataResponse) {
Log.v(TAG, "onItemDataResponse recieved");
Log.v(TAG, "ItemDataRequestStatus" + itemDataResponse.getItemDataRequestStatus());
Log.v(TAG, "ItemDataRequestId" + itemDataResponse.getRequestId());
new ItemDataAsyncTask().execute(itemDataResponse);
}

/**
* Is invoked once the call from initiatePurchaseRequest is completed.
* On a successful response, a response object is passed which contains the request id, request status, and the
* receipt of the purchase.
*
* @param purchaseResponse
*            Response object containing a receipt of a purchase
*/
@Override
public void onPurchaseResponse(final PurchaseResponse purchaseResponse) {
Log.v(TAG, "onPurchaseResponse recieved");
Log.v(TAG, "PurchaseRequestStatus:" + purchaseResponse.getPurchaseRequestStatus());
new PurchaseAsyncTask().execute(purchaseResponse);
}

/**
* Is invoked once the call from initiatePurchaseUpdatesRequest is completed.
* On a successful response, a response object is passed which contains the request id, request status, a set of
* previously purchased receipts, a set of revoked skus, and the next offset if applicable. If a user downloads your
* application to another device, this call is used to sync up this device with all the user's purchases.
*
* @param purchaseUpdatesResponse
*            Response object containing the user's recent purchases.
*/
@Override
public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpdatesResponse) {
Log.v(TAG, "onPurchaseUpdatesRecived recieved: Response -" + purchaseUpdatesResponse);
Log.v(TAG, "PurchaseUpdatesRequestStatus:" + purchaseUpdatesResponse.getPurchaseUpdatesRequestStatus());
Log.v(TAG, "RequestID:" + purchaseUpdatesResponse.getRequestId());
new PurchaseUpdatesAsyncTask().execute(purchaseUpdatesResponse);
}

/*
* Helper method to print out relevant receipt information to the log.
*/
private void printReceipt(final Receipt receipt) {
Log.v(
TAG,
String.format("Receipt: ItemType: %s Sku: %s SubscriptionPeriod: %s", receipt.getItemType(),
receipt.getSku(), receipt.getSubscriptionPeriod()));
}

/*
* Helper method to retrieve the correct key to use with our shared preferences
*/
private String getKey(final String sku) {
if (sku.equals(pinfo.amazonProductId)) {
return pinfo.purchaseStatusAmazon;
} else {
return "";
}
}

private SharedPreferences getSharedPreferencesForCurrentUser() {
final SharedPreferences settings = pinfo.context.getSharedPreferences(pinfo.getCurrentUser(), Context.MODE_PRIVATE);
return settings;
}

private SharedPreferences.Editor getSharedPreferencesEditor(){
return getSharedPreferencesForCurrentUser().edit();
}

/*
* Started when the Observer receives a GetUserIdResponse. The Shared Preferences file for the returned user id is
* accessed.
*/
private class GetUserIdAsyncTask extends AsyncTask<GetUserIdResponse, Void, Boolean> {

@Override
protected Boolean doInBackground(final GetUserIdResponse... params) {
GetUserIdResponse getUserIdResponse = params[0];

if (getUserIdResponse.getUserIdRequestStatus() == GetUserIdRequestStatus.SUCCESSFUL) {
final String userId = getUserIdResponse.getUserId();

// Each UserID has their own shared preferences file, and we'll load that file when a new user logs in.
pinfo.setCurrentUser(userId);
return true;
} else {
Log.v(TAG, "onGetUserIdResponse: Unable to get user ID.");
return false;
}
}

/*
* Call initiatePurchaseUpdatesRequest for the returned user to sync purchases that are not yet fulfilled.
*/
@Override
protected void onPostExecute(final Boolean result) {
super.onPostExecute(result);
if (result) {
PurchasingManager.initiatePurchaseUpdatesRequest(Offset.fromString(pinfo.context.getSharedPreferences(pinfo.getCurrentUser(), Context.MODE_PRIVATE)
.getString(OFFSET, Offset.BEGINNING.toString())));
}
}
}

/*
* Started when the observer receives an Item Data Response.
* Takes the items and display them in the logs. You can use this information to display an in game
* storefront for your IAP items.
*/
private class ItemDataAsyncTask extends AsyncTask<ItemDataResponse, Void, Void> {
@Override
protected Void doInBackground(final ItemDataResponse... params) {
final ItemDataResponse itemDataResponse = params[0];

switch (itemDataResponse.getItemDataRequestStatus()) {
case SUCCESSFUL_WITH_UNAVAILABLE_SKUS:
// Skus that you can not purchase will be here.
for (final String s : itemDataResponse.getUnavailableSkus()) {
Log.v(TAG, "Unavailable SKU:" + s);
}
case SUCCESSFUL:
// Information you'll want to display about your IAP items is here
// In this example we'll simply log them.
final Map<String, Item> items = itemDataResponse.getItemData();
for (final String key : items.keySet()) {
Item i = items.get(key);
Log.v(TAG, String.format("Item: %s\n Type: %s\n SKU: %s\n Price: %s\n Description: %s\n", i.getTitle(), i.getItemType(), i.getSku(), i.getPrice(), i.getDescription()));
}
break;
case FAILED:
// On failed responses will fail gracefully.
break;

}

return null;
}
}

/*
* Started when the observer receives a Purchase Response
* Once the AsyncTask returns successfully, the UI is updated.
*/
private class PurchaseAsyncTask extends AsyncTask<PurchaseResponse, Void, Boolean> {

@Override
protected Boolean doInBackground(final PurchaseResponse... params) {
final PurchaseResponse purchaseResponse = params[0];
final String userId = pinfo.getCurrentUser();

if (!purchaseResponse.getUserId().equals(userId)) {
// currently logged in user is different than what we have so update the state
pinfo.setCurrentUser(purchaseResponse.getUserId());
PurchasingManager.initiatePurchaseUpdatesRequest(Offset.fromString(pinfo.context.getSharedPreferences(pinfo.getCurrentUser(), pinfo.context.MODE_PRIVATE)
.getString(OFFSET, Offset.BEGINNING.toString())));
}
final SharedPreferences settings = getSharedPreferencesForCurrentUser();
final SharedPreferences.Editor editor = getSharedPreferencesEditor();
switch (purchaseResponse.getPurchaseRequestStatus()) {
case SUCCESSFUL:
/*
* You can verify the receipt and fulfill the purchase on successful responses.
*/
final Receipt receipt = purchaseResponse.getReceipt();
//System.out.println("---------"+receipt.getItemType());
String key = "";
switch (receipt.getItemType()) {
case CONSUMABLE:
/*int numClicks = settings.getInt(ButtonClickerActivity.NUM_CLICKS, 0);
editor.putInt(ButtonClickerActivity.NUM_CLICKS, numClicks + 10);*/
break;
case ENTITLED:
key = getKey(receipt.getSku());
editor.putBoolean(key, true);
SharedPreferences purpreferences = PreferenceManager.getDefaultSharedPreferences(pinfo.context);
SharedPreferences.Editor addEditor = purpreferences.edit();
addEditor.putBoolean("addstatus", false);
addEditor.commit();
break;
case SUBSCRIPTION:
key = getKey(receipt.getSku());
editor.putBoolean(key, true);
editor.putLong(START_DATE, new Date().getTime());
break;
}
editor.commit();

printReceipt(purchaseResponse.getReceipt());
return true;
case ALREADY_ENTITLED:
/*
* If the customer has already been entitled to the item, a receipt is not returned.
* Fulfillment is done unconditionally, we determine which item should be fulfilled by matching the
* request id returned from the initial request with the request id stored in the response.
*/
final String requestId = purchaseResponse.getRequestId();
editor.putBoolean(pinfo.requestIds.get(requestId), true);
editor.commit();
SharedPreferences purpreferences = PreferenceManager.getDefaultSharedPreferences(pinfo.context);
SharedPreferences.Editor addEditor = purpreferences.edit();
addEditor.putBoolean("addstatus", false);
addEditor.commit();
return true;
case FAILED:
/*
* If the purchase failed for some reason, (The customer canceled the order, or some other
* extraneous circumstance happens) the application ignores the request and logs the failure.
*/
Log.v(TAG, "Failed purchase for request" + pinfo.requestIds.get(purchaseResponse.getRequestId()));
return false;
case INVALID_SKU:
/*
* If the sku that was purchased was invalid, the application ignores the request and logs the failure.
* This can happen when there is a sku mismatch between what is sent from the application and what
* currently exists on the dev portal.
*/
Log.v(TAG, "Invalid Sku for request" + pinfo.requestIds.get(purchaseResponse.getRequestId()));
return false;
}
return false;
}

@Override
protected void onPostExecute(final Boolean success) {
super.onPostExecute(success);
if (success) {
//pinfo.context.startActivity(new Intent(pinfo.context,LogoSelectionActivity.class));
/*LogoSelectionActivity loa=(LogoSelectionActivity)pinfo.context;
loa.guessedMethod();
loa.finish();*/

pinfo.context.startActivity(new Intent(pinfo.context,MainActivity.class));
}
}
}

/*
* Started when the observer receives a Purchase Updates Response Once the AsyncTask returns successfully, we'll
* update the UI.
*/
private class PurchaseUpdatesAsyncTask extends AsyncTask<PurchaseUpdatesResponse, Void, Boolean> {

@Override
protected Boolean doInBackground(final PurchaseUpdatesResponse... params) {
final PurchaseUpdatesResponse purchaseUpdatesResponse = params[0];
final SharedPreferences.Editor editor = getSharedPreferencesEditor();
final String userId = pinfo.getCurrentUser();
if (!purchaseUpdatesResponse.getUserId().equals(userId)) {
return false;
}

/*
* If the customer for some reason had items revoked, the skus for these items will be contained in the
* revoked skus set.
*/

for (final String sku : purchaseUpdatesResponse.getRevokedSkus()) {
Log.v(TAG, "Revoked Sku:" + sku);
final String key = getKey(sku);
editor.putBoolean(key, false);
editor.commit();
}

switch (purchaseUpdatesResponse.getPurchaseUpdatesRequestStatus()) {
case SUCCESSFUL:
SubscriptionPeriod latestSubscriptionPeriod = null;
final LinkedList<SubscriptionPeriod> currentSubscriptionPeriods = new LinkedList<SubscriptionPeriod>();
for (final Receipt receipt : purchaseUpdatesResponse.getReceipts()) {
final String sku = receipt.getSku();
final String key = getKey(sku);
switch (receipt.getItemType()) {
case ENTITLED:
/*
* If the receipt is for an entitlement, the customer is re-entitled.
*/
editor.putBoolean(key, true);
editor.commit();
SharedPreferences purpreferences = PreferenceManager.getDefaultSharedPreferences(pinfo.context);
SharedPreferences.Editor addEditor = purpreferences.edit();
addEditor.putBoolean("addstatus", false);
addEditor.commit();
break;
case SUBSCRIPTION:
/*
* Purchase Updates for subscriptions can be done in one of two ways:
* 1. Use the receipts to determine if the user currently has an active subscription
* 2. Use the receipts to create a subscription history for your customer.
* This application checks if there is an open subscription the application uses the receipts
* returned to determine an active subscription.
* Applications that unlock content based on past active subscription periods, should create
* purchasing history for the customer.
* For example, if the customer has a magazine subscription for a year,
* even if they do not have a currently active subscription,
* they still have access to the magazines from when they were subscribed.
*/
final SubscriptionPeriod subscriptionPeriod = receipt.getSubscriptionPeriod();
final Date startDate = subscriptionPeriod.getStartDate();
/*
* Keep track of the receipt that has the most current start date.
* Store a container of duplicate subscription periods.
* If there is a duplicate, the duplicate is added to the list of current subscription periods.
*/
if (latestSubscriptionPeriod == null ||
startDate.after(latestSubscriptionPeriod.getStartDate())) {
currentSubscriptionPeriods.clear();
latestSubscriptionPeriod = subscriptionPeriod;
currentSubscriptionPeriods.add(latestSubscriptionPeriod);
} else if (startDate.equals(latestSubscriptionPeriod.getStartDate())) {
currentSubscriptionPeriods.add(receipt.getSubscriptionPeriod());
}

break;

}
printReceipt(receipt);
}
/*
* Check the latest subscription periods once all receipts have been read, if there is a subscription
* with an existing end date, then the subscription is not active.
*/
/*if (latestSubscriptionPeriod != null) {
boolean hasSubscription = true;
for (SubscriptionPeriod subscriptionPeriod : currentSubscriptionPeriods) {
if (subscriptionPeriod.getEndDate() != null) {
hasSubscription = false;
break;
}
}
editor.putBoolean(ButtonClickerActivity.HAS_SUBSCRIPTION, hasSubscription);
editor.commit();
}*/

/*
* Store the offset into shared preferences. If there has been more purchases since the
* last time our application updated, another initiatePurchaseUpdatesRequest is called with the new
* offset.
*/
final Offset newOffset = purchaseUpdatesResponse.getOffset();
editor.putString(OFFSET, newOffset.toString());
editor.commit();
if (purchaseUpdatesResponse.isMore()) {
Log.v(TAG, "Initiating Another Purchase Updates with offset: " + newOffset.toString());
PurchasingManager.initiatePurchaseUpdatesRequest(newOffset);
}
return true;
case FAILED:
/*
* On failed responses the application will ignore the request.
*/
return false;
}
return false;
}

@Override
protected void onPostExecute(final Boolean success) {
super.onPostExecute(success);
if (success) {
//pinfo.context.startActivity(new Intent(pinfo.context,ListPuzzleActivity.class));
}
}
}
}

Code for DeveloperConsole.java:

public class DeveloperConsole {
Context context;
public DeveloperConsole(Context context){
this.context=context;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();

//for enable inapp enabling status true and disabling inapp status false
editor.putBoolean("inappstatus", true);

//for either google inapp put "google" or amazon inapp put "amazon"
editor.putString("inappcatagory", "amazon");
editor.commit();
}

}

Code for MainActivity.java:

public class MainActivity extends Activity {

private Button purchaseBtn;
static AmazonPurchaseInfo pinfo;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pinfo=new AmazonPurchaseInfo(MainActivity.this);

purchaseBtn=(Button)findViewById(R.id.btn_purchase);

purchaseBtn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
if(prefs.getBoolean("inappstatus", true))
{
if(prefs.getString("inappcatagory","").equals("amazon"))
{
SharedPreferences settings = MainActivity.pinfo.getSharedPreferencesForCurrentUser();
if(settings.getBoolean(MainActivity.pinfo.purchaseStatusAmazon, false))
{
Intent intent=new Intent(MainActivity.this,NextActivity.class);
startActivity(intent);
finish();
}else{
if(checkInternetConnection()){
String requestId =
PurchasingManager.initiatePurchaseRequest(MainActivity.pinfo.amazonProductId);
MainActivity.pinfo.storeRequestId(requestId, MainActivity.pinfo.purchaseStatusAmazon);
}else{
AlertDialog.Builder adb=new AlertDialog.Builder(MainActivity.this);
adb.setMessage("Internet connectivity failure.Try again!");
adb.setPositiveButton("OK",null);
adb.show();
}
}
}
}else{
Intent intent=new Intent(MainActivity.this,NextActivity.class);
startActivity(intent);
finish();
}
}
});
}

@Override
public void onStart() {
super.onStart();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if(prefs.getBoolean("inappstatus", true))
{
if(prefs.getString("inappcatagory","").equals("amazon"))
{
if(checkInternetConnection()){
ButtonClickerObserver buttonClickerObserver = new ButtonClickerObserver(this);
PurchasingManager.registerObserver(buttonClickerObserver);
}else{
AlertDialog.Builder adb=new AlertDialog.Builder(MainActivity.this);
adb.setMessage("Internet connectivity failure.Try again!");
adb.setPositiveButton("OK",null);
adb.show();
}
}
}

}

@Override
protected void onResume() {
super.onResume();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if(prefs.getBoolean("inappstatus", true))
{
if(prefs.getString("inappcatagory","").equals("amazon"))
{
if(checkInternetConnection()){
PurchasingManager.initiateGetUserIdRequest();
}else{
AlertDialog.Builder adb=new AlertDialog.Builder(MainActivity.this);
adb.setMessage("Internet connectivity failure.Try again!");
adb.setPositiveButton("OK",null);
adb.show();
}
}
}
};

private boolean checkInternetConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isAvailable() && cm.getActiveNetworkInfo().isConnected()) {
return true;
}
else
{

return false;
}
}

}

Code for corresponding layout activity_main.java:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:gravity="center">

<Button
android:id="@+id/btn_purchase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Purchase" />

</LinearLayout>

Code for NextActivity.java:

public class NextActivity extends Activity {

private TextView text;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
text=(TextView)findViewById(R.id.textview);
text.setText("Purchase successfull.");
}
}

Code for corresponding layout activity_next.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NextActivity"
android:orientation="vertical"
android:gravity="center">

<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>

</LinearLayout>

TOP