Search code examples
sslapple-push-notificationshttp2walletpasskit

Wallet pass push notifications APNs


So I have completed all the signing and compressing process for Apple passes and have been able to connect the pass to my server for updating. The only problem I have left to solve is implementing push notifications.

I have been trying for a couple of days now but just can't seem to manage and I think I am missing something really simple. I am not too proficient with networking so I would really appreciate the help.

This is the log I get from guzzle with my best try.

  • Trying 17.188.136.150:443... * TCP_NODELAY set * Connected to api.push.apple.com (17.188.136.150) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /usr/local/etc/openssl/cert.pem CApath: /usr/local/etc/[email protected]/certs * SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=api.push.apple.com; OU=management:idms.group.533599; O=Apple Inc.; ST=California; C=US * start date: Mar 14 17:50:10 2019 GMT * expire date: Apr 12 17:50:10 2021 GMT * subjectAltName: host "api.push.apple.com" matched cert's "api.push.apple.com" * issuer: CN=Apple IST CA 2 - G1; OU=Certification Authority; O=Apple Inc.; C=US * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Failed sending HTTP POST request * Connection #0 to host api.push.apple.com left intact

I am using the same certificate I used for the pass I am trying to update and am sending the device token I received upon registering the pass. This is my php code.

$client = new Client();
  try {
    $result = $client->post('https://api.push.apple.com:443', [
    'headers' => ['apns-id' => <pass_type_id> , ':path' => '/3/device/<device_id_received_upon_registration>'],
    'json' => ['apns'=>''],
    'debug'=>true,
    'version'=>2.0,
    'ssl_key'   => [<path_to_key>, <key_password>],
    'cert'      => [<path_to_certificate (same one used for signing pass)>, <certificate_password>]]);
    }
    catch (GuzzleHttp\Exception\ClientException $e) {
        dd($e);
    }

I am really lost with this one. Please help me out. I also tried connecting to port 1295 with some other code I copy pasted but it wasn't working as it should and had even more difficulty debugging as I am more familiar with port 443.


Solution

  • It works for me, with small changes:

    $pem_file       = '<path_to_your_pem_file_including_private_key>';
    $pem_secret     = '<private_key_password>';
    
    $url = "https://api.push.apple.com/3/device/" . $device_push_token;
    
    $response = $client->post($url, [
        'headers' => ['apns-topic' => $pass_type_id],
        'json' => json_decode('{}'),
        'debug' => true,
        'version' => 2.0,
        'cert' => [$pem_file, $pem_secret]
    ]);
    
    var_dump($response->getStatusCode());
    var_dump($response->getReasonPhrase());
    

    What is changed is the payload that needs to be empty JSON object (but present) and the header name for the topic, it is apns-topic not apns-id. I also constructed the URL directly, not via the headers and used a pem file with private key included in it, not sure if those matter.

    Maybe you should also check what you pass at the end of the URL, it is not the device_id, but the device_push_token.

    NB: remember to always send the wallet push notification to the production apple endpoint, not the sandbox!