Integration API
Модуль Integration API дозволяє складській програмі, 1С, CRM або вашому скрипту працювати з магазином через HTTPS: читати каталог, виставляти залишки по складах, змінювати ціни, читати замовлення, змінювати їхній статус і оплату, записувати номер накладної (ТТН) і отримувати вебхук, коли замовлення змінилося.
Це закритий API для звʼязку сервер із сервером. Кожен виклик потребує токена з окремими правами, кожен запис підписується й іде з дозволеної IP-адреси, а кожна зміна потрапляє в журнал аудиту.
Модуль встановлюється з Модулі → Магазин модулів. Потрібен білд ядра eCMS9 526 або новіший (для імпорту товарів і статей — 600).
Headless API обслуговує вітрину: каталог, кошик, оформлення й вхід покупця. Він може бути відкритим і ніколи не змінює ціни чи залишки. Integration API — навпаки: нічого публічного, а його робота — записувати дані обліку. Якщо каталог читається, а ціну чи кількість змінити не виходить, ви, найімовірніше, звертаєтесь до Headless API, а не до цього модуля.
З чого почати
Базова адреса — https://ваш-магазин/integration_api/v1.
GET /ping— перевіряє токен і повертає його назву, права та час сервера. Без чинного токена кожна адреса відповідає401 UNAUTHORIZED; це нормальний стан магазину, якому ще не видали токен.GET /openapi.yaml— повний контракт OpenAPI-файлом, який розуміють Postman, Insomnia й генератори коду. Те саме посилання — кнопка OpenAPI на екрані модуля.
Отримати токен
Відкрийте Налаштування → API інтеграції (екран модуля називається «API інтеграції»). У блоці Токени:
- Дайте токену назву, наприклад «Облік — синхронізація залишків». Один токен на одну інтеграцію: так видно її запити, її можна вимкнути або замінити, не чіпаючи інші.
- Позначте потрібні права. Давайте мінімум.
- Для кожного права на запис вкажіть дозволені IP-адреси: одну адресу або
мережу (не ширшу за
/16, для IPv6 — не ширшу за/32). Токен із правом на запис без цього списку не створюється. - За бажанням задайте дату, до якої токен діє.
Після натискання Створити токен екран один раз покаже два значення:
- токен
ik_…, який іде в кожному запиті; - секрет підпису, яким підписуються записи.
Магазин зберігає лише відбиток токена, а секрет не зберігає взагалі, тому скопіюйте обидва значення одразу й передайте розробнику захищеним каналом. Якщо значення втрачено чи витекло, натисніть Замінити токен: видається нова пара, а стара перестає працювати одразу; назва, права, список IP та історія запитів лишаються.
| Право | Дозволяє |
|---|---|
catalog:read | товари, варіанти, категорії, бренди |
stock:read / stock:write | читати / змінювати залишки по складах |
prices:read / prices:write | читати / змінювати ціни |
orders:read | читати замовлення без персональних даних покупців |
orders:write | змінювати статус, позначку оплати й номер накладної |
customers:pii | імʼя, телефон і адреса покупця в замовленнях |
webhooks:manage | підписуватись на події замовлень |
products:write | імпортувати товар чи статтю блогу з іншого магазину |
Права, що пишуть (stock:write, prices:write, orders:write,
webhooks:manage, products:write), вимагають списку IP. Персональні дані —
окреме право свідомо: без нього в замовленні немає імені, телефону й адреси.
Зробити запит
Для читання потрібен лише токен:
curl -sS https://ваш-магазин/integration_api/v1/stock?sku=RC-1234 \
-H "Authorization: Bearer ik_xxxxxxxx"
Токен приймається лише в заголовку Authorization: Bearer, ніколи в адресі.
Звичайний HTTP відхиляється.
Підписуйте кожен запис
Запис несе ще два заголовки:
X-Timestamp: <unix-секунди>
X-Signature: hex(HMAC-SHA256(секрет, METHOD + "\n" + PATH + "\n" + QUERY + "\n" + X-Timestamp + "\n" + hex(SHA256(тіло))))
PATHпочинається з/, наприклад/integration_api/v1/stock.QUERY— рядок запиту точно в тому вигляді, в якому він іде в мережу, без?; порожній, якщо його немає.- Тіло хешується байт у байт, як відправлено; порожнє тіло — це SHA-256 порожнього рядка.
- Секрет береться так, як його показано (шістнадцятковий текст, не декодований).
- Мітка часу має відрізнятися від часу сервера не більше ніж на ±300 секунд, а
підпис приймається один раз. Два однакові запити в ту саму секунду дістануть
SIGNATURE_REPLAYED.
Повний приклад (пробний прогін зміни залишку):
BASE=https://ваш-магазин/integration_api/v1
TOKEN=ik_xxxxxxxx
SECRET=ваш-секрет-підпису
BODY='{"dry_run":true,"items":[{"sku":"RC-1234","warehouse_id":1,"quantity":12}]}'
TS=$(date +%s)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIG=$(printf 'PUT\n/integration_api/v1/stock\n\n%s\n%s' "$TS" "$BODY_HASH" \
| openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -sS -X PUT "$BASE/stock" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
-H "Idempotency-Key: stock-$TS" \
-H "Content-Type: application/json" \
--data-binary "$BODY"
Спершу перевірте підпис через POST /ping: він має бути підписаний, а його
помилки не рахуються до блокування IP. У модулі є й невеликий PHP-клієнт із таким
самим підписом — Resources/examples/client.php.
Що вміє API
| Напрям | Ендпоінти |
|---|---|
| Довідники | /warehouses, /currencies, /price-types, /order-statuses, /payment-methods, /delivery-methods |
| Каталог | /products (з варіантами, посторінково), /products/{id}, /variants (пошук за id, SKU, штрихкодом чи зовнішнім id), /categories, /brands |
| Залишки | GET /stock; PUT /stock виставляє абсолютні кількості по складах; POST /stock/adjust змінює їх на дельту |
| Ціни | GET /prices; PUT /prices виставляє базову ціну, стару ціну або ціну типу цін |
| Замовлення | GET /orders, GET /orders/{id}; PATCH /orders/{id}/status, /paid, /ttn |
| Зміни | GET /changes — що змінилося після вашого курсора |
| Вебхуки | GET, POST /webhooks, DELETE /webhooks/{id} |
Зміна статусу замовлення робить те саме, що зміна статусу в адмінці, разом із
побічними діями, налаштованими у вашому магазині: синхронізація з CRM, фіскальний
чек, SMS чи Telegram-сповіщення, дії над залишками, привʼязані до статусу.
Спершу пробуйте з dry_run.
Залишки й ціни
- Варіант задається рівно одним ідентифікатором:
variant_id,sku,barcodeабоexternal_id. Значення, що збігається з кількома варіантами, відхиляється зAMBIGUOUS; для таких використовуйтеvariant_id. - В одному запиті до 1000 рядків. Коректні рядки записуються однією транзакцією.
З
all_or_nothing: trueодин поганий рядок відхиляє весь запит. price— у власній валюті варіанта, так, як його вводять у формі товару. Ціна в основній валюті перераховується з неї.- Запис залишку чи ціни проходить тим самим шляхом, що й форма в адмінці: цінові фіди, пошук і кеш сторінок бачать цю зміну.
- Магазин, який не використовує модуль «Склади», має один склад із id 1
(його показує
GET /warehouses). Передавайте"warehouse_id": 1; кількість записується прямо в залишок варіанта. Доступно з версії модуля 1.3.1.
Замовлення й номери накладних
GET /orders ділиться на сторінки через cursor; для інкрементальної синхронізації
використовуйте updated_since. PATCH /orders/{id}/ttn записує номер накладної.
Для Нової пошти номер потрапляє в модуль Нової пошти (14 цифр, далі його статус
відстежує сам модуль); для інших доставок — у ручне поле відстеження ядра.
Поточне значення повертається як tracking у GET /orders/{id}.
Позначка paid запускає все, що запускає позначка оплати в магазині, зокрема
фіскальний чек, якщо фіскальний модуль увімкнено. Спершу перевіряйте через
dry_run.
Синхронізація через стрічку змін
GET /changes?cursor=… повертає, що змінилося після курсора, у вигляді id та назв
подій (order.status_changed, stock.updated, price.changed, …). Збережіть
next_cursor і питайте знову. Рядки зберігаються 30 днів і фільтруються за
правами вашого токена. Звузити можна параметром entity=order,stock.
Стрічка бачить зміни, що проходять через події магазину. Залишок, який рухає щось
інше (щогодинний імпорт файлу, пряма синхронізація з базою), у ній не зʼявляється.
Час від часу проходьте GET /stock повністю як страховку.
Вебхуки
Підпишіться через POST /webhooks на order.created, order.status_changed,
order.paid_changed або order.tracking_changed. Адреса має бути HTTPS на порту
443 чи 8443 і вказувати на публічний хост; до 5 активних на токен. Секрет
повертається один раз.
Доставка — це POST із заголовками X-Webhook-Event, X-Webhook-Id,
X-Webhook-Timestamp і
X-Webhook-Signature = hex(HMAC-SHA256(webhook_secret, timestamp + "." + body)).
Відповідайте 2xx протягом 10 секунд; інакше буде повтор через 1 хв, 5 хв, 30 хв,
2 год, 6 год, 12 год і 24 год. Тіло містить лише id та стани, тож деталі замовлення
дістаньте окремим запитом, а дублі відсійте за X-Webhook-Id.
Вбудовані запобіжники
- dry_run —
"dry_run": trueу тілі показує, що зміниться, і нічого не записує. Прапорець, який не є справжнім булевим значенням, відхиляється й ніколи не сприймається як бойовий запис. - Idempotency-Key — ключ довжиною 8–64 символи на запис: повторний запит повертає збережену відповідь замість повторного застосування (зберігається 24 години).
- Запобіжник ціни — зміна більше ніж на 50 % і нульова ціна відхиляються з
PRICE_GUARD, якщо в рядку немає"force": true. - Ліміти запитів — 300 читань і 60 записів на хвилину на токен; понад ліміт
відповідь
429ізRetry-After. - Блокування IP — повторні невдалі автентифікації з однієї адреси блокують її на 15 хвилин.
- Аудит — кожен застосований запис фіксується в журналі аудиту адмінки з автором «API: назва токена», IP-адресою та старим і новим значеннями.
- Журнал запитів — кожен запит видно в блоці Журнал запитів на екрані модуля; за замовчуванням він зберігається 90 днів.
Усе це налаштовується в блоці Безпека й ліміти на екрані модуля: перемикач
Лише читання (усі записи відповідають 503, читання працює), вимога HTTPS,
ліміти, розмір пакета, відсоток запобіжника ціни, вікно підпису та сповіщення в
Telegram про новий токен і блокування IP.
Відповіді й помилки
Кожна відповідь має однакову обгортку:
{"success": true, "data": {}, "error": null}
{"success": false, "data": null, "error": {"code": "UNAUTHORIZED", "message": "…"}}
Типові коди: UNAUTHORIZED, IP_NOT_ALLOWED, FORBIDDEN (токену бракує права),
SIGNATURE_MISSING, SIGNATURE_INVALID, SIGNATURE_EXPIRED,
SIGNATURE_REPLAYED, RATE_LIMITED, TOO_MANY_FAILURES, READ_ONLY,
VALIDATION_FAILED, IDEMPOTENCY_CONFLICT, INVALID_FIELD, AMBIGUOUS,
PRICE_GUARD. Повний перелік — в openapi.yaml. Час у відповідях — UTC
ISO-8601 із Z; в updated_since передавайте Z або зсув.
Часті запитання
Кожен виклик відповідає 401. Токена ще немає або він не там. Передавайте його
як Authorization: Bearer ik_…; з адреси він не читається.
Запис відповідає 401 SIGNATURE_INVALID. Звірте те, що підписуєте, з правилами
вище: шлях починається з /, запит підписується як відправлено, тіло хешується як
відправлено, секрет — шістнадцятковий текст. Перевірте через POST /ping.
Запис відповідає 403 IP_NOT_ALLOWED. Адреси того, хто викликає, немає в списку
токена. За CDN чи проксі магазин бачить адресу проксі, якщо його не налаштовано
довіряти їй.
Кілька товарів мають однаковий SKU. Такі рядки відповідають AMBIGUOUS.
Знайдіть варіанти через GET /variants?sku=… і пишіть за variant_id.
Залишок перезаписує інша задача. Якщо щогодинний імпорт теж пише в той самий склад, вони перетиратимуть одне одного. Для кожного складу оберіть одне джерело.
Чи можна зробити токен лише для читання? Так. Позначте тільки права …:read;
такому токену не потрібні ні список IP, ні підпис.