Deep linking в Telegram — сильний інструмент для вказування користувачів на конкретні дії (реєстрація послуги, відкриття картки, бронювання). Однак використання t.me/Bot?start=payload вимагає суворого дотримання обмежень API та захисту від підміни даних.
\n\nЛІМИТИ payload і обмеження API
\nTelegram дозволяє передавати в callback_data не більше 64 байт. Це критично для безпеки та продуктивності. Якщо payload занадто великий, сервер відхиляє запит або він просто не проходить валидацію. Також врахуйте, що payload має бути кодуванням UTF-8 рядковим значенням.
\n\nМАППИНГ start → сущність у БД
\nПри отриманні t.me/Bot?start=payload ви повинні поєднати цей payload з існуючим об'єктом у базі даних. Стандартна практика — генерувати унікальний lead_id (bin2hex(random_bytes(7))) і зберігати його разом із payload. Для ідемпотентності використовуйте update_id з webhook-відповіді як ключ для перевірки дублікатів.
\n\nБЕЗОПАСНОСТЬ: захист від підміни payload
\nПідміна callback_data — класична атака, яка може призвести до несанкціованої дії бота. Щоб захиститися:
\n- \n
- Зawsal валідувати update_id з webhook перед обробкою payload. \n
- Храніть мапу в БД з timestamp і перевірте, що current_update_id не збігається з збереженим. \n
- Використовуйте HMAC‑SHA256 для підписання важливих параметрів, якщо бізнес-логіка потребує додаткової перевірки. \n
ПРАКТІЧНА РЕАЛЬИЗАЦІЯ
\nНижче наведені приклади коду на PHP (Yii2/Laravel) для надсилки глибокого зв'язку та його обробки з ідемпотентністю.
\n\n// Отправка deep link через webhook с валидацией
$url = 'https://api.telegram.org/bot/sendMessage';
$data = [
'text' => 'Запись на услугу',
'callback_data' => 'deep_link_abc123def456', // ≤ 64 байтов
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer '
]);
$response = curl_exec($ch);
$body = json_decode($response, true);
if (!isset($body['ok']) || $body['ok'] !== true) {
throw new RuntimeException('Telegram API rejected request');
}
curl_close($ch);
// Обработка deep link после получения
function handleDeepLink($updateId, $callbackData) {
// Генерация lead_id для идемпотентности
$leadId = bin2hex(random_bytes(7));
// Проверка, что payload соответствует ожидаемому формату
if (strpos($callbackData, 'deep_link_') !== false) {
// Извлекаем сущность
$entityType = substr(str_replace(['deep_link_', ''], '', $callbackData), 0, strlen($callbackData) - 16);
$payload = explode('|', $callbackData)[1] ?? null;
// Сохраняем в БД (пример)
$record = new DeepLinkRecord($leadId, $entityType, 1, ['name' => $payload]);
$db->save($record);
// Сохраняем update_id для идемпотентности
$updatedUpdateId = $updateId;
} else {
throw new RuntimeException('Invalid deep link payload');
}
}
\n\nПриклад зберігання в БД (псевдокод для чіткості):
\n// Таблица deep_link_entities
// columns: id, lead_id (PK), entity_type, record_id, created_at
// Создание записи при first time
$leadId = bin2hex(random_bytes(7));
$entity = 'service'; // или 'appointment', 'product'
$record = new EntityRecord($leadId, $entity, 1, ['name' => 'Услуга А']);
$db->insert('deep_link_entities', [
'lead_id' => $leadId,
'entity_type' => $entity,
'record_id' => $record->id,
'created_at' => date('c')
]);
// Проверка дубликатов при повторном запросе
$existing = $db->find('deep_link_entities', ['lead_id' => $leadId'])
->where('entity_type', $entity)
->first();
if ($existing) {
// Payload уже обработан — возвращаем результат предыдущей операции
return $existing->processedPayload;
}\n\nЗапам'ятайте: завжди перевіряйте HTTP статус (200 OK), використовуйте json_last_error() для діагностики, і зберігайте update_id разом із payload для гарантії ідемпотентності. Це мінімізує ризик підміни та забезпечує стабільну роботу вашого бота.
"}