Этот материал является доработкой публикации: //infostart.ru/public/1276725/
ГИС МТ - Государственная информационная система мониторинга за оборотом товаров
ИС МП - Информационная система маркировки и прослеживаемости
В интерфейсе решений 1С в настройках мы можем видеть: "Интеграция с ИС МП (обувь, одежда, табак...)".
На титульном листе документа описания API, мы видим заголовок "Описание API ГИС МТ".
Обращение к API ГИС МП ничем бы не отличалось от обращения к любым другим API, если бы не необходимость использовать электронную подпись. В этом и заключается основная сложность реализации собственного клиента.
В пункте 5.1 описания API приводится реализация на встроенном языке 1С двух важных функций: ПодписатьТекст() и ПолучитьСертификатПоОтпечатку(). Странно, что пункт 5.1 описания API озаглавлен не иначе как "Пример получения токена...", однако, процедуры получения токена там нет. Поэтому предлагаю свою реализацию этой процедуры:
Процедура ПолучиьТокен(Команда)
Соединение = Новый HTTPСоединение(Сервер, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL);
// 1. Запрос авторизацией: возвращает УИД и текст, который надо подписать.
Запрос = Новый HTTPЗапрос(БазовыйАдресСтенда + "/auth/cert/key");
Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
ОтветЗапросаАвторизации = Ответ.ПолучитьТелоКакСтроку("UTF-8");
ОтветЗапросаАвторизации = СтруктураИзJSON(ОтветЗапросаАвторизации);
// 2. Текст, полученный из запроса вторизации, надо зашифровать и подписать.
ТекстДляПодписывания = ОтветЗапросаАвторизации.data;
ТекстДляПодписывания = ЗашифроватьBase64(ТекстДляПодписывания, КодировкаТекста.UTF8);
ПодписанныйТекст = ПодписатьТекст(ТекстДляПодписывания, Отпечаток, Ложь);
// Структура данных для запроса токена.
Данные = Новый Структура;
Данные.Вставить("uuid", ОтветЗапросаАвторизации.uuid);
Данные.Вставить("data", ПодписанныйТекст);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Данные); // превращаем в JSON
Данные = ЗаписьJSON.Закрыть();
Заголовки = Новый Соответствие; // заголовки для запроса токена
Заголовки.Вставить("Content-Type", "application/json; charset=UTF-8");
Заголовки.Вставить("Accept", "application/json");
// 3. Запрос получения аутентификационного токена: возвращает данные с токеном.
Запрос = Новый HTTPЗапрос(БазовыйАдресСтенда + "/auth/cert/", Заголовки);
Запрос.УстановитьТелоИзСтроки(Данные,
КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Ответ = СтруктураИзJSON(Ответ.ПолучитьТелоКакСтроку());
Токен = Ответ.token;
КонецПроцедуры
Для получения токена нужно выполнить два запроса. Первым запросом мы получаем два параметра для второго запроса. На промежуточном шаге, один из этих параметров следует зашифровать и подписать электронной подписью.
Реализация функций ПодписатьТекст() и ПолучитьСертификатПоОтпечатку() почти полностью взята из руководства:
Функция ПодписатьТекст(Сообщение, sThumbprint, bDetached)
CADESCOM_BASE64_TO_BINARY = 1; // входные данные пришли в Base64
CADESCOM_CADES_TYPE = 1; // тип усовершенствованной подписи
CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0; // атрибут штампа времени подписи
// Объект, задающий параметры создания и содержащий информацию об усовершенствованной подписи.
oSigner = Новый COMОбъект("CAdESCOM.CPSigner");
oSigner.Certificate = ПолучитьСертификатПоОтпечатку(sThumbprint);
oSigningTimeAttr = Новый COMОбъект("CAdESCOM.CPAttribute");
oSigningTimeAttr.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME;
oSigningTimeAttr.Value = ТекущаяДата();
oSigner.AuthenticatedAttributes2.Add(oSigningTimeAttr);
// Объект CadesSignedData предоставляет свойства и методы для работы с усовершенствованной подписью.
oSignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
oSignedData.ContentEncoding = CADESCOM_BASE64_TO_BINARY;
oSignedData.Content = Сообщение;
EncodingType = 0;
// Метод добавляет к сообщению усовершенствованную подпись.
sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE, bDetached, EncodingType);
Возврат sSignedMessage; // подписанное сообщение в формате Base64
КонецФункции
Функция ПолучитьСертификатПоОтпечатку(Отпечаток)
Результат = Неопределено; // Найденный сертификат (Com-объект)
CAPICOM_CURRENT_USER_STORE = 2; //2 - искать сертификат в ветке "Личное" хранилища.
CAPICOM_MY_STORE = "My"; // указываем, что ветку "Личное" берем из хранилища текущего пользователя
CAPICOM_STORE_OPEN_READ_ONLY = 0; // Открыть хранилище только на чтение
oStore = Новый COMОбъект("CAdESCOM.Store"); // Объект описывает хранилище сертификатов 246
// Открыть хранилище сертификатов.
oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_READ_ONLY);
// Вариант 1: поиск сертификата по отпечатку
CAPICOM_CERTIFICATE_FIND_SHA1_HASH = 0;
Certificates = oStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SHA1_HASH, Отпечаток);
Результат = Certificates.Item(1);
// Вариант 2: обходом по коллекции и сравнение с отпечатком
//Для Каждого ТекСертификат Из oStore.Certificates Цикл
// ТекОтпечаток = ТекСертификат.Thumbprint; // возвращается отпечаток в шестнадцатеричном виде
// Если ВРЕГ(ТекОтпечаток) = ВРЕГ(ОтпечатокСтр) Тогда
// Результат = ТекСертификат;
// Прервать;
// КонецЕсли;
//КонецЦикла;
oStore.Close(); // Закрыть хранилище сертификатов и освободить объект 61
Возврат Результат;
КонецФункции
После получения токена можно выполнять запросы уже без использования электронной подписи, как обычно:
Процедура ПеречитатьДокументы(Команда)
Соединение = Новый HTTPСоединение(Сервер, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Authorization", "Bearer " + Токен);
Парметры = "order=DESC&orderColumn=receivedAt";
// Запрос получения списка загруженных документов. Пункт 2.1.7 описания API.
Запрос = Новый HTTPЗапрос(БазовыйАдресСтенда + "/facade/doc/listV2?" + Парметры, Заголовки);
Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
Ответ = СтруктураИзJSON(Ответ.ПолучитьТелоКакСтроку());
СтруктураСтрокиРезультата = Новый Структура;
СтруктураСтрокиРезультата.Вставить("number", "Номер");
СтруктураСтрокиРезультата.Вставить("docDate", "Дата");
СтруктураСтрокиРезультата.Вставить("receivedAt", "Получен");
СтруктураСтрокиРезультата.Вставить("externalId", "ВнешнийID");
СтруктураСтрокиРезультата.Вставить("senderName", "Отправитель");
СтруктураСтрокиРезультата.Вставить("receiverName", "Получатель");
ТаблицаДокументов.Очистить();
Для каждого СтрокаРезультатов из Ответ.results Цикл
НоваяСтрока = ТаблицаДокументов.Добавить();
Для каждого КлючЗначение из СтруктураСтрокиРезультата Цикл
ИмяПоля = КлючЗначение.Ключ;
ИмяПоляНаФорме = КлючЗначение.Значение;
Если СтрокаРезультатов.Свойство(ИмяПоля) Тогда
НоваяСтрока[ИмяПоляНаФорме] = СтрокаРезультатов[ИмяПоля];
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
А теперь остановимся на отправке документов. Сложность состоит в том, что одного только токена для отправки таких запросов уже недостаточно. Отправляемые документы нужно правильно подписать электронной подписью.
Рассмотрим на примере отправки документа "Отгрузка" в формате XML. Любые другие документы будут отправляться точно также, только со своими параметрами. Сформированный и готовый к отправке документ выглядит так:
<shipment action_id="10" version="5">
<trade_participant_inn_sender>7714326233</trade_participant_inn_sender>
<trade_participant_inn_receiver>7713463607</trade_participant_inn_receiver>
<transfer_date>05.02.2021</transfer_date>
<move_document_number>000</move_document_number>
<move_document_date>05.02.2021</move_document_date>
<turnover_type>SELLING</turnover_type>
<to_not_participant>false</to_not_participant>
<products_list>
<product>
<ki>010290001986851721Bbn<'q/kFz:kO</ki>
</product>
</products_list>
</shipment>
Метод для отправки любых документов описан в в пункте 3.1 описания API. Это метод POST и для него надо сформировать тело запроса следующим образом:
ТелоЗапроса = Новый Структура;
ТелоЗапроса.Вставить("document_format", "XML");
ТелоЗапроса.Вставить("product_document", ТекстДокументаBase64);
ТелоЗапроса.Вставить("type", "LP_SHIP_GOODS_XML");
ТелоЗапроса.Вставить("signature", ПодписанныйТекст);
Параметр type это и есть тип документа "Отгрузка". Полное описание всех типов находится в справочнике "Типы документов". Параметр product_document это текст XML, зашифрованный функцией ЗашифроватьBase64(), которая описана выше. А параметр signature это результат, возвращенный функцией ПодписатьТекст(), которая также описана выше, только для документов третий параметр этой функции нужно указывать равным ИСТИНА.
Работа на стороне сервера
Добавлю пару слов о работе на стороне сервера. В приложенных обработках вы найдёте всю выше описанную реализацию на стороне клиента. Для полноценной автоматизации нужна логика, работающая в процессе сервера. Для этого нужно где-то в настройках указать используемый сертификат (так как выбрать его интерактивно на форме нет возможности). Для этого хорошо подходит справочник "Сертификаты ключей электронной подписи и шифрования" из БСП. Он есть во всех современных типовых конфигурациях.
При использовании справочника сертификатов из БСП есть один важный момент. Отпечаток сертификата в этом справочнике хранится не в том виде, в котором он нужен для функции ПодписатьТекст(). Перед вызовом ПодписатьТекст() нужно получить отпечаток в виде HEX-строки следующим образом:
ДанныеСертификата = Сертификат.ДанныеСертификата.Получить();
ск = Новый СертификатКриптографии(ДанныеСертификата);
Отпечаток = ПолучитьHexСтрокуИзДвоичныхДанных(ск.Отпечаток);
В этом коде "Сертификат" это ссылка на элемент справочника "Сертификаты ключей электронной подписи и шифрования".
Приложение
Я прилагаю две обработки. Первая была опубликована ещё до того, как я разобрался с документами. Вторая обработка уже включает отправку документа. Старался писать максимально кратко и понятно. Смотрите, пользуйтесь. Вопросы в комментариях приветствуются!
Всё тестировалось на платформе 8.3.18 в конфигурации Бухгалтерия предприятия 3.0.82.24.
Готовое решение
Печать кассовых чеков на одну ККМ с нескольких рабочих мест для 1С:УТ11.х, КА2.х, ERP 2.х
Расширение конфигурации для УТ 11.5, КА 2.5 ,ERP 2.5 (Управляемые формы) позволяет выполнять печать кассовых чеков на одну ККМ 54-ФЗ с нескольких рабочих мест. НИКАКИХ НАСТРОЕК В РАЗРАБОТКЕ - ПОДКЛЮЧИЛ И ПЕЧАТАЙ. Если у вас несколько отделов и одна ККМ - печатайте на одной ККМ! Если у вас две ККМ и одна поломалась - печатайте на одной ККМ, пока ремонтируете другую!