Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fe288c0
Fixed useAutomaticColors property
alexandercerutti Sep 2, 2025
4692c68
Deprecated Field interface in order to rename it according to Apple n…
alexandercerutti Sep 2, 2025
eb6de01
Added schemas for UpcomingPassInformation
alexandercerutti Sep 2, 2025
4b70527
Updated signatures using Field instead of PassFieldContent
alexandercerutti Sep 3, 2025
d627c07
Added StyleSchemes to generate enhanced boarding passes
alexandercerutti Sep 15, 2025
57f0b1a
Added more missing new iOS 26 properties for flights
alexandercerutti Sep 15, 2025
1944122
Added same properties to Joi Scheme
alexandercerutti Sep 15, 2025
af50eba
Updated preferredStyleSchemes setter and getter checks and messages
alexandercerutti Sep 15, 2025
13d037e
Added some descriptions and missing properties to Semantics
alexandercerutti Sep 15, 2025
c23c0be
Added new missing Semantics properties for boarding since iOS 26
alexandercerutti Sep 15, 2025
578e26e
Added URL regex validation for many URL properties in the root
alexandercerutti Sep 16, 2025
bf487db
Improved some comments
alexandercerutti Sep 16, 2025
e98bd13
Added semanticBoardingPass example to models
alexandercerutti Sep 16, 2025
51388dd
Added upgradeURL to semanticBoardingPass example model
alexandercerutti Sep 16, 2025
8fc00dc
Added implementation method to add upcoming pass information
alexandercerutti Sep 17, 2025
4dec0d9
Added commetn for upcomingPassInformation
alexandercerutti Sep 17, 2025
5657221
Added upcomingPassInformation getter
alexandercerutti Sep 17, 2025
76c9d4e
Fixed self-hosted example lack of __dirname and improved log
alexandercerutti Sep 17, 2025
d823ebf
Added a sample model for upcoming events
alexandercerutti Sep 17, 2025
95dfb64
Fixed missing __dirname in all the self-hosted examples
alexandercerutti Sep 17, 2025
756f301
Updated UpcomingPassInformationEntry schema
alexandercerutti Sep 17, 2025
95983ff
Added support to relevantDate renaming to date
alexandercerutti Sep 18, 2025
ad8fc8c
Fixed tests for iOS 26
alexandercerutti Sep 23, 2025
9593f7d
3.5.0-alpha.1
alexandercerutti Sep 23, 2025
9fe0b5f
Fixed usage of deprecated Field interface
alexandercerutti Sep 25, 2025
2e8278b
Fixed typos
alexandercerutti Sep 25, 2025
16e61c0
Fixed possible undefined date case
alexandercerutti Sep 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
156 changes: 156 additions & 0 deletions examples/models/posterEventTicketWithUpcomingEvents.pass/pass.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.passkitgenerator",
"teamIdentifier": "F53WB8AE67",
"groupingIdentifier": "ticket-demo-upcoming-events",
"description": "Description",
"organizationName": "A some kind of event happening tomorrow",
"backgroundColor": "#ffffff",
"foregroundColor": "#000000",
"labelColor": "#FF0000",
"logoText": "Demo",
"preferredStyleSchemes": ["posterEventTicket", "eventTicket"],
"eventTicket": {
"headerFields": [
{
"key": "event_date",
"label": "event-date",
"value": "26.09.2024"
}
],
"primaryFields": [
{ "key": "event_name", "label": "event-name", "value": "Dune" }
],
"additionalInfoFields": [
{
"key": "additionalInfo-1",
"label": "Additional Info 1",
"value": "The text to show"
},
{
"key": "additionalInfo-2",
"label": "Additional Info 2",
"value": "The text to show 2"
},
{
"key": "lineItem3",
"label": "Emergency Contact",
"value": "+1 8716 12736131",
"dataDetectorTypes": ["PKDataDetectorTypePhoneNumber"]
},
{
"key": "lineItem4",
"label": "Test link",
"value": "https://apple.com",
"dataDetectorTypes": ["PKDataDetectorTypeLink"],
"attributedValue": "<a href=\"https://apple.com\">Used literally on iPhone, used correctly on Watch</a>"
}
]
},
"semantics": {
"venueParkingLotsOpenDate": "2025-10-09T04:00:00+00:00",
"venueGatesOpenDate": "2025-10-09T06:00:00+00:00",
"eventLiveMessage": "This event is going to start soon! Try to relax your anus (cit.)",
"eventType": "PKEventTypeLivePerformance",
"eventName": "South Bay Jazz Festival",
"entranceDescription": "Event at The Stadium",
"venueLocation": {
"latitude": 51.555557,
"longitude": 0.238041
},
"venueName": "The Stadium",
"performerNames": ["Lady Gaga"],
"eventStartDate": "2025-10-08T22:00:00+00:00",
"eventEndDate": "2025-10-09T23:59:59+00:00",
"tailgatingAllowed": true,
"seats": [
{
"seatNumber": "5",
"seatRow": "3",
"seatSection": "100",
"seatSectionColor": "#FFD700"
}
],
"artistIDs": ["984117861"]
},
"directionsInformationURL": "https://www.displaysomeinfoexample.com",
"contactVenueWebsite": "https://www.venueexample.com",
"relevantDates": [
{
"startDate": "2025-10-09T17:00:00+01:00",
"endDate": "2025-10-09T23:59:59+01:00"
},
{
"startDate": "2025-10-10T17:00:00+00:00",
"endDate": "2025-10-10T19:00:00+00:00"
},
{
"startDate": "2025-10-11T17:00:00+00:00",
"endDate": "2025-10-11T19:00:00+00:00"
}
],
"nfc": {
"message": "message",
"encryptionPublicKey": "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADwKMBv29ByaSLiGF0FctuyB+Hs2oZ1kDIYhTVllPexNE="
},
"upcomingPassInformation": [
{
"type": "event",
"identifier": "dnb-event",
"isActive": true,
"name": "Drum'n'Bass Night",
"dateInformation": {
"date": "2025-09-18T00:00:00+01:00",
"timeZone": "Europe/Rome",
"isAllDay": true
},
"semantics": {
"venueName": "The Stadium",
"venuePlaceID": "IB452E0A3979253B0",
"venueLocation": {
"latitude": 37.334859,
"longitude": -122.00904
},
"seats": [
{
"seatNumber": "1",
"seatRow": "A",
"seatSection": "100"
}
],
"performerNames": ["Maduk"],
"venueParkingLotsOpenDate": "2025-11-09T04:00:00+00:00",
"venueGatesOpenDate": "2025-11-09T06:00:00+00:00",
"eventType": "PKEventTypeLivePerformance",
"eventName": "South Bay Jazz Festival",
"entranceDescription": "Event at The Stadium",
"eventStartDate": "2025-11-08T22:00:00+00:00",
"eventEndDate": "2025-11-09T23:59:59+00:00"
},
"additionalInfoFields": [
{
"key": "huehueheu",
"label": "Line Item 1",
"value": "Value 1"
}
],
"backFields": [
{
"key": "huehueheu1",
"label": "Line Item 1",
"value": "Value 1"
}
],
"URLs": {
"merchandiseURL": "https://www.example.com/merchandise",
"parkingInformationURL": "https://www.example.com/parking",
"transferURL": "https://www.example.com/transfer"
},
"images": {
"venueMap": {
"reuseExisting": true
}
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions examples/models/semanticBoardingPass.pass/pass.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"formatVersion": 1,
"description": "iOS 26 Semantic Boarding Pass",
"serialNumber": "2w5zzretyg7n168c7c4053e961",
"teamIdentifier": "F53WB8AE67",
"passTypeIdentifier": "pass.com.passkitgenerator",
"foregroundColor": "rgb(0, 0, 0)",
"backgroundColor": "rgb(255, 255, 255)",
"labelColor": "rgb(0, 0, 0)",
"preferredStyleSchemes": ["semanticBoardingPass", "boardingPass"],
"organizationName": "A Generic Random Organization",
"boardingPass": {},
"barcodes": [
{
"messageEncoding": "utf-8",
"message": "Passkit-generator generated this",
"format": "PKBarcodeFormatQR",
"altText": "Passkit-generator generated this"
}
],
"semantics": {
"internationalDocumentsAreVerified": true,
"internationalDocumentsVerifiedDeclarationName": "Travel Ready (custom tag)",
"airlineCode": "FI",
"flightNumber": 533,

"originalDepartureDate": "2025-09-30T14:00:00Z",
"originalArrivalDate": "2025-09-30T16:00:00Z",
"originalBoardingDate": "2025-09-30T13:30:00Z",
"departureAirportCode": "MUC",
"departureCityName": "Munich",
"departureLocationTimeZone": "Europe/Berlin",
"departureGate": "A12",
"destinationGate": "B34",
"destinationAirportCode": "KEF",
"destinationCityName": "Reykjavik",
"destinationLocationTimeZone": "Atlantic/Reykjavik",
"passengerName": { "givenName": "John", "familyName": "Doe" },
"loungePlaceIDs": ["IB452E0A3979253B0"]
},
"upgradeURL": "https://www.example.com",
"entertainmentURL": "https://www.example.com"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions examples/self-hosted/src/PKPasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@
* A feedback to Apple have been sent for this.
*/

import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";
import { promises as fs } from "node:fs";
import { fileURLToPath } from "node:url";
import path from "node:path";
import { promises as fs } from "node:fs";
import { PKPass } from "passkit-generator";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// *************************** //
// *** EXAMPLE FROM NOW ON *** //
Expand Down
3 changes: 3 additions & 0 deletions examples/self-hosted/src/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import path from "node:path";
import { PKPass } from "passkit-generator";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.route("/fields/:modelName").get(async (request, response) => {
const passName =
Expand Down
10 changes: 9 additions & 1 deletion examples/self-hosted/src/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
*/

import path from "node:path";
import { fileURLToPath } from "node:url";
import { PKPass } from "passkit-generator";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.route("/localize/:modelName").get(async (request, response) => {
const passName =
request.params.modelName +
Expand All @@ -31,7 +34,12 @@ app.route("/localize/:modelName").get(async (request, response) => {
signerKeyPassphrase: certificates.signerKeyPassphrase,
},
},
request.body || request.params || request.query,
Object.assign(
{
serialNumber: Math.random().toString(36).substring(2, 15),
},
request.body || request.query || {},
),
);

// Italian, already has an .lproj which gets included...
Expand Down
3 changes: 3 additions & 0 deletions examples/self-hosted/src/scratch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

import path from "node:path";
import { promises as fs } from "node:fs";
import { fileURLToPath } from "node:url";
import { PKPass } from "passkit-generator";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function getRandomColorPart() {
return Math.floor(Math.random() * 255);
}
Expand Down
3 changes: 3 additions & 0 deletions examples/self-hosted/src/setBarcodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

import { PKPass } from "passkit-generator";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.route("/barcodes/:modelName").get(async (request, response) => {
const passName =
request.params.modelName +
Expand Down
3 changes: 3 additions & 0 deletions examples/self-hosted/src/setExpirationDate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
*/

import path from "node:path";
import { fileURLToPath } from "node:url";
import { PKPass } from "passkit-generator";
import { app } from "./webserver.js";
import { getCertificates } from "./shared.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

app.route("/expirationDate/:modelName").get(async (request, response) => {
if (!request.query.fn) {
response.send(
Expand Down
2 changes: 1 addition & 1 deletion examples/self-hosted/src/webserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export const app = express();
app.use(express.json());

app.listen(8080, "0.0.0.0", () => {
console.log("Webserver started.");
console.log("Webserver started on port 8080");
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "passkit-generator",
"version": "3.4.0",
"version": "3.5.0-alpha.1",
"description": "The easiest way to generate custom Apple Wallet passes in Node.js",
"main": "lib/cjs/index.js",
"types": "lib/types/index.d.ts",
Expand Down
10 changes: 5 additions & 5 deletions specs/PKPass.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ describe("PKPass", () => {
});
});

describe("eventTicket new layout", () => {
describe("iOS 18 / iOS 26 new layouts", () => {
it("should contain preferredStyleSchemes if coming from an imported pass json", () => {
const passjson = modelFiles["pass.json"];
const changedPassJson = Buffer.from(
Expand Down Expand Up @@ -1355,16 +1355,16 @@ describe("PKPass", () => {
});
});

it("preferredStyleSchemes setter should throw if pass is not an eventTicket", () => {
pkpass.type = "boardingPass";
it("preferredStyleSchemes setter should throw if pass is not an eventTicket or boardingPass", () => {
pkpass.type = "storeCard";

expect(() => {
pkpass.preferredStyleSchemes = ["posterEventTicket", "eventTicket"];
}).toThrowError();
});

it("preferredStyleSchemes getter should throw if pass is not an eventTicket", () => {
pkpass.type = "boardingPass";
it("preferredStyleSchemes getter should throw if pass is not an eventTicket or boardingPass", () => {
pkpass.type = "storeCard";

expect(() => {
pkpass.preferredStyleSchemes;
Expand Down
Loading
Loading