Использование Firebase Cloud Messaging(FCM) в качестве PUSH-сервера.

Для использования push-уведомлений от fcm необходим валидный ssl сертификат.

Первым делом необходимо перейти в консоль firebase: console.firebase.google.com

Добавить проект:

Переходим в авторизацию:

Настраиваем способ входа, включаем анонимный вход(чтобы токен можно было получить без авторизации в гугл):

Добавляем авторизованный домен:

Заходим в базы данных и выставляем правила на чтение/запись для всех:

{ "rules": {".read": true,".write": true }}


Заходим в хранилище и выставляем правила:

service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write; }}}


Создаем сервис-воркер

:

importScripts('https://www.gstatic.com/firebasejs/4.2.0/firebase.js');
firebase.initializeApp({
messagingSenderId: '1053599252147'
});
const messaging = firebase.messaging();

где 1053599252147 – идентификатор отправителя(берется из проекта fcm).

Создаем

:

Показать

<html>
	<div id="token"></div>
	<script type="text/javascript" src="jquery-3.1.1.js"></script>
	<script type="text/javascript">
	//сохранение токена
		function SendTokenToServer(currentToken) {
			xmlhttp=new XMLHttpRequest();
			xmlhttp.onreadystatechange=function() {
				if (this.readyState==4 && this.status==200) {
					console.log(this.responseText);
				}
			}
			xmlhttp.open("GET","savetoken.php?token="+currentToken,true);
			xmlhttp.send();
		}
	</script>
 
	<script  type="text/javascript" src="https://www.gstatic.com/firebasejs/4.2.0/firebase.js"></script>
	<script  type="text/javascript">
			//Настройки FCM
		//https://console.firebase.google.com/u/1/
		var config = {
			apiKey: "AAAA9U9ovrM:APA91bHUKF3vF1axlPeF-slhqGpRPNuEfRJJvpAOBWiMu19C7PyCiByYDr0Mz75kTV4MZx2M-Ac-zNUakEjMBWrHG0Yr8F7FsUQPnbA8W1p350Bf8kiC7XMDUulVToygji2s09LzOqlw",
			authDomain: "sergdudko-ce384.firebaseapp.com",
			databaseURL: "https://sergdudko-ce384.firebaseio.com",
			storageBucket: "sergdudko-ce384.appspot.com",
			messagingSenderId: "1053599252147",
		};
		//инициализируем подключение к FCM
		firebase.initializeApp(config);
		const messaging = firebase.messaging();
		document.getElementById('token').innerHTML = 'NO LOAD TOKEN';
 
		//запрос на показ Web-PUSH браузеру
		messaging.requestPermission()
		.then(function() {
			console.log('Notification permission granted.');
			// Если нотификация разрешена, получаем токен.
			messaging.getToken()
			.then(function(currentToken) {
				if (currentToken) {
					console.log(currentToken);
					//отправка токена на сервер
					SendTokenToServer(currentToken);
					document.getElementById('token').innerHTML = currentToken;
				} else {
					console.log('No Instance ID token available. Request permission to generate one.');
				}
			})
			.catch(function(err) {
				console.log('An error occurred while retrieving token. ', err);
				showToken('Error retrieving Instance ID token. ', err);
			});
			// ...
		})
		.catch(function(err) {
			console.log('Unable to get permission to notify.', err);
		});
 
		/*
		//обновление токена
		messaging.onTokenRefresh(function() {
			messaging.getToken()
			.then(function(refreshedToken) {
				console.log('Token refreshed.');
			})
			.catch(function(err) {
				console.log('Unable to retrieve refreshed token ', err);
				showToken('Unable to retrieve refreshed token ', err);
			});
		}) */
 
		//окно sw
		messaging.onMessage(function(payload) {
			console.log('Message received. ', payload);
			// регистрируем пустой ServiceWorker каждый раз
			navigator.serviceWorker.register('firebase-messaging-sw.js');
			// запрашиваем права на показ уведомлений если еще не получили их
			Notification.requestPermission(function(result) {
				if (result === 'granted') {
					navigator.serviceWorker.ready.then(function(registration) {
						// теперь мы можем показать уведомление
						return registration.showNotification(payload.notification.title, payload.notification);
					}).catch(function(error) {
						console.log('ServiceWorker registration failed', error);
					});
				}
			});
		});
		</script>
</html>


Где:
• sergdudko-ce384 – идентификатор проекта

• AAAA9U9ovrM:APA91bHUKF3vF1axlPeF-slhqGpRPNuEfRJJvpAOBWiMu19C7PyCiByYDr0Mz75kTV4MZx2M-Ac-zNUakEjMBWrHG0Yr8F7FsUQPnbA8W1p350Bf8kiC7XMDUulVToygji2s09LzOqlw – ключ сервера
• 1053599252147 – идентификатор отправителя

При этом запрос для сохранения токена через

в

, xmlhttprequest требует jQuery:

//сохранение токена
function SendTokenToServer(currentToken) {
	xmlhttp=new XMLHttpRequest();
	xmlhttp.onreadystatechange=function() {
		if (this.readyState==4 && this.status==200) {
			console.log(this.responseText);
		}
	}
	xmlhttp.open("GET","savetoken.php?token="+currentToken,true);
	xmlhttp.send();
}

Инициализация базы данных(для работы с firebase требуется соответствующий скрипт https://www.gstatic.com/firebasejs/4.2.0/firebase.js):

var config = {
	apiKey: "AAAA9U9ovrM:APA91bHUKF3vF1axlPeF-slhqGpRPNuEfRJJvpAOBWiMu19C7PyCiByYDr0Mz75kTV4MZx2M-Ac-zNUakEjMBWrHG0Yr8F7FsUQPnbA8W1p350Bf8kiC7XMDUulVToygji2s09LzOqlw",
	authDomain: "sergdudko-ce384.firebaseapp.com",
	databaseURL: "https://sergdudko-ce384.firebaseio.com",
	storageBucket: "sergdudko-ce384.appspot.com",
	messagingSenderId: "1053599252147",
};
//инициализируем подключение к FCM
firebase.initializeApp(config);

Инициализация fcm:

const messaging = firebase.messaging();

Запрос на прием push-уведомлений браузеру:

messaging.requestPermission()
.then(function() {
	console.log('Notification permission granted.');
})
.catch(function(err) {
	console.log('Unable to get permission to notify.', err);
});

Получение токена с firebase и запись его через функцию SendTokenToServer(token):

messaging.getToken()
.then(function(currentToken) {
	if (currentToken) {
		console.log(currentToken);
		//отправка токена на сервер
		SendTokenToServer(currentToken);
		document.getElementById('token').innerHTML = currentToken;
	} else {
		console.log('No Instance ID token available. Request permission to generate one.');
	}
})
.catch(function(err) {
	console.log('An error occurred while retrieving token. ', err);
	showToken('Error retrieving Instance ID token. ', err);
});
 

Регистрация service worker:

messaging.onMessage(function(payload) {
	console.log('Message received. ', payload);
	// регистрируем пустой ServiceWorker каждый раз
	navigator.serviceWorker.register('firebase-messaging-sw.js');
	// запрашиваем права на показ уведомлений если еще не получили их
	Notification.requestPermission(function(result) {
		if (result === 'granted') {
			navigator.serviceWorker.ready.then(function(registration) {
				// теперь мы можем показать уведомление
				return registration.showNotification(payload.notification.title, payload.notification);
			}).catch(function(error) {
				console.log('ServiceWorker registration failed', error);
			});
		}
	});
});

Отправка уведомления через curl:

Где:
• AAAA9U9ovrM:APA91bHUKF3vF1axlPeF-slhqGpRPNuEfRJJvpAOBWiMu19C7PyCiByYDr0Mz75kTV4MZx2M-Ac-zNUakEjMBWrHG0Yr8F7FsUQPnbA8W1p350Bf8kiC7XMDUulVToygji2s09LzOqlw – ключ сервера, полученный в FCM
• Web PUSH с push.sergdudko.tk – наименование уведомления
• Body PUSH – текст уведомления
• push-button.png – иконка уведомления
• https://sergdudko.tk – откроется при клике
• cPzofyAkzZ0:APA91bFBM8_V8l-XdgIv60N7h5mQh6C_Mf8UyGbwPMCOOlQrPR1q2QJTWn_72tVNGKYObfDKius5bdNyrxe_1Pt2Sf-KLvHWJ0DXfxfSgxsuJL4_8ZvBMU_Pdg6Gb4cqllU9pV4WxjRH – токен пользователя полученный на сервере функцией messaging.getToken()

Или через php:

$url = 'https://fcm.googleapis.com/fcm/send';
$YOUR_API_KEY = 'AAAA9U9ovrM:APA91bHUKF3vF1axlPeF-slhqGpRPNuEfRJJvpAOBWiMu19C7PyCiByYDr0Mz75kTV4MZx2M-Ac-zNUakEjMBWrHG0Yr8F7FsUQPnbA8W1p350Bf8kiC7XMDUulVToygji2s09LzOqlw; // Server key
$YOUR_TOKEN_ID = ‘cPzofyAkzZ0:APA91bFBM8_V8l-XdgIv60N7h5mQh6C_Mf8UyGbwPMCOOlQrPR1q2QJTWn_72tVNGKYObfDKius5bdNyrxe_1Pt2Sf-KLvHWJ0DXfxfSgxsuJL4_8ZvBMU_Pdg6Gb4cqllU9pV4WxjRH’; // Client token id
 
$request_body = [
	'to' => $YOUR_TOKEN_ID,
	'notification' => [
		'title' => 'Web PUSH с sergdudko.tk',
		'body' => 'Body PUSH',
		'icon' => 'push-button.png',
		'click_action' => 'https://sergdudko.tk',
	],
];
$fields = json_encode($request_body);
 
$request_headers = [
	'Content-Type: application/json',
	'Authorization: key=' . $YOUR_API_KEY,
];
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
 

Отладка в Crome:

Консоль:

Если произведен вход в google account под учетной записью владельца, то можно также проверить запись в БД firebase:

Дополнение(настройка кастомных обработок):
В параметр notification можно запихать данные(подробнее тут https://firebase.google.com/docs/cloud-messaging/http-server-ref), они будут автоматически обрабатываться на соответствующих устройствах.
Пример инициирующий отправку из php:

'notification' => [
	'title' => 'Добро пожаловать', //название уведомления
	'body' => 'на сайт www.sergdudko.tk', //тело уведомления
	'icon' => 'img/push.png',				//картинка уведомления
	'tag' => 'sergdudko.tk',				//тэг уведомления, уведомления с одним тегом заменяют друг друга
	'livetime' => 300,				//время жизни уведомления
	'priority' => 10,				//приоритет уведомления 5 - норм, 10 - высокий
	'click_action' => 'https://sergdudko.tk' //действие при клике на стандартное уведомление
],

Можно создать кастомный обработчик, для удобства это должна быть внешняя функция для

, при этом в нее необходимо передать данные, по умолчанию json-данные имеют имя

. Если необходимо передать не стандартные данные(точнее с не стандартными ключами), можно использовать дополнительный массив

:

'data' => [
	'click' => 'чего-то там',
	'text' => 'клик по уведомлению',
	'function' => 'window.open("https://vk.com","_self");'
],

При этом стоит помнить, что в json хранятся и типы данных. Т.е. переданное число остается числовым типом, переданная сторока – строковый, а переданная функция должна быть функцией.
Чтобы принять данное сообщение мы заменим

if (result === 'granted') {
	navigator.serviceWorker.ready.then(function(registration) {
		// события при получении пуша, стандартная функция
		return registration.showNotification(payload.notification.title, payload.notification).then(function(notificationclick) {console.log('получили пуш');});
	}
})

На нашу функцию:

if (result === 'granted') {
	navigator.serviceWorker.ready.then(function(registration) {
		// события при получении пуша, стандартная функция
		//return registration.showNotification(payload.notification.title, payload.notification).then(function(notificationclick) {console.log('получили пуш');});
		//вызываем нашу функцию, вместо стандартной
		return notifications_incoming(payload); 
	}
})

Далее необходимо ее описать:

function notifications_incoming(payload){
  var myNotification = new Notification(payload.notification.title, {
    tag : payload.notification.tag,  
    title : payload.notification.title,
    body : payload.notification.body,
    icon : payload.notification.icon,
    time_to_live : payload.notification.livetime,
    priority : payload.notification.priority
  });
  myNotification.onshow = function() {
	// alert( 'onshow' );
  };  
  myNotification.onclick = function() {
    eval(payload.data.function);
  }; 
  myNotification.onclose = function() {
	//  alert( 'onclose' );
  };
  myNotification.onerror = function() {
	//  alert( 'onerror' );
  };
}

При этом стандартные данные будут формата

, а дополнительные

. Также для класса

есть стандартные события: показ, клик, закрытие, ошибка.