Push рассылки на PHP (Android и IOS). Минимальное готовое решение

в 11:53, , рубрики: apns, apple, gcm, Google API, php, push notifications, push-уведомления

GCM

О рассылке Push уведомлений уже много раз писали на Хабре, например тут и тут, но прямого руководства к действию до сих пор нет. Итак, если интересно, прошу под кат.

Регистрация токенов устройств

В первую очередь разработчик приложения творит магию, в которой указывает адрес регистрации, он может быть таким: htpp://test.ru/secret/android?token=value и htpp://test.ru/secret/ios?token=value.
Самое примечательное что защиты от левых регистраций просто нет, хотя может магия подвела либо была не совсем качественной, если все таки защита есть, отпишите в комментариях, я обязательно дополню статью полезным советом.

На входе получаем значение токена который приходит при установке приложения, могут быть небольшие задержки, но буквально 10-20 секунд. Токены уникальны, но можно сделать и проверку на уникальность при записи в базу данных.

Пример токена для android:

APA91bFY-2CYrriS-Dt6y9_dGHhkPVwy7njqFpfgpzGYlDT4l0SQeqKr-lc1OM0a2DQ33S3EKwy2YJn-upKxOT6rNwgk350xUM3g8VX65rkGocOQX80Ta34pwXo6fyn-usoaGUAm4lzsqbCL-gkzHZZXRX39kUQfnA

IOS:

628a3f4a28bb994bb7c9a4143950d240c6d5a1dab8621e9ed61a2109a074f832

На этом шаге с регистрацией устройств мы закончили.

Рассылка уведомлений

Apple использует сервис APNS, а Google GCM (C2DM считается устаревшим, учитывайте это) и после прочтения документации можно перейти к любимому делу, а именно к велосипедостроению, но бюджет был ограничен и я начал поиски готовых решений. Самое годное что встретилось это ApnsPHP и GCMMessage, работают как на 5.3+ так и на 5.4+.

При использовании библиотек самое главное получить правильные сертификаты и секретную фразу в случае с APNS и секретный токен для работы с GCM.

Пример готового кода для отправки уведомлений для GCM, токены советуют отправлять пачкой, работает даже при больших количествах довольно таки быстро, сервис возвращает недействительные токены (пользователи удалили приложение), их сразу стоит удалить.

function fnSendAndroid($tokens, $text, $config)
{
    $sender = new CodeMonkeysRuGCMSender($config['androidTokenAuth']);
    $message = new CodeMonkeysRuGCMMessage($tokens, array("message" => $text));

    try {
        $response = $sender->send($message);

        if ($response->getFailureCount() > 0) {
            $invalidRegistrationIds = $response->getInvalidRegistrationIds();
            foreach($invalidRegistrationIds as $invalidRegistrationId) {
                //Remove $invalidRegistrationId from DB
                // на входе значение APS91bFY-2CYrriS-Dt6y9_dGHhkPVwy7njqFpfgpzGYlDT4l0SQeqKr-lc1OM0a2DQ33S3EKwy2YJn-upKxOT6rNwgk350xUM3g8VX65rkGocOQX80Ta34pwXo6fyn-usoaGUAm4lzsqbCL-gkzHZZXRX39kUQfnA 
                 fnDeleteToken($invalidRegistrationId);
            }
        }
        if ($response->getSuccessCount()) {
            echo 'отправлено сообщений на ' . $response->getSuccessCount() . ' устройств';
        }
    } catch (CodeMonkeysRuGCMException $e) {

        switch ($e->getCode()) {
            case CodeMonkeysRuGCMException::ILLEGAL_API_KEY:
            case CodeMonkeysRuGCMException::AUTHENTICATION_ERROR:
            case CodeMonkeysRuGCMException::MALFORMED_REQUEST:
            case CodeMonkeysRuGCMException::UNKNOWN_ERROR:
            case CodeMonkeysRuGCMException::MALFORMED_RESPONSE:
                fnLog('Ошибка отправления на андроид ' . $e->getCode() . ' ' . $e->getMessage());
                break;
        }
    }
}

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

Пользователи имеют свойство удалять особо назойливые приложения, так что перед отправкой стоит удалить мертвые токены.

function feedback($config)
{
    $feedback = new ApnsPHP_Feedback(
        ApnsPHP_Abstract::ENVIRONMENT_PRODUCTION, $config['apn']['sert']
    );
    $feedback->setProviderCertificatePassphrase($config['apn']['passphrase']);
    $feedback->setRootCertificationAuthority($config['apn']['RootCertificat']);

    $feedback->connect();

    $aDeviceTokens = $feedback->receive();
    if (!empty($aDeviceTokens)) {
        foreach ($aDeviceTokens as $DeviceToken) {
            /**
             * формат
             * [timestamp] => 1406040206
             * [tokenLength] => 32
             * [deviceToken] => 738d005a11bca268e2f1bffbfed88a456e261020b9277883cde14d9c8f47cde0
             */
            //'DELETE LOW_PRIORITY FROM tokens WHERE token=:token';
            fnLog('Feedback - Удален токен ' . $DeviceToken[deviceToken]);
        }
    }

    $feedback->disconnect();
}

Затем сама отсылка:

function fnSendIos($tokens, $text, $config)
{
    $push = new ApnsPHP_Push(
        ApnsPHP_Abstract::ENVIRONMENT_PRODUCTION, $config['apn']['sert']);

    // Set the Provider Certificate passphrase
    $push->setProviderCertificatePassphrase($config['apn']['passphrase']);

    $push->setRootCertificationAuthority($config['apn']['RootCertificat']);

    $message = new ApnsPHP_Message();
    $listTokens = array();
    foreach ($tokens as $token) {
        $message->addRecipient($token);
        $listTokens[] = $token;
    }

    $push->connect();

    $message->setText($text);
    $push->add($message);
    $push->send();
    $push->disconnect();

    $aErrorQueue = $push->getErrors();
    if (!empty($aErrorQueue)) {
        fnLog('Ошибка отправки ios  -  ' . print_r($aErrorQueue, true));
        if (is_array($aErrorQueue)) {
            foreach($aErrorQueue as $error) {
                if (isset($error['ERRORS']) && is_array($error['ERRORS'])) {
                    foreach ($error['ERRORS'] as $m) {
                        if (isset($m['statusMessage']) && $m['statusMessage'] == 'Invalid token') {
                            $arrayID = $m['identifier'] - 1;
                            if (isset($listTokens[$arrayID])) {
                                //DELETE LOW_PRIORITY FROM tokens WHERE token=:token'
                                fnLog('Удален ошибочный токен ' . $listTokens[$arrayID]);
                            }
                        }

                    }
                }
            }
        }
    }
}

Если Вы не напутали с сертификатами, то рассылка пройдет успешно.

Вот такой кондоминимум получился.

Автор: ustasby

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js