# Обмен Битрикса с 1С по CommerceML: как устроен протокол

URL: https://dmeshcheryakov.ru/blog/bitrix-1c-exchange-commerceml/
Раздел: 1С-Битрикс
Теги: 1С-Битрикс, Интеграции, Каталог, XML
Опубликовано: 2026-10-02
Обновлено: 2026-10-02
Автор: Дмитрий Мещеряков (https://dmeshcheryakov.ru)

> Разбираем обмен не по галочкам в админке, а по проводу: пять HTTP-команд, структура import.xml и offers.xml, пошаговый импорт и точки расширения.

Обмен с 1С — единственная подсистема Битрикса, которую большинство разработчиков никогда не открывали. Её настраивают галочками, а когда она ломается, лечат тем же способом: снимают и ставят галочки обратно, чистят кэш, перезапускают обмен и ждут. Иногда помогает.

Между тем внутри нет ничего мистического. Это HTTP-протокол из пяти команд и XML-файл с кириллическими тегами. Разработчик, который один раз посмотрел на обмен через `curl`, чинит типовую проблему за двадцать минут вместо двух дней — просто потому, что видит, где именно оборвалась цепочка.

Эта статья — про провод, а не про админку. Что 1С отправляет, что Битрикс отвечает, где лежат файлы и в какой момент можно вмешаться.

## Что такое CommerceML

CommerceML — это открытый XML-стандарт обмена коммерческой информацией, разработанный фирмой «1С» для передачи каталогов, цен, остатков и заказов между учётной системой и сайтом. Битрикс работает со второй версией стандарта: корневой элемент называется `КоммерческаяИнформация`, а все теги внутри написаны по-русски.

Кириллица в тегах смущает, но у неё есть практическое следствие: XML читается глазами без документации. Открыв `import.xml`, вы сразу понимаете, что `<Артикул>` — это артикул, а `<ЗначенияРеквизитов>` — дополнительные поля товара. Для отладки это дороже, чем кажется.

**Версия схемы важна.** В атрибуте `ВерсияСхемы` корневого элемента 1С указывает версию CommerceML — 2.03, 2.05, 2.08 и далее. Разные версии описывают одни и те же сущности слегка по-разному, и конфигурация 1С выбирает версию сама. Прежде чем спорить о том, «почему поле не приезжает», посмотрите на этот атрибут: возможно, поля в этой версии схемы просто нет.

## Как устроен обмен: пять команд

Инициатор обмена — всегда 1С. Сайт ничего не запрашивает и не «забирает», он только отвечает. Вся точка входа — один скрипт:

```
/bitrix/admin/1c_exchange.php
```

Обмен каталогом идёт с параметром `type=catalog`, выгрузка заказов обратно в 1С — с `type=sale`. Второй параметр, `mode`, задаёт шаг.

```
1. GET ?type=catalog&mode=checkauth   → авторизация
2. GET ?type=catalog&mode=init        → параметры передачи
3. POST ?type=catalog&mode=file&filename=import.xml  → тело файла
4. GET ?type=catalog&mode=import&filename=import.xml → разбор файла
5. повтор шага 4, пока ответ = progress
```

Ответ всегда текстовый, и значение имеет **первая строка**: `success`, `progress` или `failure`. Всё остальное — детали для человека.

### Шаг 1: checkauth

1С обращается с Basic Auth под пользователем сайта, у которого есть право на обмен. В ответ приходят три строки:

```
success
PHPSESSID
a1b2c3d4e5f6...
```

Вторая и третья строки — имя и значение сессионной куки: то, что PHP вернёт из
`session_name()` и `session_id()`. Имя по умолчанию `PHPSESSID`, но оно настраивается,
поэтому 1С обязана брать его из ответа, а не подставлять константу. 1С обязана подставлять её во все следующие запросы, иначе каждый шаг будет создавать новую сессию, а вместе с ней — новый прогресс импорта. Обмен в такой ситуации бесконечно ходит по кругу, не двигаясь дальше первого шага.

### Шаг 2: init

```
zip=no
file_limit=1048576
```

`zip` говорит, принимает ли сайт архивы, `file_limit` — максимальный размер одной порции в байтах. Большой `import.xml` 1С режет на куски по `file_limit` и отправляет их последовательно с одинаковым `filename`, а Битрикс склеивает.

### Шаг 3: file

Тело POST-запроса — сырые байты файла, без multipart и без формы. Файл ложится в `/upload/1c_catalog/`.

### Шаг 4: import

Здесь начинается разбор. Битрикс читает XML, создаёт разделы, свойства и элементы — и **не пытается сделать всё за один запрос**. Отработав порцию, он отвечает:

```
progress
Импортировано 500 товаров из 12000
```

1С видит `progress` и повторяет тот же запрос. Битрикс продолжает с сохранённой позиции. И так, пока не вернётся `success`.

Понимание этого механизма решает половину проблем с обменом. Обмен не «висит» — он идёт шагами, и если шаг не укладывается в лимиты, цикл не двигается.

## Как посмотреть обмен своими глазами

Самый полезный навык в этой теме — уметь пройти обмен руками. Никакого доступа к 1С для этого не нужно.

```bash
# 1. Авторизация. -u — логин и пароль пользователя с правом на обмен
curl -s -u "exchange1c:password" \
  "https://site.ru/bitrix/admin/1c_exchange.php?type=catalog&mode=checkauth"
# success
# PHPSESSID
# 3f2a...

# 2. Параметры передачи — с полученной кукой
curl -s -b "PHPSESSID=3f2a..." \
  "https://site.ru/bitrix/admin/1c_exchange.php?type=catalog&mode=init"
# zip=no
# file_limit=1048576

# 3. Запуск импорта уже лежащего на сайте файла
curl -s -b "PHPSESSID=3f2a..." \
  "https://site.ru/bitrix/admin/1c_exchange.php?type=catalog&mode=import&filename=import.xml"
# progress
```

Три команды дают больше информации, чем час чтения форумов. Если `checkauth` возвращает `failure` — проблема в правах, и её видно сразу, а не через сообщение «обмен завершился с ошибкой» в 1С.

**Не запускайте `mode=import` на боевом сайте «чтобы посмотреть».** Это настоящий импорт: он изменит товары, цены и активность элементов ровно так же, как если бы его инициировала 1С. Для экспериментов нужна копия сайта с копией базы — та же, на которой вы проверяете обновления.

## Что внутри XML

### import.xml — структура и товары

```xml
<?xml version="1.0" encoding="UTF-8"?>
<КоммерческаяИнформация ВерсияСхемы="2.05" ДатаФормирования="2026-10-02">
  <Классификатор>
    <Ид>d5a9c1e4-...</Ид>
    <Группы>
      <Группа>
        <Ид>7b1f...</Ид>
        <Наименование>Смесители</Наименование>
      </Группа>
    </Группы>
    <Свойства>
      <Свойство>
        <Ид>a3c8...</Ид>
        <Наименование>Материал корпуса</Наименование>
      </Свойство>
    </Свойства>
  </Классификатор>
  <Каталог>
    <Товары>
      <Товар>
        <Ид>f4e2...</Ид>
        <Артикул>SM-1024</Артикул>
        <Наименование>Смеситель для кухни</Наименование>
        <Группы><Ид>7b1f...</Ид></Группы>
        <ЗначенияСвойств>
          <ЗначенияСвойства>
            <Ид>a3c8...</Ид>
            <Значение>Латунь</Значение>
          </ЗначенияСвойства>
        </ЗначенияСвойств>
      </Товар>
    </Товары>
  </Каталог>
</КоммерческаяИнформация>
```

Ключевая сущность здесь — `<Ид>`. Это GUID из 1С, и он же уезжает в поле `XML_ID` элемента или раздела инфоблока. Вся связь между двумя системами держится на нём: 1С не знает про `ID` элемента Битрикса и опознаёт товар только по GUID.

Отсюда правило, которое стоит запомнить до первой аварии: **`XML_ID` элементов и разделов трогать нельзя**. Ни руками в админке, ни скриптом миграции. Стёртый `XML_ID` означает, что следующий обмен не найдёт товар и создаст его заново — с новым `ID`, пустой статистикой и битыми ссылками в заказах.

### offers.xml — цены и остатки

```xml
<ПакетПредложений>
  <Предложения>
    <Предложение>
      <Ид>f4e2...#9a71...</Ид>
      <Цены>
        <Цена>
          <ИдТипаЦены>b2d4...</ИдТипаЦены>
          <ЦенаЗаЕдиницу>4990</ЦенаЗаЕдиницу>
          <Валюта>RUB</Валюта>
        </Цена>
      </Цены>
      <Количество>17</Количество>
    </Предложение>
  </Предложения>
</ПакетПредложений>
```

Обратите внимание на `<Ид>` предложения: `GUID_товара#GUID_характеристики`. Именно так торговое предложение привязывается к родительскому товару — Битрикс разбирает эту строку и записывает связь в служебное свойство `CML2_LINK`.

Разделение на два файла сделано ради скорости. `import.xml` тяжёлый, он несёт структуру и меняется редко. `offers.xml` лёгкий, его можно гонять хоть каждые пятнадцать минут — и именно так обычно и настраивают, чтобы остатки на витрине не расходились со складом.

## Служебные свойства CML2

При первом обмене Битрикс сам создаёт в инфоблоке набор свойств с префиксом `CML2_`. Они выглядят как мусор в списке свойств, и их регулярно пытаются удалить «для порядка». Делать этого не надо — на них держится обмен.

| Код свойства | Что хранит |
|---|---|
| `CML2_LINK` | Связь торгового предложения с товаром |
| `CML2_ARTICLE` | Артикул из 1С |
| `CML2_BAR_CODE` | Штрихкод |
| `CML2_MANUFACTURER` | Производитель |
| `CML2_BASE_UNIT` | Базовая единица измерения |
| `CML2_TAXES` | Налоговые ставки |
| `CML2_ATTRIBUTES` | Реквизиты, для которых нет отдельного свойства |
| `CML2_TRAITS` | Признаки товара |

**`CML2_ATTRIBUTES` — свалка, а не поле.** Туда падает всё, что 1С прислала в `<ЗначенияРеквизитов>` и чему Битрикс не нашёл соответствия. Если нужное значение из 1С «не приезжает», сначала загляните именно сюда: скорее всего, оно приехало, просто легло в общую кучу. Как вытащить его в нормальное свойство — тема отдельного разбора про маппинг полей.

## Где вмешаться в импорт

Импорт в конце концов создаёт и обновляет элементы инфоблока, поэтому работают обычные события инфоблоков — те же, что описаны в разборе [событий Битрикса](/blog/bitrix-events):

```php
<?php
// /bitrix/php_interface/init.php
use Bitrix\Main\EventManager;

EventManager::getInstance()->addEventHandler(
    'iblock',
    'OnAfterIBlockElementUpdate',
    function (&$arFields) {
        // Обмен приходит на свой скрипт — это самый надёжный признак контекста,
        // не зависящий от версии и от внутренних констант ядра
        $isExchange = str_contains(
            (string)($_SERVER['SCRIPT_NAME'] ?? ''),
            '/bitrix/admin/1c_exchange.php'
        );
        if (!$isExchange) {
            return;
        }
        // здесь — лёгкая работа: пометить элемент, положить ID в очередь
    }
);
```

Ключевое ограничение — цена обработчика. Он выполняется **на каждом элементе**. Обработчик, который делает один лишний запрос к базе, на каталоге в 50 000 товаров даст 50 000 запросов и превратит десятиминутный обмен в часовой. Поэтому в обработчике элемента складывают идентификаторы, а тяжёлую работу делают один раз после импорта:

```php
<?php
EventManager::getInstance()->addEventHandler(
    'catalog',
    'OnSuccessCatalogImport1C',
    function () {
        // Импорт завершён: здесь пересчитываем, отправляем, сбрасываем кэш
        // Один раз на весь обмен, а не на каждый товар
    }
);
```

**Проверьте имя и сигнатуру события в своей версии.** Набор событий модуля каталога между версиями Битрикса менялся, и обработчик, повешенный на несуществующее событие, не вызывает ошибки — он просто молча не срабатывает. Убедитесь, что обработчик реально выполняется, до того как повесите на него сброс кэша: обмен, который «прошёл успешно», но не сбросил кэш, оставит на витрине вчерашние цены, и узнаете вы об этом от покупателя.

## Что ломается чаще всего

**Обмен идёт по кругу и не завершается.** 1С не сохраняет сессионную куку из `checkauth` либо каждый запрос создаёт новую сессию. Прогресс пишется в одну сессию, а следующий шаг читает другую — и начинает сначала.

**`failure` без внятного текста.** Смотрите вторую строку ответа и лог обмена, а не сообщение в 1С. Самое частое — нехватка прав на запись в `/upload/1c_catalog/` или упавший по памяти шаг.

**Товары продублировались.** Кто-то изменил или очистил `XML_ID`. Битрикс не нашёл товар по GUID и создал новый. Восстанавливается только сопоставлением по артикулу и ручной склейкой — работа на день, поэтому `XML_ID` и не трогают.

**Цены не обновились, хотя обмен успешен.** Проверьте, что `ИдТипаЦены` из `offers.xml` сопоставлен с типом цены на сайте. Несопоставленный тип цены игнорируется тихо. Смежная классика — кэш каталога, который никто не сбросил после обмена; про политику кэширования каталога есть [отдельный разбор](/blog/bitrix-catalog-cache-policy).

**Обмен съел память или упёрся в таймаут.** Уменьшайте размер порции в настройках обмена. Увеличивать `max_execution_time` бессмысленно: шаговый механизм существует ровно для того, чтобы не упираться в лимиты, и правильный ответ — короче шаг, а не длиннее лимит. Как искать, во что именно упёрся PHP, разобрано в статье про [диагностику PHP-FPM](/blog/php-fpm-diagnostics).

## Итоги

Обмен с 1С перестаёт быть чёрным ящиком в тот момент, когда вы один раз прошли его руками через `curl`. Дальше диагностика превращается в механическую процедуру.

**Сначала смотрите, что приехало.** XML-файлы в `/upload/1c_catalog/` — единственный достоверный источник. Если поля нет в XML, проблема в 1С, и обсуждать настройки сайта бесполезно.

**Затем — на какой команде оборвалось.** Пять шагов, у каждого свой характерный отказ. `checkauth` — права, `file` — доступ к папке и размер порции, `import` — лимиты PHP.

**`XML_ID` — священная корова.** Всё остальное в инфоблоке можно менять, это — нет.

**Тяжёлую работу вешайте на завершение импорта, а не на элемент.** Разница между этими двумя вариантами на большом каталоге измеряется часами.

И одно наблюдение, которое экономит больше всего времени: почти каждый раз, когда клиент говорит «обмен сломался», обмен работает. Ломается что-то одно на его краю — права у пользователя, сопоставление типа цены, кэш, который не сбросили. Протокол из пяти команд надёжен именно потому, что в нём нечему ломаться, и это хорошая новость: искать нужно не в нём.