Реалізація імпорту товарів через API постачальника
Імпорт через API постачальника — найбільш гнучкий та актуальний способ отримання даних. Замість файлових виконань дані забираються напрямки з системи постачальника за розписанням або за подією. Складність у тому, що у кожного постачальника власний API: різні формати аутентифікації, структури відповідей та моделі пагінації.
Типи API постачальників
| Тип | Приклад | Особливості |
|---|---|---|
| REST JSON | Більшість сучасних | Пагінація cursor/offset, JWT/API-key |
| REST XML | Старі системи (1С) | Потрібен XML-парсер відповіді |
| SOAP | Корпоративні ERP | WSDL, SOAPClient |
| GraphQL | Рідко | Гнучкий вибір полів |
| oData | SAP, Microsoft | $filter, $top, $skip |
Базовий клієнт з 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) // експоненціальний backoff
));
return $stack;
}
public function get(string $path, array $params = []): array
{
// Rate limiting: не більш ніж N запитів на секунду
$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 => [],
};
}
}
Моделі пагінації
Offset-пагінація
public function fetchAllProducts(): iterable
{
$page = 1;
$perPage = 100;
do {
$response = $this->client->get('/products', [
'page' => $page,
'per_page' => $perPage,
]);
foreach ($response['data'] as $item) {
yield $item;
}
$hasMore = count($response['data']) === $perPage;
$page++;
} while ($hasMore);
}
Cursor-пагінація (ефективніше для великих таблиць)
public function fetchAllProducts(): iterable
{
$cursor = null;
do {
$params = ['limit' => 200];
if ($cursor) $params['cursor'] = $cursor;
$response = $this->client->get('/v2/products', $params);
foreach ($response['items'] as $item) {
yield $item;
}
$cursor = $response['next_cursor'] ?? null;
} while ($cursor);
}
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;
}
}
Інкрементальна синхронізація
Найцінніший режим — отримати тільки зміни з моменту останньої синхронізації:
public function fetchUpdatedSince(\DateTimeInterface $since): iterable
{
return $this->fetchAllProducts([
'updated_after' => $since->format(DATE_ATOM),
'fields' => 'sku,price,qty,name,description',
]);
}
Тривалість реалізації
- Один REST-постачальник, offset-пагінація, нормалізація, імпорт — 2 дні
- OAuth 2.0, cursor-пагінація, інкрементальна синхронізація — +1 день
- Мультипостачальник через конфіг, SOAP, rate limiting, retry — +2 дні







