In-app purchasing or internal payments in Android applications

What is it all about?



With the version of the Android Market application 2.3.0 for developers of applications for the Android platform, the opportunity has opened to provide users with payments within the applications themselves. Now you can sell levels and artifacts, videos, music, plugins and more, using only the platform’s built-in tools. Let's see how this can be done.

What do we need?



As usual, my favorite IDE, Android SDK and sample application .
It will also be useful to imagine what Service, BroadcastReceiver and, of course, Activity are.

We also need permission in the manifest file - without it nothing will work.



How does it work in principle?




Everything works through the service in the Android Market application. He knows how to send requests for details of a certain thing that a user wants to buy, to buy, receive answers about the success or failure of a purchase, and so on. All information about the things you sell must be set up through the Developer Console for a specific application. As soon as we received a signal about a successful purchase, we can begin to load content from our server.

How does it all work?



image

On the server, the market stores information about things that you can buy. The client application of the market interacts with the server. Our application interacts with it.

Our application will consist of at least:
  1. BillingService. This is a service that is associated with the application of the market, and sends him all the information about the operations and receives answers to them, if they are synchronous.
  2. BillingReceiver Gets asynchronous responses from a market application.
  3. PurchaseObserver. An entity that will notify the UI of changes in purchase status.


What messages do we send to the market?



First, we need to connect our BillingService with the market application in order to call the sendBillingRequest method of MarketBillingService. The service interface is described in the IMarketBillingService.aidl file, you can download it imagefrom here, and put it in com.android.vending.billing the package of your application. The IDE should immediately generate the IMarketBillingService.java file, which we will need to call the above method.

The method itself takes a Bundle parameter, which stores all the information. The most important is the parameter with the key "BILLING_REQUEST". It determines the type of request. They are:

CHECK_BILLING_SUPPORTED- check the availability of in-app billing.
REQUEST_PURCHASE - purchase request.
GET_PURCHASE_INFORMATION - receiving information about changes in the purchase status.
CONFIRM_NOTIFICATIONS- confirmation of the fact of receiving a notification from the market application.
RESTORE_TRANSACTIONS - recovery of transactions on already purchased items.

What are the answers?



The method synchronously returns a response, which is also a Bundle. It contains:

RESPONSE_CODE- the response code
PURCHASE_INTENT - PendingIntent, in order to start the purchase activity.
REQUEST_ID - identifier of the sent request

In case of asynchronous responses (received through BillingReceiver), they contain the following:

com.android.vending.billing.RESPONSE_CODE
Response code. The market confirms the success or failure of the request.

com.android.vending.billing.IN_APP_NOTIFY
Alert that purchase status has changed. After this message, you need to send a request with the type GET_PURCHASE_INFORMATIONin order to get the info.

com.android.vending.billing.PURCHASE_STATE_CHANGED
And here comes the detailed information about the purchase. It includes nonce, a list of orders, indicating product identifiers, their conditions and so on.

The sequence during the purchase will be as follows:

1. We send REQUEST_PURCHASE
2. We receive a synchronous response
3. We start the Activity of the purchase (also built-in to the market application)
4. We receive an asynchronous message IN_APP_NOTIFY
5. We send GET_PURCHASE_INFORMATION
6. We receive a synchronous response
7. We receive an asynchronous response PURCHASE_STATE_CHANGED
8. We send CONFIRM_NOTIFICATIONS
9. We receive synchronous response

Or maybe we'll look at the code better?



So, the main points in the code:

1. Connect to the market.


In onCreate () in BillingService we write:
     try {
      boolean bindResult = getApplicationContext().bindService(
          new Intent("com.android.vending.billing.MarketBillingService.BIND"),
          this,
          Context.BIND_AUTO_CREATE);
      if (bindResult) {
        Log.i(TAG, "Service bind successful.");
      } else {
        Log.e(TAG, "Could not bind to the MarketBillingService.");        
      }
    } catch (SecurityException e) {
      Log.e(TAG, "Security exception: " + e);
    }

//И чуть ниже:
@Override
  public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    Log.i(TAG, "MarketBillingService connected.");
    marketService = IMarketBillingService.Stub.asInterface(iBinder);
    runPendingRequests();
  }

* This source code was highlighted with Source Code Highlighter.

2. Sending requests.


In the sample application, a class hierarchy has been created for queries and this is correct. The most important thing about them is delayed shipping. The fact is that bindService happens asynchronously, which means we get a link to MarketBillingService much later than onCreate () ends, and even later than the first time we try to execute requests. Therefore, we do this:

  /**
   * Run the request, starting the connection if necessary.
   *
   * @return this request if the request was not executed in order to queue it.
   */
  public final AbstractRequest runRequest() {
    if (runIfConnected()) {
      return null;
    }
    return this;
  }

  /**
   * Try running the request directly if the service is already connected.
   *
   * @return true if the request ran successfully; false if the service
   *     is not connected or there was an error when trying to use it
   */
  public boolean runIfConnected() {
    if (service.isConnected()) {
      try {
        requestId = run();
        if (requestId >= 0) {
          service.addRequest(requestId, this);
        }
        return true;
      } catch (MyException e) {
        onException(e);
      }
    }
    return false;
  }

* This source code was highlighted with Source Code Highlighter.


The return of the request is necessary in order to remember it in the list of pending requests in the service. Then, when we get a link to MarketBillingService in onServiceConnected (), we will try to send all requests again.

3. UI notification


In BillingService we will store a link to a certain entity that stores the Handler from our UI. Then, after receiving the answers, it will be possible to do the following:

private void notifyGui(int messageId, PurchasedItem item) {
   if (observer != null) {
  observer.notifyGui(messageId, item);
   } else {
  // пошлем здесь оповещение в NotificationBar
  }
}

* This source code was highlighted with Source Code Highlighter.


Important: do not forget to reset the observer of the service from your Activity when you exit it and restore this link. It is done like this:

  @Override
  protected void onUserLeaveHint() {
    if (billingService != null) {
      unbindService(this);
      billingService.resetEventDispatcher();
      billingService = null;
    }
    super.onUserLeaveHint();
  }

  @Override
  public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    //Connected to BillingService
    if (componentName.getShortClassName().equals(serviceClassName)) {
      billingService = ((BillingService.LocalBinder) iBinder).getService();
      billingService.setObserver(new PurchaseObserver());
      try {
        billingService.checkBillingAvailability();
      } catch (MyException e) {
        Log.e(TAG, "Billing unavailable", e);
        Toast.makeText(this, "Billing unavailable", Toast.LENGTH_LONG).show();
      }
    }
  }

* This source code was highlighted with Source Code Highlighter.


4. Launch Activity Purchases.


Take a look at the PurchaseObserver example class. There is a method public void startBuyPageActivity (PendingIntent pendingIntent, Intent intent). PendingIntent is what we got from the market. And Intent is just new Intent ().
What does this method do?
In fact, the instance of your Activity is passed to this class. Further from it, through reflection, an attempt is made to get the startInstentSender method. This method appeared only in Android 2.0. If it is, then the method is called, and the Activity starts in the Activity stack of our application. If the method is not found, then the Activity starts in a separate stack.

But what about security?



The issue of security is the topic of a separate article, and there are already many. In the sample application, the Security class is responsible for security. I can only say that you need to verify purchases not in the application (as was done in the example), but on your own server so as not to give the verification logic to the hands of potential apk owners.

PS Based on developer.android.com/guide/market/billing/billing_overview.html

Also popular now: