with one click
integration-patterns
For 1C HTTP/REST/SOAP, auth, retry, webhooks
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
For 1C HTTP/REST/SOAP, auth, retry, webhooks
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | integration-patterns |
| description | For 1C HTTP/REST/SOAP, auth, retry, webhooks |
Key principle: The contract is defined before code. An HTTP service is a thin layer for parsing requests and forming responses; business logic lives in common modules. Secrets are only through ОбщегоНазначения.*ДанныеВБезопасноеХранилище*, never in code or constants.
Before writing code, fix the contract as a structure or comment:
Changing the contract without versioning is a compatibility break. Add new fields without removing old ones. Changes to the semantics of existing fields must be introduced through a new version (/v2/…).
An HTTP service handler should do only three things: parse the request, call a business function, and return a response. Do not put business logic directly in the handler.
// Handler for the POST method of resource /orders
Функция ОбработатьПОСТ(Запрос)
// 1. Parse body
ТелоСтрокой = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоСтрокой);
ПараметрыЗаказа = ПрочитатьJSON(ЧтениеJSON, Тип("Структура"));
// 2. Validate required fields
Если НЕ ЗначениеЗаполнено(ПараметрыЗаказа.НомерВнешнего) Тогда
Возврат ОтветОшибки(400, "VALIDATION_ERROR",
НСтр("ru = 'Field НомерВнешнего is required'"));
КонецЕсли;
// 3. Business call
Попытка
РезультатСозданияЗаказа = ИнтеграцияЗаказов.СоздатьЗаказ(ПараметрыЗаказа);
Исключение
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ИнтеграцияЗаказов.СоздатьЗаказ'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат ОтветОшибки(500, "INTERNAL_ERROR",
НСтр("ru = 'Internal server error'"));
КонецПопытки;
// 4. Form response
Возврат ОтветУспеха(201, РезультатСозданияЗаказа);
КонецФункции
// Helper functions for response formation
Функция ОтветОшибки(КодСостояния, КодОшибки, Сообщение)
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
СтруктураОшибки = Новый Структура("error, message", КодОшибки, Сообщение);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураОшибки);
Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть());
Возврат Ответ;
КонецФункции
Функция ОтветУспеха(КодСостояния, Данные)
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Данные);
Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть());
Возврат Ответ;
КонецФункции
All secrets (tokens, passwords, keys) are stored exclusively through the ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище / ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища wrappers (the БезопасноеХранилищеДанных information register). Never in configuration constants, session parameters, catalog attributes, or module text.
For details on each scheme, see ../security/references/auth.md.
// Получение учётных данных из безопасного хранилища
УчётныеДанные = БезопасноеХранилище.Прочитать("ИнтеграцияСВнешнимСервисом");
Логин = УчётныеДанные.Логин;
Пароль = УчётныеДанные.Пароль;
// Не логируйте: Логин/Пароль, Заголовок Authorization, тело с персданными
Соединение = Новый HTTPСоединение(
"api.example.com",
, // порт — по умолчанию 443 для HTTPS
Логин,
Пароль,
, // прокси
30, // таймаут (сек)
Новый ЗащищённоеСоединение); // OpenSSL / CertificateAuth — см. ниже
// Токен из безопасного хранилища
УстановитьПривилегированныйРежим(Истина);
ТокенДоступа = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища("ИнтеграцияСВнешнимСервисом", "ТокенДоступа");
УстановитьПривилегированныйРежим(Ложь);
Запрос = Новый HTTPЗапрос("/api/v1/resource");
Запрос.Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступа);
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Путь к сертификату и пароль — из безопасного хранилища
УстановитьПривилегированныйРежим(Истина);
ПараметрыCertAuth = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища("ИнтеграцияСертификат", "ПутьКСертификату,Пароль");
УстановитьПривилегированныйРежим(Ложь);
СертификатКлиента = Новый СертификатКлиентаФайл(
ПараметрыCertAuth.ПутьКСертификату,
ПараметрыCertAuth.Пароль);
ЗащИтоеСоединение = Новый ЗащищённоеСоединение(
, , СертификатКлиента, ,
Истина); // проверять серверный сертификат
Соединение = Новый HTTPСоединение(
"api.example.com", , , , , 30, ЗащищённоеСоединение);
An external call is unreliable. Always wrap it in Try/Except. For modifying operations, use an idempotency key and protection against duplicate execution.
Функция ВызватьВнешнийAPIСПовтором(URLПуть, ТелоЗапросаJSON, КлючИдемпотентности = "")
МаксимумПопыток = 3;
ОжиданиеМеждуПопытками = 2; // секунды
Для НомерПопытки = 1 По МаксимумПопыток Цикл
Попытка
Соединение = Новый HTTPСоединение(
"api.example.com", , , , , 30,
Новый ЗащищённоеСоединение);
Запрос = Новый HTTPЗапрос(URLПуть);
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Ключ идемпотентности — безопасное повторение без дублирования
Если ЗначениеЗаполнено(КлючИдемпотентности) Тогда
Запрос.Заголовки.Вставить("Idempotency-Key", КлючИдемпотентности);
КонецЕсли;
Запрос.УстановитьТелоИзСтроки(ТелоЗапросаJSON, КодировкаТекста.UTF8);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния >= 200 И Ответ.КодСостояния < 300 Тогда
Возврат Ответ.ПолучитьТелоКакСтроку();
ИначеЕсли Ответ.КодСостояния >= 400 И Ответ.КодСостояния < 500 Тогда
// Клиентская ошибка — не ретраить
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Ошибка запроса (HTTP %1). Повтор нецелесообразен.'"),
Ответ.КодСостояния);
ИначеЕсли Ответ.КодСостояния >= 500 Тогда
// Серверная ошибка — ретраим
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Сервер вернул %1'"), Ответ.КодСостояния);
КонецЕсли;
Исключение
ИнфоОшибки = ИнформацияОбОшибке();
УровеньЖР = ?(НомерПопытки < МаксимумПопыток,
УровеньЖурналаРегистрации.Предупреждение,
УровеньЖурналаРегистрации.Ошибка);
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ВнешняяИнтеграция.HTTPЗапрос'"), УровеньЖР, , ,
СтрШаблон(НСтр("ru = 'Попытка %1/%2. Ошибка: %3'"),
НомерПопытки, МаксимумПопыток,
ПодробноеПредставлениеОшибки(ИнфоОшибки)));
Если НомерПопытки = МаксимумПопыток Тогда
ВызватьИсключение;
КонецЕсли;
// Пауза перед следующей попыткой
ТекущаяДата = ТекущаяДата();
Пока ТекущаяДата() < ТекущаяДата + ОжиданиеМеждуПопытками Цикл
КонецЦикла;
КонецПопытки;
КонецЦикла;
Возврат "";
КонецФункции
State-changing operations (POST creation, state changes) must be protected against duplicate execution.
Idempotency patterns:
Idempotency-Key in the header (UUID generated on the client side);НомерВнешнего) in the body with a unique index on the 1С side;// Проверка дубля перед записью (серверная идемпотентность)
Функция НайтиЗаказПоВнешнемуНомеру(НомерВнешнего)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Заказы.Ссылка КАК Ссылка
|ИЗ
| Документ.ЗаказКлиента КАК Заказы
|ГДЕ
| Заказы.НомерВнешнего = &НомерВнешнего";
Запрос.УстановитьПараметр("НомерВнешнего", НомерВнешнего);
Выборка = Запрос.Выполнить().Выбрать();
Возврат ?(Выборка.Следующий(), Выборка.Ссылка, Неопределено);
КонецФункции
// В обработчике POST:
СуществующийЗаказ = НайтиЗаказПоВнешнемуНомеру(ПараметрыЗаказа.НомерВнешнего);
Если СуществующийЗаказ <> Неопределено Тогда
// Дубль — вернуть 200 с данными существующего заказа (не 201)
Возврат ОтветУспеха(200, ПолучитьДанныеЗаказа(СуществующийЗаказ));
КонецЕсли;
The error response must be predictable. The client should be able to distinguish:
| HTTP code | Error type | Client behavior |
|---|---|---|
| 400 | Input validation error | Do not retry; fix the request |
| 401 | Authentication error | Refresh the token; do not retry immediately |
| 409 | Conflict / duplicate | Do not retry; handle the duplicate |
| 422 | Business error (data is valid, but the rule is violated) | Do not retry; show the user |
| 500 | Internal error | Retry with backoff |
| 503 | Service temporarily unavailable | Retry with backoff |
// Единая структура тела ошибки
// {"error": "VALIDATION_ERROR", "message": "...", "correlationId": "..."}
Функция ОтветОшибкиС Корреляцией(КодСостояния, КодОшибки, Сообщение, КорреляцияИд)
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ.Заголовки.Вставить("X-Correlation-Id", КорреляцияИд);
ТелоОшибки = Новый Структура;
ТелоОшибки.Вставить("error", КодОшибки);
ТелоОшибки.Вставить("message", Сообщение);
ТелоОшибки.Вставить("correlationId", КорреляцияИд);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ТелоОшибки);
Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть());
Возврат Ответ;
КонецФункции
Never return the 1С call stack, module names, or internal metadata object IDs in the external API.
For working with SOAP services, use WSПрокси, created via WSОпределения. Pass authentication through the proxy parameters, not in the message body.
// Создание WSПрокси с аутентификацией
Функция СоздатьПроксиПлатёжногоШлюза()
УстановитьПривилегированныйРежим(Истина);
УчётныеДанные = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища("ПлатёжныйШлюзSOAP", "Логин,Пароль");
УстановитьПривилегированныйРежим(Ложь);
WSОпределения = Новый WSОпределения(
"https://payment.example.com/service?wsdl",
УчётныеДанные.Логин,
УчётныеДанные.Пароль,
, // прокси
30); // таймаут
Прокси = WSОпределения.СоздатьWSПрокси("PaymentService", "PaymentPort");
Прокси.Пользователь = УчётныеДанные.Логин;
Прокси.Пароль = УчётныеДанные.Пароль;
Прокси.Таймаут = 30;
Возврат Прокси;
КонецФункции
// Вызов с обработкой ошибок
Попытка
ПроксиWS = СоздатьПроксиПлатёжногоШлюза();
// XDTO-объект для тела запроса
ФабрикаXDTO = ПроксиWS.ФабрикаXDTO;
ЗапросXDTO = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("http://payment.example.com/", "PayRequest"));
ЗапросXDTO.Amount = СуммаПлатежа;
ЗапросXDTO.OrderId = Строка(ИдентификаторЗаказа);
ЗапросXDTO.Currency = "RUB";
ОтветXDTO = ПроксиWS.Pay(ЗапросXDTO);
Если ОтветXDTO.Status <> "OK" Тогда
ВызватьИсключение СтрШаблон(НСтр("ru = 'Платёжный шлюз вернул: %1'"), ОтветXDTO.Status);
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ПлатёжныйШлюз.Оплата'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
correlationId, requestId, X-Correlation-Id);Authorization header values (Bearer token, Basic credentials);ОбщегоНазначения.*ДанныеВБезопасноеХранилище*);// Корреляция через запрос
КорреляцияИд = Запрос.Заголовки.Получить("X-Correlation-Id");
Если НЕ ЗначениеЗаполнено(КорреляцияИд) Тогда
КорреляцияИд = Строка(Новый УникальныйИдентификатор);
КонецЕсли;
ЗаписьЖурналаРегистрации(
НСтр("ru = 'ИнтеграцияЗаказов.ПолучитьЗаказ'"),
УровеньЖурналаРегистрации.Информация,,,
СтрШаблон(НСтр("ru = 'correlationId=%1 result=OK orderId=%2'"),
КорреляцияИд, НомерЗаказа));
Check authentication first, before accessing business data.
// Проверка Bearer-токена во входящем запросе
Функция ПроверитьАутентификациюЗапроса(Запрос)
ЗаголовокAuth = Запрос.Заголовки.Получить("Authorization");
Если НЕ ЗначениеЗаполнено(ЗаголовокAuth) Тогда
Возврат Ложь;
КонецЕсли;
Если НЕ СтрНачинаетсяС(ЗаголовокAuth, "Bearer ") Тогда
Возврат Ложь;
КонецЕсли;
ПолученныйТокен = Сред(ЗаголовокAuth, 8); // убираем "Bearer "
УстановитьПривилегированныйРежим(Истина);
ОжидаемыйТокен = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища("ВходящийAPIТокен", "Токен");
УстановитьПривилегированныйРежим(Ложь);
// Сравнение в постоянное время (защита от timing attack)
// Для простых случаев допустимо прямое сравнение строк
Возврат (ПолученныйТокен = ОжидаемыйТокен);
КонецФункции
// В начале обработчика:
Если НЕ ПроверитьАутентификациюЗапроса(Запрос) Тогда
Возврат ОтветОшибки(401, "UNAUTHORIZED", НСтр("ru = 'Аутентификация не прошла'"));
КонецЕсли;
Changes to the contract without backward compatibility require a new version.
| Change | Compatibility | Action |
|---|---|---|
| Add a new field to the response | Compatible | Add; document |
| Add an optional field to the request | Compatible | Add with defaults |
| Remove or rename a field | Incompatible | Create /v2/…, support /v1/… |
| Change a field type | Incompatible | Create /v2/…, support /v1/… |
| Change the semantics of an existing field | Incompatible | Create /v2/… |
Specify the version in the URL: /api/v1/orders, /api/v2/orders.
| Mistake | Consequence | How to Avoid |
|---|---|---|
| Secret in a configuration constant or attribute | Leakage during configuration / database export | Only through ОбщегоНазначения.*ДанныеВБезопасноеХранилище* |
| HTTP call inside a transaction | Timeout (30 s) = blocking all related records | Move HTTP calls outside the transaction |
| No idempotency key on retries | Data duplication on a network error | Use Idempotency-Key or an external ID with a unique index |
| Returning the 1C stack in the error body | Exposes the internal system structure | Return only a stable error code and message |
| Retry on 4xx errors | Useless load, possible repetition of the conflict | Retry only 5xx and network errors |
| Logging tokens/passwords | Secrets in the registration log | Mask before writing to the ЖР |
| Business logic in the HTTP service handler | Impossible to reuse and test | Thin handler + separate common module |
| No input validation | Invalid data written to the database | Check all required fields before the business operation |
| Trigger | Action |
|---|---|
| A 1C HTTP service is being created (incoming) | Apply rules 2, 6, 9, 10 |
| An HTTP client is being created (outgoing REST) | Apply rules 3, 4, 5, 8 |
| SOAP/Web Service is being configured | Apply rule 7, 3 (secrets) |
| A contract is being designed or changed | Start with rule 1, apply rule 10 |
| Any authentication is being added | Apply rule 3 + ../security/references/auth.md |
| Reviewing integration code | Go through the checklist below |
ОбщегоНазначения.*ДанныеВБезопасноеХранилище*; nowhere are they loggedcorrelationId is logged, but not the token/password/v2/…)When writing or reviewing BSL, apply 1C standards
При написании или ревью BSL применять стандарты 1С
Orchestrator: routing work and agent phases
Оркестратор: маршрутизация работы и фаз агентов
BSL LSP navigation: definitions, refs, call graph
Rules for using RLM tools for project search and navigation in 1C/BSL