Интеграция 1С-Битрикс с Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) — инфраструктура Google для push-уведомлений на Android, iOS и в браузере. В отличие от OneSignal, FCM — это низкоуровневый транспорт без встроенного UI и сегментации. Интеграция с 1С-Битрикс строится полностью на кастомном коде: SDK на фронтенде регистрирует токен устройства, PHP-бэкенд отправляет уведомления через FCM HTTP v1 API.
FCM HTTP v1 API vs Legacy API
Google отключил Legacy HTTP API (ключ server key) в июне 2024. Все новые интеграции используют HTTP v1 API с авторизацией через OAuth 2.0 Service Account. Если в проекте осталась старая интеграция через https://fcm.googleapis.com/fcm/send — она уже не работает.
HTTP v1 endpoint: POST https://fcm.googleapis.com/v1/projects/{project_id}/messages:send
Авторизация — Bearer-токен, получаемый из Service Account JSON через Google Auth Library.
Service Account и авторизация
В Firebase Console → Project Settings → Service Accounts → Generate new private key. Скачиваем JSON-файл, кладём вне webroot:
/var/www/site/storage/firebase/service-account.json
Токен получаем через JWT:
use Google\Auth\Credentials\ServiceAccountCredentials;
class FcmAuthService
{
private ServiceAccountCredentials $credentials;
public function __construct(string $serviceAccountPath)
{
$this->credentials = new ServiceAccountCredentials(
'https://www.googleapis.com/auth/firebase.messaging',
json_decode(file_get_contents($serviceAccountPath), true)
);
}
public function getAccessToken(): string
{
$token = $this->credentials->fetchAuthToken();
return $token['access_token'];
}
}
Устанавливаем зависимость: composer require google/auth. Токен кешируем — он действует 1 час. Перегенерация при каждом запросе — расточительство.
public function getCachedToken(): string
{
$cacheKey = 'fcm_access_token';
$cached = \Bitrix\Main\Data\Cache::createInstance();
if ($cached->initCache(3500, $cacheKey, '/fcm/')) {
return $cached->getVars()['token'];
}
$token = $this->getAccessToken();
$cached->startDataCache();
$cached->endDataCache(['token' => $token]);
return $token;
}
Регистрация FCM-токена на фронтенде
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';
const app = initializeApp({
apiKey: "...",
authDomain: "project.firebaseapp.com",
projectId: "project-id",
messagingSenderId: "123456789",
appId: "1:123456789:web:abc"
});
const messaging = getMessaging(app);
// Запрашиваем разрешение и регистрируем токен
async function initPush() {
try {
const token = await getToken(messaging, {
vapidKey: 'YOUR_VAPID_KEY'
});
if (token) {
await fetch('/local/api/fcm/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Bitrix-Csrf-Token': BX.bitrix_sessid()
},
body: JSON.stringify({ fcm_token: token, platform: 'web' })
});
}
} catch (err) {
console.warn('Push permission denied:', err);
}
}
// Обработчик foreground-уведомлений (когда вкладка открыта)
onMessage(messaging, (payload) => {
new Notification(payload.notification.title, {
body: payload.notification.body,
icon: '/local/templates/main/images/push-icon.png'
});
});
Для background-уведомлений нужен Service Worker /firebase-messaging-sw.js в корне сайта:
importScripts('https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js');
firebase.initializeApp({ /* конфиг */ });
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
self.registration.showNotification(payload.notification.title, {
body: payload.notification.body,
data: payload.data,
});
});
Хранение FCM-токенов
class FcmTokenTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName(): string { return 'local_fcm_tokens'; }
public static function getMap(): array
{
return [
new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
new \Bitrix\Main\ORM\Fields\IntegerField('USER_ID'),
new \Bitrix\Main\ORM\Fields\StringField('TOKEN', ['required' => true]),
new \Bitrix\Main\ORM\Fields\StringField('PLATFORM'), // web, android, ios
new \Bitrix\Main\ORM\Fields\DatetimeField('CREATED_AT'),
new \Bitrix\Main\ORM\Fields\DatetimeField('LAST_USED_AT'),
new \Bitrix\Main\ORM\Fields\StringField('ACTIVE'),
];
}
}
При обновлении токена (FCM меняет токен при переустановке приложения) — поиск по старому токену и замена, а не дублирование записи.
Отправка уведомления
class FcmService
{
private FcmAuthService $auth;
private string $projectId;
public function sendToUser(int $userId, string $title, string $body, array $data = []): void
{
$tokens = FcmTokenTable::getList([
'filter' => ['USER_ID' => $userId, 'ACTIVE' => 'Y'],
'select' => ['TOKEN', 'PLATFORM'],
])->fetchAll();
foreach ($tokens as $tokenRow) {
$this->sendToToken($tokenRow['TOKEN'], $title, $body, $data, $tokenRow['PLATFORM']);
}
}
private function sendToToken(string $token, string $title, string $body, array $data, string $platform): void
{
$message = [
'token' => $token,
'notification' => ['title' => $title, 'body' => $body],
'data' => array_map('strval', $data), // FCM требует строки
];
// Платформо-специфичные настройки
if ($platform === 'android') {
$message['android'] = [
'priority' => 'high',
'notification' => ['channel_id' => 'orders', 'icon' => 'ic_notification'],
];
} elseif ($platform === 'ios') {
$message['apns'] = [
'headers' => ['apns-priority' => '10'],
'payload' => ['aps' => ['sound' => 'default', 'badge' => 1]],
];
}
$accessToken = $this->auth->getCachedToken();
$projectId = $this->projectId;
$url = "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['message' => $message]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"Authorization: Bearer {$accessToken}",
],
]);
$response = json_decode(curl_exec($ch), true);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 404 || ($response['error']['code'] ?? 0) === 404) {
// Токен устарел — деактивируем
FcmTokenTable::updateByToken($token, ['ACTIVE' => 'N']);
}
}
}
Топики vs индивидуальные токены
FCM поддерживает отправку по топикам (/topics/promo_electronics) — удобно для массовых рассылок без хранения токенов. Подписка на топик:
POST https://iid.googleapis.com/iid/v1:batchAdd
{
"to": "/topics/promo_electronics",
"registration_tokens": ["TOKEN1", "TOKEN2"]
}
Для транзакционных уведомлений (заказы конкретного пользователя) — только индивидуальные токены.
Сроки
| Задача | Срок |
|---|---|
| Service Account, FCM HTTP v1 клиент, кеш токена | 2–3 дня |
| Регистрация токенов (web + android/ios) | 3–4 дня |
| Отправка по событиям заказов + обработка ошибок | 2–3 дня |
| Service Worker, foreground/background уведомления | 2–3 дня |
| Управление подпиской из ЛК, топики | 3–5 дней |
| Полный комплекс | 3–4 недели |







