Как отправить Webhook из 1С:Фреш (УНФ) на произвольный внешний URL через расширение?

Программист 1С v8.3 (Управляемые формы) 1С:Управление нашей фирмой Управленческий учет IT и автоматизация бизнеса
← На главную

Коллеги, мы с вами рассмотрим актуальную и довольно сложную задачу — интеграцию 1С:УНФ, работающей в облаке 1cfresh.com, с внешним бэкендом (например, на Python). Наша цель — обеспечить отправку POST-запроса в формате JSON на внешний сервер сразу после записи документа «Заказ покупателя» в 1С. Это позволит реализовать Push-обмен (Webhooks), при котором данные передаются мгновенно, без задержек, связанных с периодическим опросом.

Однако, облачная платформа 1С:Фреш имеет жесткую политику безопасности, которая ограничивает исходящие соединения. Это вызывает главный вопрос: возможно ли через расширение отправлять запросы на произвольный внешний URL, который пользователь может задавать самостоятельно? Или же Фреш разрешает запросы только на те домены, которые были заранее жестко прописаны и прошли аудит?

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

Основные ограничения безопасности 1С:Фреш и механизм аудита

Прежде всего, важно понимать, что все расширения конфигурации в 1С:Фреш работают в так называемом безопасном режиме (Safe Mode). Это ключевое понятие, определяющее, что расширение может и не может делать.

  1. Декларация разрешений: Просто прописать URL в коде расширения недостаточно. При подготовке расширения к публикации разработчик обязан сформировать программный интерфейс или файл манифеста разрешений. В этом манифесте необходимо явно указать протокол (HTTP/HTTPS), адрес хоста и порт, к которым расширение будет обращаться.
  2. Динамические адреса: Сервис 1С:Фреш (1cfresh.com) технически не позволяет расширению, работающему в безопасном режиме, обращаться к произвольному ресурсу, адрес которого пользователь ввел в текстовое поле в режиме предприятия. Разрешения выдаются на конкретные, заранее определенные адреса (whitelist) на этапе аудита. Это сделано для защиты от потенциальных угроз безопасности и утечки данных.
  3. Типы аудита:
    1. Автоматический аудит: Проверяет синтаксис кода и базовые запреты на использование определенных функций.
    2. Ручной аудит: Обязателен для расширений, которые изменяют данные или используют сетевые вызовы. Аудиторы 1С вручную проверяют код на наличие скрытых возможностей отправки данных. Любой код, формирующий HTTP-запрос, будет изучен под микроскопом, как и любая работа с внешними API мессенджеров из 1С. Использование обфускации (запутывания) кода гарантированно приведет к отказу в публикации.
  4. Исключения для частных расширений: Теоретически, для «частных» расширений, разработанных под конкретного абонента, администрация сервиса может согласовать более широкие права, если соблюдены требования 1С к разработке облачных расширений. Однако это требует индивидуального обращения в техподдержку и предоставления веских обоснований.

Из вышесказанного следует, что напрямую отправить вебхук на произвольный внешний URL из серверного кода расширения, работающего во Фреше, не представляется возможным без дополнительных архитектурных решений.

Архитектурное решение через «Промежуточный шлюз» (Middleware) для Push-обмена

Если нам нужна возможность отправки данных на URL, который пользователь может менять динамически, и при этом сохранить надежность серверного Push-обмена, то на практике применяется схема с использованием промежуточного сервера, или шлюза (Middleware).

Рассмотрим шаги реализации:

  1. Разработка расширения для 1С:Фреш:
    • В расширении жестко прописывается и проходит аудит один единственный адрес нашего промежуточного шлюза (например, gateway.ваша-компания.ru).
    • Расширение формирует JSON с необходимыми данными из документа «Заказ покупателя» и отправляет этот JSON вместе с целевым URL клиента (на который в конечном итоге должен прийти вебхук) на этот шлюз.
  2. Создание промежуточного шлюза:
    • Этот шлюз является внешним приложением (например, на Python, Node.js или PHP), которое находится вне зоны ответственности 1С:Фреш.
    • Его задача — принять запрос от 1С:Фреш, извлечь из него целевой URL клиента и данные заказа, а затем переслать (проксировать) запрос на конечный Python-бэкенд клиента или платежный шлюз для обработки транзакций.

Это единственный надежный способ реализовать «универсальный» вебхук-отправитель для тиражного решения во Фреше, где конечный адрес получателя может быть задан пользователем.

Реализация серверного Push-обмена с учетом транзакционности

При реализации вебхука через расширение (серверный код) крайне важно учитывать момент отправки запроса. Мы не можем отправлять HTTP-запрос непосредственно внутри транзакции записи документа по нескольким причинам:

  1. Если внешний сервер ответит с задержкой или ошибкой, транзакция записи документа в 1С зависнет или откатится, что приведет к блокировкам и потере данных.
  2. Прямой вызов во время записи может значительно замедлить работу пользователя.

Правильный паттерн для 1С:Фреш выглядит следующим образом:

  1. Подписка на событие: Мы используем подписку на событие, например, ПриЗаписи (или ПриЗаписиНаСервере для управляемых форм) для документа «Заказ покупателя». Эта подписка должна выполняться после записи, но не блокировать ее.
  2. Формирование очереди сообщений: В обработчике подписки мы не отправляем HTTP-запрос напрямую, а формируем запись в специальном регистре сведений. Этот регистр будет выступать в роли очереди сообщений. Запись должна содержать всю необходимую информацию: данные заказа в виде JSON-строки, целевой URL (из настроек абонента или из данных, переданных в расширение), а также служебную информацию (статус, время создания, количество попыток отправки).
  3. Регламентное задание: Мы создаем регламентное задание, которое работает в фоне с определенной периодичностью (например, раз в минуту или раз в пять минут). Это задание будет:
    • Выбирать необработанные записи из регистра-очереди.
    • Формировать HTTP-запрос.
    • Отправлять его на наш промежуточный шлюз.
    • Обрабатывать ответ от шлюза (успех/ошибка) и обновлять статус записи в регистре.
    • В случае ошибки, регламентное задание может увеличивать счетчик попыток и помечать запись для повторной отправки, что критично при обмене данными с курьерскими службами.

Пример структуры кода для отправки запроса (этот код будет выполняться в регламентном задании):


// Предполагаем, что у нас есть структура с данными для отправки
// и целевым URL для промежуточного шлюза.
// Получение данных из очереди (регистра сведений)
СообщениеДляОтправки = ПолучитьСледующееСообщениеИзОчереди(); 

Если СообщениеДляОтправки <> Неопределено Тогда
    ЦелевойАдресШлюза = "gateway.ваша-компания.ru"; // Жестко прописанный и аудированный адрес шлюза
    ПортШлюза = 443; // Или 80

    // Формируем тело запроса, которое включает данные заказа и *конечный* URL клиента
    ДанныеДляШлюза = Новый Структура;
    ДанныеДляШлюза.Вставить("ДанныеЗаказа", СообщениеДляОтправки.ДанныеЗаказа); // Данные заказа из очереди
    ДанныеДляШлюза.Вставить("КонечныйURL", СообщениеДляОтправки.КонечныйURL); // URL клиента, куда шлюз перешлет
    
    // Преобразуем структуру в JSON
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, ДанныеДляШлюза);
    ТелоЗапросаJSON = ЗаписьJSON.Закрыть();

    HTTPСоединение = Новый HTTPСоединение(ЦелевойАдресШлюза, ПортШлюза, , , , 10, Истина); // Истина для HTTPS
    HTTPЗапрос = Новый HTTPЗапрос;
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаJSON, КодировкаТекста.UTF8, "application/json");
    
    // Добавляем необходимые заголовки, например, для авторизации на шлюзе
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    // HTTPЗапрос.Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступаКШлюзу); // Если шлюз требует авторизации

    Попытка
        HTTPОтвет = HTTPСоединение.Отправить(HTTPЗапрос, "/webhook_forwarder"); // Путь на шлюзе
        
        Если HTTPОтвет.КодСостояния = 200 Тогда
            // Успешно отправлено, помечаем сообщение как обработанное
            ОбновитьСтатусСообщения(СообщениеДляОтправки, "Отправлено");
        Иначе
            // Ошибка при отправке, логируем, увеличиваем счетчик попыток
            ЗаписатьЖурналРегистрации("Ошибка отправки вебхука: " + HTTPОтвет.СтатусСтрока, УровеньЖурналаРегистрации.Ошибка);
            ОбновитьСтатусСообщения(СообщениеДляОтправки, "Ошибка", HTTPОтвет.КодСостояния);
        КонецЕсли;
    Исключение
        // Ошибка HTTP-соединения, логируем
        ЗаписатьЖурналРегистрации("Ошибка HTTP-соединения: " + ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
        ОбновитьСтатусСообщения(СообщениеДляОтправки, "ОшибкаСоединения");
    КонецПопытки;
КонецЕсли;

Альтернативные методы интеграции

Помимо серверного Push-обмена через промежуточный шлюз, существуют другие подходы, которые могут быть применены в зависимости от требований к мгновенности и типу обмена (Push или Pull).

1. Клиентский контекст (Ограниченный Push)

В некоторых случаях предлагается делать вызов из клиентского контекста, то есть через код, выполняющийся в браузере пользователя. Это позволяет обойти серверные ограничения 1С:Фреш на whitelisting доменов, так как запрос инициируется браузером.

Однако, у этого подхода есть серьезные недостатки, из-за которых мы стремимся его избегать:

  1. Не работает для фоновых процессов: Если документ «Заказ покупателя» создан фоновым обменом, загрузкой данных или регламентным заданием, клиентский код не сработает, так как нет активной пользовательской сессии в браузере.
  2. Проблемы CORS (Cross-Origin Resource Sharing): Если внешний сервер клиента не настроен на прием запросов с домена 1cfresh.com (т.е., не отправляет соответствующие заголовки CORS), браузер заблокирует отправку данных из клиентского кода 1С (JavaScript).
  3. Проблемы Mixed Content: Если 1С:Фреш работает по HTTPS (что является стандартом), а внешний сервер клиента поднят на HTTP (без SSL), браузер заблокирует такой запрос как "смешанное содержимое" (Mixed Content).
  4. Зависимость от пользователя: Надежность отправки зависит от того, что пользователь находится в активной сессии, не закрыл браузер, не потерял интернет-соединение и т.д.

2. Pull-обмен через OData

Для задач, где данные требуется не отправлять из 1С, а забирать сторонней системой, существует типовой и широко используемый механизм — протокол OData. Это стандартный способ получения данных из 1С:Фреш баз.

Разберем, как это работает:

  1. Открытие доступа по OData: Мы можем самостоятельно открыть доступ к 1С:Фреш базе по протоколу OData. Для этого не требуется создавать расширения или проходить сложный аудит для исходящих соединений.
  2. Настройка доступа: Для активации OData нужно создать нового пользователя в приложении 1С:Фреш и в его настройках (или в настройках общих прав доступа) поставить соответствующие галки, определяющие, к каким объектам базы (документам, справочникам, регистрам) будет открыт доступ по OData. Подробная инструкция обычно доступна на сайтах 1С:Фреш.
  3. Извлечение данных: Сторонний сервис (наш Python-бэкенд) может периодически обращаться к специальным ссылкам OData для извлечения данных, используя авторизацию по логину и паролю созданного пользователя.

Несмотря на простоту реализации, у OData есть один существенный недостаток для нашей задачи: это Pull-обмен. Данные не отправляются мгновенно при записи, а забираются внешним сервисом с определенной задержкой (например, раз в несколько минут). Это не подходит, если требуется мгновенная передача данных сразу после записи «Заказа покупателя».

3. Система взаимодействия (1С:Dialogs) как альтернатива Push

Платформа 1С предлагает еще один встроенный механизм для реализации событийной модели без использования классических вебхуков — это Система взаимодействия (1С:Dialogs).

Принцип работы:

  1. Внешнее приложение может подписаться на ботов или каналы Системы взаимодействия.
  2. 1С при создании или изменении документа (например, «Заказ покупателя») отправляет сообщение в Систему взаимодействия.
  3. Внешний скрипт (наш Python-бэкенд) получает это сообщение через WebSocket API Системы взаимодействия мгновенно (это по сути Push-уведомление), без необходимости открывать входящие порты в 1С или проходить сложный аудит исходящих HTTP-запросов на произвольные URL.

Этот механизм является легальным и поддерживаемым способом для облачных решений 1С и может быть хорошей альтернативой классическим вебхукам для обеспечения мгновенной доставки уведомлений.

Заключение

Итак, мы выяснили, что прямая отправка вебхуков из 1С:Фреш на произвольный URL через серверный код расширения невозможна из-за политики безопасности и механизма аудита. Разрешения на исходящие соединения выдаются только для жестко прописанных и аудированных адресов.

Однако, для реализации Push-обмена с динамическим целевым URL, наиболее надежным и рекомендуемым решением является использование промежуточного шлюза (Middleware). В этом случае расширение во Фреше отправляет данные на фиксированный, аудированный шлюз, который затем пересылает их на конечный адрес клиента. При этом критически важно использовать подход с очередью сообщений и регламентным заданием, чтобы избежать блокировок транзакций.

Для извлечения данных из 1С мы всегда можем использовать OData (Pull-модель), а для мгновенных уведомлений без прямого HTTP-вызова — Систему взаимодействия (1С:Dialogs). Клиентский контекст для вебхуков обладает существенными ограничениями и не рекомендуется для надежных интеграций.

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

← На главную