Имя: Пароль:
1C
 
Работа с документами по API "Честный знак"
0 Saari
 
14.10.24
10:00
Изучаю работу с Честным знаком по API из 1С.
Данные по остаткам получил.
Теперь нужно научиться создавать документы "Вывод из оборота (ОСУ)", "Отмена вывода из оборота (ОСУ)".
В описании True API сказано, что тело запроса должно быть вида:
"document_format":"string",
"product_document":"<Документ в формате base64>",    "type":"string",
"signature":"<Открепленная УКЭП формата Base64>"

Вопрос к последнему параметру "signature". Как получить открепленную УКЭП?
Научите, пожалуйста.
1 MWWRuza
 
гуру
14.10.24
10:29
Ровно так-же, как и прикрепленную... Один параметр отвечает за это.
В случаее прикрепленной - ЭЦП "встраивается" в тело документа, в случае открепленной - это отдельный файл.

Сказать что-то более конкретно - надо понимать, в чем и как Вы это вообще делаете...
Вариантов может быть много.
2 Saari
 
14.10.24
10:48
Вот как я пытаюсь создать документ:

Процедура КнопкаОтправитьЗапросНажатие(Элемент)
    
    Соединение = Новый HTTPСоединение(Сервер, 443, ,,,, Новый ЗащищенноеСоединениеOpenSSL);
    
    HTTPЗапрос = Новый HTTPЗапрос(Запрос);
    HTTPЗапрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);
    HTTPЗапрос.Заголовки.Вставить("Accept", "application/json");
    HTTPЗапрос.Заголовки.Вставить("Accept-Charset", "utf-8");
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    
    
    СтрокаДляЗапроса = СформироватьЗапрос();
    
    HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаДляЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос, ИмяФайлаОтвета);
    
    Если Ответ.КодСостояния = 200 Тогда  
        Сообщить("Код ответа на запрос: " + Ответ.КодСостояния + "  - выполнен успешно.");
    Иначе  
        Сообщить("Код ответа на запрос: " + Ответ.КодСостояния + " - ошибка!" );
    КонецЕсли;
    
КонецПроцедуры

функция СформироватьЗапрос()
    
    //LK_GTIN_RECEIPT - тип документа "Вывод из оборота (ОСУ)"
    
    СтрокаДляЗапроса = "";
    СтрокаДляЗапросаТелоДокумента = "";

    
    //-- документ (тело)
    ИНН = СокрЛП(ВыбОрганизация.ИНН);
    ПричинаВыбытияТекст = "UTILIZATION"; //Утилизация
    ПричинаВыбытия_Дата = Формат(ВыбДатаВыводаИзОборота,"ДФ=yyyy-MM-dd");
    ДокументТип = "DESTRUCTION_ACT"; //Акт уничтожения (утраты/утилизации)
    ДокументНомер = СокрЛП(ВыбНомерДокумента);
    ДокументДата = Формат(ВыбДатаДокумента,"ДФ=yyyy-MM-dd");

    ДанныеДляЗапроса = Новый Структура;
                                          
    ДанныеДляЗапроса.Вставить("inn", ИНН);
    ДанныеДляЗапроса.Вставить("action", ПричинаВыбытияТекст);
    ДанныеДляЗапроса.Вставить("action_date", ПричинаВыбытия_Дата);
    
    ДанныеДляЗапроса.Вставить("document_type", ДокументТип);
    ДанныеДляЗапроса.Вставить("document_number", ДокументНомер);
    ДанныеДляЗапроса.Вставить("document_date", ДокументДата);
    ДанныеДляЗапроса.Вставить("products", Новый Массив);
    
    Для Каждого Стр Из ТабТовары Цикл
        СтрокаТЧ = Новый Структура;
        СтрокаТЧ.Вставить("gtin", Стр.gtin);
        СтрокаТЧ.Вставить("gtin_quantity", Стр.Количество);
        
        ДанныеДляЗапроса.products.Добавить(СтрокаТЧ);
    КонецЦикла;
    
    Запись_JSON_ТелоДокумента = Новый ЗаписьJSON;
        Запись_JSON_ТелоДокумента.УстановитьСтроку();
    ЗаписатьJSON(Запись_JSON_ТелоДокумента, ДанныеДляЗапроса);
    СтрокаДляЗапросаТелоДокумента = Запись_JSON_ТелоДокумента.Закрыть();
    
    
    //
    ВременныйФайл = ПолучитьИмяВременногоФайла();
    ЗаписьТекста = Новый ЗаписьТекста(ВременныйФайл, "CESU-8");
    ЗаписьТекста.Записать(СтрокаДляЗапросаТелоДокумента);
    ЗаписьТекста.Закрыть();
    
    ДД_Файла = Новый ДвоичныеДанные(ВременныйФайл);
    СтрокаДляЗапросаBase64 = Base64Строка(ДД_Файла);
    Попытка
        УдалитьФайлы(ВременныйФайл);
    Исключение
        Сообщить(ОписаниеОшибки());
    КонецПопытки;
    
    
    //-- документ (шапка)
    ТелоДокументаБейс64 = СтрокаДляЗапросаBase64;
    Подпись = ???;
    
    ДокументДанныеДляЗапроса = Новый Структура;
    ДокументДанныеДляЗапроса.Вставить("document_format", "MANUAL");
    ДокументДанныеДляЗапроса.Вставить("product_document", ТелоДокументаБейс64);
    ДокументДанныеДляЗапроса.Вставить("type", "LK_GTIN_RECEIPT");
    ДокументДанныеДляЗапроса.Вставить("signature", Подпись);

    
    //JSON
    Запись_JSON = Новый ЗаписьJSON;
        Запись_JSON.УстановитьСтроку();
    ЗаписатьJSON(Запись_JSON, ДокументДанныеДляЗапроса);
    СтрокаДляЗапроса = Запись_JSON.Закрыть();
    
    
    Возврат СтрокаДляЗапроса;
    
КонецФункции
3 Saari
 
14.10.24
10:52
и пока мне непонятно как получить открытую часть подписи.
4 MWWRuza
 
гуру
14.10.24
10:58
Ну, понятно... Вы это делаете в каком-то восьмерошном решении...
Опять-же в каком? Просто в современных конфах, на БСП, наверняка есть какие-то готовые процедуры/функции для этого.

Я например, в конфе, которая принципиально ничего общего с этим не имеет - использую универсальное, браузерное средство - CAPICOM или CADESCOM, типа так:

OutSignedData=SignedData.SignCades(Signer,1,1,0); // параметр 3 - открепленная/прикрепленная(1 - открепленная), пар 4 - кодировка(0 - Base64)

Но, это от безысходности, в Вашем случае, должно быть что-то штатное, я особо в это не вникал, тут, местные знатоки скорее подскажут.
5 MWWRuza
 
гуру
14.10.24
11:00
(3) Я получаю по отпечатку(у меня - хранится в справочнике) объект самой КЭП, тем-же капикомом, который потом он-же использует для дальнейших действий...
6 MWWRuza
 
гуру
14.10.24
11:42
Если с типовыми методами совсем всё грустно, то у КриптоПро есть даже утилита командной строки:
C:\Program Files\Crypto Pro\CSP\csptest.exe
С помощью нее можно всё это делать вплоть до того, что скриптом (батником или CMD)...
Но в Вашем случае надо трясти штатные методы.
7 MWWRuza
 
гуру
14.10.24
11:42
Судя по Вашему коду:

HTTPЗапрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);


токен Вы как-то получаете, откуда-то Вы его пихаете в заголовок запроса. А значит, подписываете как-то данные для его получения. Ну, всё остальное делается теми же средствами.
8 Saari
 
14.10.24
11:11
(7) да, получаю. Сейчас покажу процедуры.
9 Saari
 
14.10.24
11:20
Вот процедуры, которые используются при получении Токена перед отправкой запроса:

Процедура ПриОткрытии()
    
    //Поиск доступных сертификатов
    CAPICOM_CURRENT_USER_STORE = 2;
    //2 - Искать сертификат в ветке "Личное" хранилища.
    CAPICOM_MY_STORE = "My";
    // Указываем, что ветку "Личное" берем из хранилища текущего пользователя
    CAPICOM_STORE_OPEN_READ_ONLY = 0; // Открыть хранилище только на чтение
    oStore = Новый COMОбъект("CAdESCOM.Store"); // Объект описывает хранилище сертификатов
    
    oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE,
    CAPICOM_STORE_OPEN_READ_ONLY); // Открыть хранилище сертификатов
    
    //2 вариант: обходом по коллекции и сравнение с отпечатком
    Для Каждого ТекСертификат Из oStore.Certificates Цикл
        НоваяСтрока = Сертификаты.Добавить();
        НоваяСтрока.Владелец = ТекСертификат.SubjectName;
        НоваяСтрока.ПериодС = ТекСертификат.ValidFromDate;
        НоваяСтрока.ПериодДо = ТекСертификат.ValidToDate;
        НоваяСтрока.Контейнер = ТекСертификат.PrivateKey.UniqueContainerName;
        НоваяСтрока.Отпечаток = ТекСертификат.Thumbprint;
    КонецЦикла;
        
КонецПроцедуры

Процедура КнопкаПолучитьТокенНажатие(Элемент)
    
    // Получение данных для получения токена
    HTTPСоединение = Новый HTTPСоединение(Сервер,443,,,,,Новый ЗащищенноеСоединениеOpenSSL);
    HTTPЗапрос = новый HTTPЗапрос("/api/v3/auth/cert/key");
    HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("GET",HTTPЗапрос);
    ОтветСтрока = HTTPОтвет.ПолучитьТелоКакСтроку("UTF-8");
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(ОтветСтрока);
    ДанныеJSON = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    УИД = ДанныеJSON.uuid;
    ДанныеДляПолученияТокена = ДанныеJSON.data;
    
    // Подписание данных для получения токена
    ДанныеДляПолученияТокена = ПодписатьТекст(ЗашифроватьBase64(ДанныеДляПолученияТокена, КодировкаТекста.UTF8),СертификатДляОбмена,Ложь);
    
    // Получение токена с использованием подписанных данных
    Соединение = Новый HTTPСоединение(Сервер,443,,,,,Новый ЗащищенноеСоединениеOpenSSL);
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "application/json; charset=UTF-8");
    Заголовки.Вставить("Accept", "application/json");
    HTTPЗапрос = Новый HTTPЗапрос("/api/v3/auth/cert/",Заголовки);
    ЗаписьJOIN = Новый ЗаписьJSON;
    ЗаписьJOIN.УстановитьСтроку();
    ДанныеДляЗапроса = Новый Структура;
    ДанныеДляЗапроса.Вставить("uuid",УИД);
    ДанныеДляЗапроса.Вставить("data",ДанныеДляПолученияТокена);
    ЗаписатьJSON(ЗаписьJOIN,ДанныеДляЗапроса);
    СтрокаДляЗапроса = ЗаписьJOIN.Закрыть();
    
    HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаДляЗапроса,КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
    
    Токен = ПрочитатьJSON(Чтение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);
    ТекстДляПодписи = СокрЛП(ТекстДляПодписи);
    oSignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
    
    // Объект CadesSignedData предоставляет свойства и методы для работы с усовершенствованной подписью.
    oSignedData.ContentEncoding = CADESCOM_BASE64_TO_BINARY;
    oSignedData.Content = СокрЛП(ТекстДляПодписи);
    EncodingType = 0;
    sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE,
    bDetached, EncodingType);
    // Метод добавляет к сообщению усовершенствованную подпись.
    
    
    Возврат sSignedMessage; // Подпись в формате Base64
    
КонецФункции

Функция ЗашифроватьBase64(Строка, Кодировка) Экспорт
    
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
    ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла, Кодировка);
    ЗаписьТекста.Записать(Строка);
    ЗаписьТекста.Закрыть();
    Двоичные = Новый ДвоичныеДанные(ИмяВременногоФайла);
    Результат = Base64Строка(Двоичные);
    Если Лев(Результат, 4) = "77u/" Тогда
        Результат = Сред(Результат, 5);
    КонецЕсли;
    Результат = СтрЗаменить(Результат, Символы.ПС, "");
    УдалитьФайлы(ИмяВременногоФайла);
    
    Возврат Результат;
    
КонецФункции

Функция ПолучитьСертификатПоОтпечатку(ОтпечатокСтр)
    
    Рез = Неопределено; // Найденный сертификат (Com-объект)
    CAPICOM_CURRENT_USER_STORE = 2;
    //2 - Искать сертификат в ветке "Личное" хранилища.
    CAPICOM_MY_STORE = "My";
    // Указываем, что ветку "Личное" берем из хранилища текущего пользователя
    CAPICOM_STORE_OPEN_READ_ONLY = 0; // Открыть хранилище только на чтение
    oStore = Новый COMОбъект("CAdESCOM.Store"); // Объект описывает хранилище сертификатов
    
    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
    
    
    Возврат Рез;
    
КонецФункци

Они из работающего примера.
Хочу до конца разобраться в их применении.
10 Saari
 
14.10.24
11:21
Сертификат у меня есть. Теперь надо какой-то командой получить его открытую часть и зашифровать в Base64.
11 MWWRuza
 
гуру
14.10.24
11:24
Ну, я пока не видел Ваши процедуры, да и вообще - что их смотреть - на последнем этапе получения токена(беарер), Вы подписываете строку данных, которую Вам вернул ЧЗ на первый запрос, и ее отправляете назад, в ответ получаете токен.
Для этого, вы что-то используете, какое-то средство ЭЦП, но, для получения токена, Вы эту строку подписываете прикрепленной ЭЦП. Для подписи документа, делаете все то-же, только в этом средстве параметр меняете на "открепленная", и собственно все.
12 Saari
 
14.10.24
11:26
(11) Спасибо! Сейчас подумаю и попробую. Буду сообщать свои попытки и результат.
13 MWWRuza
 
гуру
14.10.24
11:27
Много букфф...
Вот это:

// Подписание данных для получения токена
    ДанныеДляПолученияТокена = ПодписатьТекст(ЗашифроватьBase64(ДанныеДляПолученияТокена, КодировкаТекста.UTF8),СертификатДляОбмена,Ложь);

Смотрите эту функцию. Скорее всего, последний ее параметр "Ложь", это и есть "Открепленная". Но, Вам виднее.
14 MWWRuza
 
гуру
14.10.24
11:29
Ну, да:

sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE,
    bDetached, EncodingType);

Вот это и есть этот параметр.
15 Saari
 
14.10.24
13:30
Пробую...
в параметр "signature" передаю значение переменной "ДанныеДляПолученияТокена" из процедуры "КнопкаПолучитьТокенНажатие()"
В ответе получаю "405 Not Allowed"...
16 ptiz
 
14.10.24
13:46
(0) В типовой это делается через отдельную ВК, ибо штатными методами 1С такое не умеет.
А вообще, в конце описания TrueAPI есть пример на 1С.
17 Saari
 
14.10.24
13:50
(16) Я примеры посмотрел. Токен получаю (все процедуры/функциии показал выше). Я явно чего-то недопонимаю... Поэтому  прошу помочь в понимании и реализации по созданию документа.
18 ptiz
 
14.10.24
13:56
(17) Я поставил УТ 11, и её использую в качестве прокси. И не пришлось этим всем заморачиваться.
19 Saari
 
14.10.24
14:09
(18) мне осталось каким-то образом добавить подпись в структуру json. И вот пытаюсь понять как это сделать.
20 MWWRuza
 
гуру
14.10.24
14:52
(15) в параметр "signature" передаю значение переменной "ДанныеДляПолученияТокена" из процедуры "КнопкаПолучитьТокенНажатие()"

При чем тут это вообще?

Что у Вас в Данных для получения токена (исходных) - ?

Если на то пошло, то должно быть что-то типа того:

ОтсоединеннаяЭЦПДокумента = ПодписатьТекст(ЗашифроваьBase64(ВашДокументВФорматеUTF8, КодировкаТекста.UTF8),СертификатДляОбмена,Ложь)

Тогда, в "ОтсоединеннаяЭЦПДокумента" будет строка ЭЦП исходного документа. Не большая, порядка 4-10 кб. Если присоединенная - то значительно больше - это объем Вашего документа + ЭЦП. Можно сохранить ее в текстовый файл, и проверить ЭЦП с помощью инструментов КриптоПро. (для отсоединенной нужно указать и файл ЭЦП, и исходнфй документ, для присоединенной только один этот файл).
И если все нормально, тогда уже двигаться дальше.
21 Saari
 
14.10.24
15:00
(20) ВашДокументВФорматеUTF8 - что будет в этой переменной?
В инструкции True API в разделе 4.1.10 показано тело документа:
{
   "inn":"1111111111",
   "buyer_inn":"7777777777",
   "action":"DONATION",
   "action_date":"2022-08-05",
   "document_type":"CONSIGNMENT_NOTE",
   "document_number":"56783",
   "document_date":"2022-07-05",
   "products":[       {
         "gtin":"04611111111111",          "gtin_quantity":4,
         "product_cost":0.00       },
      {
         "gtin":"04622222222222",          "gtin_quantity":1,
         "product_cost":0.00       }
   ] }

Мне это "тело" нужно присвоить переменной ВашДокументВФорматеUTF8?
22 Saari
 
14.10.24
15:06
Написал следующее:

ОтсоединеннаяЭЦПДокумента = ПодписатьТекст(ЗашифроватьBase64(СтрокаДляЗапросаТелоДокумента, КодировкаТекста.UTF8), СертификатДляОбмена, Ложь);
    
ДокументДанныеДляЗапроса = Новый Структура;
ДокументДанныеДляЗапроса.Вставить("document_format", "MANUAL");
ДокументДанныеДляЗапроса.Вставить("product_document", ТелоДокументаБейс64);
ДокументДанныеДляЗапроса.Вставить("type", "LK_GTIN_RECEIPT");
ДокументДанныеДляЗапроса.Вставить("signature", ОтсоединеннаяЭЦПДокумента);

    
//JSON
Запись_JSON = Новый ЗаписьJSON;
Запись_JSON.УстановитьСтроку();
ЗаписатьJSON(Запись_JSON, ДокументДанныеДляЗапроса);
СтрокаДляЗапроса = Запись_JSON.Закрыть();

Результат тот же...
23 ptiz
 
14.10.24
15:09
(22) Попробуй в УТ 11 проделать аналогичные операции - там можно будет посмотреть логи и понять разницу.
24 MWWRuza
 
гуру
14.10.24
15:36
(23) Там черт ногу сломит... Хотя, кому что привычнее и понятнее.

ТС, хотите, в своей конфе наставлю "точек сохранения в файлы" всего этого безобразия, и выложу? Может так понятнее будет.
25 MWWRuza
 
гуру
14.10.24
15:41
(21) Мне это "тело" нужно присвоить переменной ВашДокументВФорматеUTF8?

Ну, да, а что Вас смущает? Все так и есть. Формируете аналогичное со своими даннными, и подписываете.
26 Saari
 
14.10.24
16:05
Сделал так:

ВременныйФайл = ПолучитьИмяВременногоФайла();
ЗаписьТекста = Новый ЗаписьТекста(ВременныйФайл, "CESU-8");
ЗаписьТекста.Записать(СтрокаДляЗапросаТелоДокумента);
ЗаписьТекста.Закрыть();
    
ДД_Файла = Новый ДвоичныеДанные(ВременныйФайл);
СтрокаДляЗапросаBase64 = Base64Строка(ДД_Файла);
Попытка
    УдалитьФайлы(ВременныйФайл);
Исключение
    Сообщить(ОписаниеОшибки());
КонецПопытки;
    
    
//-- документ (шапка)
ТелоДокументаБейс64 = СтрокаДляЗапросаBase64;

ОтсоединеннаяЭЦПДокумента = ПодписатьТекст(ТелоДокументаБейс64, СертификатДляОбмена, Ложь);
    
Подпись = ОтсоединеннаяЭЦПДокумента;

ДокументДанныеДляЗапроса = Новый Структура;
ДокументДанныеДляЗапроса.Вставить("document_format", "MANUAL");
ДокументДанныеДляЗапроса.Вставить("product_document", ТелоДокументаБейс64);
ДокументДанныеДляЗапроса.Вставить("type", "LK_GTIN_RECEIPT");
ДокументДанныеДляЗапроса.Вставить("signature", Подпись);
    
//JSON
Запись_JSON = Новый ЗаписьJSON;
Запись_JSON.УстановитьСтроку();
ЗаписатьJSON(Запись_JSON, ДокументДанныеДляЗапроса);
СтрокаДляЗапроса = Запись_JSON.Закрыть();

И по прежнему в ответ: 405 Not Allowed
27 U4Me2
 
15.10.24
06:51
А так не пробовали
ОтсоединеннаяЭЦПДокумента = ПодписатьТекст(ТелоДокументаБейс64, СертификатДляОбмена, Истина);
28 Saari
 
15.10.24
08:40
(27) только что попробовал. Результат тот же: 405 Not Allowed.
Возвращается файл с ответом на запрос:
<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx</center>
</body>
</html>
29 Saari
 
15.10.24
08:44
В переменной "Сертификат для обмена" содержится Отпечаток подписи, получаемый при получении Токена:

oStore = Новый COMОбъект("CAdESCOM.Store");
oStore.Open(2, "My", 0);
Для Каждого ТекСертификат Из oStore.Certificates Цикл
  Отпечаток = ТекСертификат.Thumbprint;
КонецЦикла;
30 Saari
 
15.10.24
09:48
еще добавлю: тестирую обработку на демонстрационном контуре (markirovka.sandbox.crptech.ru).
31 MWWRuza
 
гуру
15.10.24
10:49
В песочнице содержатся не все марки. Точнее, их там совсем мало, специальные тестовые.
Но, ошибка будет другая из-за этого, запрос будет успешно отрабатывать, 200 возвращать, а уже в тексте ответа будет ошибка, что марка не найдена.

Или, 403, если пользователь не зарегистрирован в тестовом контуре и нет доступа.

А 405... Не знаю, ни разу такой ошибки не было.
32 MWWRuza
 
гуру
15.10.24
10:54
А вообще, тут надо пошагово отлаживать, и смотреть, на каком шаге у Вас что-то не то.
Получили джейсон запроса, посмотрели на него, насколько он корректный...
Получили отпечаток - проверили, правильный или нет.
Подписали текст - сохранили, проверили через инструменты КриптоПро... Ну, и так далее.
33 Saari
 
31.10.24
08:56
Продолжаю осваивать работу с документами по API. Пока работаю с демонстрационным контуром.
На отправленный запрос приходит ответ 201 и уникальный идентификатор документа.
Т.е. в личном кабинете документ создается (ура!), но с ошибкой: "Данные электронной подписи не соответствуют текущему участнику."
Структура JSON-файла такова:

ДокументДанныеДляЗапроса = Новый Структура;
ДокументДанныеДляЗапроса.Вставить("document_format", "MANUAL");
ДокументДанныеДляЗапроса.Вставить("product_document", ТелоДокументаСтрокаJSONBase64);
ДокументДанныеДляЗапроса.Вставить("type", "LK_GTIN_RECEIPT");
ДокументДанныеДляЗапроса.Вставить("signature", ПодписанноеТелоДокументаСтрокаJSONBase64);

Перед отправкой запроса получаю Токен.
При получении Токена подпись принимается системой, а при отправке документа подпись почему-то не хочет приниматься.

Можете подсказать где ошибка?
34 Saari
 
31.10.24
09:48
только что отправил запрос. Получил ответ с кодом 200, но документа в разделе "Документы" в личном кабинете ЧЗ не увидел...
35 Saari
 
31.10.24
09:51
ага... ответ 200 я получил когда забыл перед отправкой запроса получить токен. Поэтому в личном кабинете не находу документа.
Но почему после получения токена и создания документа приходит ответ 201 с ошибкой "Данные электронной подписи не соответствуют текущему участнику."?
36 Saari
 
31.10.24
13:40
Получлось создать документ без ошибки подписания!
Сначала нужно зашифровать тело документа в Base64, которое поместить в реквизит "product_document".
Затем нужно подписать зашифрованное тело документа в Base64 и поместить результат в реквизит "signature".
Только в функции "ПодписатьТекст()" третий параметр должен быть "Истина".
Текст функции можно увидеть в инструкции по API в Приложении2.

В результате документ создался с ошибкой "Код товара <код товара> не найден в базе данных.". Это потому что в демонстрационном контуре на остатках ничего нет.
Компьютеры — прекрасное средство для решения проблем, которых до их появления не было.