APNS (Apple Push Notification Service) with Node JS
Asked Answered
S

2

4

I am looking to create APNS (Apple Push Notification Service), where the server will be sending notifications to the iOS devices. I am able to make the push notifications work via PHP using the SAME device token and the SAME certificate, however, I would like to send notifications via Node JS instead of PHP.

I have the following valid files/certificates to help me get started:

  • cert.pem
  • key.pem
  • aps_development.cer
  • cert.p12
  • key.p12,
  • ck.pem

I've been looking through several resources/links such as:

After doing so, I was able to come up with the following sample code, where PASSWORD stands for the password of the key.pem and TOKEN stands for my device's token:

    var apn = require("apn");
    var path = require('path');
    try {
        var options = {
            cert: path.join(__dirname, 'cert.pem'),         // Certificate file path
            key:  path.join(__dirname, 'key.pem'),          // Key file path
            passphrase: '<PASSWORD>',                             // A passphrase for the Key file
            ca: path.join(__dirname, 'aps_development.cer'),// String or Buffer of CA data to use for the TLS connection
            production:false,
            gateway: 'gateway.sandbox.push.apple.com',      // gateway address
            port: 2195,                                     // gateway port
            enhanced: true                                  // enable enhanced format
        };
        var apnConnection = new apn.Connection(options);
        var myDevice = new apn.Device("<TOKEN>");
        var note = new apn.Notification();
        note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
        note.badge = 3;
        note.sound = "ping.aiff";
        note.alert = "You have a new message";
        note.payload = {'msgFrom': 'Alex'};
        note.device = myDevice;
        apnConnection.pushNotification(note);



        process.stdout.write("******* EXECUTED WITHOUT ERRORS************ :");


    } catch (ex) {
        process.stdout.write("ERROR :"+ex);
    }

I get no errors when executing this code, but The problem is that no notification is received on my iOS device. I have also tried setting the ca:null & debug:true (in options var). But same thing happens.

Again, when I use the ck.pem & device token that I have and use it with PHP, it works, but i'm not able to make it work in Node JS. PLEASE HELP!!

Thank you so much!

Serene answered 14/4, 2016 at 20:19 Comment(0)
I
4

You are probably running into the asynchronous nature of NodeJS itself. I use the same node-apn module with great success. But you don't just call it directly like you're used to in PHP - that's a synchronous model that doesn't map from PHP->Node. Your process is exiting before anything can actually happen - the apnConnection.pushNotification(note); is an asynchronous call that just barely gets started before your script returns/exits.

As noted in the node-apn docs you probably want to "listen for" additional events on apnConnection. Here's an excerpt of code that I use to log out various events that are occurring on the connection after it's created:

// We were unable to initialize the APN layer - most likely a cert issue.
connection.on('error', function(error) {
    console.error('APNS: Initialization error', error);
});

// A submission action has completed. This just means the message was submitted, not actually delivered.
connection.on('completed', function(a) {
    console.log('APNS: Completed sending', a);
});

// A message has been transmitted.
connection.on('transmitted', function(notification, device) {
    console.log('APNS: Successfully transmitted message');
});

// There was a problem sending a message.
connection.on('transmissionError', function(errorCode, notification, device) {
    var deviceToken = device.toString('hex').toUpperCase();

    if (errorCode === 8) {
        console.log('APNS: Transmission error -- invalid token', errorCode, deviceToken);
        // Do something with deviceToken here - delete it from the database?
    } else {
        console.error('APNS: Transmission error', errorCode, deviceToken);
    }
});

connection.on('connected', function() {
    console.log('APNS: Connected');
});

connection.on('timeout', function() {
    console.error('APNS: Connection timeout');
});

connection.on('disconnected', function() {
    console.error('APNS: Lost connection');
});

connection.on('socketError', console.log);

Equally important, you need to make sure your script STAYS RUNNING while the async requests are being processed. Most of the time, as you build a bigger and bigger service, you're going to end up with some kind of event loop that does this, and frameworks like ActionHero, ExpressJS, Sails, etc. will do this for you.

In the meantime, you can confirm it with this super-crude loop, which just forces the process to stay running until you hit CTRL+C:

setInterval(function() {
    console.log('Waiting for events...');
}, 5000);
Innsbruck answered 15/4, 2016 at 2:48 Comment(7)
I really appreciate your reply. Thanks for the clarifications, I now understand a bit more about it. For testing, i've placed the events inside the setInterval as you stated. Unfortunately, when i run it, it returns the following errors: **if i call "apnConnection.pushNotification(note);" then 'transmissionError' is called and i get an error code 2 (which is missing device token). **If i call "apnConnection.pushNotification(note,myDevice);" i get the following error message: [Error: Connect timed out]Serene
Try removing some of your unnecessary options, particularly ca, gateway, port, and enhanced. In my setup I use only three options: cert, key, and production: true/false. I followed the instructions at github.com/argon/node-apn/wiki/Preparing-Certificates to prepare the cert and key files.Innsbruck
Okay, I did everything you've stated. I am receiving events now if i make an error on purpose. The request seems to go through, but I'm not receiving any response. The only thing I get is: [Error: Connect timed out] from the 'transmissionError' event. After looking online, I found that people state that it may be because of a network problem. So I called HostGator to verify, but they stated that I should not have any issues at all. Do you have any idea what may cause this problem? Here is my reference link: github.com/argon/node-apn/issues/327#issuecomment-152564768Serene
Try running it locally on your own computer, not up in HostGator, and see what happens.Innsbruck
Good idea. I will download apn on my localhost and let you know!Serene
The reason why it was not working was because the port 2195 was blocked. This issue has been solved. I really appreciate all your help and time! One last question, is it a good way to send notifications using the setInterval function or should i do it using a argon's apn: github.com/argon/node-apn. Please let me knowSerene
I personally would not use setInterval if just because you can get overlapping requests if the first one takes too long to complete. But either way works - this is just my personal preference!Innsbruck
G
1

I will explain it with simple code

  1. First install apn module using this command npm install apn .
  2. Require that module in code

    var apn = require('apn');

      let service = new apn.Provider({
        cert: "apns.pem",
        key: "p12Cert.pem",
        passphrase:"123456",
        production: true //use this when you are using your application in production.For development it doesn't need
        });
    
  3. Here is the main heart of notification

let note = new apn.Notification({
    			payload:{
    			 "staffid":admins[j]._id,
    			 "schoolid":admins[j].schoolid,
    			 "prgmid":resultt.programid
    			},
    category:"Billing",
    alert:"Fee payment is pending for your approval",
    sound:"ping.aiff",
    topic:"com.xxx.yyy",//this is the bundle name of your application.This key is needed for production
    contentAvailable: 1//this key is also needed for production
    });
    console.log(`Sending: ${note.compile()} to ${ios}`);
    services.send(note, ios).then( result => {//ios key is holding array of device ID's to which notification has to be sent
        console.log("sent:", result.sent.length);
        console.log("failed:", result.failed.length);
        console.log(result.failed);
    });
    services.shutdown(); 

   

In Payload you can send data with custom keys.I hope it helps

Gilford answered 13/11, 2018 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.