Managing Pre-paid App

The Receipt Protocol

When an user purchases your app, the delivery engine at the Firefox Marketplace generates a receipt, which is a specific binary signature uniquely identifying this deal.  This series of bytes is downloaded to the mobile of the buyer, added to the code of your app.

Verifying that a running App has been legally installed simply consists in checking the validity of the several fields of the receipt, which, again, is specific and unique to each installed instance of your app.

Receipt Verifier Library

The simplest way to verify a receipt is to use the well-documented receiptVerifier library.  If you decide to take this path, then you may stop reading me, or you may pursue if you are interested in understanding the underlying mechanism.

Another reason is that the receiptVerifier library is purely JavaScript.  Hence, if, like me, you choose to extend the receipt checking on the Server Side using java only, stay here.  But if your app is HTML-only, and the Server doesn't do anything but serve static files, consider using the receiptVerifier library

Reading the Receipt

navigator.mozApps.getSelf() is the preferred way for accessing to the contents of the receipt. 

mozApps is a property of the Navigator interface pointing at an Apps object.  The Apps object collects infos for all applications "Open Web" that are installed in the app's repository on the user's device.  Hence, navigator.mozApps.getSelf() gets the App object for the app currently running, that is, your app.

In the App object, receipts is a field that gathers all receipts pertaining to your app.  And, if this field is null, you may definitively assert that the running application is not genuine!

Here is a simple javascript example for reading receipts:

var request = window.navigator.mozApps.getSelf();
request.onsuccess = function() {
   if (
      !request.result ||        // the app is not installed
      !request.result.receipts  // the app has no receipts
   ) {
      alert("Impostor intrusion. Call the Police.");
   }else {
      var receipts = request.result.receipts;
      // pursue with more fancy checks
   }
};
request.onerror = function() {
   alert("Error: " + request.error.name);
};

Verifying the Validity of the Receipt

Of course, a non-null receipt doesn't mean it is a valid receipt, and more tests are required at a finer granularity. 

Client Side

As stated above, the verification is done Server Side, in pure Java.

If you want a better defense against piracy, you might want to set up a proxy server that will be an intermediary between the app and the Firefox Marketplace.

This is what was chosen.

Therefore, Client Side, the javascript code in our app does the minimum.  It reads the App object and issues an XMLHttpRequest to our Server, passing the entire App record in Jason.  The Server checks the validity, and returns with a 402 code if the receipt was detected being invalid.

Below is the Javascript code:

function verifyReceipt() {
   var request = window.navigator.mozApps.getSelf();
   request.onsuccess = function() {
      if (
         !request.result ||        // the app is not installed
         !request.result.receipts  // the app has no receipts
      ) {
         alert("Warning: Invalid Receipt"));
      }else {
         checkCredentials(request.results); // pursue more fancy checks
      }
   };
   request.onerror = function() {
      alert("Error: " + request.error.name);
   };
}

function checkCredentials(app) {
   var appRecord = "";
   if (app) {
      var appObject = {};
      for (var name in app) appObject[name] = app[name];
      appRecord = JSON.stringify(appObject);
   }
   var xhr = new XMLHttpRequest({'mozSystem': true});
   xhr.withCredentials = true;
   xhr.open("POST", "http://myserver.jaxo.com?OP=checkReceipt", true);
   xhr.onreadystatechange = function () {
      if ((this.readyState === 4) &&
         (this.status !== 200) &&
         (this.status !== 0)
      ) {
         if (this.status === 402) { // Payment Required
            var obj = JSON.parse(this.responseText);
            alert("Warning: " + obj["msg"] + "\nInvalid Receipt"));
         }else {
            alert("Error: ", "Server code " + this.status);
         }
      }
   };
   xhr.send(appRecord);
}

Server Side

Assuming a Java Servlet handles the HTTP request, the introductory code might look something as:

import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import com.jaxo.mozutil.ReceiptVerifier.java;

@SuppressWarnings("serial")
public class MyServlet extends HttpServlet
{
   public void doPost(HttpServletRequest req, HttpServletResponse resp)
   throws IOException
   {
      String op = req.getParameter("OP");
      if (op.equals("checkReceipt")) {
         InputStream in = req.getInputStream();
         String appRecord = IOUtils.toString(in);
         IOUtils.closeQuietly(in);
         ReceiptVerifier.Status status = ReceiptVerifier.verify(appRecord);
         if (status == ReceiptVerifier.Status.OK) {
            resp.setStatus(HttpServletResponse.SC_OK);
         }else {
            resp.setStatus(HttpServletResponse.SC_PAYMENT_REQUIRED);
         }
      }else {
         resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
      }
   }
}

ReceiptVerifier is the class that performs the verification of the Receipt, and here is the source code:
ReceiptVerifier.java (which makes use of Json.java.)

The verification is made of two steps:

  1. first step does not involve the "Mozilla Marketplace": it just consists to locally1 examine the coherency of miscellaneous fields of the receipts;
  2. then, if the first step succeeded, the Marketplace is called for an ultimate authentication that the receipt does exist, and that the purchase was not refunded.
  1. Take “Local” in the sense “not involving the Mozilla Marketplace”. This first step may cope at comparing our own secret numbers, not necessarily known at the Marketplace level.  From my experience, 90% of invalid receipt will be detected at this stage.

Testing

This chapter would be incomplete without saying a word on "testing".  Why just "a word"?
Simply because Firefox OS Simulator 4.0 has it all.  It is well documented.  Using it will fulfill all your needs.

You may get the latest version in here.