Snowshoe StampWe're all familiar with the notion of using an ink stamp to provide some kind of proof that a transaction took place or that a person was at a location. These stamps run the formality gamut from the not-so-secure (think hand stamps at bars) to the very secure (stamped passports at immigration). A company from Madison, WI, SnowShoe Stamp, is attempting to bring that same mechanism into the digital realm.

Snowshoe StampUsing SnowShoe's small, plastic 3d-printed stamps (pictured to the left), a user touches the screen of her smartphone, and the unique id embedded in the stamp is confirmed via SnowShoe's servers. A smartphone app or website can use this confirmation as proof that the user participated in whatever transaction the stamp/app pair is guaranteeing. Some obvious example uses are customer loyalty programs or software piracy protection. Similar technology is used in consumer-oriented applications like Disney's AppMates, which allow physical toys to interact with tablet screens. We're going to build a Salesforce1 mobile app that uses SnowShoe's technology to confirm a user's attendance at a corporate training program.

SnowShoe Stamp Overview

SnowShoe Stamp works on websites via a javascript library. This post, from the SnowShoe site, does a great job of explaining things in detail. I want to highlight some key points, and how my implementation is different because of the nature of the Salesforce1 Platform. Snowshoe Flow

In the image above, you can see that the normal procedure is for the “stamp screen”, the screen the user actually touches with the plastic stamp, to be hosted on SnowShoe's servers. I didn't want that for a couple reasons: I wanted a custom look, and more importantly, redirects and the like would break the Salesforce1 app's navigation. So I made the stamp screen a local Visualforce page, with SnowShoe-supplied javascript to help interface with their systems.

The other key difference is in step four, where the traditional implementation actually POSTs to a callback URL specified when you create your SnowShoe app in the SnowShoe site's dev console. Again, POSTing anywhere would start to break the navigation flow, and I can make things cleaner anyway by using Javascript remoting to send the stamp data to a method in our Apex controller, to be manipulated there. SF1 Snowshoe Flow

App Overview

Our app will run from a Menu Item on the Navigation Menu inside the Salesforce1 mobile app, and will provide the user with a (fake, non-functioning) list of pretend training sessions, and a button to confirm attendance at a session. After pressing this button, the user will see a screen instructing him to present his phone to the training session administrator. In our example scenario, the administrator will touch a specific SnowShoe stamp to the phone's screen, and a record of the user's attendance will be saved in Salesforce. Here's a quick demonstration of the flow.

To accomplish this, we'll need a Course object, a Course Complete junction object (between Course and User), and some Visualforce pages and Apex controllers. Let's dive in.

Implementation

Go ahead and install the package I've created so you can easily follow along.

Before we do anything else, we need to order a developer kit from SnowShoe. This will take a few days to arrive. Next, we need to register a new app at SnowShoe. First, create a new account, then create a new app. The only field that's important in our new app is the name, and that only for reference purposes. None of the other fields will be used in our implementation. Once the app is saved, note the Application Key and App Secret. Enter those in the appropriate places in the SnowShoe_Creds__c Custom Setting as an Org Wide Default.

Our Course object will use the standard Name field, and a custom text field called Course Stamp Id, which will contain a specific identification number, associated with a specific Snowshoe stamp. This Course Stamp Id field is how we'll eventually make the connection between the stamp touched to the screen and the specific course a user has completed.

We'll need a Visualforce page for the “front end” of our training app to present our list of fake courses, and the button to get to the “stamp” page. This stamp page will be another VF page, this time with a controller implementing a remote action method, used to verify the authenticity of a touched stamp. Finally, we'll have a nice “My Completed Courses” VF page and controller to show our successful stamp actions (and the resulting Course Complete records).

Let's look at the meat of the application, the VF stamp page (using @joshbirk”˜s awesome OneStarter UI helper):

<apex:page showHeader="false" sidebar="false" controller="TrainingCheckinCtrl" docType="html-5.0" applyBodyTag="false" applyHtmlTag="false" >
<html>
    <head>
        <title></title>
		<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    </head>
    <body>
        <div id="background" style="padding-top:70px;text-align:center;">
        	<img src="http://upload.wikimedia.org/wikipedia/en/thumb/7/7e/Oldacmelogo.png/200px-Oldacmelogo.png"/><br /><br/>
        	<span style="font-size:22px;">Present your phone to the instructor<br/>to confirm training attendence.</span>
        </div><!--Replace this div with whatever you want-->
        <div id="result" style="display:none; padding-top:70px;text-align:center;"><span  style="font-size:22px;" id="resultText"></span></div>
    </body>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript" src="{!URLFOR($Resource.Snowshow, 'touchy.js')}"></script>
    <script type="text/javascript" src="{!URLFOR($Resource.Snowshow, 'sss.client.js')}"></script>
<script type="text/javascript" src="{!URLFOR($Resource.Snowshow, 'sss.util.js')}"></script>
<script type="text/javascript" src="{!URLFOR($Resource.Snowshow, 'jquery.json-2.4.min.js')}"></script>
<script>
var body = document.getElementsByTagName("body")[0];
var errorStart;
var errorTime = 0;
var insufficientCount = 0;
var touchDone = false;
var timeouts={};
var errorURL ="/apex/StampError";//set this to a URL you want to direct to for incompatible devices
var submitted=false;
var snd=new Audio('{!URLFOR($Resource.CourseAlert)}');
 
Touchy(body, {
// SnowShoe-provided javascript removed for brevity
 
    five: function (hand, finger1, finger2, finger3, finger4, finger5) {
        hand.on('start', function (points) {
            if (!submitted) {
            	snd.play();
            	submitted=true;
	            data=[];
	            for(x in points){
	                data[x] = [points[x]["x"], points[x]["y"]];
	            }
 
			 	TrainingCheckinCtrl.verifyTouch(sss.util.base64.encode($.toJSON(data)), function(result, event){
			 		if (event.status) {
	          			$("#background").hide();
	          			if (result.success)
		          			$("#resultText").html('You have attended '+result.success);
		          		else
		          			$("#resultText").html('These was an error: '+result.error);
	          			$("#result").show();
			 		}
			 	});
            }
        });
    },
 
    any:function(hand, finger){
        finger.on('start', function(){
           incrementCount();
        });
 
    }
 
});
</script>
</html>
 
</apex:page>

Most of the javascript is straight from the SnowShoe-provided self-hosted stamp page STAMP-SCREEN SDK code. I've removed it for brevity. The main changes are in the Touchy.five key's method. Here, I've removed the POST to the callback URL originally implemented by SnowShoe, and instead made a call to the “verifyTouch” remote action in the Apex controller:

@RemoteAction
public static Map<String,String> verifyTouch(String data) {
    OAuthUtility oa = new OAuthUtility();

	Http h = new Http();
	HttpRequest ssReq = new HttpRequest();
	ssReq.setMethod('POST');
	ssReq.setBody('data='+EncodingUtil.urlEncode(data,'UTF-8'));
	ssReq.setHeader('Content-Type', 'application/x-www-form-urlencoded');
	ssReq.setEndpoint(OAuthUtility.getSSEndpoint()+'/stamp');
	oa.sign(ssReq);
	HttpResponse ssRes;
	try {
		ssRes = h.send(ssReq);
	}
	catch (CalloutException e) {
		return new Map<String, String>{'error'=>e.getMessage()};
	}

	stampReturn r = (stampReturn)JSON.deserializeStrict(ssRes.getBody(), stampReturn.class);

	if (r.stamp != null && r.stamp.containsKey('serial')) {
		system.debug(r.stamp.get('serial'));
		Course__c course;
		try {
			course = [select id, Name from Course__c where Course_Stamp_Id__c = :r.stamp.get('serial')];
		}
		Catch (Exception e) {
			return new Map<String, String?{'error'=>e.getMessage()};
		}
		Completed_Course__c cc=new Completed_Course__c(Attendee__c = UserInfo.getUserId(), Course__c=course.id, name=course.Name);
		insert cc;
		return new Map<String, String>{'success'=>course.Name};
	}
	return new Map<String, String>{'error'=>'Invalid touch id'};
}

This is where the magic really happens. First, we build our callout with the points data provided by the SnowShoe javascript. Then we give it an OAuth 1.0 signature using code lifted from Pat Patterson's (@metadaddy) sfdc-oauth-playground. Once we get everything signed, we make the callout to the SnowShoe API server.

Assuming things went well, we'll get a JSON response containing the id of the stamp that was touched to the screen. If we can match that id to the “Course Stamp Id” field of an existing Course record, we create a new Completed Course record, looking up to the matched Course record, and to the running user. Back in the SF1 app, the user is notified that she has successfully “completed” the course.

One other kind of cool thing I wanted to point out. In line 29 of the Visualforce page, you can see where I declared a “snd” variable, and in line 37 I called the play() method on it. This is using audio support in HTML5, and it's supported in the Salesforce1 app! At least on the iPhone. I simply saved an mp3 file as a static resource, and used URLFor to feed it to the “new Audio” command.

Wrap Up

The obvious hole in this scenario is that a user could just create his own Completed Course record, and go to the beach instead of the training. If we were serious about implementing this for real, we'd use triggers and static variables to prevent the creation or modification of Completed Course records outside of specific code. And we'd probably even wrap everything in a managed package so a REALLY determined user couldn't write some Apex of her own to manually set those static variables.

Again, the code is available as an installable package, so grab it, and a developer org, and give things a try. Let me know how it goes.

I think this is a really neat technology with a wealth of possible applications. The folks at SnowShoe have made the implementation flexible enough that it is fairly straightforward to integrate right in to Salesforce1. I can't wait to see what other folks come up with.