Como enviar notificaciones push a iOS y Android sin intermediarios

Escrito por: Victor Araya, el jueves 22 de febrero del 2018

Sí, desarrollaremos un script en PHP para enseñarte como enviar notificaciones push a dispositivos iOS y Android, directo desde el script, sin intermediarios, pero primero lo primero.

Las últimas semanas hemos estado desarrollando una aplicación con React Native, la cuál toma todo su contenido de un backend hecho en WordPress (por supuesto :D), una de las tareas que finalice fue enviar notificaciones push a los dispositivos (iOS y Android) pero con 2 requisitos muy importantes:

  1. No utilizar un servicio externo (ya saben como firebase, OneSignal, Amazon SNS … y otros mil que existen), esto porqué generaría más trabajo para el cliente final, además de un costo extra (en la mayoría de los casos) y la idea central de nuestra aplicación es que todo sea muy simple.
  2. Que el envió de la notificación se realice desde el servidor del cliente donde se instaló el WordPress (Pero sin trampa, no se podía utilizar ningún plugin externo).

Mi primera impresión fue que sería algo complicado, juzguen ustedes…

 

Lo Primero, entender como llega una notificación push hasta nuestro teléfono?

Trataré de explicarlo en 4 sencillos pasos:

  1. El dispositivo solicita permiso para recibir notificaciones, en ese momento el dispositivo envía un request a Google/Apple (GCM/APNs) para registrar un nuevo token o ID.
  2. Ahora en nuestra aplicación tenemos un token valido del dispositivo (registrado ya en GCM/APNs), el cuál debemos ahora registrar en una base de datos nuestra para utilizar posteriormente en el envió de la notificación.
  3. Ya tenemos los tokens registrados en nuestra base de datos, ahora necesitamos hacer un request a GCM/APNs para que procesen la notificación y la envíen al dispositivo, este request debe llevar el token del dispositivo al que queremos enviar la notificación y el mensaje. (Claro para conectarnos a GCM/APNs vamos a necesitar certificados y API Keys dependiendo si es Google o Apple, lo veremos más adelante).
  4. Una vez que enviamos el request a los servidores de Apple/Google ellos procesan la notificación y realizan el envío al dispositivo.

Así de sencillo es como funciona enviar notificaciones push 🙂

La forma en que se guarden los tokens de los dispositivos para luego hacer el envió es muy variada, esa parte se las dejo a ustedes, en este artículo lo que vamos a ver es como conectarnos a Apple y a Google para hacer ya el envió de nuestra notificación.

 

Apple, HTTP/2 APNS

Les dejo la documentación oficial para que les sirva de guía.

Para conectarnos al APN’s necesitamos tener unos certificados específicos, como acostumbra apple se utilizan 2 certificados, uno para desarrollo y otro para producción. El formato en que debe estar el certificado para conectarnos a los APNS es .pem (si ya han trabajo con certificados de apple habrán notado que normalmente se exportan como .p12), por este motivo les dejo este pequeño artículo donde se explica como obtener estos certificados en formato .pem

También vamos a necesitar el Bundle ID y por supuesto el token del device al que vamos enviar la notificación

Google, GCM -> FCM

Con android es un poco más sencillo, solo necesitamos un API Key:

  1. Tenemos que crear por fuerza un proyecto nuevo en Firebase (FCM), ya que es la nueva versión de GCM 
  2. Encontremos el API KEY que necesitamos
    • Vamos a la configuración del proyecto  
    • Ingresamos a Mensajería en la Nube y veremos las credenciales del proyecto, vamos a utilizar la que dice “Clave de servidor”

De igual forma que en iOS vamos a necesitar el Bundle ID y el token del device.

Código 💪🏻

Una vez que tenemos todos los requisitos listos (iOS y Android), es hora de comenzar con el código.

Este es el script (PHP) que se encarga de hacer el envío de nuestra notificación, les dejo el código con todos los comentarios necesarios para entender que hace cada cosa, Si tienen alguna duda no duden en hacer la consulta en los comentarios 🙂

<?php
class PushNotifications {

	protected $AppBundleId; //com.yourappname.app
    protected $GoogleAPIKey; // API access key from Google API's Console
	protected $http2CurlConnection; // http2 connection
	protected $APNSCertificate; // .pem certificate
	protected $APNSServer; // apple url

	public function __construct($params) {
		$this->AppBundleId = $params['bundleId'];
		$this->GoogleAPIKey = $params['googleAPIKey'];
		$this->APNSCertificate = $params['APNSCertificate'];
		$this->APNSServer = $params['APNSServer'];
    }

	public function sendiOSNotifications($tokens, $message){
		// this is only needed with php prior to 5.5.24
		if (!defined('CURL_HTTP_VERSION_2_0')) {
		  define('CURL_HTTP_VERSION_2_0', 3);
		}

		//open ssl connection
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

		// send push notifications to all tokens
		foreach ($tokens as $token){
			$payload = $this->createiOSMessage($message);
            $this->iOSCurl($ch, $payload, $token);
		}

		// close connection
		curl_close($ch);
	}
    
    public function sendAndroidNotifications($tokens, $message){
		$fields = array(
			'registration_ids' 	=> $tokens,
			'data'			=> $this->createAndroidMessage($message);
		);

		$headers = array(
			'Authorization: key=' . $this->GoogleAPIKey,
			'Content-Type: application/json'
		);

		return $this->androidCurl($headers, $fields);
    }

    public function iOSCurl($ch, $payload, $token) {
	    $url = "{$this->APNSServer}/3/device/{$token}";

	    $cert = realpath($this->APNSCertificate);

	    $headers = array(
	        "apns-topic: {$this->AppBundleId}",
	        "User-Agent: My Sender"
	    );

	    curl_setopt_array($ch, array(
	        CURLOPT_URL => $url,
	        CURLOPT_PORT => 443,
	        CURLOPT_HTTPHEADER => $headers,
	        CURLOPT_POST => TRUE,
	        CURLOPT_POSTFIELDS => $payload,
	        CURLOPT_RETURNTRANSFER => TRUE,
	        CURLOPT_TIMEOUT => 30,
	        CURLOPT_SSL_VERIFYPEER => false,
	        CURLOPT_SSLCERT => $cert,
	        CURLOPT_HEADER => 1
	    ));

	    $result = curl_exec($ch);
	    if ($result === FALSE) {
	      throw new Exception("Curl failed: " .  curl_error($ch));
	    }

		return curl_getinfo($ch, CURLINFO_HTTP_CODE);
    }
    
    public function androidCurl($headers, $fields) {
        $ch = curl_init();
		curl_setopt( $ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send' );
		curl_setopt( $ch,CURLOPT_POST, true );
		curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
		curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
		curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
		curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
		$result = curl_exec($ch);
	    if ($result === FALSE) {
	      throw new Exception("Curl failed: " .  curl_error($ch));
	    }
        curl_close( $ch );
        return $result;
    }

    public function createiOSMessage($message){
		$payload['aps'] = array(
			'alert' => array(
				'title' => $message['title'],
				'body' => $message['message']
			),
			'badge' => 1,
			'sound' => 'default'
		);
		return json_encode($payload);
    }
    
    public function createAndroidMessage($message){
        return array(
			'message' 	=> $message['message'],
			'title'		=> $message['title'],
			'vibrate'	=> 1,
			'sound'		=> 1
			// 'subtitle'	=> 'This is a subtitle. subtitle',
			// 'tickerText'	=> 'Ticker text here...Ticker text here...Ticker text here',
			// 'largeIcon'	=> 'large_icon',
			// 'smallIcon'	=> 'small_icon'
		);
	}
}
PushNotifications.php

 

Usemos nuestra clase

<?php

$push = new PushNotifications(array(
	'bundleId' => 'com.myapp.name',
	'googleAPIKey' => 'YOUR_FCM_API_KEY',
    'APNSCertificate' => '/path/to/certificate-dev.pem',     
    'APNSServer' => 'https://api.development.push.apple.com'
));

// APNSServer for production => https://api.push.apple.com
// Remember change APNSCertificate if u are in production to

$iOSTokens = ['token1', 'token2'];
$androidTokens = ['token1', 'token2'];

$message = array('title' => 'Notification title', 'message' => 'your message');

$push->sendAndroidNotifications($androidTokens, $message);
$push->sendiOSNotifications($iOSTokens, $message);

 

😎 Ahí lo tienen!

📝 Recuerda que el APNSServer y APNSCertificate deberían variar dependiendo si estamos en modo desarrollo o producción.

 

Recibiendo la notificación en nuestra aplicación 😉

🙁 no tan rápido… hoy cubrimos como enviar notificaciones push.

Recibirlas va a depender mucho de como estemos desarrollando nuestra aplicación, cuando digo esto me refiero a: React Native, PhoneGap, Xamarin, Swift, Java, Go, etc, etc, etc

Prometo la próxima semana, hablar un poco sobre como recibir esta notificación en una aplicación desarrollada con React Native (que es una de nuestras especialidades),  se los estaré dejando por acá 🙂

 

Nos leemos!

 

Conviértete en una mejor persona!

¡Se ha enviado correctamente!