При интеграции с сервисами Сбербанка (например, P2P или эквайринг) разработчики часто сталкиваются с тем, что штатные утилиты обмена записывают результаты своей работы не в чистый XML-файл, а в текстовый лог. В таком файле служебная информация (дата, тип операции, разделители) перемешана с телом XML-запросов и ответов. Прямое использование объекта ЧтениеXML для такого файла невозможно, так как он не является валидным XML-документом с единым корнем.
В этой статье мы подробно разберем, как правильно обработать такой файл, извлечь из него нужные данные и интерпретировать ответы платежного шлюза Сбербанка.
Проанализируем типичную структуру лога, которую предоставляет Сбербанк. Обычно она выглядит следующим образом:
*** 0 | 04.03.2021 | registerOrder).<<< запрос и >>> ответ.soap:Envelope).Поскольку файл содержит множество независимых XML-блоков, наша задача сводится к двум этапам: выделение нужного фрагмента текста и его последующий парсинг.
Рассмотрим наиболее надежный способ — чтение файла как текстового документа с поиском ключевых слов. Этот метод позволяет нам точно определить, где начинается и заканчивается XML-блок.
Для реализации этого метода используем объект ТекстовыйДокумент или ЧтениеТекста. Разберем пример кода, который выделяет XML-ответ из лога:
// Создаем объект для чтения файла
ТекстЛога = Новый ЧтениеТекста("C:\Logs\sber_log.txt", КодировкаТекста.UTF8);
СтрокаЛога = ТекстЛога.ПрочитатьСтроку();
НайденоНачалоXML = Ложь;
СтрокаXML = "";
Пока СтрокаЛога <> Неопределено Цикл
// Если находим маркер ответа, начинаем собирать XML
Если Найти(СтрокаЛога, ">>> ответ") > 0 Тогда
НайденоНачалоXML = Истина;
СтрокаЛога = ТекстЛога.ПрочитатьСтроку(); // Переходим к следующей строке, где начинается XML
КонецЕсли;
Если НайденоНачалоXML Тогда
// Проверяем, не начался ли следующий блок лога (разделитель)
Если Найти(СтрокаЛога, "***") > 0 Тогда
НайденоНачалоXML = Ложь;
// Здесь можно вызвать процедуру парсинга накопленной строки СтрокаXML
РаспарситьБлокXML(СтрокаXML);
СтрокаXML = "";
Иначе
СтрокаXML = СтрокаXML + СтрокаЛога;
КонецЕсли;
КонецЕсли;
СтрокаЛога = ТекстЛога.ПрочитатьСтроку();
КонецЦикла;
Если файл лога небольшой, мы можем прочитать его целиком в память и извлечь все блоки soap:Envelope с помощью регулярных выражений. Это более современный и лаконичный подход.
Выясним причину, почему регулярные выражения эффективны: они позволяют игнорировать текстовый "мусор" между тегами и сразу получать массив чистых XML-строк. Проанализируем пример кода:
// Читаем весь файл в строку
ТекстФайла = ПолучитьТекстИзФайла("C:\Logs\sber_log.txt");
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;
// Шаблон ищет всё содержимое между тегами soap:Envelope
RegExp.Pattern = "(<soap:Envelope.*?</soap:Envelope>)";
Matches = RegExp.Execute(ТекстФайла);
Для Каждого Match Из Matches Цикл
ТекущийXML = Match.Value;
РаспарситьБлокXML(ТекущийXML);
КонецЦикла;
Когда мы выделили фрагмент XML в отдельную строку, мы можем использовать стандартный объект ЧтениеXML. Обратите внимание, что в логах Сбера данные часто передаются в виде атрибутов узлов.
Разберем, как прочитать данные из узла return, используя метод ПрочитатьАтрибут():
Процедура РаспарситьБлокXML(Знач ВходящаяСтрока)
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ВходящаяСтрока);
Пока Чтение.Прочитать() Цикл
// Нас интересует узел "return" (или "ns1:return")
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.ЛокальноеИмя = "return" Тогда
// Получаем данные через атрибуты
ИдентификаторЗаказа = Чтение.ПолучитьАтрибут("orderId");
КодОшибки = Чтение.ПолучитьАтрибут("errorCode");
ТекстОшибки = Чтение.ПолучитьАтрибут("errorMessage");
Сообщить("Заказ: " + ИдентификаторЗаказа + ", Статус: " + ТекстОшибки);
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.ЛокальноеИмя = "formUrl" Тогда
// Если данные внутри тега, читаем содержимое
СсылкаНаОплату = Чтение.ПрочитатьСодержимоеКакСтроку();
Сообщить("Ссылка: " + СсылкаНаОплату);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При работе с логами и API Сбера (платформа RBS) следует учитывать несколько критических моментов:
soap: или ns1:. При использовании ЧтениеXML лучше ориентироваться на ЛокальноеИмя, чтобы избежать проблем с изменением префиксов в будущем.UTF-8 без метки BOM. При чтении файла в 1С обязательно указывайте кодировку явно, иначе кириллица в сообщениях об ошибках (поле errorMessage) будет нечитаемой.orderStatus="6" означает окончательный отказ в платеже, а actionCode="-2007" указывает на технический отказ со стороны банка. Успешный платеж обычно имеет статус 2.paReq. Это длинная строка в формате Base64. Если вы парсите лог вручную, убедитесь, что не обрезаете ее, так как она необходима для перенаправления пользователя на страницу подтверждения банка.Если у вас есть XML-схема (XSD), предоставленная Сбербанком, процесс можно значительно упростить. Мы можем создать пакет XDTO в конфигурации и читать извлеченные строки одной командой:
// Предположим, схема загружена в пакет "SberPay"
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ИзвлеченнаяСтрокаXML);
ОбъектОтвета = ФабрикаXDTO.ПрочитатьXML(Чтение, ФабрикаXDTO.Тип("http://engine.paymentgate.ru/webservices/merchant", "registerOrderResponse"));
// Теперь данные доступны как свойства объекта
Сообщить(ОбъектОтвета.return.errorCode);
Посмотрим на ситуацию практически: использование ЧтениеXML в связке с простым строковым поиском маркеров является самым быстрым в реализации и устойчивым к изменениям форматов способом для разбора логов "на лету".