Search code examples
phpiospush-notificationapple-push-notificationsapn

Sending Apple Push Notification via PHP: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection timed out)


I have the following PHP code to send Apple Push Notifications to my app:

<?php
$body = '{"aps":{"alert":{"title":"test title","subtitle":"","body":"test body"},"badge":0,"sound":"default","additional_data":"test additional_data"}}';

$context = stream_context_create();
stream_context_set_option($context, "ssl", "local_cert", "certificate.pem");
stream_context_set_option($context, "ssl", "passphrase", "HERE_COMES_THE_PASSWORD_OF_THE_certificate.pem_FILE");
$socket = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $error, $errstr, 30, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $context);
$msg = chr(0) . chr(0) . chr(32) . pack("H*", "HERE_COMES_MY_APPLE_PUSH_TOKEN") . pack("n", strlen($body)) . $body;
$result = fwrite($socket, $msg, strlen($msg));
fclose($socket);
?>

This code is stored on my server together with the certificate file called certificate.pem.

I'm using this code unchanged since month without any problems. Today, I noticed, that I'm not getting push notifications any more.

The PHP error log shows the following:

[23-Mar-2022 11:39:45 UTC] PHP Warning:  stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection timed out) in /home2/kd37875/public_html/test/index.php on line 7
[23-Mar-2022 11:39:45 UTC] PHP Warning:  fwrite() expects parameter 1 to be resource, bool given in /home2/kd37875/public_html/test/index.php on line 9
[23-Mar-2022 11:39:45 UTC] PHP Warning:  fclose() expects parameter 1 to be resource, bool given in /home2/kd37875/public_html/test/index.php on line 10

First, I thought, there's something wrong with the certificate file. Then, I found this: https://www.pushtry.com website. If I insert the Device Token, upload the certificate.pem file, insert the Bundle ID and a message, I'm successfully receiving a push message in my app. (There's a field for password on the website but it's also working if I don't insert it. No idea, why.) So this says me, that the certificate.pem must be okay.

screenshot from https://www.pushtry.com

Do you have any idea what I'm doing wrong? Why doesn't it work any more? Did Apple change something?


Solution

  • I finally solved my problem. Apple made a change to this about a year ago. No idea why it affected me only today.

    This is the working code:

    <?php
    function sendHTTP2Push($http2_server, $apple_cert, $app_bundle_id, $message, $token) {
    if(!defined('CURL_HTTP_VERSION_2_0')) {
        define('CURL_HTTP_VERSION_2_0', 3);
    }
    $http2ch = curl_init();
    curl_setopt($http2ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
    
    curl_setopt_array($http2ch, array(
        CURLOPT_URL => "{$http2_server}/3/device/{$token}",
        CURLOPT_PORT => 443,
        CURLOPT_HTTPHEADER => array("apns-topic: {$app_bundle_id}"),
        CURLOPT_POST => TRUE,
        CURLOPT_POSTFIELDS => $message,
        CURLOPT_RETURNTRANSFER => TRUE,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSLCERT => realpath($apple_cert),
        CURLOPT_HEADER => 1
    ));
    
    $result = curl_exec($http2ch);
    if($result === FALSE) {
        throw new Exception('Curl failed with error: ' . curl_error($http2ch));
    }
    
    $status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
    return $status;
    
    curl_close($http2ch);
    }
    
    $status = sendHTTP2Push('https://api.development.push.apple.com', 'certificate.pem', 'HERE_COMES_THE_APPS_BUNDLE_ID', '{"aps":{"alert":{"title":"test title","subtitle":"","body":"test body"},"badge":0,"sound":"default","additional_data":"test additional_data"}}', 'HERE_COMES_MY_APPLE_PUSH_TOKEN');
    
    echo "Response code: ".$status;
    ?>
    

    Source: https://gist.github.com/valfer/18e1052bd4b160fed86e6cbb426bb9fc