TL;DR
- Большинство ошибок при разработке Telegram Mini Apps связано с тем, что приложение проектируют как обычный сайт, хотя оно выполняется в WebView клиента Telegram и подчиняется его ограничениям.
- Данные из
initDataUnsafeнельзя использовать для авторизации: подпись у них не проверена, поэтому любой параметр, включая идентификатор пользователя, можно подменить на клиенте. - Проблемы с вёрсткой чаще всего вызваны использованием
100vhи отсутствием отступов safe area, а не выбором CSS-фреймворка. - В Bot API 10.2 от 14 июля 2026 года вызовы методов Mini App с постороннего домена запрещены, поэтому схемы с iframe и вторым доменом требуют переработки.
- Ошибки в рекламной интеграции влияют на доход не меньше технических: показ до первой ценности, флаг
debug: trueв продакшене и начисление награды на клиенте снижают выручку и допускают фрод.
Ниже разобраны ошибки, которые чаще всего доходят до продакшена: авторизация, вёрстка, хранилище, совместимость версий, диплинки и реклама. Для каждой указано, что именно ломается и почему, и что нужно сделать.
Откуда берутся ошибки при разработке Telegram Mini Apps
Приложение выполняется внутри клиента Telegram, и это накладывает три ограничения.
- WebView отличается от браузера. Реализация WebView на iOS, Android и в десктопных клиентах разная. Заметнее всего расходятся поведение хранилища, обработка экранной клавиатуры и жесты.
- Версия API зависит от клиента. Метод, добавленный в новой версии Bot API, недоступен в старой сборке Telegram. Полифилла для таких методов нет, поэтому поддержку версий нужно закладывать в архитектуру.
- Часть интерфейса принадлежит Telegram. Шапка, нижняя панель, жест закрытия и системные отступы находятся вне вашего контроля, и вёрстка должна их учитывать.
Разовое чтение документации Telegram Mini Apps не закрывает вопрос: изменения, влияющие на совместимость, публикуются в чейнджлоге Bot API, а в 2026 году он обновлялся несколько раз. Если вы делаете первое приложение, базовый путь от бота до запуска описан в полном гайде по мини-приложениям; эта статья продолжает его и разбирает то, что ломается уже после запуска.
Ошибки безопасности и initData
Большинство проблем с безопасностью в мини-аппах связано с одним и тем же: данные, полученные от клиента, принимают за подтверждение личности пользователя. Telegram передаёт подписанные параметры запуска, но проверка подписи выполняется на стороне разработчика.
Доверять initDataUnsafe без проверки подписи
initData — это строка параметров запуска, подписанная Telegram. initDataUnsafe — та же информация, разобранная в объект на стороне клиента и доступная без проверки подписи.
Ошибка в том, что содержимое initDataUnsafe ничем не подтверждено. Объект формируется в окружении клиента, а значит его поля можно изменить перед отправкой на сервер — в том числе user.id. Если бэкенд принимает этот идентификатор как есть, пользователь получает доступ к чужому аккаунту, балансу и наградам. Документация Telegram прямо требует проверять данные до того, как они используются на сервере.
Как надо. Передавайте на бэкенд сырую строку initData и проверяйте её там. Порядок действий: собрать data-check-string из всех пар «ключ–значение», кроме hash, отсортировать по алфавиту и объединить переводами строки; получить ключ через HMAC-SHA256(bot_token, "WebAppData"); вычислить HMAC-SHA256(data_check_string, secret_key) и сравнить результат с hash за постоянное время. Токен бота не должен попадать на клиент, а идентификатор пользователя нельзя принимать из обычного параметра запроса.
Если проверку выполняет подрядчик или партнёр без доступа к токену бота, используется второй способ — подпись Ed25519, описанная в справочнике по init data. У него есть известная особенность: подпись передаётся в base64url без выравнивания, и в ряде языков символы = нужно добавить вручную, иначе декодирование завершится ошибкой.
Не проверять, насколько свежий auth_date
Проверка подписи подтверждает, что строку сформировал Telegram, но не показывает, когда это произошло. Собственного срока действия у initData нет.
Из-за этого перехваченная строка остаётся валидной неограниченно долго. Попасть к посторонним она может обычным путём: через логи прокси, отчёт об ошибке с полным телом запроса или историю запросов в браузере разработчика. Проверка подписи такой запрос пропустит.
Что делать. Отклоняйте данные, у которых auth_date старше выбранного окна. По умолчанию берут сутки; для приложений с деньгами или внутренней валютой окно разумно сократить до часа. Проверенные данные сразу меняйте на собственный сессионный токен и дальше авторизуйте запросы по нему.
Хранить сессию в localStorage
В WebView Telegram localStorage очищается непредсказуемо. На iOS и на части сборок Linux Desktop данные могут не сохраниться между запусками — на это указывают, в частности, разработчики Doubletapp в разборе на Хабре.
Приложение при этом не падает и не показывает ошибку. Оно не находит токен и открывается так, будто пользователь зашёл впервые. На стороне поддержки это выглядит как жалоба «после перезапуска слетает вход», и воспроизвести её на десктопе обычно не удаётся.
Хранилище стоит выбирать по типу данных:
Хранилище | Где хранятся данные | Для чего подходит | Ограничения |
|---|---|---|---|
| WebView конкретного клиента | Временное состояние интерфейса | Может очищаться на iOS и части десктопных сборок |
| Облако Telegram, для пары «пользователь — бот» | Настройки, доступные на всех устройствах пользователя | 1024 записи на пользователя, ключ 1–128 символов, значение до 4096 символов |
| Устройство, постоянное хранение | Локальный кэш и предпочтения | Не синхронизируется между устройствами |
| Защищённая область устройства | Чувствительные локальные значения | Не синхронизируется, зависит от версии клиента |
Бэкенд приложения | Ваш сервер | Сессии, балансы, права доступа | Требует проверенного |
Данные, определяющие права пользователя и его баланс, должны храниться только на сервере.
Вызывать методы Mini App с чужого домена
В Bot API 10.2 от 14 июля 2026 года вызов методов Mini App разрешён только с исходного домена приложения. Ограничение введено по соображениям безопасности: до этого страница на постороннем домене, открытая внутри мини-аппа, могла обращаться к методам Telegram от имени приложения.
Практическое следствие: если часть сценария вынесена на домен платёжного провайдера, партнёрскую страницу или во встроенный iframe, вызовы Telegram.WebApp оттуда перестают выполняться на обновлённых клиентах. Приложение при этом продолжает открываться, поэтому проблему замечают не сразу.
Как чинить. Оставьте на своём домене все экраны, которые обращаются к Telegram API, а обмен данными с внешними сервисами проводите через бэкенд. Проверять нужно на актуальной сборке клиента: на старых версиях ограничение не действует, и сценарий выглядит рабочим.
Ошибки вёрстки и viewport
Ошибки этой группы заметны пользователю сразу и при этом почти полностью предотвратимы на этапе вёрстки.
Использовать 100vh вместо высоты viewport от Telegram
На мобильных клиентах Mini App открывается не на весь экран, а как bottom sheet, который пользователь может развернуть жестом.
100vh вычисляется от высоты окна браузера, и она не совпадает с высотой видимой области мини-аппа. Из-за этого нижняя часть интерфейса — обычно основная кнопка — оказывается за границей экрана или под элементами Telegram.
Что делать. Вызывайте expand() при старте и рассчитывайте раскладку от viewportHeight и viewportStableHeight. Элементы, которые не должны смещаться, привязывайте к viewportStableHeight: это значение не меняется во время перетаскивания окна и анимации клавиатуры, тогда как viewportHeight обновляется непрерывно. Подпишитесь на событие изменения viewport и перерисовывайте интерфейс только по стабильным значениям.
Игнорировать safe area
В Bot API 8.0 добавлены два набора отступов, и их часто путают. safeAreaInset описывает системные зоны устройства: вырез камеры, индикатор home. contentSafeAreaInset описывает область, занятую интерфейсом самого Telegram.
Применение только одного набора решает половину задачи. Системные отступы не защищают от перекрытия шапкой Telegram, а контентные — от аппаратного выреза.
Как надо. Использовать оба. В полноэкранном режиме, добавленном в релизе Mini Apps 2.0, это особенно важно: Telegram перестаёт резервировать место под свои элементы, и всё неучтённое оказывается под ними.
Оставлять вертикальные свайпы в играх
Вертикальный свайп в Telegram сворачивает или закрывает Mini App. Если в приложении есть собственные вертикальные жесты — перетаскивание карточек, управление в игре, — клиент перехватывает жест и закрывает приложение посреди действия. Пользователь воспринимает это как сбой.
Что делать. На экранах с собственными жестами вызывайте disableVerticalSwipes() (Bot API 7.7+), а там, где ожидается обычная прокрутка, включайте свайпы обратно. Если на экране есть несохранённые данные, добавьте enableClosingConfirmation() (Bot API 6.2+).
Рисовать свою кнопку «Назад»
Telegram предоставляет системную кнопку возврата в шапке, а на Android есть ещё аппаратная кнопка. Собственная кнопка в интерфейсе добавляет третий элемент с той же функцией.
Проблема не в дублировании, а в том, что эти элементы работают с разными стеками истории. Системная кнопка возвращает к предыдущей записи истории браузера, ваша — к предыдущему экрану в логике приложения, и на разветвлённых сценариях эти пути расходятся.
Как надо. Использовать BackButton (Bot API 6.1+), привязав его к роутеру и управляя видимостью при смене маршрута. Стек навигации должен быть один.
Ошибки производительности на реальных устройствах
Значительная часть трафика мини-аппов приходит со среднебюджетных Android-устройств, а сессия начинается с нажатия на ссылку в чате. Пользователь сравнивает скорость открытия с обычным сообщением, поэтому задержка в несколько секунд воспринимается как зависание.
- Большой размер первого бандла. Разделяйте код по маршрутам и откладывайте всё, что не требуется для первого экрана. Объём начальной загрузки здесь важнее синтетических метрик производительности.
- Расчёт на SSR. Методы Telegram доступны только через объект
window, которого на сервере нет. В проектах на Next.js это проявляется при первом вызове в серверном компоненте. Логику, зависящую от Telegram, держите в клиентских компонентах и отрисовывайте скелетон до готовности SDK. - Анимации без учёта возможностей устройства. В WebView на слабом железе сложные анимации дают заметное падение частоты кадров. Анимируйте
transformиopacity, а при просадках отключайте дополнительные эффекты. - Пустой экран во время загрузки. Telegram позволяет настроить загрузочный экран мини-аппа. Скелетон вместо белого фона снижает долю выходов в первые секунды.
Ошибки версий и платформ
Вызов метода, отсутствующего в клиенте пользователя, либо не даёт эффекта, либо завершается ошибкой. В обоих случаях пользователь видит неработающую функцию, хотя код написан правильно.
Решение состоит из двух частей: зафиксировать минимальную поддерживаемую версию Bot API и проверять доступность всего, что выше неё, через isVersionAtLeast() (Bot API 6.1+). Для каждой такой функции нужен запасной вариант.
Возможность | Метод или поле | Bot API | Запасной вариант |
|---|---|---|---|
Навигация назад |
| 6.1 | Кнопка в собственной шапке |
Подтверждение закрытия |
| 6.2 | Автосохранение черновиков |
Облачные настройки |
| 6.9 | Настройки на сервере |
Управление свайпами |
| 7.7 | Ограничение зон перетаскивания |
Полноэкранный режим |
| 8.0 | Развёрнутый режим |
Safe area |
| 8.0 | Фиксированные отступы |
Локальное хранение |
| 9.0 | Сессия на сервере |
Выбор чата |
| 9.6 | Обычная ссылка для шеринга |
Тестирование в веб-версии Telegram скрывает большую часть этих проблем, поэтому релиз-кандидат нужно проверять на iOS, Android и хотя бы одном десктопном клиенте. Android отлаживается через Chrome DevTools по USB, iOS — через Safari Web Inspector. Когда доступа к устройству нет, например ошибка воспроизводится только у тестировщика, помогает встроенная консоль вроде Eruda: она показывает ошибки внутри самого приложения.
Ошибки в startapp и диплинках
Mini App получает один параметр запуска — startapp. Схемы диплинков нередко проектируют из расчёта на несколько параметров, после чего их приходится переделывать.
Вторая, более серьёзная проблема — доверие к содержимому параметра. Значение startapp передаётся в ссылке и формируется на стороне пользователя, поэтому подставить туда чужой реферальный идентификатор может кто угодно. Начисление бонусов напрямую по этому значению открывает реферальный фрод, что особенно заметно в проектах с внутренней валютой.
Что делать. Упакуйте значения в одну строку с собственным разделителем, например ref__campaign__level, и разберите её на клиенте. Перед начислением бонуса прочитайте параметр из проверенного initData на сервере и сверьте его с данными пользователя.
Ошибки при подключении рекламы
Ошибки этой группы не приводят к сбоям приложения, но снижают eCPM, нарушают выдачу наград или мешают пройти модерацию. Если вы ещё выбираете форматы, посмотрите обзор ТОП-10 мини-приложений в Telegram — там видно, как устроена монетизация в проектах с большой аудиторией.
Показывать рекламу слишком рано
Межстраничный показ на первом экране прерывает сессию до того, как пользователь получил хоть какой-то результат от приложения.
Экономически это невыгодно: стоимость привлечения уже оплачена, а показ на первом экране повышает вероятность выхода. У rewarded-формата причина другая — награда имеет ценность только тогда, когда пользователь понимает, зачем она ему нужна.
Как надо. Определите точки, где пользователь завершил действие: прошёл уровень, выполнил задание, получил награду. Показы размещайте на этих границах. Для rewarded подбирайте награду, востребованную в текущий момент: дополнительная жизнь, ускорение, бонус к балансу.
Оставить debug-режим в проде
У SDK AdsGram есть параметр debug, включающий тестовые баннеры и вывод логов. Документация AdsGram указывает, что перед релизом его нужно удалить или установить в false.
Причина в том, что тестовые показы не учитываются в статистике и не запускают reward-колбэки. Приложение с debug: true в продакшене внешне работает корректно: реклама отображается, ошибок нет, но показы не оплачиваются и награды по постбэку не приходят.
Вызывать init() на каждый показ
window.Adsgram.init({ blockId }) возвращает объект AdController. По документации AdsGram инициализация выполняется один раз для каждого blockId, а повторные вызовы возвращают тот же экземпляр.
Что делать. Создайте контроллер один раз при инициализации приложения, сохраните ссылку и вызывайте show() в местах показа. Минимальная интеграция rewarded-формата выглядит так:
<script src="https://sad.adsgram.ai/js/sad.min.js"></script>
const AdController = window.Adsgram.init({ blockId: "your-block-id" });
AdController.show()
.then(() => {
// ролик просмотрен — запрашиваем начисление награды у бэкенда
})
.catch((result) => {
// ошибка или ранний выход — награда не начисляется
console.warn(result);
});
Не обрабатывать ошибки show()
Промис, возвращаемый show(), отклоняется в двух случаях: рекламу не удалось загрузить или пользователь закрыл её раньше времени.
Если ветка отказа не обработана, возможны два сценария. При начислении награды вне промиса пользователь получает её без просмотра. При ожидании ответа без таймаута интерфейс остаётся в состоянии загрузки.
Как чинить. Обрабатывайте отказ явно и подпишитесь на события SDK: onStart, onSkip, onReward, onComplete, onError, onBannerNotFound, onNonStopShow, onTooLongSession. Событие onBannerNotFound на старте обычно означает недостаточный филл для конкретного гео или блока, а не ошибку интеграции. Предусмотрите путь к награде без рекламы, чтобы отсутствие баннера не блокировало сценарий.
Начислять награду на клиенте
Если запись награды выполняет клиентский код, запрос к бэкенду можно повторить произвольное количество раз, не показывая рекламу вообще. Это тот же класс ошибки, что и доверие к initDataUnsafe: решение о начислении принимается на основании данных, которые контролирует пользователь.
Как надо. Начисляйте награду на бэкенде, привязывая её к проверенной сессии. Для крупных площадок AdsGram предоставляет серверный постбэк: приложения с аудиторией свыше 50 000 daily average users могут указать reward URL, на который после клиентского начисления отправляется GET-запрос с telegramId пользователя. Эндпоинт должен принимать HTTPS GET на порту 443 и содержать плейсхолдер [userId], например https://example.com/reward?userid=[userId]. В debug-режиме постбэк не отправляется, поэтому проверять его нужно на боевом блоке.
Ставить рекламу там, где её не видно
Учёт показов у AdsGram основан на видимости: показ засчитывается после двух секунд непрерывного просмотра при видимости блока не менее 50%.
Отсюда следует, что вызов show() сам по себе дохода не приносит. Блок, отрисованный в свёрнутом контейнере, за пределами экрана или с нулевой высотой, формально отображается, но условию видимости не соответствует. Тот же эффект даёт показ при свёрнутом приложении.
Что делать. Размещайте блоки в видимой области, не накладывайте их друг на друга и не запускайте показ, пока мини-апп свёрнут. AdsGram допускает до 10 рекламных блоков на приложение, и этого достаточно, чтобы разделить размещения по контексту: завершение уровня, ежедневный бонус, стена заданий. При использовании одного блока во всех местах статистика не позволяет оценить эффективность каждого размещения.
Ошибки перед запуском и на модерации
Отказы на модерации чаще связаны с незавершённостью приложения, чем с нарушением правил. Требование AdsGram простое: во время проверки Mini App должен быть доступен и работать корректно. Модерация обычно занимает 4–6 часов в будни и 6–10 часов в выходные.
Что мешает пройти её с первого раза:
- Приложение закрыто авторизацией или белым списком, и проверяющий видит экран ошибки вместо интерфейса.
- Сценарий не работает на одной из платформ. Чаще всего это обработка клавиатуры на iOS или раскладка на десктопе.
- Ожидания по выплатам сформированы без учёта правил. AdsGram по умолчанию выплачивает в USDT в сети TON, доступны также USDT TRC20 и фиатные переводы, минимальная сумма вывода — $100, обработка занимает до 24 часов в будни и до 48 часов в выходные.
- Не настроена аналитика по рекламной воронке, из-за чего низкий филл невозможно отличить от неработающего размещения. Набор метрик для сравнения площадок разобран в материале AdsGram или Monetag: какая платформа лучше для монетизации Telegram Mini App.
Таблица: ошибки в ТМА и возможные причины
Симптом | Вероятная причина | Что проверить |
|---|---|---|
Пустой экран только на iOS | Очищен | Перенос сессии на бэкенд, порядок инициализации |
«Unable to retrieve launch parameters» | Приложение открыто вне Telegram | Проверка окружения, мок для локальной разработки |
Кнопка перекрыта системным вырезом | Не применены отступы safe area |
|
Раскладка смещается при открытии клавиатуры | Расчёт от | Переход на |
Приложение закрывается при перетаскивании | Включены вертикальные свайпы |
|
Метод не работает на части устройств | Версия клиента ниже минимальной |
|
Вызовы Telegram API перестали проходить | Обращение с постороннего origin | Ограничение origin в Bot API 10.2 |
Реклама отображается, статистики нет | Debug-режим в продакшене |
|
Показов меньше, чем вызовов | Блок не соответствует условию видимости | Две секунды просмотра, видимость от 50% |
Награды начисляются без просмотра | Начисление на стороне клиента | Перенос на бэкенд, S2S reward URL |
FAQ
Почему Telegram Mini App не открывается?
Основные причины — сбой инициализации, необработанная ошибка до первого рендера и потеря сессии из клиентского хранилища. Проверьте доступность URL приложения по HTTPS, готовность SDK до первого вызова Telegram API и отсутствие зависимости от localStorage после перезапуска. Если проблема воспроизводится только на iOS, проверьте ту же сборку через Safari Web Inspector.
Как открыть и протестировать Mini App в браузере?
Вне Telegram параметры запуска недоступны, поэтому SDK сообщает, что не может их получить. Для локальной разработки добавьте проверку окружения и мок Telegram-окружения, чтобы приложение отрисовывалось в обычном браузере. Дев-сервер опубликуйте через туннель — dev tunnel в VS Code или ngrok, — укажите полученный HTTPS-адрес в BotFather и открывайте приложение из бота.
Как проверить initData на сервере?
Передайте на бэкенд сырую строку initData. Соберите data-check-string из всех пар «ключ–значение», кроме hash, отсортируйте по алфавиту и объедините переводами строки, получите ключ через HMAC-SHA256(bot_token, "WebAppData"), вычислите HMAC-SHA256(data_check_string, secret) и сравните с hash. Данные с устаревшим auth_date отклоняйте, после проверки выдавайте собственный сессионный токен.
Как отлаживать Mini App на iOS и Android?
Для Android используется Chrome DevTools с удалённой отладкой по USB, для iOS — Safari Web Inspector. Если доступа к устройству нет, например ошибка воспроизводится только у тестировщика, встройте в приложение консоль вроде Eruda: она выводит ошибки внутри интерфейса. Воспроизводить проблемы следует в реальном клиенте, поскольку веб-версия скрывает особенности WebView.
Можно ли использовать localStorage в Telegram Mini App?
Технически можно, но полагаться на него нельзя. В WebView Telegram localStorage может не сохраняться на iOS и части десктопных сборок, а перезапуск способен очистить его без уведомления. Настройки, доступные на всех устройствах пользователя, храните в CloudStorage, локальные данные — в DeviceStorage или SecureStorage из Bot API 9.0, а сессии, балансы и права доступа — на бэкенде.
Ломает ли Bot API 10.2 существующие Mini Apps?
В Bot API 10.2 от 14 июля 2026 года вызов методов Mini App разрешён только с исходного домена приложения. Если приложение обращается к Telegram.WebApp из встроенного iframe, партнёрской страницы или дополнительного домена, на обновлённых клиентах такие вызовы не выполняются. Обращения к Telegram API следует оставить на зарегистрированном домене, а обмен с внешними сервисами вести через бэкенд.





