Part 2: Push Notification for Updating Apple Pass in ASP.NET

Yang Zhou
3 min readJan 10, 2019

--

I’m going to show you how to automatically update a Pass when something changed from your server. There are 3 steps: send a notification to Apple server, Wallet will make a GET request to get the latest serial numbers, Wallet will make another GET request to get the latest Pass.

I highly recommend adding debug points or logs to check which web services have been triggered, and what error message you received from Apple/Wallet.

Three Steps for Pushing Notification for Updating Apple Passes in Wallet

Step 1: Send an empty notification to Apple server

For example, you’re the United Airline and customers saved boarding Passes form your site. If a flight was delayed (data changed on your server), how can you notify all related customers? First, in your database, find push tokens for all devices that you want to notify; Then send a notification to Apple server (“gateway.push.apple.com” at port 2195) for each push token.

I made a huge mistake about the certificate here and wasted a lot of time. DON’T USE “Apple Push Notification Service SSL” certificate, RE-USE the certificate for the Pass, which is “Pass Type ID Certificate”. Also, please note that the device token actually is the pushToken you got from registering a device (How to register a device? more details here).

For my case, I didn’t see a notification pop up on my iphone, however, I saw Wallet was trying to call my web services after sending out notification. This means this step is good.

public void NotifyAppleDevices(string passTypeIdentifier, string serialNumber)
{
List<string> devicePushTokens = getDevicePushTokenList(passTypeIdentifier, serialNumber);
foreach (string deviceIdentifier in devicePushTokens)
{
SendEmptyPushNotification(pushToken);
}
}
private void SendEmptyPushNotification(string pushToken)
{
string thumbprint = "THUMBPRINT_FOR_PASS_TYPE_ID_CERTIFICATE";
string server = "gateway.push.apple.com"; // Production
using (TcpClient tcpClient = new TcpClient(server, 2195))
{
using (SslStream sslStream = new SslStream(tcpClient.GetStream()))
{
try
{
X509Certificate2Collection certs = new X509Certificate2Collection();
X509Certificate cert = GetAppleServerCert(thumbprint);
certs.Add(cert);
sslStream.AuthenticateAsClient(server, certs, SslProtocols.Default, false);
}
catch (AuthenticationException exp)
{
throw new AuthenticationException(exp.Message);
}
catch (IOException exp)
{
throw new IOException(exp.Message);
}
MemoryStream memoryStream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(memoryStream);
writer.Write((byte)0);
writer.Write((byte)0);
writer.Write((byte)32);
writer.Write(HexStringToByteArray(pushToken.ToUpper()));
string payload = "{\"aps\":\"\"}";
writer.Write((byte)0);
writer.Write((byte)payload.Length);
byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payload);
writer.Write(b1);
writer.Flush();
byte[] array = memoryStream.ToArray();
sslStream.Write(array);
sslStream.Flush();
}
}
}

Step 2: Wallet calls your web service to get serial numbers

If the first step goes well, Wallet will immediately call your web service to ask for which serial numbers need to be updated. The web service is:

Getting the Serial Numbers for Passes Associated with a Device
GET request to webServiceURL/version/devices/deviceLibraryIdentifier/registrations/passTypeIdentifier?passesUpdatedSince=tag

I wrote more details about this web service here. Wallet will GET request to your server with a timestamp tag. Searching in your database, and only returns serial numbers that need to be updated. Return a JSON with 2 items: lastUpdated and serialNumbers. You can directly use the current datetime for lastUpdated. For example:

{“lastUpdated”: “9/1/2019 12:15:57”, “serialNumbers”: [“ApplePass_1”, “ApplePass_2”]}

Next time when Wallet GET request to this web service, it will pass 9/1/2019 12:15:57 for URL parameter passesUpdatedSince. Return HTTP status 204 if serialNumbers list is empty.

If this step goes well, Wallet will continue to make another GET request to another web service to get the new Pass.

Step 3: Wallet calls your web service to get the Pass

Since you’ve sent Wallet a list of serialNumbers that need to be updated. Wallet will make GET request to each of them by calling:

Getting the Latest Version of a Pass

GET request to webServiceURLversion/passes/passTypeIdentifier/serialNumber

More details about this web service can be found here. Remember to put LastModified in the response header.

~ Happy Coding ~

--

--