Back to blog
guide
3 months ago

Apple Wallet Pass Series (Part 2): Creating Your First Digital Card

Learn how to create your first Apple Wallet pass using Node.js

15 min read
Apple Wallet Pass Series (Part 2): Creating Your First Digital Card

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:

  1. Open BoardingPass.pass/pass.json.

  2. Locate the passTypeIdentifier and teamIdentifier fields.

  3. 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.

  1. Always replace the passTypeIdentifier and teamIdentifier with your own values in both the pass.json file and the Node.js script.

  2. Ensure your certificate files (wwdr.pem, signerCert.pem, and signerCert.key) are correctly placed in the certs directory.

  3. The model path in the PKPass.from() method should point to your BoardingPass.pass directory.

  4. Customize the pass fields (header, primary, secondary, auxiliary) to match your specific use case.

  5. The barcode format and message can be adjusted based on your requirements.

Apple Wallet
TypeScript
iOS
Node.js

Part of series: Creating Custom Apple Wallet Cards: A Developer's Guide

View all posts in this series →