Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 09.08 · 35 мин
Средний
TelegramWebAppMini Apps 2.0Bot API 8.0Bot API 9.0BiometricGeolocationStars

Mini Apps 2.0: новые возможности платформы

В ноябре 2024 года Telegram выпустил Bot API 8.0 — крупнейшее обновление платформы Mini Apps с момента её запуска. Маркетингово оно получило название Mini Apps 2.0: полноэкранный режим, ярлыки на домашний экран, геолокация, биометрия, акселерометр, гироскоп, эмодзи-статусы, подписки через Stars и обмен медиа. Чуть позже Bot API 9.0 добавил DeviceStorage и SecureStorage — персистентное локальное хранилище и секрет-стор поверх Keychain / Keystore.

Этот урок покрывает практический набор новых API, схемы версионирования (isVersionAtLeast), миграцию с Mini Apps 1.x и типичные подводные камни.


Контекст: что такое Mini Apps 2.0

Mini Apps 1.x (Bot API 6.0—7.x, 2022—2024) — это web_app-объект внутри Telegram WebView с минимальным набором примитивов: MainButton, BackButton, HapticFeedback, тема, initData, CloudStorage. Такого набора достаточно для типовых лендингов, маркетплейсов и платежей через Stars, но недостаточно для игр, нативноподобных утилит и подписок.

Mini Apps 2.0 закрывает почти все системные пробелы:

КатегорияMini Apps 1.xMini Apps 2.0
Окноexpand() до 90% экранаrequestFullscreen() — настоящий полный экран в портретной/ландшафтной ориентации
УстановкаТолько через ботаaddToHomeScreen() — ярлык на рабочем столе устройства
Доступ к датчикамНетLocationManager, Accelerometer, Gyroscope, DeviceOrientation
БиометрияНетBiometricManager — Face ID / Touch ID / отпечаток
КнопкиMainButton, BackButton+ SecondaryButton, SettingsButton
ПлатежиStars (разовые)+ рекуррентные подписки через subscription_period
ХранилищеCloudStorage (1024 ключа, до 4 КБ)+ DeviceStorage (5 МБ), SecureStorage (Keychain/Keystore)
Расширения чатаНетЭмодзи-статусы, shareMessage, downloadFile
INFO

Версии и устройства

Большинство фич Mini Apps 2.0 требует Bot API 8.0+ и свежих клиентов Telegram (iOS 11.x, Android 11.x, Desktop 5.x). Проверяйте поддержку через WebApp.isVersionAtLeast('8.0') перед вызовом любого нового метода — иначе на старом клиенте получите тихий no-op или исключение.


Feature detection: version и isVersionAtLeast

Главное правило Mini Apps 2.0 — никогда не вызывайте новые методы вслепую. Используйте guard:

const tg = window.Telegram.WebApp;

console.log(tg.version);  // например, "8.0"

if (tg.isVersionAtLeast('8.0')) {
  // безопасно вызывать requestFullscreen, BiometricManager, LocationManager и т. д.
}

if (tg.isVersionAtLeast('9.0')) {
  // DeviceStorage, SecureStorage
}

isVersionAtLeast сравнивает строку версии лексикографически по компонентам, поэтому '10.0' корректно считается старше '9.0'.

TIP

Шаблон capability detection

const supports = {
  fullscreen:    tg.isVersionAtLeast('8.0'),
  biometry:      tg.isVersionAtLeast('7.2'),
  geolocation:   tg.isVersionAtLeast('8.0'),
  homeScreen:    tg.isVersionAtLeast('8.0'),
  deviceStorage: tg.isVersionAtLeast('9.0'),
};

Сохраните такую таблицу в одном месте и обращайтесь к ней через флаги — легче поддерживать и тестировать.


Full-Screen Mode

В 1.x максимум, что давал API, — это expand(): окно занимало около 90% высоты экрана. В 2.0 появился настоящий полный экран — без хедера Telegram, в обеих ориентациях. Это критично для игр, видеоплееров и иммерсивных интерфейсов.

API крутится вокруг трёх методов и нескольких событий:

const tg = window.Telegram.WebApp;

// Войти в fullscreen
tg.requestFullscreen();

// Выйти из fullscreen
tg.exitFullscreen();

// Текущее состояние
console.log(tg.isFullscreen);  // true | false

Дополнительно WebApp получил два состояния — isActive (приложение в фокусе) и isFullscreen — и события:

СобытиеКогда срабатывает
fullscreenChangedУспешный переход в/из полноэкранного режима
fullscreenFailedЗапрос не выполнен (например, платформа не поддерживает)
activatedMini App стал активным (например, пользователь вернулся в чат)
deactivatedMini App ушёл в фон
safeAreaChangedИзменилась safe area (челка, индикатор времени и т. п.)
contentSafeAreaChangedИзменилась область контента внутри safe area

Полный пример с обработкой ошибок:

tg.onEvent('fullscreenChanged', () => {
  document.body.classList.toggle('is-fullscreen', tg.isFullscreen);
});

tg.onEvent('fullscreenFailed', (event) => {
  // event.error: ALREADY_FULLSCREEN | UNSUPPORTED | другая строка
  console.warn('Fullscreen failed:', event.error);
});

document.querySelector('#go-fs').addEventListener('click', () => {
  if (!tg.isVersionAtLeast('8.0')) {
    return alert('Обновите Telegram до версии с Bot API 8.0+');
  }
  tg.requestFullscreen();
});
WARNING

Safe area обязательна

В fullscreen Telegram отдаёт всю площадь экрана, включая зоны под челкой, динамическим островом и нижней полосой жестов. Используйте CSS-переменные var(--tg-viewport-safe-area-inset-top) и аналоги, иначе UI прячется под системные элементы.


Home Screen Icon

Mini App теперь можно установить как иконку на рабочем столе устройства — получится PWA-подобный опыт без выхода из экосистемы Telegram. Запуск происходит без открытия чата с ботом, что критично для retention.

Основные методы:

// Запросить добавление на домашний экран (открывает системный диалог Telegram)
tg.addToHomeScreen();

// Проверить статус ярлыка
tg.checkHomeScreenStatus((status) => {
  // status: 'added' | 'missed' | 'unsupported' | 'unknown'
  console.log('Home screen status:', status);
});

События:

СобытиеОписание
homeScreenAddedПользователь подтвердил добавление иконки
homeScreenCheckedПришёл ответ на checkHomeScreenStatus
tg.onEvent('homeScreenAdded', () => {
  analytics.track('home_screen_added');
});

tg.onEvent('homeScreenChecked', (event) => {
  if (event.status === 'missed') {
    showInstallPrompt();
  }
});
WARNING

iOS возвращает unknown

На Telegram for iOS checkHomeScreenStatus стабильно отдаёт unknown — система не позволяет надёжно определить, добавлен ли уже ярлык. На Android логика работает корректно. Не делайте бизнес-логику, зависящую от added на iOS.

Иконку и цвета loading screen Mini App можно настроить через BotFather (команды /newapp, /editbotinfo) или через tgWebAppHeaderColor / tgWebAppBackgroundColor.


Geolocation API: LocationManager

Mini Apps 2.0 даёт явный, обёрнутый Telegram API доступ к координатам — через LocationManager. Это не navigator.geolocation: WebView в Telegram не отдаёт браузерный геолокационный API, потому что Telegram сам контролирует разрешения и UX.

Жизненный цикл:

const lm = tg.LocationManager;

// 1. Инициализация (запрашивает поддержку у клиента)
lm.init(() => {
  if (!lm.isInited) return;

  // 2. Если access ранее не был дан -- открыть настройки
  if (!lm.isAccessGranted && !lm.isAccessRequested) {
    lm.openSettings();
    return;
  }

  // 3. Запросить координаты
  lm.getLocation((location) => {
    if (location === null) {
      console.warn('Доступ к геолокации не выдан');
      return;
    }
    console.log(location.latitude, location.longitude, location.altitude);
    console.log(location.course, location.speed, location.horizontal_accuracy);
  });
});

Поля объекта LocationData:

ПолеОписание
latitudeШирота
longitudeДолгота
altitudeВысота над уровнем моря, м
courseНаправление движения, градусы
speedСкорость, м/с
horizontal_accuracyТочность по горизонтали, м
vertical_accuracyТочность по высоте, м
course_accuracyТочность направления, градусы
speed_accuracyТочность скорости, м/с
WARNING

Поведение на Android

На Android LocationManager.getLocation обновляется только тогда, когда другая системная служба триггерит геолокацию в фоне (например, Карты). На iOS обновления приходят примерно раз в секунду, как и ожидается. Для рилтайм-трекинга на Android закладывайте polling и явное информирование пользователя.

INFO

Security model

Доступ к геолокации выключен по умолчанию для каждого Mini App отдельно. Пользователь явно подтверждает разрешение в системном диалоге Telegram, и его можно отозвать через LocationManager.openSettings(). Никогда не пытайтесь обходить это через сторонние HTML5-API — они заблокированы.


Biometric Authentication: BiometricManager

BiometricManager — обёртка над Face ID / Touch ID на iOS и сканером отпечатка/лица на Android. Позволяет реализовать локальную авторизацию (разблокировка кошелька, подтверждение платежа) без передачи секретов на сервер.

Базовый flow состоит из четырёх шагов: init, requestAccess, authenticate, опциональная работа с токеном.

const bm = tg.BiometricManager;

// 1. Инициализация (узнаём, поддерживается ли биометрия на устройстве)
bm.init(() => {
  if (!bm.isInited) return;

  console.log(bm.isBiometricAvailable);  // false на старых Android
  console.log(bm.biometricType);          // 'finger' | 'face' | 'unknown'

  // 2. Запросить доступ один раз (Telegram покажет диалог)
  bm.requestAccess({ reason: 'Защитить ваш кошелёк биометрией' }, (granted) => {
    if (!granted) return;

    // 3. Аутентифицировать пользователя (отдельный вызов на каждое действие)
    bm.authenticate({ reason: 'Подтвердите перевод 100 USDT' }, (success, token) => {
      if (success) {
        // token -- строка, которую можно использовать как локальный proof
        unlockWallet(token);
      }
    });
  });
});

Полезные дополнительные методы:

// Сохранить серверный токен в системном Keychain через Telegram
bm.updateBiometricToken('server-issued-jwt', (saved) => { /* ... */ });

// Открыть системные настройки биометрии Telegram
bm.openSettings();
WARNING

Не доверяйте success без серверной проверки

success === true лишь означает, что пользователь приложил палец/посмотрел в камеру и устройство приняло биометрию. Это не аутентификация для бэкенда. Для серверных решений всё равно используйте initData и HMAC, а биометрию — как дополнительный локальный фактор (например, разблокировка приватного ключа в SecureStorage).


Secondary Button и Settings Button

В 1.x была одна MainButton внизу экрана — удобно для CTA («Купить», «Подтвердить»), но мало для двух равнозначных действий. В 2.0 добавили SecondaryButton (вторая кнопка, рядом или над MainButton) и SettingsButton (пункт меню в шапке).

// Secondary Button
tg.SecondaryButton
  .setParams({
    text: 'Отмена',
    color: '#FF3B30',
    text_color: '#FFFFFF',
    has_shine_effect: false,
    position: 'left',  // 'left' | 'right' | 'top' | 'bottom'
  })
  .show()
  .onClick(() => {
    history.back();
  });

// Settings Button (пункт «Настройки» в context-меню Mini App)
tg.SettingsButton
  .show()
  .onClick(() => {
    location.hash = '#/settings';
  });

События:

СобытиеИсточник
secondaryButtonClickedКлик по SecondaryButton
settingsButtonClickedКлик по «Settings» в шапке (если включено через BotFather)
TIP

Включите Settings в BotFather

SettingsButton.show() сработает только если в BotFather для Mini App активирован пункт меню «Settings» (/setmenubutton или соответствующий пункт у /newapp). Иначе кнопка не появится и событие не придёт.


Star Subscriptions: рекуррентные платежи

В 1.x Mini Apps умели принимать только разовые платежи в Stars. В Bot API 8.0 появились подписки — автопродление каждые subscription_period секунд. На сегодня единственное допустимое значение периода — 2592000 (30 дней), то есть месячная подписка.

Создание ссылки на подписку (со стороны бота, серверный код):

// Bot API
await bot.createInvoiceLink({
  title: 'Pro план',
  description: 'Расширенная аналитика и приоритетная поддержка',
  payload: 'sub_pro_v1',
  currency: 'XTR',                  // обозначение Telegram Stars
  prices: [{ label: 'Pro', amount: 250 }],  // 250 ⭐ в месяц
  subscription_period: 30 * 24 * 60 * 60,    // 30 дней
});

Открытие инвойса из Mini App:

tg.openInvoice(invoiceLink, (status) => {
  // status: 'paid' | 'cancelled' | 'failed' | 'pending'
  if (status === 'paid') {
    showThankYou();
  }
});

Важные хуки на стороне бота:

UpdateКогда приходит
pre_checkout_queryПеред списанием — бот должен подтвердить или отклонить
successful_paymentПосле успешной оплаты (включая каждое автопродление)
transaction_partner updatesСписания, возвраты, отмены через payments.cancelStarsSubscription
INFO

Stars — единственный способ для цифровых товаров

Согласно гайдлайнам App Store / Play Store, любые платежи за цифровые товары и услуги внутри Telegram Mini App обязаны идти через Stars. Подключать Stripe, ЮKassa и т. п. для цифрового контента нельзя — бота забанят. Для физических товаров и услуг (доставка, ивенты) обычные платежные провайдеры разрешены.


Cloud Storage: ключ-значение в облаке Telegram

CloudStorage существует с Bot API 6.9, но в контексте Mini Apps 2.0 он остаётся базовым primitive для синхронизации состояния между устройствами одного пользователя — работает поверх MTProto, без своего сервера.

const cs = tg.CloudStorage;

// Записать
cs.setItem('theme', 'dark', (err, ok) => { /* ... */ });

// Прочитать
cs.getItem('theme', (err, value) => {
  console.log(value);  // 'dark' или '' если ключа нет
});

// Прочитать пачкой
cs.getItems(['theme', 'lang'], (err, values) => {
  console.log(values);  // { theme: 'dark', lang: 'ru' }
});

// Список всех ключей
cs.getKeys((err, keys) => { /* ... */ });

// Удалить
cs.removeItem('theme', (err, ok) => { /* ... */ });
cs.removeItems(['theme', 'lang'], (err, ok) => { /* ... */ });

Лимиты:

ПараметрЗначение
Максимум ключей1024 на пользователя на бота
Длина ключа1—128 символов, [A-Za-z0-9_-]
Длина значения0—4096 символов
Шифрование at restНа стороне Telegram, прозрачно для приложения
ДоступТолько тот бот, который записал данные
WARNING

Не храните секреты в CloudStorage

CloudStorage доступен Telegram-серверам (хоть и приватен для других ботов). Для приватных ключей TON, JWT и подобного используйте SecureStorage (см. ниже) или сервер-side хранилище с проверкой initData.


DeviceStorage и SecureStorage (Bot API 9.0)

CloudStorage синхронизируется через сервер — это медленно и облагается лимитом 4 КБ на значение. Для офлайн-кеша и тяжёлых данных в Bot API 9.0 добавили DeviceStorage, для секретов — SecureStorage.

DeviceStorage

Аналог localStorage, но интегрирован в Telegram-клиент и переживает обновления WebView:

const ds = tg.DeviceStorage;

ds.setItem('cache', JSON.stringify(payload), (err) => { /* ... */ });
ds.getItem('cache', (err, value) => { /* ... */ });
ds.removeItem('cache', (err) => { /* ... */ });
ds.clear((err) => { /* ... */ });

Лимиты: до 5 МБ на пользователя на бота. Данные не уходят с устройства и недоступны другому боту.

SecureStorage

SecureStorage использует системный безопасный стор: iOS Keychain и Android Keystore. Значения шифруются at rest на уровне ОС, недоступны другим приложениям и сохраняются между переустановками Mini App (но не Telegram).

const ss = tg.SecureStorage;

// Поддерживается ли безопасный стор на устройстве?
ss.isSupported((err, supported) => {
  if (!supported) return fallbackToServerOnly();

  ss.setItem('wallet_seed', encryptedSeed, (err) => { /* ... */ });
  ss.getItem('wallet_seed', (err, value) => { /* ... */ });
  ss.removeItem('wallet_seed', (err) => { /* ... */ });
});

Лимит: до 10 ключей на пользователя на бота.

ХранилищеДоступЛимитШифрованиеUse case
CloudStorageВсе устройства1024 × 4КБTelegramНастройки, прогресс, фичефлаги
DeviceStorageТекущее устройство5 МБOS-levelКеш, офлайн-данные, тяжёлый стейт
SecureStorageТекущее устройство10 ключейKeychain/KeystoreПриватные ключи TON, JWT, секреты
TIP

Связка BiometricManager + SecureStorage

Канонический паттерн для криптокошелька в Mini App: приватный ключ (или его обёртка) лежит в SecureStorage, разблокировку даёт BiometricManager.authenticate. Сервер в этой схеме не видит seed-фразу вообще.


Дополнительные API: датчики, эмодзи, обмен медиа

Mini Apps 2.0 включает ещё несколько менее обсуждаемых, но полезных API.

Accelerometer, Gyroscope, DeviceOrientation

Для игр и AR-эффектов:

tg.Accelerometer.start({ refresh_rate: 60 }, () => {
  console.log(tg.Accelerometer.x, tg.Accelerometer.y, tg.Accelerometer.z);
});

tg.onEvent('accelerometerChanged', () => { /* читать tg.Accelerometer.* */ });
tg.onEvent('accelerometerStopped', () => { /* очистка */ });

// Аналогично: tg.Gyroscope.start(...), tg.DeviceOrientation.start(...)

Также добавили lockOrientation('portrait' | 'landscape') и unlockOrientation() — полезно в комбинации с fullscreen, чтобы поворот не ломал UI.

Эмодзи-статус

tg.requestEmojiStatusAccess((granted) => {
  if (!granted) return;
  tg.setEmojiStatus('5170233102089322756', { duration: 3600 });  // на час
});

Telegram Premium-пользователи могут установить эмодзи-статус прямо из Mini App (например, показывая, что они в игре или едут в такси).

Обмен медиа и документами

shareMessage(msg_id, callback) отправляет ранее сгенерированное ботом сообщение в произвольный чат — удобно для реферальных ссылок и кастомных мемов. downloadFile(params, callback) позволяет Mini App инициировать сохранение файла (PDF, аудио, картинка) в галерею пользователя.


Миграция Mini App 1.x → 2.0

Полностью переписывать приложение не нужно — 2.0 обратно совместима с 1.x. Стратегия миграции:

  1. Подключить актуальный telegram-web-app.js — скрипт сам обновляется на стороне Telegram, но если вы используете bundler (@telegram-apps/sdk), обновите пакет до >=3.x (поддержка Bot API 8.0/9.0).
  2. Ввести capability map — одна структура с isVersionAtLeast для всех новых фич, чтобы не разбрасывать guard’ы по коду.
  3. Поэтапно подключать API, начиная с самых ценных для UX:
    • SecondaryButton, SettingsButton — 1 час работы, заметный эффект.
    • Fullscreen — если у вас игра/видео/иммерсивный UI.
    • Home Screen Icon — для retention.
    • BiometricManager + SecureStorage — если есть кошелёк или платежи.
    • LocationManager — если есть LBS-механика.
    • Star Subscriptions — если хотите рекуррентную монетизацию.
  4. Тестировать на iOS, Android и Telegram Desktop — поведение разное (например, checkHomeScreenStatus на iOS, частота обновлений LocationManager на Android).
// Пример обёртки с graceful degradation
function tryRequestFullscreen() {
  const tg = window.Telegram.WebApp;
  if (!tg.isVersionAtLeast('8.0')) {
    // Fallback на 1.x: разворачиваем как раньше
    tg.expand();
    return;
  }
  tg.requestFullscreen();
}
INFO

Третьесторонняя валидация initData

Bot API 8.0 также позволяет третьим сторонам (например, Mini App-конструкторам) валидировать initData без знания токена бота — через схему с signature и публичным ключом Telegram. Это снимает старое ограничение, по которому SaaS-сервис обязан был хранить bot_token клиента.


Частые ошибки

  1. Вызов новых методов без isVersionAtLeast — на клиентах с Bot API младше 8.0 такие вызовы выбрасывают исключение или тихо ничего не делают. Всегда проверяйте версию.
  2. Игнорирование safe area в fullscreen — интерфейс налезает на челку и системные жесты. Используйте CSS-переменные --tg-viewport-safe-area-inset-*.
  3. Доверие BiometricManager.authenticate(success) на сервере — это локальный сигнал. Серверная авторизация всё равно через initData + HMAC.
  4. Хранение приватных ключей в CloudStorage — эти данные доступны Telegram-серверам и не зашифрованы end-to-end. Используйте SecureStorage.
  5. Подписка через любой способ кроме Stars — App Store / Play Store требуют использовать Stars для цифровых товаров. Stripe для подписок на контент в Mini App = бан бота.
  6. Сборка без feature detection для подписокsubscription_period поддерживается только начиная с Bot API 8.0. На старых клиентах бот должен предложить разовый платёж.
  7. Обновление LocationManager на Android в реальном времени — координаты приходят только когда другой сервис триггерит локацию. Для трекинга реализуйте polling или предложите пользователю открыть Карты.

Итоги

ОбластьAPIВерсия
ОкноrequestFullscreen, exitFullscreen, isFullscreen8.0
УстановкаaddToHomeScreen, checkHomeScreenStatus8.0
ГеолокацияLocationManager.init/getLocation/openSettings8.0
БиометрияBiometricManager.init/requestAccess/authenticate/updateBiometricToken7.2 / 8.0
КнопкиSecondaryButton, SettingsButton8.0
ПлатежиcreateInvoiceLink({subscription_period}) + openInvoice8.0
ХранилищеCloudStorage (стабильно), DeviceStorage, SecureStorage6.9 / 9.0
ДатчикиAccelerometer, Gyroscope, DeviceOrientation8.0
ПрочееЭмодзи-статус, shareMessage, downloadFile8.0

Mini Apps 2.0 закрывают разрыв между web-приложением в чате и нативным приложением: пользователь получает почти полноценный нативный опыт (полный экран, ярлык, биометрия, датчики), а разработчик — единый API без необходимости публиковаться в App Store / Play Store. Связка BiometricManager + SecureStorage + Star Subscriptions особенно сильна для криптокошельков и подписочных сервисов на TON.

В следующих модулях курса мы соберём всё это в production-приложение — с TON Connect для платежей в TON, биометрической разблокировкой и подпиской через Stars.


Проверка знанийKnowledge check
Зачем перед вызовом WebApp.requestFullscreen() обязательно проверять WebApp.isVersionAtLeast('8.0')?
ОтветAnswer
Метод requestFullscreen появился только в Bot API 8.0 (Mini Apps 2.0). На клиентах Telegram с более старой версией Bot API объект WebApp либо не содержит этот метод (вызов бросит TypeError), либо метод существует но игнорируется. isVersionAtLeast делает feature detection и позволяет сделать graceful fallback (например, на старый expand()).
Проверка знанийKnowledge check
Чем SecureStorage (Bot API 9.0) отличается от CloudStorage и почему приватный ключ TON-кошелька следует хранить именно в SecureStorage?
ОтветAnswer
CloudStorage синхронизирует данные через серверы Telegram между устройствами, шифрует at rest на стороне Telegram, лимит 1024 ключа по 4 КБ. SecureStorage хранит данные локально на устройстве в системном Keychain (iOS) или Keystore (Android) -- значения зашифрованы средствами ОС, недоступны другим приложениям, не уходят на сервер Telegram, лимит 10 ключей на бота. Приватный ключ кошелька -- это секрет, который никогда не должен покидать устройство пользователя в открытом виде; SecureStorage -- единственный встроенный API, который даёт такие гарантии.
Проверка знанийKnowledge check
Какие три обязательных шага составляют корректный flow биометрической авторизации через BiometricManager?
ОтветAnswer
(1) BiometricManager.init(callback) -- узнаём, поддерживается ли биометрия и какого типа (finger / face); (2) BiometricManager.requestAccess(params, cb) с полем reason -- один раз спрашиваем у пользователя разрешение использовать биометрию (Telegram показывает системный диалог); (3) BiometricManager.authenticate(params, cb) с полем reason -- запрашиваем подтверждение для конкретного действия (платёж, разблокировка). Без init isInited останется false, без requestAccess authenticate провалится; пропускать шаги нельзя.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 8