Реализация импорта товаров через API поставщика

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация импорта товаров через API поставщика
Средняя
~5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация импорта товаров через 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);
}

Scroll / stream (для больших выгрузок)

Некоторые поставщики поддерживают нстримовую выгрузку — один запрос, ответ в несколько мегабайт с построчным NDJSON (Newline Delimited JSON):

$response = $this->http->get('/products/export', ['stream' => true]);
$body     = $response->getBody();

while (!$body->eof()) {
    $line = $this->readLine($body);
    if ($line) yield json_decode($line, true);
}

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;
    }
}

Токен кэшируется до истечения — не делать лишних запросов на авторизацию.

Нормализация разных структур ответа

Каждый поставщик имеет своё 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
    {
        // Поддержка dot-notation: "product.details.sku"
        return data_get($data, $path);
    }
}

Конфиг fieldMap хранится в БД как JSON — добавление нового поставщика без изменения кода.

Инкрементальная синхронизация

Самый ценный режим — получать только изменения с момента последней синхронизации:

public function fetchUpdatedSince(\DateTimeInterface $since): iterable
{
    return $this->fetchAllProducts([
        'updated_after' => $since->format(DATE_ATOM),
        'fields'        => 'sku,price,qty,name,description',
    ]);
}

Время последней успешной синхронизации хранится в supplier_sync_log:

SELECT MAX(completed_at) FROM supplier_sync_log
WHERE supplier_id = $1 AND status = 'success';

SOAP-клиент для 1С-совместимых поставщиков

$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;
}

Сроки реализации

  • Один REST-поставщик, offset-пагинация, нормализация, импорт — 2 дня
  • OAuth 2.0, cursor-пагинация, инкрементальная синхронизация — +1 день
  • Мультипоставщик через конфиг, SOAP, rate limiting, retry — +2 дня