diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5cd2992 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +composer.lock +/Certificates.p12 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d983c76 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# PHP Passkit class for iOS Wallet +This class provides the functionality to create a pass for Wallet in Apple's iOS 6 and newer on-the-fly. It creates, signs and packages the Pass as a .pkpass file according to Apple's documentation. + +## Requirements +* PHP 5.4 or higher +* PHP [ZIP Support](http://php.net/manual/en/book.zip.php) (may be installed by default) +* Access to filesystem (script must be able to create temporary folders) + + +## Installation +#### Composer +Run: `$ composer require pkpass/pkpass` + +or add to your composer.json: `"pkpass/pkpass": "^1.0.0"` + +#### Manual +Require PKPass.php file in your php files `require('src/PKPass.php');` + + +## Usage +Please take a look at the example.php file for example usage. For more info on the JSON for the pass and how to style it, take a look at the [docs at developers.apple.com](https://developer.apple.com/library/ios/documentation/UserExperience/Reference/PassKit_Bundle/Chapters/Introduction.html). + +### Requesting the Pass Certificate +1. Go to the [iOS Provisioning portal](https://developer.apple.com/account/ios/identifier/passTypeId). +2. Create a new Pass Type ID, and write down the Pass ID you choose, you'll need it later. +3. Click the edit button under your newly created Pass Type ID and generate a certificate according to the instructions shown on the page. +4. Download the .cer file and drag it into Keychain Access. +5. Find the certificate you just imported and click the triangle on the left to reveal the private key. +6. Select both the certificate and the private key under it, then right click the certificate in Keychain Access and choose `Export 2 items…`. +6. Choose a password and export the file to a folder. + +### Getting the example.php sample to work +1. Request the Pass certificate (`.p12`) as described above and upload it to your server. +2. Set the correct path and password on [line 22](examples/example.php#L22). +3. Change the `passTypeIdentifier` and `teamIndentifier` to the correct values on lines [29](examples/example.php#L29) and [31](examples/example.php#L31) (`teamIndentifier` can be found on the [Developer Portal](https://developer.apple.com/account/#/membership)). + +After completing these steps, you should be ready to go. Upload all the files to your server and navigate to the address of the examples/example.php file on your iPhone. + + +## Included demos +* [Simple example](examples/example.php) +* [Flight ticket example](examples/full_sample/), also [online here](pkpass.dev.scholica.com/examples/full_sample/) +* [Starbucks card example](examples/starbucks_sample/), also [online here](pkpass.dev.scholica.com/examples/starbucks_sample/) + + +## Debugging passes +If you aren't able to open your pass on an iPhone, plug the iPhone into a Mac and open the 'Console' application. On the left, you can select your iPhone. You will then be able to inspect any errors that occur while adding the pass: + +![Console with Passkit error](https://s3-eu-west-1.amazonaws.com/tsfil/Screen-Shot-2017-04-29-01-32-14-SrVhh/Screen-Shot-2017-04-29-01-32-14.png) + + +## Support & documentation +Please read the instructions above and consult the [Passbook Documentation](https://developer.apple.com/passbook/) before submitting tickets or requesting support. It might also be worth to [check Stackoverflow](http://stackoverflow.com/search?q=%22PHP-PKPass%22), which contains quite a few questions about this library. + +Email me at thomas [at] scholica.com or tweet me [@tschoffelen](http://www.twitter.com/tschoffelen). \ No newline at end of file diff --git a/composer.json b/composer.json index d66fb9d..fd5bced 100644 --- a/composer.json +++ b/composer.json @@ -1,23 +1,32 @@ { - "name": "pkpass/pkpass", - "description": "PHP PKPass class for iOS Wallet", - "keywords": ["PHP","apple","ios","iphone","ipad","passbook"], - "homepage": "https://github.com/tschoffelen/PHP-PKPass", - "type": "library", - "authors": [ - { - "name": "Tom Schoffelen", - "homepage": "http://www.tomttb.com", - "email": "tom@tomttb.com" - } - ], - "require": { - "php": ">=5.4", - "ext-zip": "*" - }, - "autoload": { - "psr-4": { - "PKPass\\": "" - } - } + "name": "pkpass/pkpass", + "license": "MIT", + "description": "PHP PKPass class for iOS Wallet", + "keywords": [ + "PHP", + "apple", + "ios", + "iphone", + "ipad", + "passbook", + "wallet" + ], + "homepage": "https://github.com/tschoffelen/PHP-PKPass", + "type": "library", + "authors": [ + { + "name": "Thomas Schoffelen", + "homepage": "http://thomasschoffelen.com/", + "email": "thomas@scholica.com" + } + ], + "require": { + "php": ">=5.4", + "ext-zip": "*" + }, + "autoload": { + "psr-4": { + "PKPass\\": "src" + } + } } diff --git a/examples/example.php b/examples/example.php index e87dc2e..ed67782 100644 --- a/examples/example.php +++ b/examples/example.php @@ -1,89 +1,85 @@ setCertificate('../Certificate.p12'); // 1. Set the path to your Pass Certificate (.p12 file) -$pass->setCertificatePassword('test123'); // 2. Set password for certificate -$pass->setWWDRcertPath('../AppleWWDRCA.pem'); // 3. Set the path to your WWDR Intermediate certificate (.pem file) +// Replace the parameters below with the path to your .p12 certificate and the certificate password! +$pass = new PKPass('../Certificates.p12', 'password'); -// Top-Level Keys http://developer.apple.com/library/ios/#documentation/userexperience/Reference/PassKit_Bundle/Chapters/TopLevel.html -$standardKeys = [ - 'description' => 'Demo pass', - 'formatVersion' => 1, - 'organizationName' => 'Flight Express', - 'passTypeIdentifier' => 'pass.com.apple.test', // 4. Set to yours - 'serialNumber' => '123456', - 'teamIdentifier' => 'AGK5BZEN3E' // 4. Set to yours -]; -$associatedAppKeys = []; -$relevanceKeys = []; -$styleKeys = [ +// Pass content +$data = [ + 'description' => 'Demo pass', + 'formatVersion' => 1, + 'organizationName' => 'Flight Express', + 'passTypeIdentifier' => 'pass.com.scholica.flights', // Change this! + 'serialNumber' => '12345678', + 'teamIdentifier' => 'KN44X8ZLNC', // Change this! 'boardingPass' => [ - 'primaryFields' => [ + 'primaryFields' => [ [ - 'key' => 'origin', + 'key' => 'origin', 'label' => 'San Francisco', 'value' => 'SFO', ], [ - 'key' => 'destination', + 'key' => 'destination', 'label' => 'London', 'value' => 'LHR', ], ], 'secondaryFields' => [ [ - 'key' => 'gate', + 'key' => 'gate', 'label' => 'Gate', 'value' => 'F12', ], [ - 'key' => 'date', + 'key' => 'date', 'label' => 'Departure date', 'value' => '07/11/2012 10:22', ], ], - 'backFields' => [ + 'backFields' => [ [ - 'key' => 'passenger-name', + 'key' => 'passenger-name', 'label' => 'Passenger', 'value' => 'John Appleseed', ], ], - 'transitType' => 'PKTransitTypeAir', + 'transitType' => 'PKTransitTypeAir', ], -]; -$visualAppearanceKeys = [ - 'barcode' => [ - 'format' => 'PKBarcodeFormatQR', - 'message' => 'Flight-GateF12-ID6643679AH7B', + 'barcode' => [ + 'format' => 'PKBarcodeFormatQR', + 'message' => 'Flight-GateF12-ID6643679AH7B', 'messageEncoding' => 'iso-8859-1', ], - 'backgroundColor' => 'rgb(107,156,196)', - 'logoText' => 'Flight info', + 'backgroundColor' => 'rgb(32,110,247)', + 'logoText' => 'Flight info', + 'relevantDate' => date('Y-m-d\TH:i:sP') ]; -$webServiceKeys = []; - -// Merge all pass data and set JSON for $pass object -$passData = array_merge( - $standardKeys, - $associatedAppKeys, - $relevanceKeys, - $styleKeys, - $visualAppearanceKeys, - $webServiceKeys -); - -$pass->setJSON(json_encode($passData)); +$pass->setData($data); -// Add files to the PKPass package +// Add files to the pass package $pass->addFile('images/icon.png'); $pass->addFile('images/icon@2x.png'); $pass->addFile('images/logo.png'); -if ( !$pass->create(true)) { // Create and output the PKPass +// Create and output the pass +if(!$pass->create(true)) { echo 'Error: ' . $pass->getError(); } diff --git a/examples/full_sample/index.php b/examples/full_sample/index.php index 914f12e..46b7c27 100644 --- a/examples/full_sample/index.php +++ b/examples/full_sample/index.php @@ -1,67 +1,61 @@ 'San Francisco', - 'LAX' => 'Los Angeles', - 'LHR' => 'London', - ]; - $gates = ['F12', 'G43', 'A2', 'C5', 'K9']; - - // User-set vars - $passenger = addslashes($_POST['passenger']); - $origin = $_POST['origin']; - $origin_label = $labels[$origin]; - $destination = $_POST['destination']; - $destination_label = $labels[$destination]; - $gate = $gates[array_rand($gates)]; // Yup, pick a random gate - $date = date('m/d/Y H:i', $_POST['date']); // Convert date to string - - // Create pass - - //Set certifivate and path in the constructor - $pass = new PKPass('../../Certificate.p12', 'test123'); - - // Add the WWDR certificate - $pass->setWWDRcertPath('../AppleWWDR.pem'); - - //Check if an error occured within the constructor - if ($pass->checkError($error) == true) { - exit('An error occured: ' . $error); - } - - //Or do it manually outside of the constructor - /* - // Set the path to your Pass Certificate (.p12 file) - if($pass->setCertificate('../../Certificate.p12') == false) { - echo 'An error occured'; - if($pass->checkError($error) == true) { - echo ': '.$error; - } - exit('.'); - } - // Set password for certificate - if($pass->setCertificatePassword('test123') == false) { - echo 'An error occured'; - if($pass->checkError($error) == true) { - echo ': '.$error; - } - exit('.'); - } */ - - $pass->setJSON('{ - "passTypeIdentifier": "pass.com.apple.test", +require('../../vendor/autoload.php'); + +if(isset($_POST['passenger'])) { + // User has filled in the flight info, so create the pass now + + // Predefined data + $labels = [ + 'SFO' => 'San Francisco', + 'LAX' => 'Los Angeles', + 'LHR' => 'London', + ]; + $gates = ['F12', 'G43', 'A2', 'C5', 'K9']; + + // User-set vars + $passenger = addslashes($_POST['passenger']); + $origin = $_POST['origin']; + $origin_label = $labels[$origin]; + $destination = $_POST['destination']; + $destination_label = $labels[$destination]; + $gate = $gates[array_rand($gates)]; // Yup, pick a random gate + $date = date('m/d/Y H:i', $_POST['date']); // Convert date to string + + // Create pass + + //Set certificate and path in the constructor + $pass = new PKPass('../../Certificates.p12', 'password'); + + //Check if an error occurred within the constructor + if($pass->checkError($error) == true) { + exit('An error occurred: ' . $error); + } + + // Set pass data + $pass->setData('{ + "passTypeIdentifier": "pass.com.scholica.flights", "formatVersion": 1, "organizationName": "Flight Express", "serialNumber": "123456", - "teamIdentifier": "AGK5BZEN3E", - "backgroundColor": "rgb(107,156,196)", + "teamIdentifier": "KN44X8ZLNC", + "backgroundColor": "rgb(32,110,247)", "logoText": "FLIGHT_INFO_LABEL", "description": "Demo pass", "boardingPass": { @@ -103,39 +97,41 @@ "format": "PKBarcodeFormatQR", "message": "Flight-Gate' . $gate . '-' . $date . '-' . $passenger . '-' . $destination . '", "messageEncoding": "iso-8859-1" - } + }, + "relevantDate": "' . date('Y-m-d\TH:i:sP', $_POST['date']) . '" }'); - if ($pass->checkError($error) == true) { - exit('An error occured: ' . $error); - } - - // add files to the PKPass package - $pass->addFile('../images/icon.png'); - $pass->addFile('../images/icon@2x.png'); - $pass->addFile('../images/logo.png'); - // specify english and french localizations - $pass->addFile('en.strings', 'en.lproj/pass.strings'); - $pass->addFile('fr.strings', 'fr.lproj/pass.strings'); - if ($pass->checkError($error) == true) { - exit('An error occured: ' . $error); - } - - //If you pass true, the class will output the zip into the browser. - $result = $pass->create(true); - if ($result == false) { // Create and output the PKPass - echo $pass->getError(); - } + if($pass->checkError($error) == true) { + exit('An error occured: ' . $error); + } + + // Add files to the PKPass package + $pass->addFile('../images/icon.png'); + $pass->addFile('../images/icon@2x.png'); + $pass->addFile('../images/logo.png'); + // Specify english and french localizations + $pass->addFile('en.strings', 'en.lproj/pass.strings'); + $pass->addFile('fr.strings', 'fr.lproj/pass.strings'); + + if($pass->checkError($error) == true) { + exit('An error occured: ' . $error); + } + // Create and output the PKPass + // If you pass true, the class will output the zip into the browser. + $result = $pass->create(true); + if($result == false) { + echo $pass->getError(); + } } else { - // User lands here, there are no $_POST variables set + // User lands here, there are no $_POST variables set ?> Flight pass creator - PHP class demo - - -