Как исправить ошибку 307 Redirect и настроить интеграцию с Честным Знаком по API для маркировки табака в 1С?

Программист 1С v8.3 (Управляемые формы) Торговля и дистрибуция
← На главную

При интеграции систем 1С:Предприятие с государственной информационной системой маркировки товаров (ГИС МТ «Честный Знак») разработчики часто сталкиваются со специфическим поведением API. Типичный сценарий: вчера запросы к системе отправлялись корректно, а сегодня программа возвращает ошибку «HTTP 307 Temporary Redirect» или возвращает HTML-страницу с текстом «You need to enable JavaScript to run this app».

В этой статье мы подробно разберем, почему возникают такие проблемы, как правильно работать с доменами маркировки для табачной продукции, в чем разница между устаревающим Facade API и современным True API, а также напишем надежный отказоустойчивый код на стороне 1С для работы с запросами и авторизацией.

---

Почему возникает ошибка HTTP 307 Redirect и «You need to enable JavaScript»?

Давайте проанализируем архитектуру ГИС МТ. Честный Знак обслуживает десятки товарных групп (обувь, одежда, лекарства, молоко, вода, табак и др.). Для оптимизации нагрузки и разграничения бизнес-логики ЦРПТ разделяет физические и логические серверы (хосты) для разных товарных категорий.

Многие разработчики по привычке используют базовый адрес ismp.crpt.ru для всех запросов. Однако для товарной группы «Табачная продукция» (а также альтернативного табака и никотиносодержащей продукции) выделен собственный контур. При попытке отправить запрос по табаку на общий адрес ismp.crpt.ru, балансировщик ГИС МТ определяет товарную группу и возвращает HTTP-статус 307 Temporary Redirect, указывая в заголовке ответа Location корректный адрес — markirovka.crpt.ru (или tobacco.crpt.ru).

Поведение платформы 1С:Предприятие при работе с объектом HTTPСоединение имеет важную особенность: 1С по умолчанию не выполняет автоматическую переадресацию при получении ответов класса 3xx. Вместо этого метод ВызватьHTTPМетод() (или Получить()/ОтправитьДляОбработки()) возвращает объект HTTPОтвет с кодом состояния 307. Если ваш код жестко завязан на ожидание успешного кода 200 или 201, алгоритм аварийно завершает работу.

Что касается сообщения «You need to enable JavaScript to run this app» — это стандартная заглушка одностраничных веб-приложений (SPA), написанных на React или Angular. Когда вы отправляете некорректно сформированный запрос (например, с неверным токеном, опечаткой в URL или на отключенный эндпоинт), защитные механизмы или маршрутизатор ГИС МТ перенаправляют ваш запрос на веб-интерфейс личного кабинета. Поскольку объект HTTPСоединение в 1С не умеет выполнять JavaScript-код, встроенный в полученную HTML-страницу, вы видите этот текст в теле ответа вместо ожидаемого JSON-массива.

---

Разница между Facade API и True API

В процессе развития ГИС МТ разработчики ЦРПТ приняли решение полностью отказаться от использования так называемого Facade API (эндпоинты, содержащие в пути /api/v4/facade/... или /api/v3/facade/...). Эти методы служили промежуточным слоем и создавали избыточную нагрузку на базы данных.

На смену им пришел стандарт True API. Адреса его методов содержат обязательный префикс: /api/v3/true-api/.... Если вы продолжаете использовать методы Facade API (например, для получения количества КМ или информации о кодах маркировки), система будет принудительно возвращать перенаправления 307, ошибки 410 (Gone) или выдавать нерелевантные ответы.

Для стабильной работы интеграции необходимо перевести все запросы 1С на использование методов True API. Например:

---

Реализация ручной обработки редиректов 307 в 1С

Давайте напишем универсальную функцию, которая будет выполнять HTTP-запросы к ГИС МТ и в случае получения кода 307 автоматически извлекать новый адрес из заголовка Location, повторно инициализировать соединение и возвращать корректный результат.

Рассмотрим пример реализации такой функции:


Функция ВыполнитьЗапросСОбработкойРедиректа(Знач Хост, Знач Порт, Знач ПутьЗапроса, Знач Метод, Знач Заголовки, Знач ТелоЗапроса = "") Экспорт

    ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
    Соединение = Новый HTTPСоединение(Хост, Порт, , , , , ЗащищенноеСоединение);
    
    Запрос = Новый HTTPЗапрос(ПутьЗапроса);
    Для Каждого КлючИЗначение Из Заголовки Цикл
        Запрос.Заголовки.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
    КонецЦикла;
    
    Если НЕ ПустаяСтрока(ТелоЗапроса) Тогда
        Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеМаркераПоследовательностиБайтов.НеИспользовать);
    КонецЕсли;
    
    Попытка
        Ответ = Соединение.ВызватьHTTPМетод(Метод, Запрос);
    Исключение
        ВызватьИсключение "Ошибка сетевого соединения при обращении к " + Хост + ": " + ОписаниеОшибки();
    КонецПопытки;
    
    // Проверяем временное перенаправление 307
    Если Ответ.КодСостояния = 307 Тогда
        
        НовыйАдрес = Ответ.Заголовки.Получить("Location");
        Если НовыйАдрес = Неопределено Тогда
            ВызватьИсключение "Получен ответ 307, но заголовок Location отсутствует.";
        КонецЕсли;
        
        // Анализируем полученный URL для выделения нового хоста и пути
        // Пример URL в Location: https://markirovka.crpt.ru/api/v3/true-api/...
        НовыйХост = "";
        НовыйПуть = "";
        
        // Убираем протокол, если он присутствует
        ВременныйАдрес = СтрЗаменить(НовыйАдрес, "https://", "");
        ВременныйАдрес = СтрЗаменить(ВременныйАдрес, "http://", "");
        
        ПозицияСлэша = Найти(ВременныйАдрес, "/");
        Если ПозицияСлэша > 0 Тогда
            НовыйХост = Лев(ВременныйАдрес, ПозицияСлэша - 1);
            НовыйПуть = Сред(ВременныйАдрес, ПозицияСлэша);
        Иначе
            НовыйХост = ВременныйАдрес;
            НовыйПуть = "";
        КонецЕсли;
        
        // Выполняем повторный запрос на новый хост
        Возврат ВыполнитьЗапросСОбработкойРедиректа(НовыйХост, Порт, НовыйПуть, Метод, Заголовки, ТелоЗапроса);
        
    КонецЕсли;
    
    Возврат Ответ;

КонецФункции

---

Пошаговый алгоритм авторизации в True API для табачной продукции

Для успешного выполнения запросов к True API нам потребуется авторизованный токен (JWT-токен). Время жизни токена составляет 10 часов, поэтому рекомендуется кэшировать его в информационной базе 1С и обновлять по истечении этого срока.

Процесс получения токена состоит из трех шагов:

  1. Запрос криптографической последовательности (данных для подписания).
  2. Подписание полученных данных с помощью локального закрытого ключа УКЭП (усиленной квалифицированной электронной подписи).
  3. Отправка подписанных данных обратно в ГИС МТ для получения сессионного токена.

Давайте реализуем этот процесс на языке 1С. Для подписания данных будем использовать встроенный объект платформы МенеджерКриптографии.


Функция ПолучитьТокенАвторизации(СертификатСубъекта) Экспорт

    // Шаг 1: Получаем случайную строку (UUID) для подписания
    Хост = "markirovka.crpt.ru"; // Используем правильный домен для табака напрямую
    ПутьЗапроса = "/api/v3/true-api/auth/key";
    
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "application/json; charset=UTF-8");
    
    Ответ = ВыполнитьЗапросСОбработкойРедиректа(Хост, 443, ПутьЗапроса, "GET", Заголовки);
    
    Если Ответ.КодСостояния <> 200 Тогда
        ВызватьИсключение "Не удалось получить ключ авторизации. Код: " + Ответ.КодСостояния;
    КонецЕсли;
    
    // Читаем UUID и Data для подписи
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку("UTF-8"));
    ДанныеАвторизации = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    СтрокаДляПодписи = ДанныеАвторизации.data;
    ИдентификаторКлюча = ДанныеАвторизации.uuid;
    
    // Шаг 2: Подписываем данные с помощью ЭЦП
    КриптоДанные = ПолучитьДвоичныеДанныеИзСтроки(СтрокаДляПодписи, КодировкаТекста.UTF8);
    
    МенеджерКрипто = Новый МенеджерКриптографии("", "", 80); // Используем системный CSP
    МенеджерКрипто.ПарольДоступаКЗакрытомуКлючу = "ВашПарольКСертификату";
    
    // Подписываем данные в формате PKCS#7 detached (отсоединенная подпись)
    ДвоичныеДанныеПодписи = МенеджерКрипто.Подписать(КриптоДанные, СертификатСубъекта, Истина);
    СтрокаПодписиBase64 = Base64Строка(ДвоичныеДанныеПодписи);
    
    // Шаг 3: Отправляем подписанную строку назад в Честный Знак
    ПутьАвторизации = "/api/v3/true-api/auth/simpleSignIn";
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    НовыйСтруктура = Новый Структура;
    НовыйСтруктура.Вставить("uuid", ИдентификаторКлюча);
    НовыйСтруктура.Вставить("data", СтрокаПодписиBase64);
    ЗаписатьJSON(ЗаписьJSON, НовыйСтруктура);
    ТелоЗапросаАвторизации = ЗаписьJSON.Закрыть();
    
    ОтветТокен = ВыполнитьЗапросСОбработкойРедиректа(Хост, 443, ПутьАвторизации, "POST", Заголовки, ТелоЗапросаАвторизации);
    
    Если ОтветТокен.КодСостояния <> 200 Тогда
        ВызватьИсключение "Ошибка авторизации в ГИС МТ. Код: " + ОтветТокен.КодСостояния;
    КонецЕсли;
    
    ЧтениеJSON.УстановитьСтроку(ОтветТокен.ПолучитьТелоКакСтроку("UTF-8"));
    РезультатТокена = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    // Возвращаем JWT-токен
    Возврат РезультатТокена.token;

КонецФункции

---

Пример работы с True API для проверки кодов маркировки сигарет

Теперь, когда мы научились получать токен авторизации и правильно обрабатывать возможные редиректы, рассмотрим пример отправки запроса для проверки статуса кодов маркировки пачек или блоков сигарет через True API.

Для табачной продукции критически важно правильно передавать структуру кодов в запросах. Коды блоков и пачек содержат спецсимволы (группы применения FNC1, разделители GS1), которые при формировании JSON должны экранироваться. Общедоступные методы True API позволяют получать краткую информацию без авторизации, но для получения детальной информации о владельце и статусе КМ авторизация обязательна.

Рассмотрим пример детальной проверки статуса КМ:


Процедура ПроверитьКодыМаркировкиТабака(Токен, МассивКодовМаркировки)

    Хост = "markirovka.crpt.ru";
    ПутьЗапроса = "/api/v3/true-api/cises/info";
    
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "application/json; charset=UTF-8");
    Заголовки.Вставить("Authorization", "Bearer " + Токен);
    
    // Формируем массив КМ для отправки в Честный Знак
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, МассивКодовМаркировки);
    ТелоЗапроса = ЗаписьJSON.Закрыть();
    
    Ответ = ВыполнитьЗапросСОбработкойРедиректа(Хост, 443, ПутьЗапроса, "POST", Заголовки, ТелоЗапроса);
    
    Если Ответ.КодСостояния = 200 Тогда
        
        ЧтениеJSON = Новый ЧтениеJSON;
        ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку("UTF-8"));
        РезультатПроверки = ПрочитатьJSON(ЧтениеJSON);
        ЧтениеJSON.Закрыть();
        
        // Обрабатываем полученные данные
        Для Каждого ДанныеКода Из РезультатПроверки Цикл
            Сообщить("Код: " + ДанныеКода.cis + " | Статус: " + ДанныеКода.status);
        КонецЦикла;
        
    Иначе
        Сообщить("Не удалось проверить коды маркировки. Код состояния: " + Ответ.КодСостояния);
        Сообщить("Ответ сервера: " + Ответ.ПолучитьТелоКакСтроку("UTF-8"));
    КонецЕсли;

КонецПроцедуры

---

Резюме и практические рекомендации

Для того чтобы ваша интеграция 1С с ГИС МТ по табачной группе работала стабильно и не вызывала ошибок 307 Redirect, придерживайтесь следующих правил:

  1. Используйте правильный хост: Сразу направляйте все табачные запросы на домен markirovka.crpt.ru вместо универсального ismp.crpt.ru. Это снизит нагрузку на систему и исключит лишние сетевые перенаправления.
  2. Откажитесь от Facade API: Полностью исключите из кода вызовы эндпоинтов, содержащих сегмент /facade/. Перепишите интеграцию на методы семейства /true-api/.
  3. Обрабатывайте перенаправления: Даже при правильной настройке серверов ЦРПТ может временно перераспределять нагрузку между дата-центрами. Наличие в вашем коде 1С механизма рекурсивного или итерационного разбора HTTP-ответов со статусом 307 гарантирует бесперебойную работу обмена.
  4. Контролируйте токен авторизации: Следите за временем жизни JWT-токена. Запрашивайте его заново раз в 9-10 часов или обрабатывайте ошибку 401 (Unauthorized) в ответах от сервера как сигнал к принудительному обновлению сессии.
← На главную