Apple Wallet Pass Series (Part 2): Creating Your First Digital Card
Learn how to create your first Apple Wallet pass using Node.js
Apple Wallet Pass Series (Part 2): Creating Your First Digital Card
Now you've already generated your necessary certificates and .key files and .pem files from the first part of this series. Here is where we actually create our first apple wallet pass.
Start up your node.js application and start by installing the passkit-generator package
$ npm install passkit-generator --save
This is an open source library that allows you to split the static objects such as the logo, background, icons, etc. from the dynamic ones such as barcodes serialNumbers, user information. It aslso has a lot of cool features like allowing pass creation and popultuoin to not happen at run time as well as creating a pass from scratch or a specific premade model.
To start creating our first pass we can either create it from scratch using the pass.json file, but I think its helpful if we start with a visual example of what it could be like. The pass model creation can be performed both manually through prepareing the pass.json file or with the auxiliary of a web tool Alexander Cutti developed, Passkit Visual Designer, which can let you design your model through a neat user interface.
I'd prefer to use the pass.json method since that allows for more granular control. To start I would recommend downloading a working model and changing it since that will allow you to see how each part changes the resulting pass file. Lets start by downloading the example pass from apple developer downloads area. Now extract that folder and navigate to the WalletCompanionFiles folder and then into the SamplePasses folder, from there you will see multiple .pass folders, pick the one that matches your use case most to start I will be using the BoardingPass for this guide. Copy the entire folder into your project directory. Now you should have a project structure that looks like this, and now we are close to creating our first pass.
/your_project_directory/
├── BoardingPass.pass/
│ ├── icon.png
│ ├── icon@2x.png
│ ├── logo.png
│ ├── logo@2x.png
│ ├── pass.json
├── certs/
│ ├── signerCert.key
│ ├── signerCert.pem
│ ├── wwdr.pem
Before we start coding, it's crucial to modify the pass.json
file in your BoardingPass.pass
folder:
-
Open
BoardingPass.pass/pass.json
. -
Locate the
passTypeIdentifier
andteamIdentifier
fields. -
Replace these with your own values obtained previously.
This step is essential for the pass to be recognized as yours and to work correctly.
Now, let's create a Node.js script to generate our pass. Here's an example based on the script you provided:
const { PKPass } = require("passkit-generator");
const fs = require("fs").promises;
const path = require("path");
const getCertificatesContentsSomehow = async () => {
const certPath = path.join(__dirname, "certs");
console.log("Reading certificate files from:", certPath);
try {
const wwdr = await fs.readFile(path.join(certPath, "wwdr.pem"));
const signerCert = await fs.readFile(path.join(certPath, "signerCertificate.pem"));
const signerKey = await fs.readFile(path.join(certPath, "signerCertificateKey.key"));
// Note: signerKeyPassphrase is optional and depends on your setup
// const signerKeyPassphrase = "your_passphrase_here";
console.log("All certificate files read successfully");
return { wwdr, signerCert, signerKey /*, signerKeyPassphrase */ };
} catch (error) {
console.error("Error reading certificate files:", error);
throw error;
}
};
const generatePass = async () => {
try {
console.log("Generating pass...");
const { wwdr, signerCert, signerKey /* , signerKeyPassphrase */ } = await getCertificatesContentsSomehow();
const serialNumber ="PASS" + Date.now()
const pass = await PKPass.from({
model: path.join(__dirname, "passModels", "BoardingPass.pass"),
certificates: {
wwdr,
signerCert,
signerKey,
// signerKeyPassphrase, // Include if your key is passphrase protected
},
}, {
serialNumber:serialNumber,
description: "Boarding Pass for Flight 815",
});
// Adding some settings to be written inside pass.json
pass.localize("en", {
"gate": "GATE",
"flight": "FLIGHT",
"passenger": "PASSENGER",
});
pass.setBarcodes("36478105430"); // Random value for example
console.log("Pass created successfully");
// Generate the pass file as a buffer
console.log("Generating pass file...");
const buffer = await pass.getAsBuffer();
// Here you can use the buffer in any way you want
// save to S3, upload to a storage site and return the link
// in this example we will save it to the current directory
// Save the pass file
const outputPath = path.join(
__dirname,
`BoardingPass_${serialNumber}.pkpass`
);
await fs.writeFile(outputPath, buffer);
console.log(`Pass file generated successfully: ${outputPath}`);
return { buffer, outputPath };
} catch (error) {
console.error("Error generating pass:", error);
throw error;
}
};
// Self-executing function to run the script
(async () => {
try {
const passPath = await generatePass();
console.log("Pass generation complete. File saved at:", passPath);
} catch (error) {
console.error("Failed to generate pass:", error);
}
})();
Save this script as generatePass.js
in your project directory and run it using Node.js:
node generatePass.js
If successful, this will generate a .pkpass
file in your project directory.
-
Always replace the
passTypeIdentifier
andteamIdentifier
with your own values in both thepass.json
file and the Node.js script. -
Ensure your certificate files (
wwdr.pem
,signerCert.pem
, andsignerCert.key
) are correctly placed in thecerts
directory. -
The
model
path in thePKPass.from()
method should point to yourBoardingPass.pass
directory. -
Customize the pass fields (header, primary, secondary, auxiliary) to match your specific use case.
-
The barcode format and message can be adjusted based on your requirements.