top of page
  • Writer's pictureObsidian Soft

How to use Google IAP v3 for Beginners (Part 2)

Updated: Jun 1, 2020

So, util code has been added. All eight java files have been correctly named and put in the util package. Remember to update the package names in all of these files as I have just used as a sample the package name: com.example.myapp.util


AIDL Interface:


The next step is to create the AIDL interface inside com.android.vending.billing. This is a little tricky.


In Android Studio, go to app->src->main and right click. Choose New->Directory and name it aidl.

Start creating packages within aidl until you have created com.android.vending.billing.

Right click on com.android.vending.billing and choose New->AIDL->AIDL file. Give it the name IInAppBillingService. You will see that Android Studio changes its package name automatically to com.example.myapp (your package name) and doesn't create it within com.android.vending.billing. You have to move the interface file IInAppBillingService.aidl from com.example.myapp to com.android.vending.billing by dragging. After this, delete the empty com.example.myapp package within the aidl directory that was automatically created by Android Studio. Once you have done all this, your file structure should look like this:




Copy the code from the given IInAppBillingService file and paste into your aidl file. Don't change anything in it.


IInAppBillingService
.rtf
Download RTF • 19KB

Adding IAP code to your Activity:


Now, I have come to the final part of our IAP implementation i.e. adding the code in the menu activity. You need to have a button on the menu screen saying something like "Remove Ads" and when user clicks on it, the purchase cycle is started.


Start by adding a button for removing ads to the layout file of your MenuActivity. Make sure to add onClickListener to it in your MenuActivity's java file. Add a boolean variable called show_ads to your global variables file (I am assuming that you have one ) and set it to true.


Make the menu activity implement IabBroadcastReceiver.IabBroadcastListener.


public class MenuActivity extends Activity implements OnClickListener, IabBroadcastReceiver.IabBroadcastListener


Declare the following in your MenuActivity:


/**************IAP PURCHASE CODE**********/ // name of the product id that we used while setting up our managed product in google //developer console. static final String SKU_PREMIUM = "com.example.myapp.removeads"; static final int RC_REQUEST = 10001; IabHelper mHelper; boolean mIsPremium = false; // Provides purchase notification while this app is running IabBroadcastReceiver mBroadcastReceiver; /***************************************************/


Inside the OnCreate method of your MenuActivity, copy paste the following:

String base64EncodedPublicKey = "";


Go to your application's page in Google developer console and copy the app's public key (a base-64 string). You can find this under Development Tools->Services & APIs->Licensing & In-App Billing


Paste this as the value for base64EncodedPublicKey. It shouldn't have any spaces in it.


Copy the following code and paste this inside the OnCreate Method of your Menu Activity after the line where you have defined base64EncodedPublicKey.


/*****************************/


mHelper = new IabHelper(this, base64EncodedPublicKey);

// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);

// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
    public void onIabSetupFinished(IabResult result) {


        if (!result.isSuccess()) {
            // Oh noes, there was a problem.
            complain("Problem setting up in-app billing: " + result);
            return;
        }

        // Have we been disposed of in the meantime? If so, quit.
        if (mHelper == null) return;
        mBroadcastReceiver = new IabBroadcastReceiver(MenuActivity.this);
        IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
        registerReceiver(mBroadcastReceiver, broadcastFilter);

        // IAB is fully set up. Now, let's get an inventory of stuff we own.

        try {
            mHelper.queryInventoryAsync(mGotInventoryListener);
        } catch (IabHelper.IabAsyncInProgressException e) {
            complain("Error querying inventory. Another async operation in progress.");
        }
    }
});

QueryInventoryFinishedListener:


Copy paste the following code inside your MenuActivity as an inner class:




// 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) {


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

        // Is it a failure?
        if (result.isFailure()) {
            complain("Failed to query inventory: " + result);
            return;
        }



        /*
         * Check for items we own. Notice that for each purchase, we check
         * the developer payload to see if it's correct! See
         * verifyDeveloperPayload().
         */

        // Do we have the premium upgrade?
        Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);

        mIsPremium = (premiumPurchase != null && premiumPurchase.getPurchaseState() == 0 && verifyDeveloperPayload(premiumPurchase));


        if (mIsPremium) {
           //todo
        //set your show_ads global variable to false here
        }
            else{
       //todo
         //your code for showing ads
        //set your show_ads global variable to true here
        }
        setWaitScreen(false);

    }
};


OnIabPurchaseFinishedListener:


Copy paste the following as an inner class into your MenuActivity:


// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {


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

        if (result.isFailure()) {
            if (result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
                alert("You already own the 'Remove Ads' version of this App.");
            } else if (result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED || result.getResponse() == IabHelper.IABHELPER_USER_CANCELLED) {
                alert("The purchase was cancelled.");
            } else {
                complain("Error purchasing: " + result);
            }
            setWaitScreen(false);
            return;
        }
        if (!verifyDeveloperPayload(purchase)) {
            complain("Error purchasing. Authenticity verification failed.");
            setWaitScreen(false);
            return;
        }
        if (purchase.getSku().equals(SKU_PREMIUM)) {
            // bought the premium upgrade!

            alert("Thank you for buying the Remove Ads version this app!");
            mIsPremium = true;
            //todo remove any ads if showing here
           //todo set your show_ads global variable to false here
            setWaitScreen(false);
        }

    }
};


onActivityResult():


Copy and paste the following code to your MenuActivity:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (mHelper == null) return;

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {

        super.onActivityResult(requestCode, resultCode, data);
    }

}

receivedBroadcast():


Copy and paste the following code to your MenuActivity:

@Override
public void receivedBroadcast() {
    // Received a broadcast notification that the inventory of items has changed

    try {
        mHelper.queryInventoryAsync(mGotInventoryListener);
    } catch (IabHelper.IabAsyncInProgressException e) {
        complain("Error querying inventory. Another async operation in progress.");
    }
}

removeAdsButtonClicked():


This method will be called when the remove ads button is clicked.

Copy and paste its code to your MenuActivity:


// User clicked the Remove ads button.
public void removeAdsButtonClicked() {

    setWaitScreen(true);

    /* TODO: for security, generate your payload here for verification. See the comments on
     *        verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
     *        an empty string, but on a production app you should carefully generate this. */
    String payload = "";

    try {
        mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, payload);
    } catch (IabHelper.IabAsyncInProgressException e) {
        complain("Error launching purchase flow. Another async operation in progress.");
        setWaitScreen(false);
    }
}

You may see todo messages about generating payload. I haven't handled it and kept it the same way.


verifyDeveloperPayload():


Copy and paste the following code to your MenuActivity:

boolean verifyDeveloperPayload(Purchase p) {
    String payload = p.getDeveloperPayload();

    /*
     * TODO: verify that the developer payload of the purchase is correct. It will be
     * the same one that you sent when initiating the purchase.
     *
     * WARNING: Locally generating a random string when starting a purchase and
     * verifying it here might seem like a good approach, but this will fail in the
     * case where the user purchases an item on one device and then uses your app on
     * a different device, because on the other device you will not have access to the
     * random string you originally generated.
     *
     * So a good developer payload has these characteristics:
     *
     * 1. If two different users purchase an item, the payload is different between them,
     *    so that one user's purchase can't be replayed to another user.
     *
     * 2. The payload must be such that you can verify it even when the app wasn't the
     *    one who initiated the purchase flow (so that items purchased by the user on
     *    one device work on other devices owned by the user).
     *
     * Using your own server to store and verify developer payloads across app
     * installations is recommended.
     */

    return true;
}

Again, I haven't changed it. You can write your own code here if you want.


Disposing IAP helper :


Add the following lines to your OnDestroy() method of Menu Activity for disposing the IABHelper mHelper:

// very important:
if (mBroadcastReceiver != null) {
    unregisterReceiver(mBroadcastReceiver);
}

// very important:

if (mHelper != null) {
    mHelper.disposeWhenFinished();
    mHelper = null;
}

Some other helper methods for IAP flow:


// Enables or disables the "please wait" screen.
void setWaitScreen(boolean set) {
    findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);
    findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);
}

void complain(String message) {

    alert("Error: " + message);
}

void alert(String message) {
    AlertDialog.Builder bld = new AlertDialog.Builder(this);
    bld.setMessage(message);
    bld.setNeutralButton("OK", null);

    bld.create().show();
}

Call removeAdsButtonClicked() method in the onClick event of your remove ads button that I asked you to make in the beginning of this post.


Now, we are done with the MenuActivity. If there are errors in your code, add the required imports and these errors should disappear apart from the error in setWaitScreen(boolean set).


Basically, what is happening here is that we show a wait screen while purchase is happening. Add an image such as this to your drawable folder and name it "wait.png".


Enclose your entire layout file in a framelayout and add the wait image to it in the following way:


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/screen_background">

    <ImageView
        android:id="@+id/screen_wait"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_gravity="center"

        android:src="@drawable/wait"
        android:visibility="gone" />

<!--       Add your entire main layout file after this comment
but remember to give it the following id:
android:id="@+id/screen_main"

    -->

</FrameLayout>

This should take care of the last error in Menu Activity.


Remember to update your show_ads global variable accordingly depending on the value of mIsPremium value. I have added //todo comments wherever this is needed for your convenience.

Also, check this global show_ads value before showing any ad and show the ad only if this value is true.


Testing IAP:


Generate a signed apk/bundle. Release it to the closed track for testing. Send an email link to your testers that you added while making the closed track in your Google Developer Console. When they will opt in the testing, they can test your IAP and will be shown test options for purchasing or cancelling. So, you can test your IAP easily without the credit cards being charged. Also, remember that you need to wait for the closed track version of your app to be published before your testers can install and test it.


When you are ready to go into production, disable logging by setting mHelper.enableDebugLogging(true) to mHelper.enableDebugLogging(false) in your MenuActivity.


This is all from my end for explaining IAP. I hope I covered all the steps . If you like this post, do leave a comment and tell me to make my day 😉.




25 views0 comments

Recent Posts

See All
bottom of page