I'm currently writing a passbook API that allows users to send push notifications to passes. Now I'm at the point of testing it but my push notification aren't arriving. I'm writing the push sending code in PHP and this is what I have so far:
$payload = json_encode(array("aps" => array("alert" => "test", "sound" => "default")));
//send it to all devices found
while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
error_log($row['pushToken']);
//write the push message to the apns socket connection
$msg = chr(0) .
pack("n",32) .
pack('H*', $row['pushToken']) .
pack("n",strlen($payload)) .
$payload;
fwrite($fp, $msg);
}
Is there anything I'm missing here or doing wrong? The certificate is valid and connecting to the APNS server doesn't give me any error.
Thanks!
I've solved this problem by re-creating the certificate, doing the openssl magic again and use the new certicates. Now it's working, so I think there was something bugged in my certificate :)
Related
I am working on push notifications and I want to send chat push notifications to IOS using php. But when I send 5 push notifications to apns (Apple Push Notification Server) then apns discard the old push notification and send only the most recent push notification to device when device get online.
I search the for solution on internet and one solution that I found is to set the notification expiry time. So I implemented this solution by my actual problem not solved.
Is there any way that solve my problem. Suggest any usefull solution or reference site.
IOS push notification with PHP
Below is my sample code
public function sendIOSNotification($tokens, $data, $envoirement = 'production') {
try {
$payload = json_encode($this->setIosNotificationDataParameters($data));
$deviceTokens = str_replace(array(' ', '<', '>'), '', $tokens['ios']);
// FUNCTION NOTIFICATIONS
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', config('push-notification.appNameIOS.certificate_' . $envoirement));
stream_context_set_option($ctx, 'ssl', 'passphrase', 'push');
//send notification
$fp = stream_socket_client(
config('push-notification.appNameIOS.ios_push_notification_' . $envoirement), $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx
);
$res = [];
foreach ($deviceTokens as $deviceToken) {
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken)
. pack('n', strlen($payload)) . $payload
.pack('N', time()).pack('N', time() + 86400);
$res = json_encode($result);
}
fclose($fp);
\Log::info("=== IOS Notification Send Successfully ===");
return true;
} catch (\Exception $ex) {
$messages = $ex->getMessage() . '::' . $ex->getFile() . '( ' . $ex->getLine() . ' )';
\Log::ifno("===Push Notificaion Exception===");
\Log::ifno($messages);
return true;
}
}
You cannot do what you intend according to the documentation. It is the documented behavior:
Quality of Service, Store-and-Forward, and Coalesced Notifications
Apple Push Notification service includes a Quality of Service (QoS)
component that performs a store-and-forward function. If APNs attempts
to deliver a notification and the destination device is offline, APNs
stores the notification for a limited period of time and delivers it
when the device becomes available again. This component stores only
the most recent notification per device and per app. If a device is
offline, sending a notification request targeting that device causes
the previous request to be discarded. If a device remains offline for
a long time, all its stored notifications in APNs are discarded.
source: apple push notifications documentation
This means just an offline notification per user per app.
You should architecture your application in a different way. First of all, for a chat application you cannot expect sending thousand push notifications when the device is back online. You will have to implement some extra mechanism for your app to retrieve the old messages if you want to show them in your app
I'm developing apple application for my company,
I'm working on server side using PHP, laravel framework to send message to APNS.
For testing, I use hardcode device token.
I get it work on one device that I hardcode.
so I'm advance to function that send apns, a device token that requested in function.
so when I call this function, it get device token from database and send to apns.
I get error
fwrite(): SSL operation failed with code 1. OpenSSL Error messages: error:1409F07F:SSL routines:SSL3_WRITE_PENDING:bad write retry
and
fwrite(): SSL: Broken pipe
I change the code back to first testing, but it does not work like before.
anyone have idea what I made wrong or did I made a mistake in my request?
for note this is my code
$apnsServer = 'ssl://gateway.sandbox.push.apple.com:2195';
$privateKeyPassword = 'my_private_key';
$id = $request->input('id');
$message = "message_here";
$userSelected = User::select('device_token')->where('id', $id)->first();
$deviceToken = str_replace(' ', '', $userSelected['device_token']);
$pushCertAndKeyPemFile = $_SERVER['DOCUMENT_ROOT'].'my_certificate.pem';
$stream = stream_context_create();
stream_context_set_option($stream, 'ssl', 'passphrase', $privateKeyPassword);
stream_context_set_option($stream, 'ssl', 'local_cert', $pushCertAndKeyPemFile);
$connectionTimeout = 30;
$connectionType = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
$connection = stream_socket_client($apnsServer, $errorNumber, $errorString, $connectionTimeout, $connectionType, $stream);
if (!$connection){
echo "Failed to connect to the APNS server. Error = $errorString <br/>";
exit;
}else{
echo "Successfully connected to the APNS. Processing...</br>";
}
$messageBody['aps'] = array('alert' => $message, 'sound' => 'default', 'badge' => 1);
$payload = json_encode($messageBody);
$notification = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
$wroteSuccessfully = fwrite($connection, $notification, strlen($notification));
if (!$wroteSuccessfully){
echo "Could not send the message<br/>";
} else {
echo "Successfully sent the message<br/>";
}
After some research, i've found my problem.
When our server send PUSH message to APNS, there is response error need to check for every request.
After check error, i get error message 8, invalid token
When using development certificate, and there is user register using development application, they get device token
but when you change to production, you have to change production certificate and registered user need to get new device token to make PUSH notification work
because device token you get in development apps, will not work with production certificate..
it's totally my mistake to miss important things..
I create all certificates to develope push notify for my app iPhone.
If I test it with file .pem created by developer certificates it work, but if I use file .pem created by production certificates it not work.
This is my code:
public function push($deviceToken,$badge,$message,$deviceType) {
$sound = "default";
// Construct the notification payload
$body = array();
$body['aps'] = array("alert" => $message);
if ($badge)
$body['aps']['badge'] = $badge;
if ($sound)
$body['aps']['sound'] = $sound;
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apns-prod.pem');
$fp = stream_socket_client("ssl://gateway.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
print "Failed to connect $err $errstrn";
return;
} else {
print "Connection OK\n";
}
$payload = json_encode($body);
$msg = chr(0) . pack('n',32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack('n',strlen($payload)) . $payload;
fwrite($fp, $msg);
fclose($fp);
}
Any suggest?
Production push certificates will only work when you send notification to an app signed with an ad-hoc distribution certificate (or final release). Developer push certificates will only work with apps signed with a developer distribution certificate (ie installed to the device from XCode).
You can try your production certificate by changing you scheme properties:
Click on your target scheme, on top left of Xcode window, then Edit Scheme.
In the "Run yourTarget" tab, set Release as build configuration and "None" as debugger.
Of course, your target must have an ad-hoc provisionning profile configured.
Plug your iPhone and Run with iOSDevice configuration.
Does it work ?
EDIT : my appologizes, I would say Release instead of Ad-hoc (i corrected it)
Be sure to create a NEW adhoc and or appstore distribution certificate AFTER creating the certificates for the push process. The adhoc and appstore certificates change for push-enables apps.
I am not sure how to send a push notification to multiple tokens. With my script it seems to only send a push notification to one... as per Apple it would be better to have one open connection and keep it open and send messages when needed. Not sure how to do it on my php server...
$payload['aps'] = array('alert' => "New Cave report for ".$caveName,'badge' => 1, 'sound' => 'default');
$payload['condition'] = array('conditionID' => $ccID, 'caveName' => $caveName);
$payload = json_encode($payload);
// Connection Part
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = '/var/www/web543/files/apns-dev.pem';
if (!file_exists($apnsCert) )
{
echo "Certification file not found!";
} else
{
$streamContext = stream_context_create();
stream_context_set_option($streamContext,'ssl','local_cert',$apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort,$error,$errorString,2,STREAM_CLIENT_CONNECT,$streamContext);
if ( !$apns )
{
echo "Connection Failed!".$errorString;
} else
{
$db->query("SELECT DISTINCT token FROM notifications WHERE userID != '{$userID}'");
if ($db->num_rows()>0)
{
while ($db->next_record())
{
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $db->f('token'))) . chr(0) . chr(strlen($payload)) . $payload;
if ( fwrite($apns, $apnsMessage) === FALSE )
{
echo "Can not write";
}
}
}
}
fclose($apns);
}
some output of $apnsMessage
Token: 5a984922e19eab54f78fd54e24d5b02a3d30ccdbbeee34aadbdacaa687ee1261 Message: Z˜I"áž«T÷ÕN$Õ°*=0ÌÛ¾î4ªÛÚʦ‡îa†{"aps":{"alert":"New Cave report for Test Entry","badge":1,"sound":"default"},"condition":{"conditionID":"1","caveName":"Test Entry"}}
Token: c607acc70bd4885bf56f3b4827523023bf93a1d644626768ab0304bb3b4414dc Message: ƬÇÔˆ[õo;H'R0#¿“¡ÖDbgh«»;D܆{"aps":{"alert":"New Cave report for Test Entry","badge":1,"sound":"default"},"condition":{"conditionID":"1","caveName":"Test Entry"}}
Token: 785ec3128972bd3d4c3e6fa1eeead97b73b0696e2361339a2467e6ba775b83ea Message: x^Ér½=L>o¡îêÙ{s°in#a3š$gæºw[ƒê†{"aps":{"alert":"New Cave report for Test Entry","badge":1,"sound":"default"},"condition":{"conditionID":"1","caveName":"Test Entry"}}
Token: c592487e3c71e921d0b7a825b66ed5e58070fee709131535ac391f14febbcfdc Message: Å’H~Token: 061bc20ba3a0fc17c689e052b42b5789f502a52d43180ea114e3212077045315 Message: £ üƉàR´+W‰õ¥-C¡ã! wS†{"aps":{"alert":"New Cave report for Test Entry","badge":1,"sound":"default"},"condition":{"conditionID":"1","caveName":"Test Entry"}}
Token: 26fb66fef67a122ca456f106363115285d4d7156e7c8ab6e51bd5bfa9bab2d03 Message: &ûfþöz,¤Vñ61(]MqVçÈ«nQ½[ú›«-†{"aps":{"alert":"New Cave report for Test Entry","badge":1,"sound":"default"},"condition":{"conditionID":"1","caveName":"Test Entry"}}
Ok. I found the problem.
When compiling as Debug it will send only on the first device when using the DEV certification. If I use Deployment like Ad-Hoc I have to use the Prod Servers and Prod Certificates.
Now everything works.
I think you have to run this script in loop according to the count of token.
Means first get the token array and then according to the count make a loop. Then take one device token from the index 0 and run this script. Then selcect another token at index 1 and then run again this script.
To send push notification to multiple device you have connect each time to apple server and close connection and again open and send.This is the process.
Yeah you should not be making and tearing down the socket connection every time you need to send the push. Once you establish the socket connection, you can write as many as push messages you want to any number of devices.
What is the exact problem that you are observing? Is your socket connection getting dropped?
Have a look at this question if you need additional help : How to send Push Notification to multiple devices?
I have a quick question. For the apple push notification service, I need to have my own server, which will send out the push notifications to the apple servers, right?
Can I do this with a simple PHP script on a regular webhosting account, or do I need a dedicated server with full blown admin access for that?
If this is possible in PHP, can anyone point me to some samples that can help me get started on this? Right now, I am pretty confident I won't have trouble implementing the client-side part, but the server side is still somewhat of a mystery to me...
Thank you!
Florian
You probably will be able to do this on a limited hosting account, as long as you can leave the connection open to the server most of the time. Some sample code:
http://code.google.com/p/php-apns/
Note also that some companies are starting up services to help you specifically with push hosting (I'll keep the post neutral and not mention names, I'm not sure which services are running just yet).
The Main problem with APNS is ports
so many providers doesnt open 2195 port
so concentrate on that initially then go for the host provider
Here is the code what i tried, but one problem is not able to get device notification
Hi ,
i tried the following code (PHP)
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apple_push_notification_production.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
if($apns)
{
echo "Connection Established<br/>";
$deviceToken = '**********';//masked
$body = array();
$body['aps'] = array(’alert’ => "test message");
//$body['aps']['badge'] = 1;
$payload = json_encode($body);
$apnsMessage = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
print "sending message :" . $apnsMessage . "<br/>";
print "sending payload :" . $payload . "<br/>";
fwrite($apns, $apnsMessage);
}
else
{
echo "Connection Failed";
echo $errorString;
echo $error;
}
socket_close($apns);
fclose($apns);
reply is Connection Established
sending message :�� d^÷Îå0ZCd%1ÄuwOOYš'ÊÈ}ârðm¾Í�,{"aps":{"\u2019alert\u2019":"test message"}}
sending payload :{"aps":{"\u2019alert\u2019":"test message"}}
But am not able to get the notification
any help?