Розробка Telegram Mini App з інтеграцією Бітрікс24
Інтеграція Telegram Mini App з Бітрікс24 відрізняється від інтеграції з 1С-Бітрікс тим, що основний транспорт тут — REST API Бітрікс24, а не кастомні PHP-ендпоінти. Бітрікс24 надає повноцінний REST з OAuth, вебхуками та подіями. Типовий сценарій: корпоративний міні-портал або CRM-інструмент прямо в Telegram — співробітник може прийняти лід, змінити статус угоди, переглянути завдання, не відкриваючи браузер.
Сценарії використання
Найпопулярніші кейси:
- Мобільний CRM-дашборд — перегляд лідів/угод, зміна статусу, додавання коментаря прямо з Telegram
- Корпоративне замовлення — співробітники зовнішньої мережі (дилери, агенти) оформлюють заявки через Mini App, заявки потрапляють у Бітрікс24 CRM
- Self-service портал — клієнт бачить статус своїх заявок, історію звернень
- Таск-менеджер — список завдань співробітника, зміна статусу, додавання файлів
Авторизація: OAuth Бітрікс24 з Mini App
Бітрікс24 REST API вимагає OAuth 2.0 access_token. З Mini App прямий OAuth flow ускладнений (немає браузерних редиректів), тому авторизацію реалізуємо через посередника.
Схема:
Telegram → Mini App відкривається
→ Mini App передає initData на наш сервер-посередник
→ Сервер валідує initData, визначає Telegram user_id
→ Шукає прив'язаний access_token Бітрікс24 для цього user_id
→ Якщо є → повертає JWT для Mini App
→ Якщо немає → повертає посилання на OAuth-авторизацію в Бітрікс24
Первинне прив'язування Telegram ↔ Бітрікс24:
// Контролер OAuth callback
class Bx24OAuthController
{
public function callback(Request $request): Response
{
$code = $request->get('code');
$tgUserId = $request->session()->get('pending_tg_user_id');
// Обмінюємо code на токен
$tokenData = $this->exchangeCode($code);
// Зберігаємо токен із прив'язкою до Telegram user_id
\Local\TgBx24\TokenStorage::save($tgUserId, [
'access_token' => $tokenData['access_token'],
'refresh_token' => $tokenData['refresh_token'],
'expires_at' => time() + $tokenData['expires_in'],
'domain' => $tokenData['domain'],
'user_id' => $tokenData['user_id'],
]);
// Повідомляємо бота, що авторизація пройшла
$this->bot->sendMessage($tgUserId, 'Авторизацію в Бітрікс24 виконано успішно.');
return redirect('/auth/success');
}
private function exchangeCode(string $code): array
{
$response = Http::post('https://oauth.bitrix.info/oauth/token/', [
'grant_type' => 'authorization_code',
'client_id' => config('bx24.client_id'),
'client_secret' => config('bx24.client_secret'),
'code' => $code,
]);
return $response->json();
}
}
Зберігання токенів — в окремій таблиці або Redis:
class TokenStorage
{
private const TABLE = 'tg_bx24_tokens';
public static function save(int $tgUserId, array $tokenData): void
{
// Шифруємо токени перед записом у БД
$encrypted = \Local\Crypto::encrypt(json_encode($tokenData));
\Bitrix\Main\Application::getConnection()->queryExecute(
"INSERT INTO " . self::TABLE . " (tg_user_id, token_data, updated_at)
VALUES (?, ?, NOW())
ON DUPLICATE KEY UPDATE token_data = ?, updated_at = NOW()",
[$tgUserId, $encrypted, $encrypted]
);
}
public static function getValidToken(int $tgUserId): ?array
{
$result = \Bitrix\Main\Application::getConnection()->query(
"SELECT token_data FROM " . self::TABLE . " WHERE tg_user_id = ?",
[$tgUserId]
);
$row = $result->fetch();
if (!$row) return null;
$data = json_decode(\Local\Crypto::decrypt($row['token_data']), true);
// Оновлюємо прострочений токен
if ($data['expires_at'] < time() + 300) {
$data = self::refreshToken($data);
}
return $data;
}
private static function refreshToken(array $data): array
{
$response = \Bitrix\Main\Web\HttpClient::post(
'https://oauth.bitrix.info/oauth/token/',
[
'grant_type' => 'refresh_token',
'client_id' => \Bitrix\Main\Config\Option::get('local.tg_bx24', 'client_id'),
'client_secret' => \Bitrix\Main\Config\Option::get('local.tg_bx24', 'client_secret'),
'refresh_token' => $data['refresh_token'],
]
);
// оновлюємо дані, зберігаємо, повертаємо
return $newData;
}
}
Mini App: дашборд співробітника CRM
Фронтенд на React із Telegram WebApp SDK:
import { useEffect, useState } from 'react';
const tg = window.Telegram.WebApp;
interface Deal {
ID: string;
TITLE: string;
OPPORTUNITY: string;
STAGE_ID: string;
CONTACT_NAME: string;
}
function CrmDashboard() {
const [deals, setDeals] = useState<Deal[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
tg.ready();
tg.expand();
loadDeals();
}, []);
async function loadDeals() {
const res = await fetch('/tg-api/crm/deals', {
headers: { 'X-Tg-Init-Data': tg.initData },
});
const data = await res.json();
setDeals(data.deals);
setLoading(false);
}
async function updateStage(dealId: string, stageId: string) {
await fetch(`/tg-api/crm/deals/${dealId}/stage`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Tg-Init-Data': tg.initData,
},
body: JSON.stringify({ stage_id: stageId }),
});
tg.HapticFeedback.notificationOccurred('success');
loadDeals();
}
// ... рендер
}
Серверний проксі до Бітрікс24 REST:
class CrmDealsProxy
{
public function getDeals(int $tgUserId): array
{
$tokenData = TokenStorage::getValidToken($tgUserId);
if (!$tokenData) {
throw new \RuntimeException('Not authorized', 401);
}
$bx24 = new \Local\TgBx24\Bx24Client($tokenData['access_token'], $tokenData['domain']);
$result = $bx24->call('crm.deal.list', [
'filter' => ['ASSIGNED_BY_ID' => $tokenData['user_id'], 'CLOSED' => 'N'],
'select' => ['ID', 'TITLE', 'OPPORTUNITY', 'STAGE_ID', 'CONTACT_ID'],
'order' => ['DATE_MODIFY' => 'DESC'],
'start' => 0,
]);
return $result['result'] ?? [];
}
public function updateDealStage(int $tgUserId, int $dealId, string $stageId): bool
{
$tokenData = TokenStorage::getValidToken($tgUserId);
$bx24 = new \Local\TgBx24\Bx24Client($tokenData['access_token'], $tokenData['domain']);
$result = $bx24->call('crm.deal.update', [
'id' => $dealId,
'fields' => ['STAGE_ID' => $stageId],
]);
return !empty($result['result']);
}
}
Сповіщення через бота при подіях Бітрікс24
Для сповіщень використовуємо вебхуки подій Бітрікс24. Наприклад, при зміні статусу ліда — надсилаємо повідомлення відповідальному:
// Обробник вхідної події від Бітрікс24
class EventHandler
{
public function handleLeadUpdate(array $event): void
{
$leadId = $event['data']['FIELDS_AFTER']['ID'];
$assignee = $event['data']['FIELDS_AFTER']['ASSIGNED_BY_ID'];
// Шукаємо tg_user_id за bx24 user_id
$tgUserId = $this->findTelegramUser($assignee);
if (!$tgUserId) return;
$statusName = $this->getLeadStatusName($event['data']['FIELDS_AFTER']['STATUS_ID']);
$this->bot->sendMessage($tgUserId,
"Лід #{$leadId} змінено\nНовий статус: {$statusName}\n" .
"[Відкрити в Mini App](https://t.me/" . BOT_USERNAME . "/crm?start=lead_{$leadId})",
['parse_mode' => 'Markdown']
);
}
}
Склад робіт
- Реєстрація застосунку Бітрікс24 (OAuth), налаштування Mini App у @BotFather
- Сервер-посередник: валідація Telegram initData, зберігання OAuth-токенів
- React Mini App: обирається під конкретний сценарій (CRM / завдання / заявки)
- REST-проксі до Бітрікс24 API з оновленням токенів
- Вебхуки подій Бітрікс24 → сповіщення в Telegram
- Первинне налаштування: онбординг користувача, прив'язка акаунту
Терміни: MVP під один сценарій (наприклад, дашборд угод) — 3–5 тижнів. Повнофункціональний інструмент із кількома розділами — 8–14 тижнів.







