Представьте: ваш интернет-магазин продаёт 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 дня.
Сроки ориентировочные — точная оценка даётся после анализа документации поставщика. Запросите предварительную оценку вашего проекта — мы рассчитаем срок и стоимость индивидуально. Свяжитесь с нами, и мы подготовим детальное предложение. Получите консультацию — оценим ваш проект за один рабочий день.







