Уявіть: ваш інтернет-магазин продає 10 000 товарів, а постачальник змінює ціни двічі на день. Ручне завантаження через Excel займає години та призводить до застарілих цін і помилок у залишках. Пряма інтеграція через API постачальника вирішує цю проблему. Дані оновлюються автоматично — без людської участі. За 5 років ми реалізували понад 50 таких інтеграцій — від простих REST-зв'язок до мультипостачальникових систем з OAuth 2.0 та SOAP. Наш досвід підтверджує, що правильна архітектура гарантує стабільність і точність даних. Наприклад, для інтернет-магазину автозапчастин з 500 000 SKU налаштували інкрементальну синхронізацію з 5 постачальниками, кожен зі своїм API. Результат: ціни та залишки актуальні із затримкою не більше 15 хвилин.
Складність у тому, що API постачальників сильно різняться. Формати аутентифікації, структури відповідей, моделі пагінації — все індивідуально. Без правильної архітектури інтеграція перетворюється на хаос. Ми використовуємо перевірені патерни, які спрощують додавання нових постачальників і забезпечують стабільність.
Як вибрати тип API для постачальника?
| Тип | Приклад | Особливості |
|---|---|---|
| REST JSON | Більшість сучасних | Пагінація cursor/offset, JWT/API-key |
| REST XML | Старі системи (1С) | Потрібен XML-парсер відповіді |
| SOAP | Корпоративні ERP | WSDL, SOAPClient |
| GraphQL | Рідко у постачальників | Гнучкий вибір полів |
| oData | SAP, Microsoft | $filter, $top, $skip |
Визначення типу — перший крок. Ми завжди починаємо з аналізу документації постачальника: якщо є REST API специфікація — половина роботи зроблена.
Базовий клієнт з retry та rate limiting
class SupplierApiClient
{
private \GuzzleHttp\Client $http;
private RateLimiter $rateLimiter;
public function __construct(
private SupplierApiConfig $config,
) {
$this->http = new \GuzzleHttp\Client([
'base_uri' => $config->baseUrl,
'timeout' => 30,
'handler' => $this->buildHandlerStack(),
]);
}
private function buildHandlerStack(): \GuzzleHttp\HandlerStack
{
$stack = \GuzzleHttp\HandlerStack::create();
$stack->push(\GuzzleHttp\Middleware::retry(
function (int $retries, $request, $response, $exception) {
if ($retries >= 3) return false;
if ($exception instanceof \GuzzleHttp\Exception\ConnectException) return true;
if ($response && $response->getStatusCode() >= 500) return true;
return false;
},
fn(int $retries) => 1000 * (2 ** $retries)
));
return $stack;
}
public function get(string $path, array $params = []): array
{
$this->rateLimiter->throttle($this->config->id, $this->config->rateLimit);
$response = $this->http->get($path, [
'query' => $params,
'headers' => $this->buildHeaders(),
]);
return json_decode($response->getBody(), true);
}
private function buildHeaders(): array
{
return match ($this->config->authType) {
'bearer' => ['Authorization' => 'Bearer ' . $this->config->token],
'api_key' => ['X-API-Key' => $this->config->apiKey],
'basic' => ['Authorization' => 'Basic ' . base64_encode(
$this->config->login . ':' . $this->config->password
)],
default => [],
};
}
}
Експоненційний backoff (1000, 2000, 4000 мс) знижує навантаження на сервер постачальника та підвищує ймовірність успіху при тимчасових збоях. Rate limiting запобігає блокуванню за перевищення лімітів запитів.
Чому важлива нормалізація даних?
Кожен постачальник має своє JSON-поле для назви, ціни, артикулу. Без нормалізації код стає «кашеподібним» — у кожному методі перевірки та вилучення. Ми використовуємо fieldMap з dot-notation, який зберігається в БД як JSON. Додавання нового постачальника — просто запис у таблицю, без зміни коду.
class SupplierResponseNormalizer
{
private array $fieldMap;
public function normalize(array $raw): array
{
return [
'sku' => $this->extract($raw, $this->fieldMap['sku']),
'name' => $this->extract($raw, $this->fieldMap['name']),
'price' => (float) $this->extract($raw, $this->fieldMap['price']),
'qty' => (int) $this->extract($raw, $this->fieldMap['qty']),
'description' => $this->extract($raw, $this->fieldMap['description']),
'images' => $this->extractImages($raw),
];
}
private function extract(array $data, string $path): mixed
{
return data_get($data, $path);
}
}
Коли потрібна інкрементальна синхронізація?
Інкрементальна синхронізація незамінна, коли обсяг даних великий або частота оновлень висока. Вона запитує лише зміни з моменту останнього оновлення, використовуючи параметр updated_after. Час останньої успішної синхронізації зберігається в БД. Це скорочує обсяг переданих даних у рази — в проекті з 500 000 SKU навантаження на API знизилося на 90%.
Пагінація та порівняння методів
| Тип | Простота | Ефективність при зсувах | Обсяг передачі |
|---|---|---|---|
| Offset | Висока | Низька | Повний скид |
| Cursor | Середня | Висока | Лише різниця |
| Scroll | Низька | Висока | Потоково |
Cursor-пагінація стабільніша за offset при частих змінах, оскільки використовує унікальний ідентифікатор останнього запису. Offset проста, але неефективна при зсувах даних. Для великих обсягів ми рекомендуємо cursor або scroll.
OAuth 2.0 авторизація
Ряд постачальників вимагає OAuth 2.0 client credentials. Токен кешується до завершення терміну дії — це виключає зайві запити.
class OAuth2TokenProvider
{
private ?string $accessToken = null;
private ?int $expiresAt = null;
public function getToken(): string
{
if ($this->accessToken && time() < ($this->expiresAt - 60)) {
return $this->accessToken;
}
$response = Http::asForm()->post($this->tokenUrl, [
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => 'products:read stocks:read',
]);
$data = $response->json();
$this->accessToken = $data['access_token'];
$this->expiresAt = time() + $data['expires_in'];
return $this->accessToken;
}
}
SOAP-клієнт для 1С-сумісних постачальників
Для інтеграції з системами на базі 1С використовуємо SOAP. WSDL-документація описує методи та структури даних.
$client = new \SoapClient($this->wsdlUrl, [
'login' => $this->login,
'password' => $this->password,
'encoding' => 'UTF-8',
'soap_version' => SOAP_1_2,
'cache_wsdl' => WSDL_CACHE_DISK,
]);
$result = $client->GetProductList([
'DateFrom' => $since->format('Y-m-d\TH:i:s'),
'Categories' => $this->categoryFilter,
]);
foreach ($result->Products->Product as $product) {
yield (array) $product;
}
Типові проблеми та їх вирішення
| Проблема | Рішення |
|---|---|
| Різні формати полів | Нормалізація через fieldMap |
| Мережеві збої | Retry з експоненційним backoff |
| Перевищення лімітів запитів | Rate limiting + черга |
| Застарілі залишки | Інкрементальна синхронізація |
| Повільна пагінація | Cursor-пагінація замість offset |
Що входить в роботу
- Аналіз документації API постачальника (OpenAPI, WSDL, Postman-колекції).
- Розробка клієнта з retry, rate limiting, аутентифікацією (OAuth 2.0, API-key, Basic).
- Реалізація пагінації (offset, cursor, scroll).
- Нормалізація полів під єдиний формат (sku, name, price, qty).
- Налаштування інкрементальної синхронізації по updated_after.
- Тестування стабільності при мережевих збоях і таймаутах.
- Документація інтеграції (схема даних, конфігурація, інструкція з додавання нового постачальника).
- Навчання вашої команди (1-2 години воркшопу).
- Підтримка протягом місяця після запуску (виправлення багів, доналаштування).
Терміни реалізації
Реалізуємо під ключ. Орієнтовні терміни:
- Один REST-постачальник з offset-пагінацією та нормалізацією — від 2 днів.
- Додавання OAuth 2.0, cursor-пагінації та інкрементальної синхронізації — +1 день.
- Мультипостачальник з конфігурованими налаштуваннями, SOAP, rate limiting — +2 дні.
Терміни орієнтовні — точна оцінка дається після аналізу документації постачальника. Запросіть попередню оцінку вашого проекту — ми розрахуємо термін і вартість індивідуально. Зв'яжіться з нами, і ми підготуємо детальну пропозицію. Отримайте консультацію — оцінимо ваш проект за один робочий день.







