Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Passes Laravel Package

chiiya/laravel-passes

Create and serve Apple Wallet passes from your Laravel app. chiiya/laravel-passes offers a fluent API for defining pass types, fields, images, and barcodes, then generates signed .pkpass files ready for download or distribution.

View on GitHub
Deep Wiki
Context7

Getting Started

This release (1.2.0) is a maintenance drop: Laravel 13 support, Dependabot bumps for CI actions, and a small typing fix in the Google domain classes. There are no new end-user features or breaking changes, so it's a safe patch-level upgrade from 1.1.x. The assessment below applies to the package as a whole.

Minimal setup:

composer require chiiya/laravel-passes
php artisan vendor:publish --tag="passes-config"

Then populate credentials in .env:

  • Apple: path to your .p12 signing certificate, its password, the WWDR intermediate cert path, team identifier, and pass type identifier.
  • Google: service account JSON path (or credentials array) and your Wallet issuer ID.

Where to look first:

  1. config/passes.php — the authoritative list of options and expected credential keys.
  2. src/Apple/ and src/Google/ — the pass/domain class hierarchies. Subclassing these is the normal extension route.
  3. The package test suite — usually the fastest way to see a fully populated, valid pass object without reading every value object.

First use case: a single-event ticket or loyalty card. Build a pass object, set the type identifier, serial number, barcode, and a handful of fields, then stream it to the browser as a .pkpass download. Get that working end-to-end before touching push updates or Google Wallet — most integration pain lives in certificates, not code.

Implementation Patterns

Build passes as throwaway objects, persist the inputs. Pass objects are value-object graphs; serialize the data you used to build them (serial number, holder, barcode payload, expiry) into your own table, and rebuild the pass on demand. This keeps you free of the package's internal structure when it changes.

One route per wallet, one controller action each. A typical shape:

Route::get('/passes/{pass}/apple', [PassController::class, 'apple'])->name('passes.apple');
Route::get('/passes/{pass}/google', [PassController::class, 'google'])->name('passes.google');

For Apple, return the download with the correct MIME type (application/vnd.apple.pkpass) and a filename ending in .pkpass. For Google, the usual pattern is a signed "Save to Google Wallet" URL rather than a file download — render it into a button on the confirmation page.

Template + override. Define a base pass configuration (colors, logo, fields layout) in a config file or a dedicated PassTemplate class, then merge per-user values on top. This avoids duplicating a dozen field definitions across every pass type you support.

Google class/object separation. Google Wallet models classes (the reusable definition) and objects (the per-user instance). Create or patch the class once during deployment or on first use, then create objects referencing it. This release added missing generic type annotations to that domain layer, which matters only if you run static analysis — see below.

Push updates (Apple). If you support live-updating passes, you need the web service endpoints Apple calls: device registration, device unregistration, and the update-poll endpoint that returns changed serial numbers. Implement these as your own controllers backed by a pass_registrations table; the package handles pass generation, not the transport. Then trigger APNs pushes when the underlying data changes, typically from a model observer or a queued job.

Queue anything expensive. Image composition and signing are CPU-bound. If you generate passes at signup or on order confirmation, dispatch a job and email a link, or cache the generated bytes keyed by (pass_id, updated_at).

Testing. Assert on the built pass's array/JSON representation rather than the binary. For Apple signing, either stub the signer in feature tests or keep a fixture certificate in tests/fixtures with a matching password in phpunit.xml. Never let tests reach the real APNs or Google APIs — use Http::fake().

Static analysis. Run Larastan/PHPStan at level 6+ and treat this package's types as part of your contract. The generics fix in 1.2.0 removes a class of "missing type parameter" noise in the Google namespace; if you had baseline entries suppressing those, re-check them after upgrading.

Gotchas and Tips

Certificates are the #1 failure mode. A pass that generates without error can still be rejected by iOS Wallet if the signing certificate is expired, doesn't match the pass type identifier, or the WWDR intermediate is missing from the chain. Verify with openssl pkcs12 -info on the .p12, and set a calendar reminder for expiry — nothing in the code will warn you until users complain.

Team identifier vs. pass type identifier. These are different values and both are required. Swapping them produces a pass that installs but never updates, which is a miserable bug to diagnose.

Image dimensions are strict. Apple validates icon and logo sizes (and expects @2x variants). Wrong dimensions are silently ignored or rejected at install time. Keep source images in one directory and generate variants deterministically rather than hand-cropping per pass type.

Serial numbers must be unique per pass instance, not per template. Reusing a serial across users causes one user's pass to overwrite another's on the same device. Pair every serial with a random authentication token if you support the update web service.

Google credentials need the Wallet Objects API enabled on the service account's project, and the service account email must be granted access in the Google Wallet Business Console. A 403 here almost always means console permissions, not code.

Signed URLs expire. Generate Google save links at render time, not at order-creation time and store them in the database — stale links are a common support ticket.

Debug by unzipping. Save the raw .pkpass, rename to .zip, and inspect pass.json plus the manifest.json/signature pair. If pass.json looks right but Wallet refuses the file, the problem is the signature, not your data.

Localization folders must be added as directories with the right locale naming (e.g. en.lproj); a typo results in fields silently falling back to the default language.

Extension points worth knowing: subclass the Apple Pass and Google domain classes to add your own field helpers; override config at runtime via config()->set() in a service provider for multi-tenant setups with per-tenant certificates; register a custom signer if you pull certificates from a vault (AWS Secrets Manager, Laravel Vault, etc.) rather than the filesystem.

Upgrade note for 1.2.0: the only runtime-affecting change is the Laravel 13 constraint. Check that your app's laravel/framework constraint and any locked transitive dependencies allow it; on Laravel 10/11/12 there is no reason to rush, but there is also no risk in taking it. If you pin CI actions by SHA in your own workflows, the Dependabot bumps in this release are a reminder to do the same.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource