Меню

Настройка работы с push уведомлениями через Firebase в мобильном приложении Apache Cordova

RSS
Настройка работы с push уведомлениями через Firebase в мобильном приложении Apache Cordova
 
Настройка работы с push уведомлениями через Firebase в мобильном приложении Apache Cordova

Задача: Apache Cordova предоставляет удобный способ разработки кроссплатформенных мобильных приложений. В данной статье расскажу, как настроить отправку push уведомлений на платформах iOS и Android с использованием Firebase.
 
Для отправки Push-уведомления конкретному пользователю с помощью Firebase и Apache Cordova.
Получение токена устройства:
Код
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
    window.FirebasePlugin.getToken(function (token) {
        // Сохраняем этот токен в базе данных сайта
        // alert(token); 
    }, function (error) {
        alert(error);
    });
}

Обновление токена устройства (при смене)
Код
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
    window.FirebasePlugin.onTokenRefresh(function (token) {
        // Обновляем токен в базе данных сайта
        // alert(token);
    }, function (error) {
        // alert(error);
    });
}
Изменено: Михаил Базаров - 09.10.2024 16:18:14
 
Можно не устанавливать плагин
cordova plugin add cordova-plugin-firebasex
Если не нужна аналитика и прочее от Firebase, кроме пушей.
Тогда разрешение запрашиваем вот так:
Код
cordova.plugins.firebase.messaging.onTokenRefresh(function() {
    console.log("Device token updated");
});

Примеры остальных настроек плагина (устновка бейджика, своего звука итд) на странице плагина в Github
Изменено: Михаил Базаров - 09.10.2024 16:28:07
 
C июня 2024 старое API Firebase заблокировано, нужно использовать HTTP v1.
Теперь токен подключения не постоянен и нужно использовать OAuth для авторизации на сервере Push

В моем примере я знаю токены устройств пользователя и отправляю ему пуш при новом заказе.
Само собой, разнесите все по функциям или обверните в class с методами.
Считывайте json файла с данными с помощью file_put_content.
Не оставляйте все такой не читаемой портянкой

Быстрый пример, оптимизируйте на свое усмортение, оставляю одной говно портянкой, что бы было понятнее что происходит:
Код
if (isset($arUser['UF_PUSH_DEVICE_TOKENS']) && count($arUser['UF_PUSH_DEVICE_TOKENS']) > 0) {

    // Получение Oauth токена ===================
    $serviceAccountFile = '{
                      "type": "service_account",
                      "project_id": "ID ПРИЛОЖЕНИЯ В FIREBASE",
                      "private_key_id": "000ab**********942c",
                      "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE***xQoFiug=\n-----END PRIVATE KEY-----\n",
                      "client_email": "firebase-adminsdk-cnzfe@**********.com",
                      "client_id": "1001***8621",
                      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
                      "token_uri": "https://oauth2.googleapis.com/token",
                      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
                      "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-cnzfe%40ID ПРИЛОЖЕНИЯ.iam.gserviceaccount.com",
                      "universe_domain": "googleapis.com"
                    }';
    function base64UrlEncode($text)
    {
        return str_replace(
            ['+', '/', '='],
            ['-', '_', ''],
            base64_encode($text)
        );
    }

    $authConfig = json_decode($serviceAccountFile);
    $secret = openssl_get_privatekey($authConfig->private_key);
    $header = json_encode([
        'typ' => 'JWT',
        'alg' => 'RS256'
    ]);
    $time = time();
    $start = $time - 60;
    $end = $time + 3600;
    $payload = json_encode([
        "iss" => $authConfig->client_email,
        "scope" => "https://www.googleapis.com/auth/firebase.messaging",
        "aud" => "https://oauth2.googleapis.com/token",
        "exp" => $end,
        "iat" => $start
    ]);
    $base64UrlHeader = base64UrlEncode($header);
    $base64UrlPayload = base64UrlEncode($payload);
    $result = openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $secret, OPENSSL_ALGO_SHA256);
    $base64UrlSignature = base64UrlEncode($signature);
    $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
    $options = array('http' => array(
        'method' => 'POST',
        'content' => 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' . $jwt,
        'header' =>
            "Content-Type: application/x-www-form-urlencoded"
    ));
    $context = stream_context_create($options);
    $responseText = file_get_contents("https://oauth2.googleapis.com/token", false, $context);
    $response = json_decode($responseText, true);
    // Получение Oauth токена ==============================


    $url = "https://fcm.googleapis.com/v1/projects/ID ПРИЛОЖЕНИЯ В FIREBASE/messages:send";

    $headers = [
        'Authorization: Bearer ' . $response['access_token'],
        'Content-Type: application/json',
    ];

    foreach ($arUser['UF_PUSH_DEVICE_TOKENS'] as $DEVICE_TOKEN) {
        $notification = [
            'message' => [
                'token' => $DEVICE_TOKEN,
                'notification' => [
                    'title' => 'Новый заказ',
                    'body' => '"Новый заказ N' . $arResult["ORDER"]['ID'],
                ],
            ],
        ];

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($notification));

        $response = curl_exec($ch);

        if ($response === false) {
            // echo 'Error sending push notification: ' . curl_error($ch);
        } else {
            // echo 'Push notification sent successfully: ' . $response;
        }

        curl_close($ch);
    }
}
Изменено: Михаил Базаров - 09.10.2024 16:40:16
 
В новой версии API что бы поставить звук по умолчанию для уведомления, теперь не подходит просто передать ключ
sound: default в блоке 'notification'
Нужно вот так, под каждую платформу:
Код
$notification = [
    'message' => [
        'token' => $DEVICE_TOKEN,
        'notification' => [
            'title' => 'Новый заказ',
            'body' => '"Еда в Миг" новый заказ N' . $arResult["ORDER"]['ID'],
        ],
        'android' => [
            'notification' => [
                'sound' => 'default'
            ]
        ],
        'apns' => [
            'payload' => [
                'aps' => [
                    'sound' => 'default'
                ]
            ]
        ]
    ],
];
Изменено: Михаил Базаров - 10.10.2024 10:59:52
 
Другой вариант получения Oauth токена

Код
     /**
     * Запрос токена авторизации с посощью CURL
     * 
     *  Получения json файла приватного ключа. Firebase console - открыть проект - project settings - вкладка service accounts - кнопка generate new private key
     *
     * @param string $PrivateKey - параметр private_key из json файла
     * @param string $AccountName - параметр client_email из json файла
     * @param int|null $AccessTokenMaximumExpirationTime - Время действия токена. Допустимо значение от 1сек до 3600 сек. По умолчанию 3600 сек. (1 час)
     * @param int|null $RequestTimeout - устанавливет CURLOPT_CONNECTTIMEOUT и CURLOPT_TIMEOUT
     * @return string - токен  или пустая строка
     * @throws \Exception
     */
    function RequestAccessToken(string $PrivateKey, string $AccountName, ?int $AccessTokenMaximumExpirationTime = 3600, ?int $RequestTimeout = 10): string{
        if($PrivateKey != '' and $AccountName != ''){
            $StartTime = time();
            
            $Header = json_encode([
                'alg' => 'RS256',
                'typ' => 'JWT'
            ]);

            if(!isset($RequestTimeout) or $RequestTimeout < 0) $RequestTimeout = 10

            if(!isset($AccessTokenMaximumExpirationTime) or $AccessTokenMaximumExpirationTime <= 0 or $AccessTokenMaximumExpirationTime > 3600){
               $AccessTokenMaximumExpirationTime = 3600;
            }

            $AccessTokenExpirationEndTime = $StartTime + $AccessTokenMaximumExpirationTime;
            
            $Aud = 'https://oauth2.googleapis.com/token';
            
            $Data = json_encode([
                'iss' => $AccountName,
                'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
                'aud' => $Aud,
                'exp' => $AccessTokenExpirationEndTime,
                'iat' => $StartTime
            ]);
            
            $Base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($Header));
            $Base64UrlData = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($Data));
            
            $Signature = '';
            
            $PrivateKey = openssl_get_privatekey($PrivateKey);
            
            openssl_sign("{$Base64UrlHeader}.{$Base64UrlData}", $Signature, $PrivateKey, OPENSSL_ALGO_SHA256);
            
            $Base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($Signature));
            
            $JWT = "{$Base64UrlHeader}.{$Base64UrlData}.{$Base64UrlSignature}";
            
            $PostFields = [
                'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                'assertion' => $JWT
            ];
            
            $Ch = curl_init($Aud);
            curl_setopt($Ch, CURLOPT_CONNECTTIMEOUT, $RequestTimeout);
            curl_setopt($Ch, CURLOPT_TIMEOUT, $RequestTimeout);
            curl_setopt($Ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($Ch, CURLOPT_POSTFIELDS, $PostFields);

            $Result = curl_exec($Ch);
            
            $Errno = 0;
            $Error = '';
            
            if($Result === false){
                $Errno = curl_errno($Ch);
                $Error = curl_error($Ch);
            }
            
            curl_close($Ch);
            
            if($Errno > 0 and $Error != ''){
                throw new \Exception($Error, $Errno);

            }else{
                if($Result !== false){
                    $Result = @json_decode($Result, true);

                    if(is_array($Result)){
                        if(isset($Result['access_token'])){
                            return trim($Result['access_token']);

                        }else if(isset($Result['error'])){
                            throw new \Exception($Result['error']['message'], $Result['error']['code']);

                        }
                    }
                }
            }
        }else{
            if($PrivateKey == ''){
                throw new \Exception('Не указан закрытый ключ.', 110);
            }

            if($AccountName == ''){
                throw new \Exception('Не указано имя учетной записи.', 110);
            }
        }
        
        return '';
    }
Изменено: Михаил Базаров - 16.10.2024 13:07:18
Читают тему (гостей: 1)
Форма ответов
 
Текст сообщения*
Перетащите файлы
Ничего не найдено
Файл
Загрузить файлы
 

Блог-note: заметки разработчика

Мобильные версии страниц и поисковые системы

Согласно требования поисковых систем: Яндекс и Google, в случае если у вашего сайта имеется мобильная версия, нужно увед...

Показать только один тип цены в каталоге Битрикс

Достаточно часто, при создании сайта на битрикс, можно столкнуться с такой проблемой: на сайте имеется несколько групп о...

Вывести свойство отдельно ото всех или исключить из всех

Иногда, на сайте, под управлением 1С-Битрикс, нужно вывести какое-то конкретное свойство отдельно ото всех, или вообще н...

Вывод даты создания элемента в правильном формате в Битрикс

Если нужно вывести дату создания новости, статьи или товара в каталоге, в принципе любого элемента инфоблока- можно восп...

INPUT type="file" Предпросмотр превью картинки до загрузки

Рассмотрим на примере компонента "Форма добавления-редактирования элементов инфоблока", как сделать ее более удобной для...

Ускорение работы сайта на 1С-Битрикс

Данная статья написана специально под видеоролик (приложен в конце статьи), опубликованный на моем ВКVideo канал...

Прямое открытие мессенджеров по ссылке из приложения Apache Cordova

Задача: При клике по ссылкам на Telegram и WhatsApp в мобильном приложении необходимо сразу открывать мессенджеры без пр...

Валидация пароля и подтверждения при регистрации в битрикс

Встала задачка сделать валидацию пароля и его подтверждения в стандартной форме регистрации сайта под управлением Битрик...

Скопировать номер телефона из поля пользователя в телефон для регистрации

Задача, на конкретном сайте: раньше все пользователи регистрировались по стандартному режиму, через логин(e-mail) и паро...