App World Dynamic Licensing HOWTO
December 14, 2009 Leave a comment
I started releasing commercial applications on BlackBerry App World and yesterday was the first time I used the Dynamic Licensing model. I devised a solution based on Google App Engine and this post shows how this can be achieved and also includes sample source code for other developers in a similar situation. Dynamic Licensing provides a mechanism for granting activation codes to user who purchase applications. The system works like this:
As with almost all commercial, downloadable applications, online purchase and activation codes have become the norm. The end-user pays for the product, downloads it and waits for an activation code to be sent to his email address. Once he receives the code, he enters it into the app and he can begin using it. This is straightforward if the developer sells the app on his own store that is hosted on his own server. If he is to sell it on other stores, however, things become tricky. Usually, well established stores like MobiHand and others have a mechanism that allows a developer to interface with their storefront for various purposes. One of these instances is when activation codes are involved. In cases like these, here is a quick run-down of how the purchase flow works:
- End-user visits Online Store
- End-user buys app from the Online Store
- Online Store contacts the Developer Server (using an HTTP POST request) with end-user specific details
- Developer Server generates the activation key
- Developer Server responds to the Online Store POST request with an activation key
- Online Store then presents this activation key to the End-user
It is the Developer’s responsibility to get his activation server up and running to ensure steps 3-5 are completed. Sometimes this can be a painful task; especially after you’ve spent a few grueling weeks in developing your latest and greatest application. My solution is to setup a free account on Google App Engine to host my activation server routines there. Why bother you ask? Here are some advantages that I can see:
- App Engine is free and can easily be converted to a paid model (reasonably priced) based only on usage of resources
- App Engine offers Java so its more likely to be familiar to a BlackBerry developer
- No administrative overhead with setting up a server or maintaining it
- It’s cool if you like to get into “Cloud Computing”
- It’s very easy to move off Google App Engine and onto your own server when you’re ready because of the standardized modules
I’m not going to talk you through how to get an account, sign-up, etc. Google has tons of docs and tutorials on how to do so. I’ll assume you’ve already got your App Engine and know how to deploy apps on it. What we’re going to create is a servlet and optionally, some classes to help us persist the incoming information to the app engine database. If you’re using Eclipse with the Google App Engine plugin, then creating a project is simple. On Eclipse simply click File->New->Project and then choose Web Application Project under the Google folder. By default, a new project will be created with a servlet having the “doGet” method. This method handles only HTTP GET requests. You also need to create your own “doPost” method. This is usually because stores including App World will POST the data to your URL. What I do is just swap the doGet with doPost.
I then write my routines to handle the incoming request. I usually capture all relevant information, extract some key parameters, use it to generate my activation key and provide a response. For the sake of simplifying things. Here is the code for my servlet that captures information from BlackBerry App World, generates the Activation Code and responds with that code:
package net.zenconsult.keyserver; import java.io.IOException; import javax.jdo.PersistenceManager; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.zenconsult.codec.binary.Hex; @SuppressWarnings("serial") public class KeyServerServlet extends HttpServlet { public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // The RIM POST Format: // PIN=12341234&email=customeremail@email.com&product=product&version=1.2&transactionid=123&test=false String PIN = req.getParameter("PIN"); String email = req.getParameter("email"); String product = req.getParameter("product"); String version = req.getParameter("version"); String transactionId = req.getParameter("transactionid"); String test = req.getParameter("test"); String verify = req.getParameter("verify"); //Init vlaues if(email == null) { email = ""; } if(product == null) { product = ""; } if(version == null) { version = ""; } if(transactionId == null) { transactionId = ""; } if(test == null) { test = "true"; } // I realize using String.matches is taboo, but I did it anyway. Feel free to change it // Since you can also pass your own parameters, I am passing the "verify" parameter; security by obscurity, I know, // but when using App Engine, your Servlet is publicly accessible, so... if((PIN.matches("\\p{XDigit}{8}")) && (verify != null) && (verify.equalsIgnoreCase("bbappworld")) ) { // This is my own Key Generation Routine KeyGen kg = new KeyGen(PIN); String hexString = new String(Hex.encodeHex(kg.genKey())); String Key = hexString.toUpperCase(); if(test.equals("false")) { PersistenceManager pm = PMF.get().getPersistenceManager(); Entry entry = new Entry(email, product, version, transactionId); pm.makePersistent(entry); pm.close(); } resp.setContentType("text/plain"); resp.getWriter().println("key="+Key); } } }
If you paste the code above into your Eclipse, you will run into some errors. These are the typical ones and how they can be fixed:
- The KeyGen Class isn’t available. This is my own KeyGen routine for generating Activation Keys. It takes the BlackBerry PIN as an argument and does some magic on it. You have to write this one yourself.
- The Hex Class isn’t available. In my case, I wanted to work with Hex strings and convert them back and forth. I looked up the source to the Apache Commons Codec project and copied across the source code for the Hex class and all its dependencies into my own project. You do not need to use it, but if you do, then I advise you to look at the Apache Commons Project.
- The PMF class is missing. This class is used if you want to Persist or store data. The PMF class source code is standard and Google App Engine has the sample code for it here. If you need it, I’ve attached the code later on in this post.
As far as I recall this is what you will need to address. Now, lets move onto storing the incoming data on the database. I store each request in a class called an Entry. Thus, each POST request to my servlet will be checked and if it isn’t tagged as a test, then it will be stored to the database. The Entry object is marked as persistable and this makes it easy to store in the App Engine datastore. Here is the code for the Entry object:
package net.zenconsult.keyserver; import java.util.Date; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Entry { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String email; @Persistent private String product; @Persistent private String version; @Persistent private String transactionId; @Persistent private Date creationDate; public Entry(String email, String product, String version, String transactionId) { setEmail(email); setProduct(product); setVersion(version); setTransactionId(transactionId); setCreationDate(new Date()); } /** * @param key the key to set */ public void setKey(Key key) { this.key = key; } /** * @return the key */ public Key getKey() { return key; } /** * @param email the email to set */ public void setEmail(String email) { this.email = email; } /** * @return the email */ public String getEmail() { return email; } /** * @param product the product to set */ public void setProduct(String product) { this.product = product; } /** * @return the product */ public String getProduct() { return product; } /** * @param version the version to set */ public void setVersion(String version) { this.version = version; } /** * @return the version */ public String getVersion() { return version; } /** * @param transactionId the transactionId to set */ public void setTransactionId(String transactionId) { this.transactionId = transactionId; } /** * @return the transactionId */ public String getTransactionId() { return transactionId; } /** * @param creationDate the creationDate to set */ public void setCreationDate(Date creationDate) { this.creationDate = creationDate; } /** * @return the creationDate */ public Date getCreationDate() { return creationDate; } }
Once you get this up and running, you have about the simplest mechanism for working with Dynamic Licensing with not only BlackBerry App World, but other stores as well. You can, of course, make this as grandiose as you wish; that part is left as an exercise. I’m just giving you a point to start at.