Интеграция учетных систем 1С:Предприятие с CRM-платформами — одна из самых частых задач, с которыми сталкивается разработчик. В процессе реализации обмена данными с amoCRM многие начинающие специалисты совершают типовые ошибки, связанные с непониманием протокола HTTP, некорректной передачей параметров в GET-запросах или потерей сессии авторизации.
В этой статье мы подробно разберем, почему возникает ошибка 401 Unauthorized после успешного входа, как правильно передавать параметры запроса в 1С, а также рассмотрим современный и безопасный подход к интеграции с использованием актуального amoCRM API v4 и протокола авторизации OAuth 2.0.
Рассмотрим ситуацию, описанную на форуме. Разработчик успешно отправляет POST-запрос на авторизацию, получает ответ 200 OK с подтверждением успешного входа, но при следующем запросе на получение списка сделок сервер возвращает ошибку 401 - Нет авторизации.
Выясним причину такого поведения системы. Дело в том, что HTTP-протокол по своей природе не хранит состояние (stateless). Сервер «не помнит», кто к нему обращался мгновение назад, если клиент не идентифицирует себя в каждом последующем запросе. В старых версиях API amoCRM для идентификации пользователя использовались сессии и временные файлы Cookies.
При успешном запросе на авторизацию сервер отправляет в заголовках ответа параметр Set-Cookie. Чтобы последующие запросы выполнялись успешно, 1С должна перехватить эту куку и передавать ее в заголовке каждого последующего запроса. Разберем по шагам, как это реализовать в коде 1С.
// 1. Создаем HTTP-соединение
Соединение = Новый HTTPСоединение("subdomain.amocrm.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL);
// 2. Формируем запрос на авторизацию (Legacy-метод)
ЗапросАвторизации = Новый HTTPЗапрос("/private/api/auth.php?type=json");
ЗапросАвторизации.Заголовки.Вставить("Content-Type", "application/json");
Авторизация = Новый Структура;
Авторизация.Вставить("USER_LOGIN", "my_login");
Авторизация.Вставить("USER_HASH", "my_hash");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Авторизация);
ТелоЗапроса = ЗаписьJSON.Закрыть();
ЗапросАвторизации.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
ОтветАвторизации = Соединение.ОтправитьДляОбработки(ЗапросАвторизации);
// 3. Получаем и сохраняем Cookie из заголовков ответа
СессионнаяКука = ОтветАвторизации.Заголовки.Получить("Set-Cookie");
Теперь при выполнении любых других операций (например, получение списка лидов) мы обязаны передавать сохраненную куку в заголовках нового запроса:
ЗапросДанных = Новый HTTPЗапрос("/api/v2/leads");
// Передаем сессионную куку обратно на сервер
ЗапросДанных.Заголовки.Вставить("Cookie", СессионнаяКука);
Вторая популярная проблема — возникновение ошибки 400 Bad Request при попытке получить данные. Разработчики часто пытаются передавать параметры выборки (например, количество строк limit_rows) в теле GET-запроса через JSON, используя метод ОтправитьДляОбработки().
Проанализируем эту ошибку. Согласно спецификации протокола HTTP, метод GET предназначен исключительно для получения информации с сервера. Он не должен содержать тело запроса. Если передать тело в GET-запросе, сервер либо проигнорирует его, либо вернет ошибку некорректного запроса (400).
Правильный способ передачи параметров для GET-запросов — добавление их непосредственно в строку адреса ресурса (Query Parameters) после знака вопроса ?. Параметры разделяются между собой символом амперсанда &.
Вместо метода ОтправитьДляОбработки() для GET-запросов в 1С необходимо использовать метод Получить() объекта HTTPСоединение.
Посмотрим на пример правильного формирования GET-запроса с параметрами:
// Формируем адрес ресурса с параметрами в URL
ПараметрыЗапроса = "limit_rows=100&limit_offset=0";
АдресРесурса = "/api/v2/leads?" + ПараметрыЗапроса;
Запрос = Новый HTTPЗапрос(АдресРесурса);
Запрос.Заголовки.Вставить("Cookie", СессионнаяКука);
// Отправляем GET-запрос с помощью метода Получить()
РезультатЗапроса = Соединение.Получить(Запрос);
ДанныеОтвета = РезультатЗапроса.ПолучитьТелоКакСтроку();
Если параметров много, рекомендуется собирать их динамически из структуры или соответствия, чтобы избежать ошибок ручного форматирования строки:
ПараметрыСтруктура = Новый Структура;
ПараметрыСтруктура.Вставить("limit_rows", "100");
ПараметрыСтруктура.Вставить("limit_offset", "50");
СтрокаПараметров = "";
Для Каждого КлючИЗначение Из ПараметрыСтруктура Цикл
СтрокаПараметров = СтрокаПараметров + ?(СтрокаПараметров = "", "", "&") + КлючИЗначение.Ключ + "=" + КлючИЗначение.Значение;
КонецЦикла;
Запрос.АдресРесурса = "/api/v2/leads?" + СтрокаПараметров;
Критически важно: Описанные выше методы авторизации по USER_HASH и сессионным кукам, а также использование REST API v2 полностью устарели и отключены со стороны amoCRM. Современные интеграции должны использовать исключительно протокол OAuth 2.0 и актуальную версию API v4.
Рассмотрим ключевые изменения, которые необходимо учитывать при разработке:
access_token (короткоживущий токен доступа, действует 24 часа) и refresh_token (токен обновления, действует 3 месяца).Authorization со значением Bearer [access_token]./api/v4/leads, /api/v4/contacts и т.д.limit_rows и limit_offset упразднены. В API v4 для постраничной навигации используются параметры limit (количество записей на страницу, максимум 250) и page (номер страницы).Для начала работы необходимо зарегистрировать интеграцию в личном кабинете amoCRM (раздел "амоМаркет" -> "Установленные" -> "Создать интеграцию"). Вы получите три ключа: ID интеграции (client_id), Секретный ключ (client_secret) и временный Код авторизации (authorization_code).
Разберем, как выполнить первичный обмен кода авторизации на рабочие токены в 1С:
Функция ПолучитьПервичныеТокены(АдресСубдомена, IDИнтеграции, СекретныйКлюч, КодАвторизации, СсылкаДляОбратногоВызова)
Соединение = Новый HTTPСоединение(АдресСубдомена, , , , , 10, Новый ЗащищенноеСоединениеOpenSSL);
Запрос = Новый HTTPЗапрос("/oauth2/access_token");
Запрос.Заголовки.Вставить("Content-Type", "application/json");
// Формируем параметры в соответствии с требованиями amoCRM OAuth 2.0
ПараметрыOAuth = Новый Структура;
ПараметрыOAuth.Вставить("client_id", IDИнтеграции);
ПараметрыOAuth.Вставить("client_secret", СекретныйКлюч);
ПараметрыOAuth.Вставить("grant_type", "authorization_code");
ПараметрыOAuth.Вставить("code", КодАвторизации);
ПараметрыOAuth.Вставить("redirect_uri", СсылкаДляОбратногоВызова);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ПараметрыOAuth);
ТелоЗапроса = ЗаписьJSON.Закрыть();
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Возврат Ответ.ПолучитьТелоКакСтроку(); // Здесь придут access_token и refresh_token
Иначе
ВызватьИсключение "Ошибка авторизации: " + Ответ.ПолучитьТелоКакСтроку();
КонецЕсли;
Исключение
ЗаписатьЛогОшибки(ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Полученные токены необходимо сохранить в базу данных 1С (например, в константы или специальный регистр сведений). Поскольку срок действия access_token равен 24 часам, перед каждым сеансом работы или при получении ошибки 401 необходимо обновлять токен с помощью refresh_token:
Функция ОбновитьТокены(АдресСубдомена, IDИнтеграции, СекретныйКлюч, ТекущийРефрешТокен)
Соединение = Новый HTTPСоединение(АдресСубдомена, , , , , 10, Новый ЗащищенноеСоединениеOpenSSL);
Запрос = Новый HTTPЗапрос("/oauth2/access_token");
Запрос.Заголовки.Вставить("Content-Type", "application/json");
ПараметрыOAuth = Новый Структура;
ПараметрыOAuth.Вставить("client_id", IDИнтеграции);
ПараметрыOAuth.Вставить("client_secret", СекретныйКлюч);
ПараметрыOAuth.Вставить("grant_type", "refresh_token");
ПараметрыOAuth.Вставить("refresh_token", ТекущийРефрешТокен);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ПараметрыOAuth);
ТелоЗапроса = ЗаписьJSON.Закрыть();
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Обязательно перезаписываем оба токена в 1С, так как старый refresh_token сгорает!
Возврат Ответ.ПолучитьТелоКакСтроку();
Иначе
ВызватьИсключение "Не удалось обновить токен доступа: " + Ответ.ПолучитьТелоКакСтроку();
КонецЕсли;
КонецФункции
Теперь спроектируем правильный GET-запрос для получения сделок из amoCRM версии API v4 с использованием токена доступа Bearer и новых параметров постраничной навигации.
Процедура ПолучитьСписокСделокV4()
Субдомен = "mycompany.amocrm.ru";
РабочийТокен = ПолучитьАктуальныйAccessTokenИзБазы(); // Метод получения сохраненного токена
Соединение = Новый HTTPСоединение(Субдомен, , , , , 10, Новый ЗащищенноеСоединениеOpenSSL);
// Запрашиваем 1-ю страницу, лимит 50 сделок
АдресРесурса = "/api/v4/leads?limit=50&page=1";
Запрос = Новый HTTPЗапрос(АдресРесурса);
Запрос.Заголовки.Вставить("Authorization", "Bearer " + РабочийТокен);
Запрос.Заголовки.Вставить("Content-Type", "application/json");
Попытка
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
СтрокаJSON = Ответ.ПолучитьТелоКакСтроку();
// Обрабатываем полученный JSON со сделками
ОбработатьДанныеСделок(СтрокаJSON);
ИначеЕсли Ответ.КодСостояния = 204 Тогда
Сообщить("Сделок больше нет (пустая страница).");
ИначеЕсли Ответ.КодСостояния = 401 Тогда
Сообщить("Токен истек, требуется обновление!");
// Вызываем процедуру обновления токенов
Иначе
Сообщить("Неизвестная ошибка сервера. Код: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
ВызватьИсключение "Ошибка соединения с amoCRM: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
В современной разработке на платформе 1С (начиная с версии 8.3.10) рекомендуется использовать универсальный метод ВызватьHTTPМетод() вместо устаревших специализированных методов. Это особенно актуально при обновлении данных сделок в amoCRM v4, где активно применяется HTTP-метод PATCH (частичное изменение данных).
Рассмотрим пример обновления названия сделки с помощью метода PATCH:
Процедура ОбновитьСделкуV4(IDСделки, НовоеНазвание)
Субдомен = "mycompany.amocrm.ru";
РабочийТокен = ПолучитьАктуальныйAccessTokenИзБазы();
Соединение = Новый HTTPСоединение(Субдомен, , , , , 10, Новый ЗащищенноеСоединениеOpenSSL);
// Эндпоинт для обновления конкретной сделки
АдресРесурса = "/api/v4/leads/" + Формат(IDСделки, "ЧГ=0");
Запрос = Новый HTTPЗапрос(АдресРесурса);
Запрос.Заголовки.Вставить("Authorization", "Bearer " + РабочийТокен);
Запрос.Заголовки.Вставить("Content-Type", "application/json");
// Формируем структуру изменяемых полей
ДанныеСделки = Новый Структура;
ДанныеСделки.Вставить("name", НовоеНазвание);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ДанныеСделки);
ТелоЗапроса = ЗаписьJSON.Закрыть();
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// Используем современный метод ВызватьHTTPМетод для PATCH-запроса
Ответ = Соединение.ВызватьHTTPМетод("PATCH", Запрос);
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Сделка успешно обновлена!");
Иначе
Сообщить("Не удалось обновить сделку. Код ответа: " + Ответ.КодСостояния);
КонецЕсли;
КонецПроцедуры
При интеграции 1С с amoCRM важно соблюдать следующие правила:
HTTPСоединение.Получить().access_token и refresh_token) и перезаписывать их в своей базе данных, так как старый refresh_token аннулируется сервером сразу же после его использования.ВызватьHTTPМетод().