Подписано постановление правительства РФ № 1944 от 21 ноября 2023 о продаже маркированной продукции с предварительной проверкой в информационной системе «Честный ЗНАК».
Основные этапы запуска данного режима:
1 апреля 2024
Табачная продукция, Пиво, сидр, слабоалкогольные напитки (в кегах)
1 мая 2024 — для сетей из 50 и более магазинов
Молочные товары и упакованная вода
1 сентября 2024 — для всех
Молочные товары и упакованная вода
1 ноября 2024 года
Парфюмерия, БАДы и антисептики, Одежда и обувь, Фототовары, Шины, Пиво и слабоалкогольные напитки.
5 февраля 2025
Соки и безалкогольные напитки
Как можно выполнять такую проверку из 1С 8 и 7.7:
На самом деле, все просто.
- В личном кабинете Честного Знака можно получить токен авторизации для ККТ (см 4-ю картинку).
- Определить сервер (CDN площадку), через который пойдет проверка марки
- До 01.04.2024 можно использовать markirovka.crpt.ru
- После 01.04.2024 надо получать список доступных площадок проверки кода через API сервиса https://cdn.crpt.ru/api/v4/true-api/cdn/info
- ЦРПТ вернет список доступных серверов, из них надо будет выбрать тот, что наименее нагружен
- Нагрузку определяем через api/v4/true-api/cdn/health/check , там возвращается среднее время ожидания
- Используя HTTPЗапрос (post) передать марку и ИНН на этот сервер (площадку) через API сервиса api/v4/true-api/codes/check
- Получить обратно информацию по марке в JSON, разобрать ее.
- Сделать вывод, можно ли продавать данную марку.
- При продаже на ККТ в отраслевой реквизит чека (тег 1260) передать идентификатор запроса и отметку времени, которые вернет ЦРПТ.
Электронная подпись не нужна!
Как передавать результаты в чек ККТ:
1260:отраслевой реквизит предмета расчета
1262: 030 идентификатор ФОИВ
1263: 21.11.2023 дата документа основания
1264: 1944 номер документа основания
1265: UUID=56662403-e336-4114-88f3-6bd727f61a02&Time=1710432829649 значение отраслевого реквизита
В публикацию включены обработки, демонстрирующие весь процесс для 1С 8.х и 1С 7.7:
- Получение площадок CDN из ЦРПТ через /cdn/info
- Выбор самой быстрой из них через /cdn/health/check
- Запрос данных о статусе марки через /codes/check и разбор полученного ответа json
- Определение возможности продажи марки
ВАЖНО:
Марки должны передаваться на контроль с символом <GS> в тех местах, где он положен по правилам маркировки.
Обычно он идет перед крипто-хвостом, т.е. перед маркерами 91,92,93 в коде марки.
Символ этот должен передаваться в JSON формате экранированным, как \u001d
Пример запроса: { "codes":[ "01048657365749062155esJWe\u001d93dGVz" ], "fiscalDriveNumber": "1234567890123456" }
Чтобы обработки из этой публикации корректно отправляли символ <GS> на контроль, марку надо вводить в следующем виде: 0104601662006162215rP23&(29)93wOyt
Т.е символ <GS> обозначать как (29)
Пример запроса статуса марки для 1С 8.х:
Функция ВыполнитьОнлайнКонтрольМаркиЧестныйЗнак(Марка,Токен,ИНН,НомерФН,ТестовыйКонтур,ПлощадкаCDN) Экспорт
Заголовки = Новый Соответствие();
Заголовки.Вставить("X-API-KEY",Токен);
Заголовки.Вставить("Accept-Charset","utf-8");
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
HTTPЗапрос = Новый HTTPЗапрос("api/v4/true-api/codes/check",Заголовки);
Если ЗначениеЗаполнено(ИНН) Тогда
ЧастьЗапросаИНН = ",""inn"":"""+СокрЛП(ИНН)+"""";
Иначе
ЧастьЗапросаИНН = "";
КонецЕсли;
Если ЗначениеЗаполнено(НомерФН) Тогда
ЧастьЗапросаФН = ",""fiscalDriveNumber"":"""+СокрЛП(НомерФН)+"""";
Иначе
ЧастьЗапросаФН = "";
КонецЕсли;
ТелоЗапросаJSON = "{""codes"":["""+СокрЛП(Марка)+"""]"+ЧастьЗапросаИНН+ЧастьЗапросаФН+"}";
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Если ЗначениеЗаполнено(ПлощадкаCDN) Тогда
АдресРесурса = ПлощадкаCDN;
Иначе
АдресРесурса = ?(ТестовыйКонтур=Истина,"markirovka.sandbox.crptech.ru","markirovka.crpt.ru");
КонецЕсли;
Попытка
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(Неопределено,Неопределено);
Соединение = Новый HTTPСоединение(АдресРесурса,443,,,Неопределено,1.5,ЗащищенноеСоединение);
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос); //post
Исключение
Сообщить(ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
ТелоОтветаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();
Сообщить("Код состояния: "+HTTPОтвет.КодСостояния);
Сообщить("Текст ответа: "+ТелоОтветаJSON);
Возврат Истина;
КонецФункции
Пример запроса CDN-площадок для 1С 8.х
Функция ЗапроситьДанныеПлощадокCDN(Токен,ТестовыйКонтур) Экспорт
Заголовки = Новый Соответствие();
Заголовки.Вставить("X-API-KEY",Токен);
Заголовки.Вставить("Accept-Charset","utf-8");
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
HTTPЗапрос = Новый HTTPЗапрос("api/v4/true-api/cdn/info",Заголовки);
АдресРесурса = ?(ТестовыйКонтур=Истина,"markirovka.sandbox.crptech.ru","cdn.crpt.ru"); //Тут хост продуктивного контура другой
Попытка
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(Неопределено,Неопределено);
Соединение = Новый HTTPСоединение(АдресРесурса,443,,,Неопределено,1.5,ЗащищенноеСоединение);
HTTPОтвет = Соединение.Получить(HTTPЗапрос); //GET
Исключение
Сообщить(ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
ТелоОтветаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();
Сообщить("Код состояния: "+HTTPОтвет.КодСостояния);
Сообщить("Текст ответа: "+ТелоОтветаJSON);
Если HTTPОтвет.КодСостояния = 200 Тогда
Возврат Истина;
Иначе
Сообщить("Не удалось получить площадку CDN. Код ответа: "+Строка(HTTPОтвет.КодСостояния));
Возврат Ложь;
КонецЕсли;
КонецФункции
Пример проверки CDN-площадки для 1С 8.х
Функция ПроверитьПлощадкуCDN(Токен,ПлощадкаCDN) Экспорт
Заголовки = Новый Соответствие();
Заголовки.Вставить("X-API-KEY",Токен);
Заголовки.Вставить("Accept-Charset","utf-8");
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
HTTPЗапрос = Новый HTTPЗапрос("api/v4/true-api/cdn/health/check",Заголовки);
АдресРесурса = ПлощадкаCDN;
ПортРесурса = 443;
ИспШифрование = Истина;
ВремяЗапроса = 0;
Попытка
Если ИспШифрование Тогда
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(Неопределено,Неопределено);
Иначе
ЗащищенноеСоединение = Неопределено;
КонецЕсли;
Соединение = Новый HTTPСоединение(АдресРесурса,ПортРесурса,,,Неопределено,1.5,ЗащищенноеСоединение);
ВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
HTTPОтвет = Соединение.Получить(HTTPЗапрос); //Get
ВремяОкончания = ТекущаяУниверсальнаяДатаВМиллисекундах();
ВремяЗапроса = ВремяОкончания - ВремяНачала;
Исключение
//Нет соединения или таймаут - продажа разрешена
Сообщить(ОписаниеОшибки());
Возврат -1;
КонецПопытки;
ТелоОтветаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();
Сообщить("Код состояния: "+HTTPОтвет.КодСостояния);
Сообщить("Текст ответа: "+ТелоОтветаJSON);
Если HTTPОтвет.КодСостояния=200 Тогда
//ВремяОжиданияСтр = ПолучитьИзСтрокиJSON(ТелоОтветаJSON,"avgTimeMs");
Возврат ВремяЗапроса ;
Иначе
Сообщить("Не удалось проверить площадку CDN. Код ответа: "+Строка(HTTPОтвет.КодСостояния));
Возврат -1;
КонецЕсли;
КонецФункции
Пример запроса статуса марки для 1С 7.7 :
Функция ВыполнитьОнлайнКонтрольМаркиЧестныйЗнак(Марка,Токен,ИНН,НомерФН,ТестовыйКонтур,ПлощадкаCDN)
Если ПустоеЗначение(ПлощадкаCDN)=0 Тогда
АдресРесурса = ПлощадкаCDN;
Иначе
АдресРесурса = ?(ТестовыйКонтур=1,"markirovka.sandbox.crptech.ru","markirovka.crpt.ru");
КонецЕсли;
АдресРесурса = "https://"+АдресРесурса+"/api/v4/true-api/codes/check";
HTTPЗапрос = СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
HTTPЗапрос.Option(2,"utf-8");
HTTPЗапрос.SetTimeouts(1500, 1500, 1500, 1500);
HTTPЗапрос.Open("POST",АдресРесурса,1);
HTTPЗапрос.SetRequestHeader("X-API-KEY", СокрЛП(Токен));
HTTPЗапрос.SetRequestHeader("Accept-Charset", "utf-8");
HTTPЗапрос.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
Если ПустаяСтрока(ИНН)=0 Тогда
ЧастьЗапросаИНН = ",""inn"":"""+СокрЛП(ИНН)+"""";
Иначе
ЧастьЗапросаИНН = "";
КонецЕсли;
Если ПустаяСтрока(НомерФН)=0 Тогда
ЧастьЗапросаФН = ",""fiscalDriveNumber"":"""+СокрЛП(НомерФН)+"""";
Иначе
ЧастьЗапросаФН = "";
КонецЕсли;
ТелоЗапросаJSON = "{""codes"":["""+СокрЛП(Марка)+"""]"+ЧастьЗапросаИНН+ЧастьЗапросаФН+"}";
HTTPЗапрос.Send(ТелоЗапросаJSON);
ТаймаутОжидания = 5;
Попытка
РезЗапроса = HTTPЗапрос.WaitForResponse(ТаймаутОжидания);
Исключение
Сообщить(ОписаниеОшибки());
Возврат 0;
КонецПопытки;
Если РезЗапроса = -1 Тогда
КодСостояния = HTTPЗапрос.status();
КодСостоянияТекст = HTTPЗапрос.statusText();
ТелоОтветаJSON = СокрЛП(HTTPЗапрос.ResponseText());
Сообщить("Код состояния: "+Строка(КодСостояния));
Сообщить("Состояние: "+КодСостоянияТекст);
Сообщить("Текст ответа: "+ТелоОтветаJSON); //Строка длинная, может не влезть, в файле для скачивания она делится на короткие строки
Возврат 1;
Иначе
Сообщить("Нет связи с ЦРПТ.");
Возврат 0;
КонецЕсли;
КонецФункции
Пример запроса CDN-площадок для 1С 7.7 :
Функция ЗапроситьДанныеПлощадокCDN(Токен,ТестовыйКонтур) Экспорт
АдресРесурса = ?(ТестовыйКонтур=1,"markirovka.sandbox.crptech.ru","cdn.crpt.ru"); //Тут хост продуктивного контура другой
АдресРесурса = "https://"+АдресРесурса +"/api/v4/true-api/cdn/info";
HTTPЗапрос = СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
HTTPЗапрос.Option(2,"utf-8");
HTTPЗапрос.SetTimeouts(3000, 3000, 3000, 3000);
HTTPЗапрос.Open("GET",АдресРесурса,1);
HTTPЗапрос.SetRequestHeader("X-API-KEY", СокрЛП(Токен));
HTTPЗапрос.SetRequestHeader("Accept-Charset", "utf-8");
HTTPЗапрос.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
ВремяНачала = _GetPerformanceCounter();
HTTPЗапрос.Send();
ТаймаутОжидания = 5;
Попытка
РезЗапроса = HTTPЗапрос.WaitForResponse(ТаймаутОжидания);
Исключение
Сообщить(ОписаниеОшибки());
Возврат 0;
КонецПопытки;
ВремяОкончания = _GetPerformanceCounter();
ВремяЗапроса = ВремяОкончания - ВремяНачала;
Если РезЗапроса = -1 Тогда
КодСостояния = HTTPЗапрос.status();
КодСостоянияТекст = HTTPЗапрос.statusText();
ТелоОтветаJSON = СокрЛП(HTTPЗапрос.ResponseText());
Сообщить("Код состояния: "+Строка(КодСостояния));
Сообщить("Состояние: "+КодСостоянияТекст);
Сообщить("Текст ответа: "+ТелоОтветаJSON);
Если КодСостояния=200 Тогда
Возврат 1;
Иначе
Сообщить("Не удалось получить площадку CDN. Код ответа: "+Строка(КодСостояния));
Возврат 0;
КонецЕсли;
Иначе
Сообщить("Нет связи с ГИС МТ.");
Возврат Рез;
КонецЕсли;
КонецФункции
Пример проверки CDN-площадки для 1С 7.7
Функция ПроверитьПлощадкуCDN(Токен,ПлощадкаCDN) Экспорт
АдресРесурса = ПлощадкаCDN;
ПортРесурса = 443;
ИспШифрование = 1;
Если ИспШифрование=1 Тогда
АдресРесурса = "https://"+АдресРесурса;
Иначе
АдресРесурса = "http://"+АдресРесурса;
КонецЕсли;
АдресРесурса = АдресРесурса +"/api/v4/true-api/cdn/health/check";
HTTPЗапрос = СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
HTTPЗапрос.Option(2,"utf-8");
HTTPЗапрос.SetTimeouts(3000, 3000, 3000, 3000);
HTTPЗапрос.Open("GET",АдресРесурса,1);
HTTPЗапрос.SetRequestHeader("X-API-KEY", СокрЛП(Токен));
HTTPЗапрос.SetRequestHeader("Accept-Charset", "utf-8");
HTTPЗапрос.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
ВремяНачала = _GetPerformanceCounter();
HTTPЗапрос.Send();
ТаймаутОжидания = 5;
Попытка
РезЗапроса = HTTPЗапрос.WaitForResponse(ТаймаутОжидания);
Исключение
Сообщить(ОписаниеОшибки());
Возврат -1;
КонецПопытки;
ВремяОкончания = _GetPerformanceCounter();
ВремяЗапроса = ВремяОкончания - ВремяНачала;
Если РезЗапроса = -1 Тогда
КодСостояния = HTTPЗапрос.status();
КодСостоянияТекст = HTTPЗапрос.statusText();
ТелоОтветаJSON = СокрЛП(HTTPЗапрос.ResponseText());
Сообщить("Код состояния: "+Строка(КодСостояния));
Сообщить("Состояние: "+КодСостоянияТекст);
Сообщить("Текст ответа: "+ТелоОтветаJSON);
Если КодСостояния=200 Тогда
//ВремяОжиданияСтр = ПолучитьИзСтрокиJSON(ТелоОтветаJSON,"avgTimeMs");
Возврат ВремяЗапроса;
Иначе
Сообщить("Не удалось получить площадку CDN. Код ответа: "+Строка(КодСостояния));
Возврат -1;
КонецЕсли;
Иначе
Сообщить("Нет связи с ГИС МТ.");
Возврат -1;
КонецЕсли;
КонецФункции
UPD 15.03.2024
Добавлено получение площадок CDN
Добавлен выбор наилучшей площадки CDN
UPD 21.03.2024
Добавлена проверка площадок CDN
Добавлена передача номера ФН при проверке марки (это рекомендуется делать, чтобы при "подозрительной активности" ЦРПТ блокировало запросы по номеру ФН а не токен целиком")
UPD 25.03.2024
Добавлено важно замечание относительно символа <GS> в коде марки, отправляемом на контроль.
Обновлены обработки.
UPD 27.03.2024
Все таймауты увеличены до 15 секунд. Как показала практика эксплуатации, 3 секунд крайне мало.
UPD 01.04.2024
Добавил экранирование символа GS1 (код 29) при передаче в ЦРПТ.
UPD 02.04.2024
Добавил обработку тега isOwner в ответе ЦРПТ при проверке марки
Проверено на следующих конфигурациях и релизах:
- Управление торговлей, редакция 10.3, релизы 10.3.87.2