PassKit integration with Passbook Pass Lesson Management System (in CakePHP)

Patrick Kosterman

Patrick Kosterman

Need a bespoke wallet solution? Patrick works with clients to deliver exactly what they need.
Share on facebook
Share on twitter
Share on linkedin

We are very honoured to have Patrick Kosterman, co-Founder of Rock Kong, writing a special guest post for us this week. He has recently used the PassKit API to implement Lesson Passes for his students. He tells us what he set about to do and how he achieved this. We thoroughly recommend you read through this whole article – even if you don’t run a rocking guitar lesson business – and then check out the cool demo video at the end. Over to you Patrick

How Rock Kong integrated PassKit into their Lesson Management System

Objectives:

  • Be first to Hong Kong market with a Passbook Lesson Pass
  • Provide ‘ultimate convenience’ to Rock Kong’s customers:
    • remove the need for paper
    • remove the need for remembering how many lessons are left, and;
    • make use of every student having a smartphone
    • give every student their very own ‘Lesson Pass’
  • Develop for less than HK$5000 and, have a maintenance cost of less than HK$500 per month
  • Increase teaching time for tutors, by removing ‘admin’ for tutors
  • Attract more customers by having a ‘cool’ marketing campaign (and improved SEO results)

Background

When running our music school we ran into a couple of problems. First, we have our own Lesson Management System (LMS), that we use to book lessons and register lesson packages for the students. This is something that is consuming more and more time, since we have a few teachers that work for us and quite some students. Secondly, upon till now we have not allowed students access to the LMS, since it’s an internal system.
When I first looked at Passbook I saw an opportunity to solve a few of the above problems:

  1. With a lesson pass, we could get rid of having ‘lesson packages’ in our system, and every student will get their own pass with a lesson balance
  2. For the users that have Passbook, they can have a lesson pass on their phone that our teachers can just scan to deduct the lessons. This would save quite some admin work
  3. Students can see how many lessons they still have left, and in future we could even offer students the opportunity to immediately top up their passes via Paypal  Again, this would be a nice time saver for admin work

To implement the above points in a way that would save me from doing all the manual admin work, I would have to integrate Passbook with our own LMS.

To integrate Passbook with the LMS, I made use of the PassKit API. I found their framework very easy to use, and the API is clearly documented and written.

Method

Here are the steps to follow:
Step 1:
Register for an account with PassKit to obtain an API key and secret.
Step 2:
Design a lesson pass template in the PassKit Pass Designer.
You might choose to use a membership pass style so every person could upload their own photo, but I chose to use an Event Ticket with a Blurred Background (because it just looked cooler) and I chose to  have 4 dynamic fields.  

  • Student name;
  • Issue/update date;
  • Expiry date;
  • Balance;

Make sure that you select these as dynamic but not something that the student can update themselves (there are check boxes in the Pass designer so it’s pretty easy to make the change).

Step 3:
Connect with the PassKit API
I wrote a CakePHP component that uses the PassKit PHP class to connect with the API. You can download the PassKit PHP class for free from their API Wiki.
CakePHP component code (requires the PassKit PHP class to be in your Vendor directory):

<?php
App::uses('Component', 'Controller');
App::import('Vendor', 'PassKit', array('file' => 'Passkit/PassKit.php'));
class PasskitComponent extends Component {
// Passkit key
private $pk_key;
// Passkit secret
private $pk_secret;
public function __construct(ComponentCollection $collection, $settings = array())
{
if(isset($settings["pk_key"]))
{
$this->pk_key = $settings["pk_key"];
}
if(isset($settings["pk_secret"]))
{
$this->pk_secret = $settings["pk_secret"];
}
}
/**
* Returns pass details on succesful issue
*/
public function issuePass($pass_template, $pass_data) {
$pk = new PassKit($this->pk_key, $this->pk_secret, false);
$pass_details = $pk->issue_pass($pass_template, $pass_data);
return $pass_details;
}
/**
* Returns true on succesful update
*/
public function updatePass($pass_serial, $pass_template, $pass_data)
{
$pk = new PassKit($this->pk_key, $this->pk_secret, false);
// Check if valid pass
if(!$pk->set_pass_serial($pass_serial, $pass_template))
{
echo 'This is not a valid Pass ID.';
return;
}
// validate the pass with PassKit
if(!$pk->pass_validate()){
echo 'This pass is not valid for this PassKit account.';
return;
}
return $pk->pass_update($pass_data);
}
/**
* Get all pass information based upon pass id (which we get via custom URL)
*/
public function getPassDetails($pass_id)
{
$pk = new PassKit($this->pk_key, $this->pk_secret, false);
// Check if valid pass
if(!$pk->set_pass_id($pass_id))
{
echo 'This is not a valid Pass ID.';
return;
}
// validate the pass with PassKit
if(!$pk->pass_validate()){
echo 'This pass is not valid for this PassKit account.';
return;
}
// Get pass details and return
return $pk->get_pass_details();
}
}
?>

The component only supports the API calls that I needed when I was integrating Passbook with my system:

  • issuePass – Issues a new pass;
  • updatePass – Updates an existing pass;
  • getPassDetails – Returns the pass details for an existing pass;

To add the PassKitt Component into the CakePHP controller, I use the following in the controller:

public $components = array('Passkit' => array(
'pk_key' => ‘key’,
'pk_secret' => ‘secret’
));

Step 4:
Link the internal records with PassKit’s records.
So, every time a lesson pass is created in my internal system, it also creates the pass via the PassKit API, and stores the pass-id and pass-url in my internal database. See below code snippet for an example:

// Internal saving happens here (the CakePHP way).. You can replace this by
// any MySql that you use to save your internal records. Note that
// $this->data contains data that comes from a web form.
$this->LessonPass->save($this->data);
// Now we prepare the data array as to be saved via PassKit’s API (note that
// the key’s in the array are my field names in the pass template:
$pass_data["Student name"] = $this->data[“student”];
$pass_data["Balance"] = $this->data["balance"];
$pass_data["Issue date"] = $this->data["issue_date"];
$pass_data["Expiry date"] = $this->data["expiry_date"];
// We want to embed an update URL in the bar-code, so that when one of my
// teachers scans the pass it immediately gives them the correct update link
// that updates the pass via our internal Lesson Management System. %pid
// will embed the pass id.
$pass_data["barcodeContent"] = "http://www.mysystem.com/update?p=%pid";
// Issue the pass
$pass_details = $this->Passkit->issuePass($pass_template, $pass_data);
// If successful we update our internal record with the pass serial
// and pass url
if(isset($pass_details->success) && $pass_details->success == 1)
{
$data[“passkit_serial”] = $pass_details->serial;
$data[“passkit_url”] = $pass_details->url;
$this->LessonPass->save($data);
}
// After issuing of the passkit we can display/send/sms the pass URL to the user.

Step 5:
After the pass has been created and the student installed the pass on their phone, deduct the lesson and save a record in our internal system by scanning the pass.
The update URL to update the pass has been embedded in the barcode, so that means that when a teacher scan’s the pass, it automatically forwards the request with pass-id to our internal system. The teacher is required to login to our internal system first before he is able to deduct anything from the pass.
The code to deduct a value from a lesson pass looks like this:

// $passid contains the pass-id that comes in via the GET request
// Lookup the pass via the API. If it’s a valid pass then $pass_details will be
// set
$pass_details = $this->Passkit->getPassDetails($passId);
$lesson_pass = null;
if($pass_details != null)
{
// Look up the pass in local DB, so we can match balance (make sure
// it’s legit). The local pass record has the pass serial and template.
// These two make a pass unique as well
$lessonPass = $this->LessonPass->find("first", array(
"recursive" => -1,
"conditions" => array(
"passkit_serial" => $pass_details["serial_number"],
"passkit_template" => $pass_details["template_name"],
"balance" => $pass_details["pass_data"]["Balance"]
 )
 ));
// If we can't find the pass, that means someone messed with the
// balance and things didnt go through our system, so exit
if($lessonPass == null)
{
throw new NotFoundException(__('Invalid lesson pass'));
}
}
else
{
 throw new NotFoundException(__('Invalid lesson pass'));
}
// If $lesson_pass != null that means the pass exists and is valid. So we can
// continue to deduct a lesson
// First book the lesson record in the local system (this will also deduct
// it from the local system
$data["LessonRecord"]["user_id"] = AuthComponent::user("id"); // Teacher $data["LessonRecord"]["lesson_date"] = date("Y-m-d"); $data["LessonRecord"]["lesson_pass_id"] = $lessonPass["LessonPass"]["id"];
$data["LessonRecord"]["balance"] = $lessonPass[“LessonPass”][“balance”] - 1;
$data["LessonRecord"]["remarks"] = "Booked via API. ";
$this->LessonRecord->save($data);
// Now book it via the PassKit API, so the student’s pass gets updated
$passData["Balance"] = $data["LessonRecord"]["balance"];
$result = $this->Passkit->updatePass(pass_details[“serial_number”], $passkit_details[“template_name”], $passData);
// Check if pass was updated OK
if($result === true)
{
echo "Passkit update OK.";
}

Results:
Watch this video to see:

  • How cool the lesson Pass is for the customer – I love the real time updates;
  • How easy and convenient it is for the tutor to simply scan the Pass and auto update the lesson balance – hoorah no more scrappy note books;
  • How the lesson management system is updated automatically and reduces the amount of administrative overhead for Rock Kong management

This lesson Pass is just being implemented as a pilot but we will come back in 1 month to update on the success.  Initial indications from students is they love it and want it now!  I would be interested to hear from anyone else that has implemented a similar lesson pass and please feel free to use my code in here to design and develop you own.

Rock on.
Rock Kong