Add to Apple Wallet from Your .NET Application: A Step-by-Step Guide

By admin
The iOS Wallet app allows users to manage payment cards, boarding passes, tickets, gift cards, and other passes. Let’s learn how to set up, build, and distribute

Apple
ORG


Wallet
PRODUCT

passes from a .NET application.

I recently worked on the ‘Add to

Apple
ORG

Wallet’ functionality at work, allowing the addition of Event tickets to

Apple Wallet
ORG

.

The iOS Wallet app allows users to manage payment cards, boarding passes, tickets, gift cards, and other passes.


The Wallet Pass
FAC

is time and location enabled, so passes can be configured to display on the user’s device at the appropriate moment. Passes can also be updated with push notifications making it easy to notify users if details change.

In this blog post, let’s learn how you can set up, build, and distribute

Apple
ORG


Wallet
PRODUCT

passes using .NET application.

I will use an AWS Lambda Function to host the

API
ORG

endpoint for distributing the pass files. However, you can use your existing application hosting mechanism for this.

This article is sponsored by

AWS
ORG

and is part of my AWS Series.


Apple Wallet Passes

PRODUCT

Apple Wallet Passes are created as a package/bundle, containing a pass.json file that defines the pass, and image assets such as the logo and the icon.

The pass.json file contains the information that identifies the pass, the text information, and details shown on the pass in the

Wallet
ORG

app.

The below image shows the files inside a sample pass file. It has the pass.json , logo and icon images, the manifest file, and also the signature file to avoid tampering with the pass file once it’s generated.

Imaging showing the files inside a pass file. It contains the pass.json file, along with the icon and logo images, the manifest file and the signature file for the pass contents.

Generating Wallet Passes Using .NET

To generate

Apple Wallet Pass
PRODUCT

files from .NET applications, you can use the dotnet-passbook

NuGet
PRODUCT

package.

The

NuGet
PRODUCT

package makes it very easy and straightforward to create

Wallet Pass
PRODUCT

files. The

NuGet
ORG

package provides a

PassGenerator
ORG

class that takes in

one
CARDINAL

or more instances of PassGeneratorRequest .

The

PassGenerator
ORG

converts these requests into a byte array, which represents the pass bundle that contains

one
CARDINAL

or more pass files.

public async Task<APIGatewayHttpApiV2ProxyResponse> GetAppleWalletPasses() { var eventId = Guid.NewGuid(); var eventName = "

YouTube Demo Event
WORK_OF_ART

"; var venueName = "

YouTube Online
WORK_OF_ART

"; var eventDate = DateTime.Now.AddDays(55); var icon = await _appleWalletConfiguration.GetIcon(); var logo = await _appleWalletConfiguration.

GetLogo
ORG

(); var request = GeneratePassRequest(Guid.NewGuid().ToString(), eventId, icon, logo, eventName, eventDate, venueName, "Subscriber"); var generator = new

PassGenerator
PERSON

(); var requests = new List<PassGeneratorRequest>() { request }; var pass = generator.Generate(requests); }

The PassGeneratorRequest identifies

one
CARDINAL

pass that will be added to the

Apple
ORG

Wallet. If you have multiple passes to be added in the same bundle, create multiple PassGeneratorRequest .

The below code shows a sample PassGeneratorRequest that adds a pass with a

EventTicket
FAC

style. It sets the relevant properties to be shown on

the Apple Wallet Pass
ORG

.

var request = new PassGeneratorRequest { Style = PassStyle.EventTicket, PassTypeIdentifier = _appleWalletConfiguration.PassTypeIdentifier, SerialNumber = serialNumber,

GroupingIdentifier
ORG

= eventId.

ToString
PERSON

(),

BackgroundColor
ORG

= "#823EB7", LabelColor = "#000000", ForegroundColor = "#ffffff", Images = { {

PassbookImage
ORG

.Icon, icon}, {

PassbookImage
ORG

.Icon2X, icon}, {

PassbookImage
ORG

.Icon3X, icon}, {

PassbookImage
ORG

.Logo, logo}, {

PassbookImage
ORG

.

Logo2X
ORG

, logo}, {

PassbookImage
ORG

.

Logo3X
PRODUCT

, logo}, }, Description = eventName, OrganizationName = "Rahul", RelevantDate = eventDate,

ExpirationDate
WORK_OF_ART

= eventDate.AddDays(1),

AppleWWDRCACertificate
ORG

= _appleWalletConfiguration.AppleWWDRCACertificate(),

PassbookCertificate
PRODUCT

= _appleWalletConfiguration.PassbookCertificate() }; request.

AddHeaderField(new StandardField("time"
PERSON

, eventDate.ToShortTimeString(), eventDate.ToShortDateString())); request.

AddPrimaryField(new
PERSON

StandardField("name", null, eventName)); request.

AddSecondaryField(new
PERSON

StandardField("venue", "

Venue
WORK_OF_ART

", venueName)); request.

AddAuxiliaryField(new StandardField("ticketType
PERSON

", "Ticket Type", ticketType)); request.AddBackField(new StandardField("ticketHolderName-back", "Ticket holder", "Rahul Nath")); request.

AddBackField(new StandardField("ticketType-back
PERSON

", "Ticket Type", ticketType));

For information on the different fields, the pass styles available, and what each property represents check out

the Wallet Guidelines
WORK_OF_ART

and the documentation.

Setting Up Apple Certificates

To generate

Apple Wallet Passes
PRODUCT

, you need to register with

the Apple Developer Portal
ORG

and have the appropriate certificates ready to sign the Wallet passes.

You need

two
CARDINAL

certificates

Your application/company Passbook certificate along with its password


Apple WWDR
ORG

(

WorldWide Developer Relations
ORG

) certificate

You can find the full instructions to generate the certificates in the dotnet-passbook

NuGet
PRODUCT

package documentation here.

You can store these certificates and other related configurations for generating the Wallet as part of the application configuration – appsettings.json file.

I chose to store the certificate files as base64 encoded string which can be converted to a

X509Certificate2
DATE

in .NET code as shown below.

public class AppleWalletConfiguration { public string WWDRCertificateBase64 { get; set; } public string PassTypeIdentifier { get; set; } public string PassbookCertificateBase64 { get; set; } public string PassbookPassword { get; set; } public string IconUrl { get; set; } public string

LogoUrl
PRODUCT

{ get; set; } public

X509Certificate2
DATE

AppleWWDRCACertificate() => new(Convert.

FromBase64String(WWDRCertificateBase64
GPE

)); public

X509Certificate2 PassbookCertificate
WORK_OF_ART

() => new(Convert.FromBase64String(PassbookCertificateBase64), PassbookPassword); public async Task<byte[]> GetLogo() => await

LogoUrl
PRODUCT

.GetBytesAsync(); public async Task<byte[]> GetIcon() => await IconUrl.GetBytesAsync(); }

Distributing Wallet Pass Files Using .NET

To distribute

the Wallet Pass Files
FAC

generated, you can host this in an

API
ORG

endpoint and share the link via email, a button on a web page, or an app.

You can host this along with your other application code on the Cloud or your own servers.

For this post, I’ll host

this AWS Lambda
PRODUCT

, as it’s a quick and easy way to host such kind of API endpoints. Using the latest

Lambda Annotations Framework
WORK_OF_ART

makes it even easier to create and host API endpoints.

All we need to do is add

the Lambda Annotation Framework NuGet
LAW

package and wrap our Pass Generation function with the

LambdaFunction
FAC

attribute. Since this is going to be exposed as an

API
ORG

will also add the

HttpApi
ORG

attribute and specify the route and the method Get .

[LambdaFunction(Policies = "AWSLambdaBasicExecutionRole")] [HttpApi(LambdaHttpMethod.Get, "/apple-wallet")] public async Task<APIGatewayHttpApiV2ProxyResponse> GetAppleWalletPasses() { var eventId = Guid.NewGuid(); var eventName = "

YouTube Demo Event
WORK_OF_ART

"; … var generator = new

PassGenerator
PERSON

(); var requests = new List<PassGeneratorRequest>() { request }; var pass = generator.Generate(requests); return new APIGatewayHttpApiV2ProxyResponse() { Body = Convert.

ToBase64String(pass
ORG

), IsBase64Encoded = true,

StatusCode
ORG

=

200
CARDINAL

, Headers = new Dictionary<string, string> { { "

Content-Type
WORK_OF_ART

", "application/vnd.apple.pkpasses" }, { "

Content-Disposition
WORK_OF_ART

", "attachment; filename=tickets.pkpasses.zip;

filename*=UTF-8”tickets.pkpasses.zip
GPE

" } } }; }

The

HttpApi
ORG

attribute allows us to expose

the Lambda Function
ORG

over a Lambda Function URL or an HTTP API Gateway endpoint.

Both of these mechanisms expect binary content to be encoded as base64 string, with the appropriate HTTP headers set. To achieve this we can use the APIGatewayHttpApiV2ProxyResponse object as shown above.

Once deployed, you can navigate

the API Gateway
PRODUCT

URL endpoint or expose a

Lambda Function
PRODUCT

URL, and navigate to the URL from an iPhone

Safari
PERSON

browser, to add it to

Apple Wallet
ORG

.

Below is the screenshot of the pass generated using the above code, with the appropriate information we passed to create the pass.


Apple Wallet Passes
ORG

added to phone showing the front and the back details of the pass.

For debugging pass files you can use the PKPassValidator service, where you can upload a pass file and it will show any issues with the file generated.

The

Apple
ORG

Wallet pass generated above is static and does not update itself.

However most of the time you would need to deliver live updates to these passes in case any information changes for example date changes, venue changes, status updates, etc. For this, we can configure the WebServiceUrl , on the pass that it will poll for pass updates

We will learn how to configure and set up the

CallbackUrl
ORG

in a future blog post. Until then, happy coding! 👋