laulamanapps / apple-passbook-bundle
Generate Apple Passbooks from your Symfony application
Installs: 19 136
Dependents: 0
Suggesters: 1
Security: 0
Stars: 4
Watchers: 1
Forks: 2
Open Issues: 2
pkg:composer/laulamanapps/apple-passbook-bundle
Requires
- php: >=7.3
- ext-openssl: *
- laulamanapps/apple-passbook: ^1.1
Requires (Dev)
- ext-zip: *
- matthiasnoback/symfony-config-test: ^4.1
- symfony/browser-kit: ^5.1
- symfony/event-dispatcher: ^5.1
- symfony/framework-bundle: ^5.1
- symfony/http-foundation: ^5.1
- symfony/routing: ^5.1
- symfony/yaml: ^5.1
Suggests
- symfony/event-dispatcher: ^5.1
- symfony/framework-bundle: ^5.1
- symfony/http-foundation: ^5.1
- symfony/routing: ^5.1
This package is auto-updated.
Last update: 2025-10-10 05:26:01 UTC
README
This package provides Symfony configuration for LauLamanApps Apple Passbook Package
Installation
With composer, add:
$ composer require laulamanapps/apple-passbook-bundle
Run Tests
To make sure everything works you can run tests:
$ make tests-unit $ make tests-integration $ make tests-infection
Get certificate
Head over to the Apple Developer Portal to get yourself a certificate to sign your passbooks with.
Convert the certificate and key to a .p12 file using the Keychain Access
Configure Bundle
#config/packages/laulamanapps_apple_passbook.yml laulamanapps_apple_passbook: certificate: '%env(APPLE_PASSBOOK_CERTIFICATE)%' password: '%env(APPLE_PASSBOOK_CERTIFICATE_PASSWORD)%' team_identifier: '%env(APPLE_PASSBOOK_TEAM_IDENTIFIER)%' pass_type_identifier: '%env(APPLE_PASSBOOK_PASS_TYPE_IDENTIFIER)%'
Add the ENV variables to the .env file
##> laulamanapps/apple-passbook-bundle APPLE_PASSBOOK_CERTIFICATE=path/to/certificate.p12 APPLE_PASSBOOK_CERTIFICATE_PASSWORD=password APPLE_PASSBOOK_PASS_TYPE_IDENTIFIER=pass.com.your.pass.identifiers APPLE_PASSBOOK_TEAM_IDENTIFIER=identifier APPLE_PASSBOOK_WEB_SERVICE_URL='http://example.com/' ##< laulamanapps/apple-passbook-bundle
Create & Compile Passbook
namespace App\Controller; use LauLamanApps\ApplePassbook\Build\Compiler; use LauLamanApps\ApplePassbook\GenericPassbook; use LauLamanApps\ApplePassbook\MetaData\Barcode; use LauLamanApps\ApplePassbook\Style\BarcodeFormat; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Annotation\Route; final class PassbookController extends AbstractController { /** * @var Compiler */ private $passbookCompiler; public function __construct(Compiler $passbookCompiler) { $this->passbookCompiler = $passbookCompiler; } /** * @Route("/download/passbook/", name="download_passbook") */ public function download(): Response { $passbook = new GenericPassbook('8j23fm3'); $passbook->setTeamIdentifier('<TeamId>'); $passbook->setPassTypeIdentifier('<PassTypeId>'); $passbook->setOrganizationName('Toy Town'); $passbook->setDescription('Toy Town Membership'); $barcode = new Barcode(); $barcode->setFormat(BarcodeFormat::pdf417()); $barcode->setMessage('123456789'); $passbook->setBarcode($barcode); $data = $this->passbookCompiler->compile($passbook); $response = new Response($data); $response->headers->set('Content-Description', 'File Transfer'); $response->headers->set('Content-Type', 'application/vnd.apple.pkpass'); $response->headers->set('Content-Disposition', 'filename="passbook.pkpass"'); return $response; } }
Configure Build in Webservices
This package comes with build in controllers for all Apple passbooks webservice URLs.
It is using Symfonys build in EventDispatcher.
Enable this by adding the following configuration to the config/routes/routes.yaml
passbook_routes: resource: '@ApplePassbookBundle/Controller/' type: annotation
The controllers will dispatch the following events, the following information is available:
/* Available on All events */ $event->getPassTypeIdentifier(); $event->getStatus(); /* Available on DeviceRegisteredEvent */ $event->getAuthenticationToken(); $event->getDeviceLibraryIdentifier(); $event->getSerialNumber(); /* Available on DeviceRequestUpdatedPassesEvent */ $event->getAuthenticationToken(); $event->getDeviceLibraryIdentifier(); $event->getPassesUpdatedSince(); /* Available on DeviceUnregisteredEvent */ $event->getAuthenticationToken(); $event->getDeviceLibraryIdentifier(); $event->getSerialNumber(); /* Available on RetrieveUpdatedPassbookEvent */ $event->getAuthenticationToken(); $event->getSerialNumber(); $event->getPassTypeIdentifier(); $event->getUpdatedSince();
Now Subscribe to the events:
The idea here is that you handle the event and mark the events as handled by calling setters on the event itself.
The event by default has the status Status::unhandled()
namespace App\Integration\Symfony\EventSubscriber; use DateTimeImmutable; use LauLamanApps\ApplePassbook\GenericPassbook; use LauLamanApps\ApplePassbookBundle\Event\DeviceRegisteredEvent; use LauLamanApps\ApplePassbookBundle\Event\DeviceRequestUpdatedPassesEvent; use LauLamanApps\ApplePassbookBundle\Event\DeviceUnregisteredEvent; use LauLamanApps\ApplePassbookBundle\Event\RetrieveUpdatedPassbookEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ApplePassbookSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ DeviceRegisteredEvent::class => 'onDeviceRegistered', DeviceRequestUpdatedPassesEvent::class => 'onDeviceRequestUpdatedPasses', RetrieveUpdatedPassbookEvent::class => 'onRetrieveUpdatedPassbook', DeviceUnregisteredEvent::class => 'onDeviceUnregistered', ]; } public function onDeviceRegistered(DeviceRegisteredEvent $event): void { $passbook = $this->passbookRepository->getBySerial($event->getSerialNumber()); if ($event->getAuthenticationToken() <> $passbook->getAuthToken()) { $event->notAuthorized(); return; } /** * Save in Database */ $event->deviceRegistered(); } public function onDeviceRequestUpdatedPasses(DeviceRequestUpdatedPassesEvent $event): void { $passbooks = $this->passbookRepository->getSerialsSince( $event->getPassTypeIdentifier(), $event->getDeviceLibraryIdentifier(), $event->getPassesUpdatedSince() ); if ($passbooks) { $serials = []; foreach ($passbooks as $passbook) { $serials[] = $passbook->getSerialNumber(); } $event->setSerialNumbers($serials, new DateTimeImmutable()); return; } $event->notFound(); } public function onRetrieveUpdatedPassbook(RetrieveUpdatedPassbookEvent $event): void { try { $entity = $this->passbookRepository->getBySerial($event->getSerialNumber()); if ($entity->getAuthToken() !== $event->getAuthenticationToken()) { $event->notAuthorized(); return; } if ($event->getUpdatedSince() && $entity->getUpdatedAt() < $event->getUpdatedSince()) { $event->notModified(); return; } $passbook = new GenericPassbook($event->getSerialNumber()); /* Generate Passbook */ $event->setPassbook($passbook); } catch (NoResultException $e) { $event->notFound(); } } public function onDeviceUnregistered(DeviceUnregisteredEvent $event): void { $passbook = $this->passbookRepository->getBySerial($event->getSerialNumber()); if ($event->getAuthenticationToken() <> $passbook->getAuthToken()) { $event->notAuthorized(); return; } /** * Remove from Database */ $event->deviceUnregistered(); } }
Credits
This package has been developed by LauLaman.