Блог 1С программиста (1С:Предприятие 8.3, конфигурация «Управление нашей фирмой»)

Статья: HTTP-сервис: интеграция УНФ с сайтом

HTTP-сервис в 1С:УНФ: приём заказов с сайта без задвоений

Создание HTTP-сервиса, публикация на веб-сервере, разбор JSON, идемпотентное создание заказа покупателя и правила безопасности при публикации базы наружу.

Конфигурация
1С:УНФ
Платформа
8.3.24 и новее
Уровень
Продвинутый
Опубликовано
19.08.2026
Время чтения
12 мин

Содержимое

Задача звучит одинаково у всех: сайт оформил заказ — он должен появиться в УНФ. Вариантов три: стандартный интерфейс OData, обмен файлами и собственный HTTP-сервис. OData быстро включается, но отдаёт наружу структуру базы и плохо переживает изменения. Файловый обмен надёжен, но не даёт мгновенного ответа сайту. HTTP-сервис — когда нужен контролируемый контракт и ответ в реальном времени.

Создание сервиса

В конфигураторе (или, что правильнее для типовой, в расширении): ветка «Общие → HTTP-сервисы». Создаём сервис IntegrationAPI с корневым URL api, добавляем шаблон URL /orders и метод POST с обработчиком ЗаказыPOST.

Обработчик в модуле сервиса:

Функция ЗаказыPOST(Запрос)

	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");

	Попытка
		ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку("UTF-8");
		Данные = ПрочитатьJSONВСтруктуру(ТелоЗапроса);
	Исключение
		Возврат ОтветСОшибкой(400, "Некорректный JSON: " + ОписаниеОшибки());
	КонецПопытки;

	ВнешнийНомер = ОбщегоНазначенияКлиентСервер.СтруктураСвойство(Данные, "order_id");
	Если Не ЗначениеЗаполнено(ВнешнийНомер) Тогда
		Возврат ОтветСОшибкой(400, "Не передан order_id");
	КонецЕсли;

	// Идемпотентность: повторный запрос с тем же order_id не создаёт второй заказ.
	СуществующийЗаказ = НайтиЗаказПоВнешнемуНомеру(ВнешнийНомер);
	Если ЗначениеЗаполнено(СуществующийЗаказ) Тогда
		Ответ.УстановитьТелоИзСтроки(ОтветПоЗаказу(СуществующийЗаказ, "duplicate"), КодировкаТекста.UTF8);
		Возврат Ответ;
	КонецЕсли;

	Попытка
		Заказ = СоздатьЗаказПокупателя(Данные, ВнешнийНомер);
	Исключение
		ЗаписьЖурналаРегистрации("Интеграция.Заказ с сайта",
			УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки() + Символы.ПС + ТелоЗапроса);
		Возврат ОтветСОшибкой(500, "Не удалось создать заказ");
	КонецПопытки;

	Ответ.УстановитьТелоИзСтроки(ОтветПоЗаказу(Заказ, "created"), КодировкаТекста.UTF8);
	Возврат Ответ;

КонецФункции

Вспомогательные функции — там же, в модуле сервиса:

Функция ПрочитатьJSONВСтруктуру(Строка)

	Чтение = Новый ЧтениеJSON;
	Чтение.УстановитьСтроку(Строка);
	Результат = ПрочитатьJSON(Чтение, Истина);
	Чтение.Закрыть();

	Возврат Результат;

КонецФункции

Функция ОтветСОшибкой(КодСостояния, Текст)

	Ответ = Новый HTTPСервисОтвет(КодСостояния);
	Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");

	Запись = Новый ЗаписьJSON;
	Запись.УстановитьСтроку();
	ЗаписатьJSON(Запись, Новый Структура("status, message", "error", Текст));

	Ответ.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8);

	Возврат Ответ;

КонецФункции

Поиск дубля и создание документа

Внешний номер заказа хранится в дополнительном реквизите или в отдельном регистре сведений — это и есть ключ идемпотентности. Регистр сведений предпочтительнее: он индексируется и не требует изменения структуры документа.

Функция НайтиЗаказПоВнешнемуНомеру(ВнешнийНомер)

	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Соответствие.Заказ КАК Заказ
		|ИЗ
		|	РегистрСведений.ЗаказыСайта КАК Соответствие
		|ГДЕ
		|	Соответствие.ВнешнийНомер = &ВнешнийНомер";
	Запрос.УстановитьПараметр("ВнешнийНомер", ВнешнийНомер);

	Выборка = Запрос.Выполнить().Выбрать();

	Возврат ?(Выборка.Следующий(), Выборка.Заказ, Неопределено);

КонецФункции

Само создание документа — обычный код: находим или создаём контрагента по телефону и почте, заполняем шапку и товары, записываем документ, пишем строку в регистр соответствия. Проводить документ сразу не обязательно: заказ с сайта чаще требует подтверждения менеджером.

Ключевое правило: при любой ошибке заказ не должен потеряться. Записывайте исходный JSON в журнал регистрации или в регистр сведений «Входящие сообщения» до начала разбора. Тогда сбой конвертации превращается в «разобрать три записи руками», а не в «клиент не получил товар».

Публикация на веб-сервере

Конфигуратор → «Администрирование → Публикация на веб-сервере». Обязательно включите флажок публикации HTTP-сервисов и отметьте нужный сервис в списке. Публикация создаёт файл default.vrd и настройки в Apache или IIS.

Проверка из командной строки:

curl -X POST "https://1c.example.ru/unf/hs/api/orders" ^
  -u "SiteIntegration:пароль" ^
  -H "Content-Type: application/json" ^
  -d "{\"order_id\":\"W-10231\",\"phone\":\"+70000000000\",\"items\":[{\"sku\":\"A-100\",\"qty\":2}]}"

Обратите внимание на фрагмент /hs/ в адресе — так публикуются именно HTTP-сервисы, дальше идёт корневой URL сервиса и шаблон.

Безопасность

Публикация базы наружу — это то место, где ошибка стоит дороже всего:

  • отдельный пользователь для интеграции, с минимальными правами и без интерактивного входа;
  • только HTTPS, сертификат на веб-сервере, никакого HTTP «пока тестируем»;
  • ограничение по IP на уровне веб-сервера или брандмауэра, если сайт имеет фиксированный адрес;
  • в публикации отключить всё лишнее: веб-клиент, OData, если они не нужны;
  • логирование всех обращений и регулярный просмотр журнала регистрации;
  • лимит на размер тела запроса и защита от повторной отправки — идемпотентность, о которой шла речь выше.

Что делать с долгими операциями

Если обработка заказа занимает больше двух-трёх секунд, не держите соединение: примите данные, положите их в регистр «входящих», верните 202 Accepted и обрабатывайте фоновым заданием. Сайт получит быстрый ответ, а вы — возможность повторить обработку после сбоя, не теряя данные.