{"id":505,"date":"2013-02-14T11:36:59","date_gmt":"2013-02-14T03:36:59","guid":{"rendered":"http:\/\/passkit.com\/blog\/?p=505"},"modified":"2024-10-12T13:34:29","modified_gmt":"2024-10-12T13:34:29","slug":"create-passbook-pass-for-lessons-using-cakephp","status":"publish","type":"post","link":"https:\/\/passkit.com\/blog\/create-passbook-pass-for-lessons-using-cakephp\/","title":{"rendered":"PassKit integration with Passbook Pass Lesson Management System (in CakePHP)"},"content":{"rendered":"<p>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 &#8211; even if you don&#8217;t run a rocking guitar lesson business &#8211; and then check out the cool demo video at the end. Over to you Patrick<\/p>\n<h2>How Rock Kong integrated PassKit into their Lesson Management System<\/h2>\n<h1><strong><strong><span style=\"text-decoration: underline;\">Objectives:<\/span><br \/>\n<\/strong><\/strong><\/h1>\n<ul>\n<li>Be first to Hong Kong market with a Passbook Lesson Pass<\/li>\n<li>Provide \u2018ultimate convenience\u2019 to Rock Kong\u2019s customers:\n<ul>\n<li>remove the need for paper<\/li>\n<li>remove the need for remembering how many lessons are left, and;<\/li>\n<li>make use of every student having a smartphone<\/li>\n<li>give every student their very own \u2018Lesson Pass\u2019<\/li>\n<\/ul>\n<\/li>\n<li>Develop for less than HK$5000 and, have a maintenance cost of less than HK$500 per month<\/li>\n<li>Increase teaching time for tutors, by removing \u2018admin\u2019 for tutors<\/li>\n<li>Attract more customers by having a \u2018cool\u2019 marketing campaign (and improved SEO results)<\/li>\n<\/ul>\n<h1><span style=\"text-decoration: underline;\">Background<\/span><\/h1>\n<p>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&#8217;s an internal system.<br \/>\nWhen I first looked at Passbook I saw an opportunity to solve a few of the above problems:<strong><strong><br \/>\n<\/strong><\/strong><\/p>\n<ol>\n<li>With a lesson pass, we could get rid of having &#8216;lesson packages&#8217; in our system, and every student will get their own pass with a lesson balance<\/li>\n<li>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<\/li>\n<li>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\u00a0Paypal\u00a0 Again, this would be a nice time saver for admin work<\/li>\n<\/ol>\n<p>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.<br \/>\n<strong><br \/>\n<\/strong>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.<\/p>\n<h1><span style=\"text-decoration: underline;\">Method<\/span><\/h1>\n<p>Here are the steps to follow:<br \/>\n<span style=\"text-decoration: underline;\">Step 1:<\/span><br \/>\nRegister for an account with <a title=\"Passkit\" href=\"http:\/\/www.passkit.com\" target=\"_blank\" rel=\"noopener\">PassKit<\/a> to obtain an API key and secret.<br \/>\n<span style=\"text-decoration: underline;\">Step 2:<\/span><br \/>\nDesign a lesson pass template in the <a href=\"http:\/\/app.passkit.com\">PassKit Pass Designer.<\/a><br \/>\nYou 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 \u00a0have 4 dynamic fields. \u00a0<strong><strong><br \/>\n<\/strong><\/strong><\/p>\n<ul>\n<li>Student name;<\/li>\n<li>Issue\/update date;<\/li>\n<li>Expiry date;<\/li>\n<li>Balance;<\/li>\n<\/ul>\n<p>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\u2019s pretty easy to make the change).<\/p>\n<p><span style=\"text-decoration: underline;\">Step 3:<\/span><br \/>\nConnect with the PassKit API<br \/>\nI 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.<br \/>\nCakePHP component code (requires the PassKit PHP class to be in your Vendor directory):<\/p>\n<pre>&lt;?php\nApp::uses('Component', 'Controller');\nApp::import('Vendor', 'PassKit', array('file' =&gt; 'Passkit\/PassKit.php'));\nclass PasskitComponent extends Component {\n\/\/ Passkit key\nprivate $pk_key;\n\/\/ Passkit secret\nprivate $pk_secret;\npublic function __construct(ComponentCollection $collection, $settings = array())\n{\nif(isset($settings[\"pk_key\"]))\n{\n$this-&gt;pk_key = $settings[\"pk_key\"];\n}\nif(isset($settings[\"pk_secret\"]))\n{\n$this-&gt;pk_secret = $settings[\"pk_secret\"];\n}\n}\n\/**\n* Returns pass details on succesful issue\n*\/\npublic function issuePass($pass_template, $pass_data) {\n$pk = new PassKit($this-&gt;pk_key, $this-&gt;pk_secret, false);\n$pass_details = $pk-&gt;issue_pass($pass_template, $pass_data);\nreturn $pass_details;\n}\n\/**\n* Returns true on succesful update\n*\/\npublic function updatePass($pass_serial, $pass_template, $pass_data)\n{\n$pk = new PassKit($this-&gt;pk_key, $this-&gt;pk_secret, false);\n\/\/ Check if valid pass\nif(!$pk-&gt;set_pass_serial($pass_serial, $pass_template))\n{\necho 'This is not a valid Pass ID.';\nreturn;\n}\n\/\/ validate the pass with PassKit\nif(!$pk-&gt;pass_validate()){\necho 'This pass is not valid for this PassKit account.';\nreturn;\n}\nreturn $pk-&gt;pass_update($pass_data);\n}\n\/**\n* Get all pass information based upon pass id (which we get via custom URL)\n*\/\npublic function getPassDetails($pass_id)\n{\n$pk = new PassKit($this-&gt;pk_key, $this-&gt;pk_secret, false);\n\/\/ Check if valid pass\nif(!$pk-&gt;set_pass_id($pass_id))\n{\necho 'This is not a valid Pass ID.';\nreturn;\n}\n\/\/ validate the pass with PassKit\nif(!$pk-&gt;pass_validate()){\necho 'This pass is not valid for this PassKit account.';\nreturn;\n}\n\/\/ Get pass details and return\nreturn $pk-&gt;get_pass_details();\n}\n}\n?&gt;<\/pre>\n<p>The component only supports the API calls that I needed when I was integrating Passbook with my system:<\/p>\n<ul>\n<li>issuePass &#8211; Issues a new pass;<\/li>\n<li>updatePass &#8211; Updates an existing pass;<\/li>\n<li>getPassDetails &#8211; Returns the pass details for an existing pass;<\/li>\n<\/ul>\n<p>To add the PassKitt Component into the CakePHP controller, I use the following in the controller:<\/p>\n<pre>public $components = array('Passkit' =&gt; array(\n'pk_key' =&gt; \u2018key\u2019,\n'pk_secret' =&gt; \u2018secret\u2019\n));<\/pre>\n<p><span style=\"text-decoration: underline;\">Step 4:<\/span><br \/>\nLink the internal records with PassKit\u2019s records.<br \/>\nSo, 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:<\/p>\n<pre>\/\/ Internal saving happens here (the CakePHP way).. You can replace this by\n\/\/ any MySql that you use to save your internal records. Note that\n\/\/ $this-&gt;data contains data that comes from a web form.\n$this-&gt;LessonPass-&gt;save($this-&gt;data);\n\/\/ Now we prepare the data array as to be saved via PassKit\u2019s API (note that\n\/\/ the key\u2019s in the array are my field names in the pass template:\n$pass_data[\"Student name\"] = $this-&gt;data[\u201cstudent\u201d];\n$pass_data[\"Balance\"] = $this-&gt;data[\"balance\"];\n$pass_data[\"Issue date\"] = $this-&gt;data[\"issue_date\"];\n$pass_data[\"Expiry date\"] = $this-&gt;data[\"expiry_date\"];\n\/\/ We want to embed an update URL in the bar-code, so that when one of my\n\/\/ teachers scans the pass it immediately gives them the correct update link\n\/\/ that updates the pass via our internal Lesson Management System. %pid\n\/\/ will embed the pass id.\n$pass_data[\"barcodeContent\"] = \"http:\/\/www.mysystem.com\/update?p=%pid\";\n\/\/ Issue the pass\n$pass_details = $this-&gt;Passkit-&gt;issuePass($pass_template, $pass_data);\n\/\/ If successful we update our internal record with the pass serial\n\/\/ and pass url\nif(isset($pass_details-&gt;success) &amp;&amp; $pass_details-&gt;success == 1)\n{\n$data[\u201cpasskit_serial\u201d] = $pass_details-&gt;serial;\n$data[\u201cpasskit_url\u201d] = $pass_details-&gt;url;\n$this-&gt;LessonPass-&gt;save($data);\n}\n\/\/ After issuing of the passkit we can display\/send\/sms the pass URL to the user.<\/pre>\n<p><span style=\"text-decoration: underline;\">Step 5:<\/span><br \/>\nAfter 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.<br \/>\nThe update URL to update the pass has been embedded in the barcode, so that means that when a teacher scan\u2019s 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.<br \/>\nThe code to deduct a value from a lesson pass looks like this:<\/p>\n<pre>\/\/ $passid contains the pass-id that comes in via the GET request\n\/\/ Lookup the pass via the API. If it\u2019s a valid pass then $pass_details will be\n\/\/ set\n$pass_details = $this-&gt;Passkit-&gt;getPassDetails($passId);\n$lesson_pass = null;\nif($pass_details != null)\n{\n\/\/ Look up the pass in local DB, so we can match balance (make sure\n\/\/ it\u2019s legit). The local pass record has the pass serial and template.\n\/\/ These two make a pass unique as well\n$lessonPass = $this-&gt;LessonPass-&gt;find(\"first\", array(\n\"recursive\" =&gt; -1,\n\"conditions\" =&gt; array(\n\"passkit_serial\" =&gt; $pass_details[\"serial_number\"],\n\"passkit_template\" =&gt; $pass_details[\"template_name\"],\n\"balance\" =&gt; $pass_details[\"pass_data\"][\"Balance\"]\n )\n ));\n\/\/ If we can't find the pass, that means someone messed with the\n\/\/ balance and things didnt go through our system, so exit\nif($lessonPass == null)\n{\nthrow new NotFoundException(__('Invalid lesson pass'));\n}\n}\nelse\n{\n throw new NotFoundException(__('Invalid lesson pass'));\n}\n\/\/ If $lesson_pass != null that means the pass exists and is valid. So we can\n\/\/ continue to deduct a lesson\n\/\/ First book the lesson record in the local system (this will also deduct\n\/\/ it from the local system\n$data[\"LessonRecord\"][\"user_id\"] = AuthComponent::user(\"id\"); \/\/ Teacher $data[\"LessonRecord\"][\"lesson_date\"] = date(\"Y-m-d\"); $data[\"LessonRecord\"][\"lesson_pass_id\"] = $lessonPass[\"LessonPass\"][\"id\"];\n$data[\"LessonRecord\"][\"balance\"] = $lessonPass[\u201cLessonPass\u201d][\u201cbalance\u201d] - 1;\n$data[\"LessonRecord\"][\"remarks\"] = \"Booked via API. \";\n$this-&gt;LessonRecord-&gt;save($data);\n\/\/ Now book it via the PassKit API, so the student\u2019s pass gets updated\n$passData[\"Balance\"] = $data[\"LessonRecord\"][\"balance\"];\n$result = $this-&gt;Passkit-&gt;updatePass(pass_details[\u201cserial_number\u201d], $passkit_details[\u201ctemplate_name\u201d], $passData);\n\/\/ Check if pass was updated OK\nif($result === true)\n{\necho \"Passkit update OK.\";\n}<\/pre>\n<p><span style=\"text-decoration: underline;\">Results:<\/span><br \/>\nWatch this video to see:<\/p>\n<ul>\n<li>How cool the lesson Pass is for the customer &#8211; I love the real time updates;<\/li>\n<li>How easy and convenient it is for the tutor to simply scan the Pass and auto update the lesson balance &#8211; hoorah no more scrappy note books;<\/li>\n<li>How the lesson management system is updated automatically and reduces the amount of administrative overhead for Rock Kong management<\/li>\n<\/ul>\n<p>This lesson Pass is just being implemented as a pilot but we will come back in 1 month to update on the success. \u00a0Initial indications from students is they love it and want it now! \u00a0I 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.<\/p>\n<p>Rock on.<br \/>\nRock Kong<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":13751,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15],"tags":[105,106,162,163,170,186,187,227,230,231,249,258,260,266,268,319],"class_list":["post-505","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-create-passbook-passes","tag-creating-innovative-passbook-pass","tag-how-to-use-passbook","tag-how-to-use-passkit","tag-innovative-passes","tag-lesson-management-system","tag-lesson-pass","tag-pass-designer","tag-pass-kit","tag-pass-kit-api","tag-passbook-ideas","tag-passbook-management","tag-passbook-notifications","tag-passbook-solutions","tag-passbook-tutorials","tag-start-using-passbook"],"_links":{"self":[{"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/posts\/505","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/comments?post=505"}],"version-history":[{"count":2,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/posts\/505\/revisions"}],"predecessor-version":[{"id":18198,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/posts\/505\/revisions\/18198"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/media\/13751"}],"wp:attachment":[{"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/media?parent=505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/categories?post=505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/passkit.com\/blog\/wp-json\/wp\/v2\/tags?post=505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}