com.appsimobile.appsii.iab.IabPurchaseHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.appsimobile.appsii.iab.IabPurchaseHelper.java

Source

/* Copyright (c) 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.appsimobile.appsii.iab;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;

import org.json.JSONException;

/**
 * A specific iab-helper implementation. Gives access to the purchase function
 * if the iab-library
 */
public class IabPurchaseHelper extends BaseIabHelper {

    /**
     * Creates an instance. After creation, it will not yet be ready to use. You must perform
     * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does
     * not
     * block and is safe to call from a UI thread.
     *
     * @param ctx Your application or Activity context. Needed to bind to the in-app billing
     * service.
     */
    public IabPurchaseHelper(Context ctx) {
        super(ctx);
    }

    /**
     * Handles an activity result that's part of the purchase flow in in-app billing. If you
     * are calling {@link #launchPurchaseFlow}, then you must call this method from your
     * Activity's {@link android.app.Activity@onActivityResult} method. This method
     * MUST be called from the UI thread of the Activity.
     *
     * @param resultCode The resultCode as you received it.
     * @param data The data (Intent) as you received it.
     *
     * @return Returns true if the result was related to a purchase flow and was handled;
     * false if the result was not related to a purchase, in which case you should
     * handle it normally.
     */
    public static int handleActivityResult(int resultCode, Intent data,
            OnIabPurchaseFinishedListener purchaseListener, String itemType, String devPayload) {

        if (data == null) {
            logError("Null data in IAB activity result.");
            return IABHELPER_BAD_RESPONSE;
        }

        int responseCode = getResponseCodeFromIntent(data);
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
        String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

        if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {
            logDebug("Successful resultcode from purchase activity.");
            logDebug("Purchase data: " + purchaseData);
            logDebug("Data signature: " + dataSignature);
            logDebug("Extras: " + data.getExtras());
            logDebug("Expected item type: " + itemType);

            if (purchaseData == null || dataSignature == null) {
                logError("BUG: either purchaseData or dataSignature is null.");
                logDebug("Extras: " + data.getExtras().toString());
                return IABHELPER_UNKNOWN_ERROR;
            }

            Purchase purchase;
            try {
                purchase = new Purchase(itemType, purchaseData, dataSignature);
                String sku = purchase.getSku();

                // Verify signature
                if (!Security.verifyPurchase(RSA_CODE, purchaseData, dataSignature)) {
                    logError("Purchase signature verification FAILED for sku " + sku);
                    return IABHELPER_VERIFICATION_FAILED;
                }
                logDebug("Purchase signature successfully verified.");
                if (!Security.verifyDeveloperPayload(purchase, devPayload)) {
                    logError("Purchase payload verification FAILED for sku " + sku);
                    return IABHELPER_DEVELOPER_PAYLOAD_FAILED;
                }
                logDebug("Developer payload successfully verified.");
            } catch (JSONException e) {
                logError("Failed to parse purchase data.");
                e.printStackTrace();
                return IABHELPER_BAD_RESPONSE;
            }

            if (purchaseListener != null) {
                purchaseListener.onIabPurchaseSuccess(purchase);
            }
            return BILLING_RESPONSE_RESULT_OK;

        } else if (resultCode == Activity.RESULT_OK) {
            // result code was OK, but in-app billing response was not OK.
            logDebug("Result code was OK but in-app billing response was not OK: " + getResponseDesc(responseCode));
            return responseCode;
        } else if (resultCode == Activity.RESULT_CANCELED) {
            logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
            return IABHELPER_USER_CANCELLED;
        } else {
            logError("Purchase failed. Result code: " + Integer.toString(resultCode) + ". Response: "
                    + getResponseDesc(responseCode));
            return IABHELPER_UNKNOWN_PURCHASE_RESPONSE;
        }
    }

    /**
     * Returns whether subscriptions are supported.
     */
    public boolean subscriptionsSupported() {
        return mSubscriptionsSupported;
    }

    public int launchInAppItemPurchaseFlow(Activity act, String sku, int requestCode, String developerPayload) {
        return launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, developerPayload);
    }

    public int launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, String extraData) {
        return launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, extraData);
    }

    /**
     * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
     * which will involve bringing up the Google Play screen. The calling activity will be paused
     * while
     * the user interacts with Google Play, and the result will be delivered via the activity's
     * {@link android.app.Activity#onActivityResult} method, at which point you must call
     * this object's {@link #handleActivityResult} method to continue the purchase flow. This method
     * MUST be called from the UI thread of the Activity.
     *
     * @param act The calling activity.
     * @param sku The sku of the item to purchase.
     * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or
     * ITEM_TYPE_SUBS)
     * @param requestCode A request code (to differentiate from other responses --
     * as in {@link android.app.Activity#startActivityForResult}).
     * @param developerPayload Extra data (developer payload), which will be returned with the
     * purchase data
     * when the purchase completes. This extra data will be permanently bound to that purchase
     * and will always be returned when the purchase is queried.
     */
    public int launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,
            String developerPayload) {
        checkSetupDone("launchPurchaseFlow");

        if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
            return IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE;
        }

        try {
            logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
            Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType,
                    developerPayload);
            int response = getResponseCodeFromBundle(buyIntentBundle);
            if (response != BILLING_RESPONSE_RESULT_OK) {
                logError("Unable to buy item, Error response: " + getResponseDesc(response));
                return response;
            }

            PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
            logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
            act.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, new Intent(), 0, 0, 0);
            return BILLING_RESPONSE_RESULT_OK;
        } catch (SendIntentException e) {
            logError("SendIntentException while launching purchase flow for sku " + sku);
            e.printStackTrace();
            return IABHELPER_SEND_INTENT_FAILED;
        } catch (RemoteException e) {
            logError("RemoteException while launching purchase flow for sku " + sku);
            e.printStackTrace();
            return IABHELPER_REMOTE_EXCEPTION;
        }
    }

    /**
     * Consumes a given in-app product. Consuming can only be done on an item
     * that's owned, and as a result of consumption, the user will no longer own it.
     * This method may block or take long to return.
     *
     * @throws IabException if there is a problem during consumption.
     */
    int consume(String sku, String token) {
        checkSetupDone("consume");

        try {
            if (token == null || token.equals("")) {
                logError("Can't consume " + sku + ". No token.");
                return IABHELPER_MISSING_TOKEN;
            }

            logDebug("Consuming sku: " + sku + ", token: " + token);
            int response = mService.consumePurchase(3, mContext.getPackageName(), token);
            if (response == BILLING_RESPONSE_RESULT_OK) {
                logDebug("Successfully consumed sku: " + sku);
            } else {
                logDebug("Error consuming consuming sku " + sku + ". " + getResponseDesc(response));
            }
            return response;
        } catch (RemoteException e) {
            Log.wtf("Appsii", "Remote exception while consuming. Sku: " + sku, e);
            return IABHELPER_REMOTE_EXCEPTION;
        }
    }

    /**
     * Callback that notifies when a purchase is finished.
     */
    public interface OnIabPurchaseFinishedListener {

        /**
         * Called to notify that an in-app purchase finished. If the purchase was successful,
         * then the sku parameter specifies which item was purchased. If the purchase failed,
         * the sku and extraData parameters may or may not be null, depending on how far the
         * purchase
         * process went.
         *
         * @param info The purchase information (null if purchase failed)
         */
        void onIabPurchaseSuccess(Purchase info);
    }

}