Telegram ботом сегодня уже никого не удивишь. Даже на платформе 1Сных http-сервисов люди создают разные интересные проекты: как для развлечения, так и вполне реальные утилитарные решения, вроде администрирования кластера через inline-клавиатуру. Однако, я пока еще не встречал разработок или статей, рассказывающих о работе с другой веткой этого, если так можно сказать, telegram-стека - о Mini Apps.
Так вот я сейчас о них и расскажу.
Что такое Mini Apps?
Telegram Mini Apps - это технология, которая позволяет очень просто и нативно запускать самописные веб-приложения прямо внутри Telegram, а также предоставляет API для связи между вашим приложением и мессенджером, чтобы все было быстро и отзывчиво.

Да, да, я опять буду показывать свой проект :P
Для использования приложения в качестве Mini App, оно должно быть доступно по какому-либо URL. Этот URL прописывается в BotFather (автор рассчитывает, что вы уже знакомы с процессом создания ботов), после чего приложение становится доступно по отдельной кнопке на нижней панели в диалоге с ботом.
Что касается API, то работа с ним осуществляется при помощи подключения js файла от самого Telegram в HTML
<script src="https://telegram.org/js/telegram-web-app.js"></script>
Для большего понимания давайте создадим небольшое приложение, но так, чтобы это отражало работу с данным механизмом из 1С.
Мегамагазин
Идея такая: будет справочник с полями: Наименование, Описание, Картинка - номенклатура в вакууме. Данные из этого справочника мы оформим в HTML и позволим пользователю выбирать позиции в этаком импровизированном онлайн магазине.
Начнем с HTML. Для начала я создам целиком страницу вместе с несколькими карточками, а потом вынесу карточки отдельно, дабы была возможность клепать их сколько угодно. Вот такой файлик у меня получился:
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="/redirect.php?url=aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9ib290c3RyYXBANS4zLjIvZGlzdC9jc3MvYm9vdHN0cmFwLm1pbi5jc3M=" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
	<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
	<style>
		
		body{
			padding: auto;
			padding-bottom: 20px;
			padding-top: 20px;
		}
		.card{
			text-align: center;
			margin: auto;
		}
	</style>
</head>
<body>
<div class="card" style="width: 18rem;">
  <img src="https://catherineasquithgallery.com/uploads/posts/2021-03/1614610387_167-p-zadnii-fon-dlya-fotoshopa-238.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>
<div class="card" style="width: 18rem;">
  <img src="https://catherineasquithgallery.com/uploads/posts/2021-03/1614610387_167-p-zadnii-fon-dlya-fotoshopa-238.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>
<div class="card" style="width: 18rem;">
  <img src="https://catherineasquithgallery.com/uploads/posts/2021-03/1614610387_167-p-zadnii-fon-dlya-fotoshopa-238.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>
</body>
</html>
Пока, думаю, оригинальность дизайна для нас не очень важна, поэтому я использовал базовые карточки из Bootstrap 5

Теперь вынесем одиночную карточку отдельно и немного поменяем под свои нужды. Для примера я захардкожу ее в отдельный метод и буду заменять переменные с @ на нужные мне данные, а картинку отправлять Base64 строкой. Но вы можете организовать это так, как вам удобно. Желательно, конечно, реализовать это при помощи Ajax
<div class="card" style="width: 18rem;">
  <img src="data:image/png;base64, @Картинка64" class="card-img-top" alt="@Наименование">
  <div class="card-body">
    <h5 class="card-title">@Наименование</h5>
    <p class="card-text">@Описание</p>
    <button class=""btn btn-primary"" id=""@Номер"">В корзину</button>
  </div>
</div>
Остальной документ вынесу точно так же:
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="/redirect.php?url=aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9ib290c3RyYXBANS4zLjIvZGlzdC9jc3MvYm9vdHN0cmFwLm1pbi5jc3M=" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
	<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
	<style>
		body{
			padding: auto;
			padding-bottom: 20px;
			padding-top: 20px;
		}
		.card{
			text-align: center;
			margin: auto;
		}
	</style>
</head>
<body>
   @Карточки
</body>
</html>
Теперь перейдем в 1С. Добавлю обычный справочник для товаров: Описание - Строка и Картинка - ХранилищеЗначений. Теперь необходимо написать обработчик для http-сервиса, который будет хостить наше приложение. Первыми создадим 2 метода, которые будут просто возвращать наши HTML макеты:
Функция ВернутьОсновнойДокумент()
	
	Возврат
	"<!DOCTYPE html>
	|<html>
	|<head>
	|<meta charset=""utf-8"">
	|<meta name=""viewport"" content=""width=device-width, initial-scale=1"">
	|<link href=""https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"" rel=""stylesheet"" integrity=""sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"" crossorigin=""anonymous"">
	|<script src=""https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"" integrity=""sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"" crossorigin=""anonymous""></script>
	|
	|<style>
	|
	|body{
	|padding: auto;
	|padding-bottom: 20px;
	|padding-top: 20px;
	|}
	|
	|.card{
	|text-align: center;
	|margin: auto;
	|}
	|
	|
	|</style>
	|
	|</head>
	|<body>
	|
	|@Карточки
	|
	|</body>
	|</html>";
	
КонецФункции
Функция ВернутьКарточку()
	
	Возврат 
	"<div class=""card"" style=""width: 18rem;"">
	|<img src=""data:image/png;base64, @Картинка64"" class=""card-img-top"" alt=""@Наименование"">
	|<div class=""card-body"">
	|<h5 class=""card-title"">@Наименование</h5>
	|<p class=""card-text"">@Описание</p>
	|<button class=""btn btn-primary"" id=""@Номер"">В корзину</button>
	|</div>
	|</div>";
	
КонецФункции
И сборщик этих макетов в единый документ по выборке из справочника Товары:
Функция СобратьДокумент() Экспорт
	
	HTMLКарточек   = "";
	HTMLОбщий      = ВернутьОсновнойДокумент();
	Запрос         = Новый Запрос;
	
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	Товары.Наименование КАК Наименование,
		|	Товары.Описание КАК Описание,
		|	Товары.Картинка КАК Картинка
		|ИЗ
		|	Справочник.Товары КАК Товары";
	
	РезультатЗапроса = Запрос.Выполнить();
	
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		
		КартинкаДД  = ВыборкаДетальныеЗаписи.Картинка.Получить();
		КартинкаB64 = Base64Строка(КартинкаДД);
		
		Карточка = ВернутьКарточку();
		Карточка = СтрЗаменить(Карточка, "@Наименование", ВыборкаДетальныеЗаписи.Наименование);
		Карточка = СтрЗаменить(Карточка, "@Описание"    , ВыборкаДетальныеЗаписи.Описание);
		Карточка = СтрЗаменить(Карточка, "@Картинка64"  , КартинкаB64);
		
		HTMLКарточек = HTMLКарточек + Карточка + Символы.ПС + Символы.ПС;
		
	КонецЦикла;
	
	HTMLОбщий = СтрЗаменить(HTMLОбщий, "@Карточки", HTMLКарточек);
	
	Возврат HTMLОбщий;
КонецФункции
Попробуем заполнить несколько товаров и закинуть это на http-сервис.


Выглядит уже неплохо, но пока ничего не делает. Добавим наше приложение в Telegram через BotFather, чтобы убедится в правильности размеров элементов, и перейдем к работе с API мини приложений.
У BotFather необходимо выбрать бота из списка и нажать Bot Settings

Затем Menu Button.

Там будет всего один вариант - Configure menu button, при выборе которого нам предложат отправить URL своего приложения и надпись, которая будет красоваться на кнопке.

Это один из нескольких доступных способов предоставления доступа к приложению - он создает постоянную кнопку возле поля ввода сообщения. Однако, кнопку открытия можно отправлять и как кнопку клавиатуры под сообщением при помощи прямой ссылки. Но мы это пока рассматривать не будем.
Вот так это теперь выглядит на ПК:

И так на телефоне

Теперь необходимо сделать это все интерактивным.
telegram-web-app.js
Подключаемый скрипт для Mini App создает особый объект работы с мессенджером, если веб-приложение открыто внутри Telegram. Необходимо добавить его в свой макет.

И вызвать объект. Вот пример скрипта с несколькими базовыми настройками:
      
    let tg      = window.Telegram;
  
    if(tg != undefined){
      if (tg.WebApp != undefined && tg.WebApp.initData != undefined){
       
      let safe    = tg.WebApp.initData;
      
      tg.WebApp.backgroundColor = '#3d3d3d';
      tg.WebApp.headerColor = '#212121';
      tg.WebApp.expand(); 
  
      }    
    }
Сперва мы объявляем переменную tg, куда помещаем объект для работы с Telegram. Далее необходима проверка на существование этого объекта, так как если данное приложение будет запущено не внутри мессенджера, то объект определен не будет и обращение через точку вызовет исключение. Внутри проверки у нас есть три строки: цвет фона, цвет шапки и метод expand(), который позволяет растянуть окно приложения на всю доступную высоту, так как по умолчанию шторка веб-приложения вылетает не в полный размер.

Но куда важнее переменная safe, которой присваивается tg.WebApp.initData.
Одной из ключевых особенностей скрипта telegram-web-app.js является возможность уже при запуске приложения получить информацию о пользователе, работающем с ним. Для этого существуют два метода
WebApp.initData и WebApp.initDataUnsafe 
Честно говоря, существование initDataUnsafe вызывает много вопросов, так как очевидно, что передавать на сервер "достоверную" информацию, спокойно фальсифицируемую руками в отладчике, нельзя. Мы и не будем. Рассмотрим лучше, что нам возвращает initData
query_id=xxxx-4QbxxxxxxBuFAEjT&user=%7B%22id%22%3Axxxxxxx%2C%22first_name%22%3A%22Anton%22%2C%22last_name%22%3A%22Titowets%22%2C%22username%22%3A%22xxxx%22%2C%22language_code%22%3A%22ru%22%2C%22allows_write_to_pm%22%3Atrue%7D&auth_date=1702715987&hash=986xxxx9aa131c473bd830de5e7670cf24df15c0781611043f65babd7b960

P.S. веб-отладчик можно включить в настройках desktop версии Telegram, или использовать отладку по USB для мобильной
Оно возвращает простую строку, где находятся данные о пользователе. Но это не только данные сами по себе - их можно проверить на достоверность. Хотя чтобы такое провернуть, нужно решить буквально целый ребус

Но процедуру я уже написал раньше, так что теперь не придется.
Функция ПараметрыЗапросаВСоответствие(Знач СтрокаПараметров) Экспорт
	
	СоответствиеВозврата = Новый Соответствие;
	КоличествоЧастей     = 2;
	МассивПараметров     = СтрРазделить(СтрокаПараметров, "&", Ложь);
	
	Для Каждого Параметр Из МассивПараметров Цикл
		
		МассивКлючЗначение = СтрРазделить(Параметр, "=");
		
		
		Если МассивКлючЗначение.Количество() = КоличествоЧастей Тогда
                        СоответствиеВозврата.Вставить(МассивКлючЗначение[0], МассивКлючЗначение[1]);
		КонецЕсли;
		
	КонецЦикла;
		
	Возврат СоответствиеВозврата;
	
КонецФункции
Функция ОбработатьДанныеTMA(Знач СтрокаДанных, Знач Токен) Экспорт    
	
	СтрокаДанных    = РаскодироватьСтроку(СтрокаДанных, СпособКодированияСтроки.КодировкаURL);
	СтруктураДанных = ПараметрыЗапросаВСоответствие(СтрокаДанных);
	Ключ            = "WebAppData";
	Хэш             = "";
	
	Результат = HMACSHA256(ПолучитьДвоичныеДанныеИзСтроки(Ключ), ПолучитьДвоичныеДанныеИзСтроки(Токен)); 
	
	ТЗ = Новый ТаблицаЗначений;
	ТЗ.Колонки.Добавить("Ключ");
	ТЗ.Колонки.Добавить("Значение");
	
	Для Каждого Данные Из СтруктураДанных Цикл
			
		НоваяСтрока 	        = ТЗ.Добавить();		
		НоваяСтрока.Ключ        = Данные.Ключ;
		НоваяСтрока.Значение 	= Данные.Значение;
	
	КонецЦикла;
	
	ТЗ.Сортировать("Ключ");
	
	СоответствиеВозврата = Новый Соответствие;
	DCS = "";
	
	Для Каждого СтрокаТЗ Из ТЗ Цикл
		
		Если СтрокаТЗ.Ключ <> "hash" Тогда
			DCS = DCS + СтрокаТЗ.Ключ + "=" + СтрокаТЗ.Значение + Символы.ПС;
			СоответствиеВозврата.Вставить(СтрокаТЗ.Ключ, СтрокаТЗ.Значение); 
		Иначе
			Хэш = СтрокаТЗ.Значение;
		КонецЕсли;
		
	КонецЦикла;
	
	DCS 	= Лев(DCS, СтрДлина(DCS) - 1);
	Подпись = HMACSHA256(Результат, ПолучитьДвоичныеДанныеИзСтроки(DCS));
	Финал   = ПолучитьHexСтрокуИзДвоичныхДанных(Подпись);
	
	Если Финал = вРег(Хэш) Тогда
		Ответ = Истина;
	Иначе
		Ответ = Ложь;
	КонецЕсли;
	
	СоответствиеВозврата.Вставить("passed", Ответ);
	
	Возврат СоответствиеВозврата;
	
КонецФункции
Функция ОбработатьДанныеTMA() возвращает соответствие с полученными параметрами и результат проверки под ключом passed. Единственное, что вам необходимо будет достать, так это несколько методов из БСП: тут используются функции для работы с криптографией HMAC SHA-256, которые есть в модуле РаботаВМоделиСервисаБТС. Список необходимых методов:

Однако, вам не придется этого делать, если вы воспользуетесь TelegramEnterprise, так как там это уже есть:
TelegramEnterprise - базовая open-source библиотека интеграции с Telegram
- Множество реализованных методов для работы с Telegram API
- Простая установка - нужно лишь забрать 2 общих модуля
- Бесплатно и с открытым исходным кодом на GitHub
*Конец рекламной паузы*
Теперь мы знаем, как получить данные и можем их обрабатывать. Тут есть два пути:
1. Мы можем забить на объект Telegram
У нас уже есть информация о пользователе, а её отправку можно повесить, например, на какую-нибудь кнопку. Как только на эту кнопку нажмут, мы тут же узнаем, кто это сделал. Но это уже не вопросы работы с TMA, а просто стандартные методы работы через Ajax
//Функция отправки GET запроса
function ping(url) {
  
  return new Promise(function(resolve, reject) {
    
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url , true);
    xhr.onload = function() {
      
      if (xhr.status === 200) 
      {
        resolve(true);
      } 
      else 
      {
        resolve(false);
      }
    };
    xhr.onerror = function() {
      resolve(false);
    };
    xhr.send();
  });
}
//Обработка нажатия 
myCoolButton.onclick = function(event) {
    let safe    = window.Telegram.WebApp.initData;
    ping(url + '?' + safe).then(function(success){
        console.log('Aright!');
    });
}
Далее мы можем сохранить у себя полученные данные или отправить сообщение ботом при помощи нового http-запроса из 1С.
Процедура Привет(Знач Данные) Экспорт //Данные - строка initData
	
	ДанныеПользователя 	= ОбработатьДанныеTMA(Данные);
		
	Если ДанныеПользователя["passed"] Тогда
			
		ИнформацияПользователя = ДанныеПользователя["user"];
		ИнформацияПользователя = МетодыРаботыHttp.JsonВСтруктуру(
			ПолучитьДвоичныеДанныеИзСтроки(ИнформацияПользователя));
			
		ID = МетодыРаботыHttp.ЧислоВСтроку(ИнформацияПользователя["id"]);	
        МетодыРаботыTelegram.ОтправитьСообщение(ID,"Привет " + ИнформацияПользователя["first_name"]);
    КонецЕсли;
КонецПроцедуры
Этот способ хорош тем, что позволяет использовать уже существующие приложения, которые помимо TMA работают и сами по себе. Лично я этот способ и использовал, так как у меня был готовый работающий сайт, отправляющий сообщения боту по кнопке. Осталось лишь добавить
window.Telegram.WebApp.close()
для красивого закрытия шторки приложения, после того как пользователь сделал выбор (это показано на гифке в начале). При этом, данный функционал никак не мешает жить приложению в качестве обычного сайта дальше - объект вне телеграма не создается, функция не выполняется.
2. Использовать telegram-web-app дальше
Для того, чтобы наше приложение меньше походило на мобильную версию сайта и больше на TMA, мы можем использовать еще некоторые стандартные функции. Работать с ними довольно просто - в качестве основного элемента управления выступает огромная кнопка подтверждения, которая сама создастся и адаптируется под нужный размер, если включить ее отображение через функцию
coolButton = window.Telegram.WebApp.MainButton;
coolButton.show();
coolButton.text = 'Оформить';
Мы все так же работаем с WebApp объекта Telegram и обращаемся там к MainButton. У нее есть несколько полей и методов (все можно посмотреть тут), основные из которых методы show() и hide() для показа/скрытия, enable() и disable() для активности/неактивности, поля с говорящими названиями text, color и textcolor. Оформленный выше код у меня используется вот так:

Очень кустарно, но без мухлежа - все из 1С. Дальше я буду приводить код цивильно в отдельных блоках, но просто знайте, что для использования его необходимо будет подключить в HTML вашего приложения - как внешний файл, ну или хотя бы текстом, как я здесь. В нормальной ситуации и HTML, конечно, не должен формироваться в 1С, но так просто нагляднее.
После добавления кода, кнопка появляется:

Теперь нам необходимо сделать механизм оформления. Для этого немного отредактируем карточки, добавив в них чекбокс "нахождения в корзине". Никто не мешает вместо него использовать для этих целей и счетчик, и отдельный список сохранения выбранных товаров.
Ради наглядности, скрывать свой чекбокс я не буду
Функция ВернутьКарточку()
	
	Возврат 
	"<div class=""card"" style=""width: 18rem;"">
	|<img src=""data:image/png;base64, @Картинка64"" class=""card-img-top"" alt=""@Наименование"">
	|<div class=""card-body"">
	|<h5 class=""card-title"">@Наименование</h5>
	|<p class=""card-text"">@Описание</p>
	|<button class=""btn btn-primary"" id=""@Номер"">В корзину</button>
	//Тут новый чекбокс---------
	|<input type=""checkbox"" id=""cbx_@Номер"" name=""cbx_@Номер"" />
	|<label for=""cbx_@Номер"">Выбрано</label>
	//--------------------------
	|</div>
	|</div>";
	
КонецФункции

А на кнопку "В корзину" повешу активацию этого чекбокса.
let allButtons  = document.querySelectorAll('button');
    
	allButtons.forEach(function(item, i, arr) {
    
        item.onclick = function(event){ 
    
            let id           = item.id;     
            let thatCheckbox = document.getElementById('cbx_' + id);
    
            if(thatCheckbox.checked){
                item.textContent = 'В корзину';
            }else{
                item.textContent = 'Убрать из корзины';
            }
    
            thatCheckbox.checked = !thatCheckbox.checked;
        }
    });

Остается лишь обработать нажатие на кнопку "Оформить". У всех стандартных элементов TMA есть единая точка входа обработки событий - Telegram.WebApp.onEvent. Для нашей кнопки это выглядит так.
    Telegram.WebApp.onEvent('mainButtonClicked', function(){
		
        let goodsArr = []; // Массив для выбранных товаров
        //Обход всех элементов
        allButtons.forEach(function(item, i, arr) {
	        let id           = item.id;     
	        let thatCheckbox = document.getElementById('cbx_' + id);
	    
	        if(thatCheckbox.checked){
	          	goodsArr.push(id); //Если выбран - добавляем
	       	}
	    });
        let tgdata    = window.Telegram.WebApp.initData; 
        //Создаем объект с полями order - массив товаров и user - со строкой initData
        let reqdata   = {'order': goodsArr, 'user': tgdata};  
        //Отправляем методом POST, конвертируя объект в JSON
        post('https://api.athenaeum.digital/node/bot/goods_order', JSON.stringify(reqdata)).then(function(success){
	    	window.Telegram.WebApp.close(); //Закрываем после ответа
	    });
    });
А в 1С полученные данные уже обрабатываются:
Функция ОтправитьСписокПокупок(Данные, Токен) Экспорт
	
	ЧтениеJSON  = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(Данные);
	ОтветОбъект = ПрочитатьJSON(ЧтениеJSON);
	
	ДанныеПользователя     = ОбработатьДанныеTMA(ОтветОбъект["user"], Токен);
	ИнформацияПользователя = ДанныеПользователя["user"];
	
	ЧтениеJSON  = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(ИнформацияПользователя);
	ИнформацияПользователя = ПрочитатьJSON(ЧтениеJSON);
	
	Если ДанныеПользователя["passed"] Тогда
		
		ТекстСообщения = "Ваш заказ:" + Символы.ПС;
		
		Для Каждого Товар Из ОтветОбъект["order"] Цикл
			
			Наименование   = Справочники.Товары.НайтиПоКоду(Товар).Наименование;
			ТекстСообщения = ТекстСообщения + Наименование + Символы.ПС;
			
		КонецЦикла;
		
		МетодыРаботыTelegram.ОтправитьСообщение(МетодыРаботыHttp.ЧислоВСтроку(ИнформацияПользователя["id"])
			, ТекстСообщения, Токен);
		
	КонецЕсли;
		
	Возврат "ok";	
	
Конецфункции
И вот наш магазин готов!

Мы отправляем запрос в 1С и от туда же отправляем ответ, так что, разумеется, там можно не только сформировать сообщение пользователю, но и создать документы, сделать записи в регистры, выполнить доп. обработку, etc.
Тут я рассказал не про все доступные возможности, но про основные - наиболее вероятные и полезные в реальном использовании. Небольшая, но полная документация по всей системе TMA есть тут:
https://core.telegram.org/bots/webapps
А тут весь код модуля нашего магазина (кроме методов БСП для HMAC SHA256 и отправки сообщения в Телеграм из TelegramEnterprise):
Ну а пока это все, спасибо за внимание!

Мой GitHub: https://gitub.com/Bayselonarrend Лицензия MIT: https://mit-license.org
Вступайте в нашу телеграмм-группу Инфостарт
 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                    
