← All @molecule/* packages · App templates
@molecule/api-wallet-passFeature · wallet-pass · API (Node) · v1.0.1 · Apache-2.0
Apple Wallet (.pkpass) + Google Wallet pass-object generation. Signs Apple manifests with CMS detached signatures and Google pass JWTs with RS256. Framework-neutral HTTP handlers for ticket / coupon delivery.
npm install @molecule/api-wallet-pass@molecule/api-wallet-pass is a ready-made wallet-pass feature for the API (Node) side. It composes the core interfaces it needs, so it works with whichever providers your app has bonded.
import { createApplePass, createGoogleWalletJwt } from '@molecule/api-wallet-pass'
// Apple
const pkpass = await createApplePass(passJsonPayload, signingCerts, assets)
// Google — event ticket (default)
const jwt = createGoogleWalletJwt(passClass, passObject, serviceAccount)
const saveUrl = `https://pay.google.com/gp/v/save/${jwt}`
// Google — coupon (offer pass type)
const couponJwt = createGoogleWalletJwt(passClass, passObject, serviceAccount, undefined, 'coupon')Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Apple Wallet (.pkpass) and Google Wallet pass-object generation for server-side ticket / coupon delivery.
Two independent sub-APIs:
.pkpass zip (CMS detached
signature over manifest.json, using the Apple developer-portal
"Pass Type ID" cert + WWDR intermediate). Output is a Buffer ready
to send with Content-Type: application/vnd.apple.pkpass.https://pay.google.com/gp/v/save/<jwt> for the "save to wallet" flow.Two HTTP handlers — {@link createApplePassHandler} and
{@link createGoogleWalletPassHandler} — wrap the generators with the same
minimal request/response contract used elsewhere in @molecule/api-*,
keeping the package framework-neutral.
The package never displays text on its own and has no companion locale bond: pass field labels are caller-supplied and already localized.
import { createApplePass, createGoogleWalletJwt } from '@molecule/api-wallet-pass'
// Apple
const pkpass = await createApplePass(passJsonPayload, signingCerts, assets)
// Google — event ticket (default)
const jwt = createGoogleWalletJwt(passClass, passObject, serviceAccount)
const saveUrl = `https://pay.google.com/gp/v/save/${jwt}`
// Google — coupon (offer pass type)
const couponJwt = createGoogleWalletJwt(passClass, passObject, serviceAccount, undefined, 'coupon')
feature
npm install @molecule/api-wallet-pass node-forge
npm install -D @types/node-forge
ApplePassBarcodeApple Wallet barcode entry, used for both the legacy barcode field and
the modern barcodes[] array on pass.json.
interface ApplePassBarcode {
/** Barcode visual format. */
format: ApplePassBarcodeFormat
/** Encoded payload (URL, ticket id, JWT, etc.). */
message: string
/** Encoding for `message`. Almost always `'iso-8859-1'`. */
messageEncoding: string
/** Optional human-readable text shown beneath the barcode. */
altText?: string
}
ApplePassCertificatesCertificate bundle required to sign an Apple .pkpass.
Apple's PassKit signing flow uses a CMS detached signature over the
manifest.json file. Three artefacts are required:
signerCertPem — the developer-portal "Pass Type ID" cert (PEM).signerKeyPem — the matching private key (PEM, optionally encrypted).wwdrCertPem — Apple's WWDR intermediate cert chained into the CMS.Pass password if signerKeyPem is encrypted.
interface ApplePassCertificates {
/** PEM-encoded "Pass Type ID" leaf certificate. */
signerCertPem: string
/** PEM-encoded private key matching `signerCertPem`. */
signerKeyPem: string
/** PEM-encoded Apple WWDR intermediate certificate. */
wwdrCertPem: string
/** Decryption passphrase for `signerKeyPem`, if it is encrypted. */
password?: string
}
ApplePassDataThe minimum set of fields Apple's signing server validates. Other fields (relevantDate, locations, beacons, NFC, etc.) are passed through verbatim via the index signature so callers can populate any of Apple's documented keys without us having to re-export the entire schema.
interface ApplePassData {
/** Schema version. Always `1` for the current PassKit format. */
formatVersion: 1
/** Reverse-DNS pass-type id from the Apple developer portal. */
passTypeIdentifier: string
/** Apple developer team id (10 chars). */
teamIdentifier: string
/** Per-pass unique serial number. */
serialNumber: string
/** Customer-facing org name shown in the lock screen. */
organizationName: string
/** Localized short description for accessibility. */
description: string
/** Pass background color in `'rgb(r, g, b)'` form. */
backgroundColor?: string
/** Foreground (primary text) color in `'rgb(r, g, b)'` form. */
foregroundColor?: string
/** Label text color in `'rgb(r, g, b)'` form. */
labelColor?: string
/** Logo text shown next to the logo image. */
logoText?: string
/** Modern multi-barcode array. Prefer over the legacy `barcode` field. */
barcodes?: ApplePassBarcode[]
/** Legacy single-barcode field. Apple still requires it for old iOS versions. */
barcode?: ApplePassBarcode
/** Style-specific field group — exactly one of these keys must be present. */
eventTicket?: ApplePassStyleFields
boardingPass?: ApplePassStyleFields
coupon?: ApplePassStyleFields
generic?: ApplePassStyleFields
storeCard?: ApplePassStyleFields
/**
* Web-service URL Apple Wallet pings for pass updates. Required when
* `authenticationToken` is set.
*/
webServiceURL?: string
/** Token presented to the web service for pass updates. */
authenticationToken?: string
/** Pass-through for any remaining Apple-defined keys. */
[extra: string]: unknown
}
ApplePassFieldA single primary/secondary/auxiliary/back/header field on an Apple Wallet
pass. Apple's schema lets each field carry an arbitrary key, a localized
label, and a value (string or number). Additional formatting fields
(currencyCode, dateStyle, etc.) are passed through verbatim.
interface ApplePassField {
/** Stable field id. Must be unique within its parent style group. */
key: string
/** Localized label shown above the value. */
label?: string
/** Field value (string, number, ISO-8601 date, etc.). */
value: string | number
/** Localized text alignment. Apple-defined enum. */
textAlignment?:
| 'PKTextAlignmentLeft'
| 'PKTextAlignmentCenter'
| 'PKTextAlignmentRight'
| 'PKTextAlignmentNatural'
/** ISO-4217 currency code; turns the value into a localized currency string. */
currencyCode?: string
/** Date style for date-typed values. */
dateStyle?:
| 'PKDateStyleNone'
| 'PKDateStyleShort'
| 'PKDateStyleMedium'
| 'PKDateStyleLong'
| 'PKDateStyleFull'
/** Time style for date-typed values. */
timeStyle?:
| 'PKDateStyleNone'
| 'PKDateStyleShort'
| 'PKDateStyleMedium'
| 'PKDateStyleLong'
| 'PKDateStyleFull'
/** Number style for numeric values. */
numberStyle?:
| 'PKNumberStyleDecimal'
| 'PKNumberStylePercent'
| 'PKNumberStyleScientific'
| 'PKNumberStyleSpellOut'
/** Optional change message template (`'%@'` is replaced with the new value). */
changeMessage?: string
}
ApplePassStyleFieldsThe structured field groups that live under one of the pass-style keys
(eventTicket, boardingPass, etc.) in pass.json.
interface ApplePassStyleFields {
headerFields?: ApplePassField[]
primaryFields?: ApplePassField[]
secondaryFields?: ApplePassField[]
auxiliaryFields?: ApplePassField[]
backFields?: ApplePassField[]
}
CreateApplePassHandlerOptionsOptions for {@link createApplePassHandler}.
interface CreateApplePassHandlerOptions {
/** Loader that turns `passId` into the pass payload + signing material. */
resolve: ApplePassResolver
/** Optional file-name template for the `Content-Disposition` header. */
fileName?: (passId: string) => string
}
CreateApplePassOptionsOptions accepted by {@link createApplePass}. The certificate bundle is
required; assets are optional but most apps need at least an icon.png.
interface CreateApplePassOptions {
/** Pass payload — becomes `pass.json` inside the zip. */
passData: ApplePassData
/** Apple signing material — see {@link ApplePassCertificates}. */
certificates: ApplePassCertificates
/** Optional binary assets keyed by file name. */
assets?: ApplePassAssets
}
CreateGoogleWalletJwtOptionsOptions accepted by {@link createGoogleWalletJwt}.
interface CreateGoogleWalletJwtOptions {
/** Pass class definition (template). */
passClass: GoogleWalletClass
/** Pass object definition (per-user instance). */
passObject: GoogleWalletObject
/** Google issuer service account. */
serviceAccount: GoogleWalletServiceAccount
/** JWT origins (audience domains). Defaults to `['https://wallet.google']`. */
origins?: string[]
/**
* Which Google Wallet pass type to emit. Defaults to `'eventTicket'`. Use
* `'offer'` (or `'coupon'`) for coupons — see {@link GoogleWalletPassType}.
*/
passType?: GoogleWalletPassType
}
CreateGoogleWalletPassHandlerOptionsOptions for {@link createGoogleWalletPassHandler}.
interface CreateGoogleWalletPassHandlerOptions {
/** Loader that turns `passId` into pass class + object + signing creds. */
resolve: GoogleWalletPassResolver
/** Override the Google Wallet save-link prefix (used by tests). */
saveUrlPrefix?: string
}
GoogleWalletClassGoogle Wallet "pass class" — the shared template all matching pass objects inherit from. Apps typically create one class per event / loyalty-program and many objects underneath it.
The shape mirrors Google's REST schema; only id is required at this
layer. All other Google fields (eventName, venue, dateTime, redemptionIssuers,
etc.) are accepted via the index signature, so the same interface serves
every {@link GoogleWalletPassType} (event ticket, offer/coupon, loyalty, …).
interface GoogleWalletClass {
/** Globally unique class id, formatted `'<issuerId>.<suffix>'`. */
id: string
/** Pass-through for Google-defined keys (eventName, venue, etc.). */
[key: string]: unknown
}
GoogleWalletObjectGoogle Wallet "pass object" — the per-user instance of a class.
The shape mirrors Google's REST schema; id and classId are required.
All other Google fields (state, ticketHolderName, barcode, etc.) are
accepted via the index signature, so the same interface serves every
{@link GoogleWalletPassType} (event ticket, offer/coupon, loyalty, …).
interface GoogleWalletObject {
/** Globally unique object id, formatted `'<issuerId>.<suffix>'`. */
id: string
/** Class id this object inherits from. */
classId: string
/** Pass-through for Google-defined keys (state, barcode, etc.). */
[key: string]: unknown
}
GoogleWalletServiceAccountGoogle service-account credentials used to sign Wallet JWTs.
Both fields come from the JSON key file Google issues for the
Wallet-issuer service account: client_email and private_key.
interface GoogleWalletServiceAccount {
/** Service-account email. */
clientEmail: string
/** PEM-encoded RSA private key (with `\n` newlines). */
privateKey: string
}
WalletPassRequestMinimal request shape consumed by the wallet-pass handlers.
interface WalletPassRequest {
/** Path params; both handlers expect `passId`. */
params: { passId: string }
}
WalletPassResponseMinimal response shape consumed by the wallet-pass handlers.
interface WalletPassResponse {
/** Set a single response header. */
setHeader: (name: string, value: string) => void
/** Set the HTTP status code. */
setStatus: (status: number) => void
/** Write a binary buffer body and end the response. */
sendBuffer: (buffer: Buffer) => void
/** Write a JSON body and end the response. */
sendJson: (body: unknown) => void
/** Issue a redirect (302). */
redirect: (url: string) => void
}
ZipFileEntryOne file to embed in the zip.
interface ZipFileEntry {
/** Path inside the archive (no leading slash). */
name: string
/** Raw bytes to store. */
data: Buffer
}
ApplePassAssetsOptional asset attachments included alongside pass.json in the .pkpass
zip. Keys are the file names Apple expects (e.g. 'icon.png',
'icon@2x.png', 'logo.png', 'strip.png', 'thumbnail.png'). Each
value is the raw bytes of that asset.
type ApplePassAssets = Record<string, Buffer | Uint8Array>
ApplePassBarcodeFormatApple Wallet barcode format. format controls how the redemption code is
rendered on the back of the pass.
'PKBarcodeFormatQR' — QR code (most common for event tickets).'PKBarcodeFormatPDF417' — PDF417 (US airline boarding passes).'PKBarcodeFormatAztec' — Aztec (international rail / boarding passes).'PKBarcodeFormatCode128' — Code-128 1D barcode.type ApplePassBarcodeFormat =
'PKBarcodeFormatQR' | 'PKBarcodeFormatPDF417' | 'PKBarcodeFormatAztec' | 'PKBarcodeFormatCode128'
ApplePassResolverResolver that loads pass payload + signing material for a given passId. The handler stays decoupled from the storage layer (DataStore, files, cache, etc.) by accepting the resolver as a closure.
type ApplePassResolver = (passId: string) => Promise<
| {
passData: ApplePassData
certificates: ApplePassCertificates
assets?: ApplePassAssets
}
| undefined
>
ApplePassStyleApple Wallet pass-style discriminator. Drives the visual layout of the pass on-device.
type ApplePassStyle = 'boardingPass' | 'coupon' | 'eventTicket' | 'generic' | 'storeCard'
GoogleWalletPassResolverResolver that loads Google Wallet pass class + object + signing service account for a given passId.
type GoogleWalletPassResolver = (passId: string) => Promise<
| {
passClass: GoogleWalletClass
passObject: GoogleWalletObject
serviceAccount: GoogleWalletServiceAccount
origins?: string[]
/**
* Which Google Wallet pass type to issue. Defaults to `'eventTicket'`.
* Return `'offer'`/`'coupon'` (etc.) to deliver a coupon.
*/
passType?: GoogleWalletPassType
}
| undefined
>
GoogleWalletPassTypeGoogle Wallet pass type. Selects which class/object payload keys the signed
JWT carries — Google routes each pass to a different on-device surface by
these keys, so the type MUST match the schema of passClass/passObject.
'eventTicket' — event tickets (eventTicketClasses/eventTicketObjects).'offer' / 'coupon' — offers & coupons (offerClasses/offerObjects);
Google Wallet has no separate "coupon" type — a coupon IS an offer, so the
two spellings are aliases.'loyalty' — loyalty cards (loyaltyClasses/loyaltyObjects).'giftCard' — gift cards (giftCardClasses/giftCardObjects).'flight' — boarding passes (flightClasses/flightObjects).'transit' — transit passes (transitClasses/transitObjects).'generic' — generic passes (genericClasses/genericObjects).type GoogleWalletPassType =
'eventTicket' | 'offer' | 'coupon' | 'loyalty' | 'giftCard' | 'flight' | 'transit' | 'generic'
buildZipBuffer(entries)Build a STORE-method zip Buffer from a list of in-memory file entries.
function buildZipBuffer(entries: ZipFileEntry[]): Buffer<ArrayBufferLike>
entries — Files to embed.Returns: The complete zip bytes.
crc32(data)Compute a CRC-32/IEEE checksum (the variant zip uses).
Pure-JS implementation — small enough that pulling in a dependency for this one function would be silly.
function crc32(data: Buffer<ArrayBufferLike>): number
data — Bytes to checksum.Returns: 32-bit unsigned CRC.
createApplePass(passData, certificates, assets)Generate a fully-signed Apple Wallet .pkpass archive.
function createApplePass(
passData: ApplePassData,
certificates: ApplePassCertificates,
assets?: ApplePassAssets,
): Promise<Buffer<ArrayBufferLike>>
passData — The pass.json payload (see {@link ApplePassData}).certificates — Signing material — see {@link ApplePassCertificates}.assets — Optional file-name → bytes map (icon.png, logo.png, ...).Returns: A Buffer of the zipped, signed .pkpass ready to send with Content-Type: application/vnd.apple.pkpass.
createApplePassHandler(options)Build a (req, res) => Promise<void> handler for
GET /wallet/apple/:passId returning the signed .pkpass blob.
function createApplePassHandler(
options: CreateApplePassHandlerOptions,
): (req: WalletPassRequest, res: WalletPassResponse) => Promise<void>
options — Resolver + optional file-name builder.Returns: Handler.
createGoogleWalletJwt(passClass, passObject, serviceAccount, origins, passType)Build and RS256-sign a Google Wallet JWT containing the pass class and
pass object. The returned string can be embedded directly into the
https://pay.google.com/gp/v/save/<jwt> redirect URL.
The passType selects which Google Wallet class/object keys the payload
carries, so the SAME function creates event tickets, offers/coupons, loyalty
cards, gift cards, boarding passes, transit passes, or generic passes —
routed via {@link PASS_TYPE_PAYLOAD_KEYS}. It is NOT hardcoded to event
tickets.
function createGoogleWalletJwt(
passClass: GoogleWalletClass,
passObject: GoogleWalletObject,
serviceAccount: GoogleWalletServiceAccount,
origins?: readonly string[],
passType?: GoogleWalletPassType,
): string
passClass — Pass class definition (template).passObject — Pass object definition (per-user instance).serviceAccount — Service-account email + RSA private key.origins — Optional origin domains; defaults to ['https://wallet.google'].passType — Google Wallet pass type; defaults to 'eventTicket'. Use 'offer' (or 'coupon') to issue a coupon. See {@link GoogleWalletPassType}.Returns: A signed JWT string.
createGoogleWalletPassHandler(options)Build a (req, res) => Promise<void> handler for
GET /wallet/google/:passId issuing a 302 redirect to the
https://pay.google.com/gp/v/save/<jwt> URL.
function createGoogleWalletPassHandler(
options: CreateGoogleWalletPassHandlerOptions,
): (req: WalletPassRequest, res: WalletPassResponse) => Promise<void>
options — Resolver + optional save-URL prefix override.Returns: Handler.
sha1Hex(data)Compute the SHA-1 hash of arbitrary bytes — re-exported helper used by tests that need to verify manifest hashes.
function sha1Hex(data: Buffer<ArrayBufferLike>): string
data — Bytes to hash.Returns: Lowercase 40-char hex digest.
PKPASS_CONTENT_TYPEApple .pkpass MIME type.
const PKPASS_CONTENT_TYPE: 'application/vnd.apple.pkpass'
node-forgeBoth generators cover their vendor's full pass-type range. The Apple
generator accepts all five PassKit styles (eventTicket, boardingPass,
coupon, generic, storeCard) via {@link ApplePassData}. The Google
generator selects its pass type via the passType argument
({@link GoogleWalletPassType}) and routes to the matching JWT payload keys —
eventTicket, offer/coupon, loyalty, giftCard, flight, transit,
or generic — defaulting to eventTicket.
Signing material is caller-supplied PEM strings — this package reads no
environment variables and makes NO network calls (signing is fully local).
Store the PEMs as your app's own secrets and load them in your resolver.
Apple: the developer-portal "Pass Type ID" certificate + its private key
({@link ApplePassCertificates}.password decrypts an encrypted key) and
Apple's WWDR intermediate certificate. Google: a service account
(clientEmail + RSA private key) attached to your Wallet issuer account.