Helper library for all your Apple PassKit (Apple Wallet, Apple Passbook) needs: create passes, sign pass packages, receive [de]install notifications and send pass updates.
Attention: Apple Developer Account required!
- Create pass packages (
*.pkpass
files):- With Fluent-styled
PassInfoBuilder
andPassPackageBuilder
- Using
byte[]
and/orStream
as content images - Using
byte[]
orStream
orX509Certificate2
as certificates - Receive
MemoryStream
as result (save it to file or write to HttpResponse)
- With Fluent-styled
- Receive notifications from Apple about pass [de]installations and send updates:
- Add
UsePassKitMiddleware
into yourStartup.Configure()
- Implement
IPassKitService
for real processing.
- Add
var options = new PassKitOptions()
{
PassCertificate = new X509Certificate2(File.ReadAllBytes("pass.pfx")),
ConfigureNewPass =
p => p.Standard
.PassTypeIdentifier("your-pass-type-identifier")
.TeamIdentifier("your-team-identifier")
// Add more "defaults" here if needed
};
IPassKitHelper passKitHelper = new PassKitHelper(options);
public void ConfigureServices(IServiceCollection services)
{
services.AddPassKitHelper(options =>
{
options.PassCertificate = new X509Certificate2(File.ReadAllBytes("pass.pfx"));
options.ConfigureNewPass =
p => p.Standard
.PassTypeIdentifier("your-pass-type-identifier")
.TeamIdentifier("your-team-identifier")
// Add more "defaults" here if needed
});
}
Check Apple's PassKit Package Format Reference for detailed description of all fields and valid values.
Also, check Pass Design and Creation for accepted image types and sizes.
var pass = passKitHelper.CreateNewPass()
// This pass already have `PassTypeIdentifier`, `TeamIdentifier`
// and all other values you configured in options.
.Standard
.SerialNumber("PassKitHelper")
.OrganizationName("PassKit")
.Description("PassKitHelper demo pass")
.VisualAppearance
.Barcodes("1234567890128", BarcodeFormat.Code128)
.LogoText("PassKit Helper demo pass")
.ForegroundColor("rgb(44, 62, 80)")
.BackgroundColor("rgb(149, 165, 166)")
.LabelColor("rgb(236, 240, 241)")
.StoreCard
.PrimaryFields
.Add("version")
.Label("Library version")
.Value(libraryVersion)
.AuxiliaryFields
.Add("github")
.Label("GitHub link")
.Value("https://github.com/justdmitry/PassKitHelper");
var passPackage = passKitHelper.CreateNewPassPackage(pass)
.Icon(await File.ReadAllBytesAsync("images/icon.png"))
.Icon2X(await File.ReadAllBytesAsync("images/[email protected]"))
.Icon3X(await File.ReadAllBytesAsync("images/[email protected]"))
.Logo(await File.ReadAllBytesAsync("images/logo.jpg"))
.Strip(await File.ReadAllBytesAsync("images/strip.jpg"))
.Strip2X(await File.ReadAllBytesAsync("images/[email protected]"))
.Strip3X(await File.ReadAllBytesAsync("images/[email protected]"));
MemoryStream packageFile = await passPackage.SignAndBuildAsync();
// Now you have to "deliver" package file to user using any channel you have
// (save as attachment in email, download from your webapp etc)
await File.WriteAllBytesAsync("Sample.pkpass", packageFile.ToArray());
Code above will create this beautiful pass:
Apple's server can call your endpoint/server to notify about user installed/deinstalled your pass, to fetch updated version of pass (when user 'pulls down' pass in Wallet). You will be able to send pushes when you want to update pass in user's wallet. Check Apple's PassKit Web Service Reference for technical details.
public class PassKitService : IPassKitService
{
public Task<int> RegisterDeviceAsync(…) {…}
public Task<int> UnregisterDeviceAsync(…) {…}
public Task<(int Status, string[]? Passes, string? Tag)> GetAssociatedPassesAsync(…) {…}
public Task<(int StatusCode, MemoryStream? PassData)> GetPassAsync(…) {…}
public Task ProcessLogsAsync(…) {…}
}
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IPassService, PassService>();
}
public void Configure(IApplicationBuilder app)
{
...
app.UsePassKitMiddleware("/callbacks/passkit");
...
}
You need publicly-accessible url/hostname for your server, and it must be secured with https/ssl.
Add this information into your passes:
var pass = passKitHelper.CreateNewPass()
// your
// original
// pass content
// goes here
.WebService
.AuthenticationToken("some-random-secret-string")
.WebServiceURL("https://you.server.com/callbacks/passkit")
AuthenticationToken is some "secret" string that you use to differentiate legal pass owners and malicious hackers.
WebServiceURL is hostname of your server and path that equal to one in UsePassKitMiddleware
in previous step.
When users install your pass package to their iOS and Mac devices - Apple server call your RegisterDeviceAsync
. Save pushToken
value in database, and when you need to update pass on user device - call IPassKitHelper.SendPushNotificationAsync(pushToken)
.
- Join Apple Developer Program (https://developer.apple.com/account/)
- Create certificate signing request:
- Obtain your copy of OpenSSL (see https://www.openssl.org/ and/or https://wiki.openssl.org/index.php/Binaries)
- Run:
openssl req -new -newkey rsa:2048 -nodes -keyout pass.key -out pass.csr
and answer questions it asks.
- Obtain certificate:
- Go to https://developer.apple.com/account/resources/certificates/list and create new certificate, using
pass.csr
file from previous step. - Download certificate (
pass.cer
)
- Go to https://developer.apple.com/account/resources/certificates/list and create new certificate, using
- Convert key and certificate into X509 file:
- Run
openssl x509 -in pass.cer -inform der -outform pem -out pass.cer.pem
to convert certificate from DER to PEM format - Run
openssl pkcs12 -export -out pass.pfx -inkey pass.key -in pass.cer.pem
to combine certificate and key files into singlepass.pfx
file. Protect it with password (openssl will ask) to prevent unauthorized usage. - Move
pass.pfx
to your production server, delete all otherpass.*
files.
- Run
Use NuGet package PassKitHelper.
For netstandard2.0
:
- Microsoft.AspNetCore.Http.Abstractions, v2.1.1
- Microsoft.Extensions.Http, v2.1.1
- Newtonsoft.Json, v13.0.1
- System.Security.Cryptography.Pkcs, v4.7.0
- System.Text.Encodings.Web, v4.7.2
For net6.0
/ net8.0
/ net9.0
:
- Microsoft.Extensions.Http, v6.0.1 / v8.0.1 / v9.0.0
- System.Security.Cryptography.Pkcs, v6.0.5 / v8.0.1 / v9.0.0
- System.Text.Json, v6.0.11 / v8.0.5 / v9.0.0
Tests can be run with dotnet test
.
- Thanks for inspiration to Tomas McGuinness and his dotnet-passbook package.
- Thanks to maxpixel.net for sample kitten images.