PHPackages                             laulamanapps/google-wallet - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. laulamanapps/google-wallet

ActiveLibrary[API Development](/categories/api)

laulamanapps/google-wallet
==========================

Generate Google Wallet passes from PHP

v1.1.0(1mo ago)02955MITPHPPHP ^8.1CI passing

Since Jul 8Pushed 1mo agoCompare

[ Source](https://github.com/LauLamanApps/google-wallet)[ Packagist](https://packagist.org/packages/laulamanapps/google-wallet)[ RSS](/packages/laulamanapps-google-wallet/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (3)Used By (5)

Google Wallet
=============

[](#google-wallet)

Generate Google Wallet passes from PHP. The Google counterpart of [laulamanapps/apple-passbook](https://github.com/LauLaman/apple-passbook).

Passes are distributed via "Add to Google Wallet" links: an RS256-signed JWT whose payload embeds the pass classes and objects (a so-called "fat JWT"). Because the classes and objects travel inside the JWT, **no Google Wallet REST API call is needed** — the only dependency is `ext-openssl` for signing.

Installation
------------

[](#installation)

```
composer require laulamanapps/google-wallet
```

Or use one of the framework integrations, which wire the service account and save-URL factory from configuration:

- **Symfony**: [laulamanapps/google-wallet-symfony](https://github.com/LauLamanApps/google-wallet-symfony)
- **Laravel**: [laulamanapps/google-wallet-laravel](https://github.com/LauLamanApps/google-wallet-laravel)

Service account setup
---------------------

[](#service-account-setup)

1. Sign up for a Google Wallet issuer account in the [Google Pay &amp; Wallet Console](https://pay.google.com/business/console) and note your issuer id.
2. Create a Google Cloud service account with the Wallet Object Issuer role and download its JSON key file.
3. Link the service account to your issuer account in the console.

Quickstart
----------

[](#quickstart)

```
use LauLamanApps\GoogleWallet\Object\Barcode;
use LauLamanApps\GoogleWallet\Object\BarcodeType;
use LauLamanApps\GoogleWallet\Object\GenericClass;
use LauLamanApps\GoogleWallet\Object\GenericObject;
use LauLamanApps\GoogleWallet\Object\Image;
use LauLamanApps\GoogleWallet\Object\TextModule;
use LauLamanApps\GoogleWallet\PassPayload;
use LauLamanApps\GoogleWallet\SaveUrlFactory;
use LauLamanApps\GoogleWallet\ServiceAccount;

$serviceAccount = ServiceAccount::fromJsonFile('/path/to/service-account.json');

// Ids are '.'
$object = new GenericObject('3388000000012345678.member-0001', '3388000000012345678.membership');
$object->setCardTitle('ACME Membership');
$object->setHeader('John Doe');
$object->setSubheader('Gold member');
$object->setHexBackgroundColor('#4285f4');
$object->setLogo(Image::fromUri('https://example.com/logo.png', 'ACME logo'));
$object->setBarcode(new Barcode(BarcodeType::QrCode, 'member-0001'));
$object->addTextModule(new TextModule('Member since', '2020'));

$payload = new PassPayload();
$payload->addGenericClass(new GenericClass('3388000000012345678.membership'));
$payload->addGenericObject($object);

$factory = new SaveUrlFactory($serviceAccount, ['https://example.com']);
$saveUrl = $factory->create($payload);
// https://pay.google.com/gp/v/save/eyJhbGciOiJSUzI1NiIs...
```

Render the url as an [Add to Google Wallet button](https://developers.google.com/wallet/generic/resources/brand-guidelines)and you are done.

Pass types
----------

[](#pass-types)

Next to `GenericClass`/`GenericObject` the same pattern is available for:

- `EventTicketClass` / `EventTicketObject`
- `OfferClass` / `OfferObject`
- `LoyaltyClass` / `LoyaltyObject`
- `TransitClass` / `TransitObject`

Add them to the `PassPayload` with the matching `add*Class()`/`add*Object()` methods.

Location-triggered notifications
--------------------------------

[](#location-triggered-notifications)

Google Wallet shows a [Nearby Passes notification](https://developers.google.com/wallet/generic/use-cases/trigger-push-notifications)when a pass holder is close to one of the pass's merchant locations (the user must have granted always-on location access; Google controls the notification text and timing). Add up to 10 locations per object:

```
$object->addMerchantLocation(52.3676, 4.9041);
```

Updating passes
---------------

[](#updating-passes)

Once a pass is saved to a wallet you can change it through the Google Wallet REST API. Unlike Apple Passbook there are no push tokens or APNs involved: Google pushes updates to devices automatically after an API call. The API client uses `ext-curl` by default (or bring your own `HttpClient` implementation); an OAuth2 access token is obtained and cached for you via the service-account JWT-bearer grant.

```
use LauLamanApps\GoogleWallet\Api\AccessTokenProvider;
use LauLamanApps\GoogleWallet\Api\Message;
use LauLamanApps\GoogleWallet\Api\WalletApiClient;
use LauLamanApps\GoogleWallet\Object\GenericObject;
use LauLamanApps\GoogleWallet\Object\State;
use LauLamanApps\GoogleWallet\ServiceAccount;

$serviceAccount = ServiceAccount::fromJsonFile('/path/to/service-account.json');
$client = new WalletApiClient(new AccessTokenProvider($serviceAccount));

// Partially update an issued pass (PATCH; use updateObject() for a full PUT)
$object = new GenericObject('3388000000012345678.member-0001', '3388000000012345678.membership');
$object->setHeader('John Doe');
$object->setState(State::Active);
$client->patchObject($object);

// Notify pass holders
$client->addMessage($object, new Message('Membership renewed', 'Your membership is extended until 2027.'));

// Fetch the current server-side state of a pass
$data = $client->getObject('genericObject', '3388000000012345678.member-0001');
```

The same pattern works for classes (`insertClass()`, `updateClass()`, `patchClass()`) and for all pass types. Errors surface as `LauLamanApps\GoogleWallet\Exception\ApiException`.

Receiving save/delete callbacks
-------------------------------

[](#receiving-savedelete-callbacks)

Google can notify you whenever a user saves or deletes a pass. Set a callback url on the pass class (available on all five class types) and Google will POST a signed message to that endpoint on every save and delete:

```
$class = new GenericClass('3388000000012345678.membership');
$class->setCallbackUrl('https://example.com/wallet/callback'); // must be https://
```

Google signs every callback with the `ECv2SigningOnly` scheme (ECDSA over NIST P-256 with SHA-256, sender `GooglePayPasses`, your issuer id as recipient). Verify the raw request body with `CallbackVerifier`; it fetches Google's root signing keys, checks the intermediate signing key against them, checks the message signature against the intermediate key and rejects expired keys and messages:

```
use LauLamanApps\GoogleWallet\Callback\CallbackVerifier;
use LauLamanApps\GoogleWallet\Callback\EventType;
use LauLamanApps\GoogleWallet\Callback\GoogleKeyProvider;
use LauLamanApps\GoogleWallet\Exception\CallbackException;

$verifier = new CallbackVerifier('3388000000012345678', GoogleKeyProvider::production());

try {
    $event = $verifier->verify($rawPostBody);
} catch (CallbackException $exception) {
    // Respond with a 4xx and do NOT act on the payload.
}

$event->getClassId();   // '3388000000012345678.membership'
$event->getObjectId();  // '3388000000012345678.member-0001'
$event->getEventType(); // EventType::Save or EventType::Delete
$event->getNonce();     // callbacks are delivered at least once; deduplicate on the nonce
```

Warning

Never act on a callback without running it through `CallbackVerifier`. The endpoint is public and anyone can POST to it; only the signature chain proves the request came from Google. `verify()` never returns unverified data — every failure throws a `CallbackException`.

Callbacks are best-effort and may arrive more than once: use `getNonce()` to deduplicate. Reuse one `CallbackVerifier`/`GoogleKeyProvider` instance where possible — the root signing keys are cached per `GoogleKeyProvider` instance. `GoogleKeyProvider::test()` points at Google's test root keys for integration testing.

Signing a JWT yourself
----------------------

[](#signing-a-jwt-yourself)

`SaveUrlFactory` covers the common case. If you need the raw JWT, use `JwtSigner` directly:

```
use LauLamanApps\GoogleWallet\JwtSigner;

$signer = new JwtSigner($serviceAccount);
$jwt = $signer->sign([
    'iss' => $serviceAccount->getClientEmail(),
    'aud' => 'google',
    'typ' => 'savetowallet',
    'iat' => time(),
    'origins' => ['https://example.com'],
    'payload' => $payload->toArray(),
]);
```

Credits
-------

[](#credits)

- [Laurens Laman](https://github.com/LauLaman)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance91

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

2

Last Release

44d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/930ca3b1756d00a63c8ff3363e81f16f961cf3ef79ff0c9d362670c36d97e456?d=identicon)[LauLaman](/maintainers/LauLaman)

---

Top Contributors

[![LauLaman](https://avatars.githubusercontent.com/u/8283992?v=4)](https://github.com/LauLaman "LauLaman (5 commits)")

---

Tags

phpgooglewalletpasscouponevent ticketloyalty

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/laulamanapps-google-wallet/health.svg)

```
[![Health](https://phpackages.com/badges/laulamanapps-google-wallet/health.svg)](https://phpackages.com/packages/laulamanapps-google-wallet)
```

###  Alternatives

[jeroendesloovere/geolocation-php-api

This Geolocation PHP class connects to Google Maps API to find latitude/longitude or address.

76327.6k4](/packages/jeroendesloovere-geolocation-php-api)[x-fran/g-trends

Google Trends API for PHP

11857.7k](/packages/x-fran-g-trends)[aurawindsurfing/google-translate

Free Laravel package for Paid Google Translate REST API with your own API key

1120.8k1](/packages/aurawindsurfing-google-translate)[takpesar/tron

A PHP library to create Tron wallet addresses and manage wallets using the Tron API

183.3k](/packages/takpesar-tron)[passkit/passkit-php-grpc-sdk

PHP gRPC SDK for Apple Wallet and Google Pay Membership / Loyalty / Access Cards, Coupons, Flights &amp; Event-Tickets.

132.5k](/packages/passkit-passkit-php-grpc-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
