📚 BusinessPlatform Docs

Экспорт каталога: Shopify CSV и Yandex YML

Обновлено 17 октября 2026, Europe/Moscow. Общий Commerce API, без новых очередей, ключей и миграций. Экспорт синхронный: успешный GET сразу скачивает файл, Job не создаётся.

База: https://app.irisaicrm.shop/api/v1/commerce/projects/{projectId}. Серверный JWT существующего клиента; достаточно commerce:read или commerce:imports:read. Write-only права не подходят. Доступ только к своему tenant. Импорт может быть отключён — экспорт от этого не зависит.

GET Query Content-Type Content-Disposition
/exports/shopify.csv нет text/csv; charset=utf-8 attachment; filename=catalog-shopify.csv
/exports/yandex.yml необязательный baseUrl application/xml; charset=utf-8 attachment; filename=catalog-yandex.yml

Оба файла — UTF-8 без BOM. Нужен обычный X-Request-Id UUID, Idempotency-Key не нужен. Ошибки возвращаются существующим JSON-конвертом Commerce API. Ответ содержит nosniff/no-store. Снимок PostgreSQL RepeatableRead / READ ONLY: категории, продукты и варианты относятся к одному состоянию; чтение не меняет каталог, остатки, очередь или outbox.

CSV: редактирование и обратный импорт

Используется тот же канонический заголовок, что у /imports/template/shopify.csv: прежние 22 совместимые Shopify колонки плюс стандартная product.metafields.businessplatform.catalog. Прежние CSV без этой необязательной колонки по-прежнему принимаются. Основные поля — Handle, Title, Body (HTML), Vendor, Type, Tags, Published, Option1..3, Variant SKU/Inventory Tracker/Qty/Price/Compare At Price, Image Src/Position, Variant Image, Status. Все значения корректно заключаются в кавычки, кавычки удваиваются, многострочный текст сохраняется.

Одна строка на вариант; дополнительные фотографии — отдельные image-only строки. Категория/путь отображается в Type, цена — RUB, остаток — доступное количество с сохранением признака отслеживания. В CSV включаются все неархивированные продукты, в том числе draft/discontinued и неактивные варианты. discontinued отображается стандартным Shopify Status=archived; при обратном импорте BP восстанавливает discontinued. Пустой каталог — только заголовок.

Обычный Shopify CSV не поддерживает variant metafields и имеет максимум три опции. Поэтому product metafield в первой строке продукта хранит JSON версии 1: исходные характеристики продукта и вариантов, имена/активность/порядок SKU, описание, slug/порядок продукта и точный путь категории. Это стандартная колонка product metafield Shopify, а не отдельный CSV-формат Velvio. При загрузке непосредственно в Shopify для неё нужно заранее определить соответствующий product metafield; Shopify не превратит содержимое JSON в собственные поля вариантов. BP импортёр распознаёт её и восстанавливает сохранённые доступные поля без потерь, включая более трёх характеристик и названия категорий с символом >.

Цена, остаток, track-stock, статус продукта и фотографии берутся из обычных колонок: их можно редактировать. Характеристики/описание/имена/порядок из metafield имеют приоритет над отображаемой проекцией. Если меняется состав SKU, удалить metafield или обновить его согласованно: несоответствие даёт invalid_export_metadata в dry-run. В колонке до 4 MiB, обычные поля до 32000 символов. Формат не является полной резервной копией БД: новые внутренние UUID назначаются платформой, пустые категории без товаров CSV не представляет. Импорт изображений по-прежнему скачивает файлы в хранилище целевого бизнеса.

YML и ссылки

yml_catalog содержит shop/name/company, RUB currency, categories с parentId и offers. Категориям назначаются устойчивые положительные числовые ID до 18 цифр, offer.id = SKU, устойчивый group_id объединяет варианты одного продукта. Добавление другой категории или продукта не перенумеровывает существующие ID. Атрибут date содержит начало получения снимка с явным часовым поясом. Отдельный offer на активный вариант активного продукта; скрытые/архивированные категории и их потомки исключают предложение. Нулевой отслеживаемый остаток даёт available=false, неотслеживаемый — available=true. Передаются price/oldprice (когда зачёркнутая цена выше текущей), categoryId, все упорядоченные picture, name, vendorCode/shop-sku, description/count и характеристики param. При совпадении имени характеристики вариант уточняет значение продукта. XMLWriter экранирует текст и атрибуты; недопустимые XML-символы дают 422 export_invalid_data. Пустой каталог — валидный документ с пустым offers, без фиктивного товара.

baseUrl — абсолютный HTTP/HTTPS URL магазина, можно с префиксом пути. Например HTTPS://Example.com/shop/ нормализуется в https://example.com/shop; ссылка товара — https://example.com/shop/products/{slug}. Query/fragment, credentials, относительные URL и управляющие символы отклоняются с 400. Параметр не скачивается сервером, не меняет Host/redirect и не создаёт SSRF. Неизвестные и повторные query также отклоняются.

Если baseUrl отсутствует, shop/url и offer/url опускаются, домен не выдумывается. Это каталог YML для обмена данными; конкретный рекламный/маркетплейс-потребитель может требовать ссылки и собственные дополнительные обязательные поля — тогда передать baseUrl и проверить его требования. YML не является полным lossless backup: черновики и неактивные варианты исключены, атрибуты представлены плоскими param, внутренние ID/состояния BP не переносятся.

Ограничения и проверка

До 2000 продуктов/категорий, 10000 вариантов, CSV — до 10000 строк с учётом продолжений изображений; файл до 100 MiB. Пределы полей CSV совпадают с импортёром (обычные 32000 символов, metafield 4 MiB); слишком длинное HTML-экранированное описание или metadata также дают 413 export_limit, без усечения и выдачи файла, который не импортируется обратно. Несогласованная/циклическая категория или путь глубже 8 уровней — 409 export_category_invalid. Действует общий GET deadline 5 секунд, формирование учитывает cancellation; при timeout можно безопасно повторить GET. Очень большой каталог потребует отдельного последующего фонового экспорта, сейчас он не добавляется.

Проверки входят в tools/CommerceImportChecks: round-trip через настоящий импортёр, произвольные характеристики, четыре опции, неактивные SKU, несколько изображений, Unicode/кавычки/переносы/угловые скобки, пустые CSV/XML, UTF-8 YML, ссылки/отказы baseUrl, реальные SQL/HTTP, scope и tenant isolation, отсутствие записей outbox. Живой smoke экспорта — флаг BP_EXPORT_SMOKE в существующем CommerceImportSmoke.

Первичные спецификации: Shopify CSV/product metafields, Yandex YML.

Подтверждённый выпуск

Развёрнуто 17.10.2026: 20261017-104655, код 11dc7d03736fd4b31354ceaadcd311a57e68f48a, GitHub Actions/Packages 39015416054. Digest ядра: sha256:375098fa4227e82be309f828e560439d356cd142284eec295ace67f834ecf2a7. Миграций/новых ключей/изменений nginx нет; существующие настройки и volume импорта сохранены, лендинг/витрины не обновлялись.

CI 39015415913: 146 import/export + 227 Commerce + 59 Velvio + 35 regression checks, четыре задачи зелёные. Проверены 43 страницы справки. Живой прогон — 41 HTTP-запрос, включая CSV и YML без/с baseUrl (200), write-only scope (403), credentials в baseUrl (400), а также действующий импорт/изображения/upsert. Тестовый бизнес неопубликован, товар архивирован, платёжных действий не было. Квитанции — docs/development/catalog-export-{checks,deployment,production-smoke}.json. Пользовательская приёмка кнопок экспорта в Velvio остаётся на стороне клиента.