B2B API для партнёров
Назначение
Этот API предназначен для B2B‑клиентов (оптовиков, владельцев розничных интернет‑магазинов), которым нужно:
- получать список доступных точек выдачи (pick_up_points);
- получать список брендов по артикулу сразу по нескольким складам;
- получать список предложений (остатков) по выбранным брендам и артикулу.
Все методы расположены по префиксу: /api/b2b/*.
Авторизация
Авторизация настраивается в админке:
- Страница:
Настройка сайта→ вкладка1С интеграция, блок B2B API. - Хранение настроек —
App\Settings\GeneralSettings:b2b_enabled— включение/выключение B2B‑API;b2b_token— токен для авторизации.
При включении B2B‑API:
- если токена ещё нет — он автоматически генерируется;
- есть отдельная кнопка «Сгенерировать / перевыпустить токен B2B».
Каждый запрос к /api/b2b/* должен содержать заголовок:
Authorization: Bearer {B2B_TOKEN}Ошибки авторизации:
503 Service Unavailable— B2B‑API выключен (b2b_enabled = false);500 Internal Server Error— токен не настроен;401 Unauthorized— токен не передан или неверен.
1. Список офисов (точек выдачи)
POST /api/b2b/get-pick-up-points
Назначение: вернуть список точек выдачи, которые может использовать партнёр.
Запрос:
- Тело может быть пустым.
- Обязателен только заголовок
Authorization.
Ответ:
{
"result": true,
"message": "OK",
"pick_up_points": [
{
"id": 1,
"caption": "Основной магазин",
"country": null,
"region": null,
"city": null,
"address": "г. Москва, ул. Примерная, д. 1",
"phone": "+7 (900) 000-00-00",
"email": "info@example.com",
"description": "Основная точка выдачи",
"timetable": "Пн‑Пт 9:00–18:00"
}
]
}Поля заполняются из модели PickUpPoint (name, description, address, phone, email, schedule).
Пример запроса (curl):
curl -X POST "https://example.com/api/b2b/get-offices" \
-H "Authorization: Bearer YOUR_B2B_TOKEN"2. Получение брендов по артикулу
POST /api/b2b/get-brands
Назначение: найти все пары «бренд + артикул» по указанному артикулу на нескольких складах.
Тело запроса:
{
"article": "OC247",
"pick_up_points": [1, 2]
}article— обязательный, строка (артикул).pick_up_points— необязательный массив ID офисов:- если не указан, используются склады из
GeneralSettings::storages_for_crosses; - если указан — берутся только склады, привязанные к заданным офисам.
- если не указан, используются склады из
Ответ:
{
"result": true,
"message": "OK",
"brands": [
{
"brand": "KNECHT/MAHLE",
"article": "OC247",
"name": "Фильтр масляный",
"storages": [
{ "storage_id": 10, "storage_name": "Склад 1" },
{ "storage_id": 11, "storage_name": "Склад 2" }
]
}
]
}Особенности реализации:
- По каждому складу вызывается адаптер (
AdapterManager::getAdapter) и его методgetBrands(article). - Результаты фильтруются:
- по точному совпадению нормализованного артикула (функция
normalizeArticle); - пары без названия (
nameпустое или"-") отбрасываются.
- по точному совпадению нормализованного артикула (функция
- Названия брендов нормализуются через
BrandNormalizer, чтобы синонимы объединялись. - Пары с одинаковыми
brand + articleагрегируются: вstoragesзаписываются все склады, где пара найдена.
Пример запроса (curl):
curl -X POST "https://example.com/api/b2b/get-brands" \
-H "Authorization: Bearer YOUR_B2B_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"article": "OC247",
"pick_up_points": [1, 2]
}'3. Получение остатков по бренду и артикулу
POST /api/b2b/get-parts
Назначение: вернуть список предложений (остатков) по выбранным брендам и артикулу с разных складов.
Тело запроса:
{
"article": "OC247",
"brands": ["KNECHT/MAHLE", "MANN-FILTER"],
"pick_up_points": [1, 2]
}article— обязательный, строка.brands— обязательный массив строк — названия брендов (как в ответеget-brands).pick_up_points— необязательный массив ID офисов (логика такая же, как вget-brands).
Ответ:
{
"result": true,
"message": "OK",
"parts": [
{
"brand": "KNECHT/MAHLE",
"article": "OC247",
"name": "Фильтр масляный",
"quantity": 5,
"price": 950.0,
"delivery_days": 1,
"storage_id": 10,
"storage_name": "Склад 1"
},
{
"brand": "KNECHT/MAHLE",
"article": "OC247",
"name": "Фильтр масляный",
"quantity": 3,
"price": 930.0,
"delivery_days": 2,
"storage_id": 11,
"storage_name": "Склад 2"
}
]
}Особенности реализации:
- Выбор складов — так же, как в
get-brands(поstorages_for_crossesи, опционально, по офисам). - Для каждого склада:
- поднимается адаптер и вызывается его метод
getParts(brands, article); - из списка позиций берутся только те, у которых
quantity > 0; - для каждой позиции формируется минимальный набор полей:
brand,article,name,quantity(int),price(float, если доступна),delivery_days(если вернул адаптер),storage_id,storage_name.
- поднимается адаптер и вызывается его метод
- Закупочные цены, наценки и внутренний payload адаптеров в B2B‑ответ не попадают.
Пример запроса (curl):
curl -X POST "https://example.com/api/b2b/get-products" \
-H "Authorization: Bearer YOUR_B2B_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"article": "OC247",
"brands": ["KNECHT/MAHLE", "MANN-FILTER"],
"pick_up_points": [1, 2]
}'Типичный сценарий работы для партнёра
Забрать список офисов
- Вызвать
POST /api/b2b/get-pick-up-points. - Сохранить список точек выдачи и их ID.
- Вызвать
Поиск брендов по артикулу
- Вызвать
POST /api/b2b/get-brandsс полямиarticleи (опционально)pick_up_points. - Показать пользователю список брендов, полученный в поле
brands.
- Вызвать
Получение остатков по выбранным брендам
- Пользователь выбирает один или несколько брендов.
- Вызывается
POST /api/b2b/get-partsсarticle,brandsи (опционально)pick_up_points. - Клиентский код отображает полученные предложения, может сортировать по цене/сроку/складу.
4. Загрузка прайс-листа (upload-price-list)
POST /api/b2b/upload-price-list
Назначение: обновить один из сконфигурированных прайс-листов на сайте, передав файл (CSV/Excel/архив) напрямую из внешней учётной системы.
Особенности метода:
- авторизация выполняется по B2B-токену (Bearer);
- формат ответа унифицирован с остальными B2B-методами.
Тело запроса (multipart/form-data)
Аргументы:
id(integer, required) — ID прайс-листа в системе сайта (из справочникаprice_lists).
Это тот же ID, который вы настраиваете в админке при конфигурации прайса.document(file, required) — файл прайс-листа:- поддерживаются те же форматы, что и при ручной загрузке в админке (CSV, TXT, XLS/XLSX, ZIP/RAR с одним файлом внутри и т.п.);
- максимальный размер файла ограничен настройкой
price-lists.import.max_file_size.
tech_key(string, optional) — зарезервировано под будущее; сейчас авторизация выполняется через заголовокAuthorization.
Ответ
{
"result": true,
"message": "Price list upload scheduled for import.",
"task_id": 123
}result— булевый флаг успеха;message— текстовое сообщение;task_id— ID задачи импорта (price_list_import_tasks.id), по которому можно отслеживать статус.
Примеры ошибок:
{
"result": false,
"message": "Price list is not active."
}{
"result": false,
"message": "Uploaded file is not valid."
}{
"result": false,
"message": "Failed to store uploaded file."
}HTTP-коды:
200— запрос принят, задача импорта создана;400— проблема с данными (неактивный прайс-лист, невалидный файл и т.п.);500— внутренняя ошибка при сохранении файла.
Пример запроса (curl)
curl -X POST "https://example.com/api/b2b/upload-price-list" \
-H "Authorization: Bearer YOUR_B2B_TOKEN" \
-F "id=1" \
-F "document=@/path/to/your/pricelist.csv"5. Получение статуса импорта прайс-листа
POST /api/b2b/get-price-list-status
Назначение: по task_id узнать текущий статус задачи импорта прайс-листа, созданной методом upload-price-list.
Тело запроса
{
"task_id": 123
}task_id— обязательный, целое число, идентификатор задачи импорта (price_list_import_tasks.id), который вернул методupload-price-list.
Ответ (успешный)
{
"result": true,
"message": "OK",
"task": {
"id": 123,
"price_list_id": 1,
"status": "completed",
"status_label": "Завершено",
"source_type": "manual",
"total_rows": 1500,
"processed_rows": 1500,
"imported_rows": 1450,
"skipped_rows": 50,
"error_message": null,
"started_at": "2026-02-10T10:15:23+03:00",
"completed_at": "2026-02-10T10:16:02+03:00"
}
}Поля:
status— машинное значение статуса:pending— в очереди;processing— в работе;completed— завершено;failed— ошибка.
status_label— человекочитаемый статус (по текущей локали админки).source_type— способ загрузки:manual,email,ftp,url.
total_rows,processed_rows,imported_rows,skipped_rows— счётчики строк.error_message— текст ошибки, если статусfailed.started_at,completed_at— время старта/завершения задачи в формате ISO 8601 (илиnull, если ещё не установлены).
Примеры ошибок:
{
"message": "The given data was invalid.",
"errors": {
"task_id": [
"The selected task_id is invalid."
]
}
}HTTP-коды:
200— запрос успешен, информация о задаче возвращена;422— ошибка валидации параметров (например,task_idне передан или неверного типа).
Пример запроса (curl)
curl -X POST "https://example.com/api/b2b/get-price-list-status" \
-H "Authorization: Bearer YOUR_B2B_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": 123
}'