API FAQ


Przykład w PHP z użyciem biblioteki Guzzle 6 <?php
// KONFIGURACJA INTEGRACJI
$shopName 'test_shop';
$apiKey '$2y$10$XCUXFm5gcunQ.tyqzNtfJLZ80.gbQwuzO7yvETu/z/xWebOy/kywExgnQOkB.';
$domain 'arena.pl';
$shopEngineName 'SuperShop v8.8';
$pluginName 'CUSTOM INTEGRATION v1.2';

// KONFIGURACJA KONKRETNEGO ŻĄDANIA
$endpoint 'categories/search';
$requestData = [
  
'filters' => [
    [
      
'field' => 'id',
      
'conditions' => [
        [
'operator' => '>=''value' => 3],
        [
'operator' => '<''value' => 300],
      ],
    ],
    [
      
'field' => 'name',
      
'conditions' => [
        [
'operator' => 'NOT IN''value' => ['Berety''Rękawiczki']],
      ],
    ],
  ],
  
'perPage' => 2,
];

$guzzle = new \GuzzleHttp\Client([
    
'base_uri' => 'https://' $domain '/api/v4/',
    \
GuzzleHttp\RequestOptions::HEADERS => [
        
'User-Agent' => "{$shopEngineName} ({$pluginName})",
        
'Accept' => 'application/json',
    ],
    \
GuzzleHttp\RequestOptions::AUTH => [$shopName$apiKey],
]);
$response $guzzle->post($endpoint, [\GuzzleHttp\RequestOptions::JSON => $requestData]);

var_dump($response->getStatusCode());

$responseData $response->getBody()->__toString();

if (
$response->getStatusCode() === 200) {
    
var_dump(json_decode($responseDatatrue));
} else {
    
var_dump($responseData);
}
Przykład w PHP z użyciem rozszerzenia curl <?php
// KONFIGURACJA INTEGRACJI
$shopName 'test_shop';
$apiKey '$2y$10$XCUXFm5gcunQ.tyqzNtfJLZ80.gbQwuzO7yvETu/z/xWebOy/kywExgnQOkB.';
$domain 'arena.pl';
$shopEngineName 'SuperShop v8.8';
$pluginName 'CUSTOM INTEGRATION v1.2';

// KONFIGURACJA KONKRETNEGO ŻĄDANIA
$endpoint 'categories/search';
$requestData = [
  
'filters' => [
    [
      
'field' => 'id',
      
'conditions' => [
        [
'operator' => '>=''value' => 3],
        [
'operator' => '<''value' => 300],
      ],
    ],
    [
      
'field' => 'name',
      
'conditions' => [
        [
'operator' => 'NOT IN''value' => ['Berety''Rękawiczki']],
      ],
    ],
  ],
  
'perPage' => 2,
];

$ch curl_init();
curl_setopt($chCURLOPT_URL"https://{$domain}/api/v4/{$endpoint}");
curl_setopt($chCURLOPT_HTTPHEADER, ['Content-Type: application/json''Accept: application/json']);
curl_setopt($chCURLOPT_USERAGENT"{$shopEngineName} ({$pluginName})");
curl_setopt($chCURLOPT_USERPWD$shopName ':' $apiKey);

curl_setopt($chCURLOPT_POST1);
// for patch you'll need curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');

curl_setopt($chCURLOPT_POSTFIELDSjson_encode($requestData));

curl_setopt($chCURLOPT_RETURNTRANSFER1);

$responseData curl_exec($ch);
$responseCode curl_getinfo($chCURLINFO_HTTP_CODE);
curl_close($ch);

var_dump($responseCode);

if (
$responseCode === 200) {
    
var_dump(json_decode($responseDatatrue));
} else {
    
var_dump($responseData);
}

Skąd wziąć dane do logowania?
Panel sprzedawcy → Ustawienia sklepu → API
W jaki sposób się zalogować do API?
Każde żądanie musi posiadać nagłówek Authorization zgodny z Basic Auth, tak jak w przykładach.
Czy przy tworzeniu lub aktualizacji produktu muszę podawać wszystkie pola?
Nie, część pól ma domyślne wartości, zaś przy aktualizacji nie przesłane pola nie są zmieniane.
Jak działają akcje PATCH?
Zmieniają one wyłącznie pola które zostały przesłane. Jeżeli przesłany zostanie null (lub w niektórych przypadkach pusta tablica), to dana wartość zostanie ustawiona na null.
Skąd wziąć listę wszystkich kategorii i atrybutów
Należy użyć akcji POST /api/v4/categories/search i paginować po 100 kategorii. W każdej kategorii są wypisane dostępne w niej atrybuty. Każda kategoria może mieć inne atrybuty, np. `Odzież damska → Koszule` i `Odzież męska → Koszule` mają zupełnie oddzielnie zdefiniowane rozmiary.
Jak usuwać produkty?
Niestety jeszcze nie oferujemy pełnego usuwania produktów.
Najprościej jest użyć metody PATCH aby ustawić produkt jako nieaktywny.
Jeżeli wtyczka widzi tylko aktualną listę produktów i nie jest w stanie śledzić usuwania produktów w systemie sprzedawcy, to można:
  • zaktualizować wszystkie produkty;
  • upewnić się że wszystkie aktualizacje skończyły się kodem 200;
  • wywołać akcję products/deactivate/{timestamp-rozpoczęcia-aktualizacji}.
Co zrobić gdy produkt ma wiele rozmiarów i kolorów?
O grupach wariantowych dowiesz się więcej z sekcji pomocy.
Produkty które mają znaleźć się w grupie muszą mieć przypisaną kategorię i atrybuty.
Istnieją 2 sposoby:
  • utworzyć nieaktywną grupę wariantową, ustawiając w niej parametry (obrazkowe lub atrybutowe)
  • z odpowiedzi wyciągnąć id grupy wariantowej
  • tworzyć/aktualizować produkty ustawiając w nich variantGroupId
  • włączyć (metodą PATCH) grupę wariantową
  • w odpowiedzi sprawdzić czy grupa wariantowa jest poprawna (pole invalidationReason wynosi NULL)
  • utworzyć grupę wariantową, włączoną, ustawiając w niej parametry (obrazkowe lub atrybutowe) i produkty
  • w odpowiedzi sprawdzić czy grupa wariantowa jest poprawna (pole invalidationReason wynosi NULL)
Zachęcamy, by tworzone automatycznie grupy wariantowe miały nazwę auto_{id_grupy_w_sklepie_sprzedawcy}, dzięki temu powinno był łatwiej odnajdywać się podczas synchonizacji danych.
Jak dodać grupę wariantową oraz produkty do niej przez api?
Przykład w PHP z użyciem biblioteki Guzzle 6 <?php
use GuzzleHttp\Client;
use 
GuzzleHttp\Exception\ClientException;
use 
GuzzleHttp\Exception\ServerException;
use 
GuzzleHttp\RequestOptions;

// KONFIGURACJA INTEGRACJI
$shopName 'test_shop';
$apiKey '$2y$10$XCUXFm5gcunQ.tyqzNtfJLZ80.gbQwuzO7yvETu/z/xWebOy/kywExgnQOkB.';
$domain 'arena.pl';
$shopEngineName 'SuperShop v8.8';
$pluginName 'CUSTOM INTEGRATION v1.2';

// PRZYGOTOWANIE KLIENTA HTTP
$guzzle = new Client([
    
'base_uri' => 'https://' $domain '/api/v4/',
    
RequestOptions::HEADERS => [
    
'User-Agent' => "{$shopEngineName} ({$pluginName})",
    
'Accept' => 'application/json',
    ],
    
RequestOptions::AUTH => [$shopName$apiKey],
]);

// FUNKCJA WYKONUJĄCA TRZY PRÓBY WYSŁANIA REQUESTA, GDY DOSTANIEMY ODPOWIEDŹ 5xx
function wrapApiCall(callable $func)
{
    
$tries 3;
    for (
$i 1$i <= $tries$i++) {
        try {
            return 
$func();
        } catch (
ServerException $e) {
            if (
$i === $tries) {
                throw 
$e;
            }
            
sleep((** ($i)) * 1000);
        }
    }
}
// FUNKCJA TWORZĄCA GRUPĘ WARIANTOWĄ
function createVariantGroup(string $name, array $parametersClient $guzzle): int
{
    
$requestSearchData = [
        
"filters" => [
            [
                
"field" => "name",
                
"conditions" => [
                    [
                        
"operator" => "=",
                        
"value" => $name,
                    ],
                ],
            ],
        ],
    ];

    
$requestData = [
        
"name" => $name,
        
"parameters" => $parameters
    
];
    
$variantGroupId null;
    
$response wrapApiCall(function () use ($guzzle$requestSearchData) {
        return 
$guzzle->post("variantGroups/search", [RequestOptions::JSON => $requestSearchData]);
    });
    if (
json_decode($response->getBody(), true)['results']) {
        
$variantGroupId json_decode($response->getBody(), true)['results'][0]['id'];
        
$response wrapApiCall(function () use ($guzzle$variantGroupId$requestData) {
            return 
$guzzle->patch("variantGroups/{$variantGroupId}", [RequestOptions::JSON => $requestData]);
        });
        
$variantGroupId json_decode($response->getBody()->__toString(), true)['id'];
    } else {
        
$response wrapApiCall(function () use ($guzzle$requestData) {
            return 
$guzzle->post("variantGroups", [RequestOptions::JSON => $requestData]);
        });
        
$variantGroupId json_decode($response->getBody()->__toString(), true)['id'];
    }

    
var_dump("Dodanie grupy wariantowej {$name} powiodło się.");

    return 
$variantGroupId;
}

// FUNKCJA SPRAWDZAJĄCA POPRAWNOŚĆ GRUPY WARIANTOWEJ ORAZ AKTYWUJĄCA JĄ
function enableVariantGroupAndCheckIsValid(int $variantGroupIdClient $guzzle)
{
    
$response wrapApiCall(function () use ($guzzle$variantGroupId) {
        return 
$guzzle->patch("variantGroups/{$variantGroupId}", [RequestOptions::JSON => [
            
"enabled" => true
        
]]);
    });

    
$responseData json_decode($response->getBody()->__toString(), true);

    
$invalidationReason $responseData['invalidationReason'];
    if (
$response && is_null($invalidationReason)) {
        
var_dump("Aktywacja grupy {$responseData['name']} powiodła się");
    } else {
        
var_dump($invalidationReason);
    }
}

// FUNKCJA DODAJĄCA PRODUKT DO GRUPY WARIANTOWEJ
function addProductToVariantGroup(array $productClient $guzzle)
{
    try {
        
$response wrapApiCall(function () use ($product$guzzle) {
            return 
$guzzle->patch(
                
"products/bySellerProductId/{$product['sellerProductId']}",
                [
RequestOptions::JSON => $product]
             );
        });
    } catch (
ClientException $e) {
        if (
$e->getResponse()->getStatusCode() !== 404) {
            throw 
$e;
        }
        
$response wrapApiCall(function () use ($product$guzzle) {
            return 
$guzzle->post("products", [RequestOptions::JSON => $product]);
        });
    }

    
$responseData $response->getBody()->__toString();
    
$productId json_decode($responseDatatrue)['id'];

    
var_dump("Dodanie produktu o id {$productId} powiodło się");
}

// STWORZENIE PRODUKTU
function createProductData(
    
string $name,
    
string $sellerProductId,
    
string $imgageUrl,
    
int $variantThumbnail,
    
int $attribiuteValueId,
    
int $variantGroupId
): array {
    return [
        
"sellerProductId" => $sellerProductId,
        
"name" => $name,
        
"availability" => 1,
        
"quantity" => 100,
        
"price" => 1250,
        
"productImages" => [
            [
                
"url" => $imgageUrl,
                
"isThumbnail" => true,
                
"position" => 0,
            ],
        ],
        
"categoryId" => 8503// BODY
        
"attributes" => [
            [
                
"id" => 20191// ROZMIAR
                
"valueId" => $attribiuteValueId,
            ],
        ],
        
"variantGroupId" => $variantGroupId,
        
"variantThumbnail" => $variantThumbnail,
    ];
}

// PARAMETRY GRUPY WARIANTOWEJ
$sellerProductId "YY";
$parameters = [
    [ 
// GRUPOWANIE PO OBRAZKU
        
"type" => "Thumbnail",
        
"attributeId" => null,
    ],
    [ 
// GRUPOWANIE PO ROZMIARZE
        
"type" => "Attribute",
        
"attributeId" => 20191,
    ],
];

// DODANIE GRUPY WARIANTOWEJ
$variantGroupId createVariantGroup("auto_{$sellerProductId}"$parameters$guzzle);

// DODANIE PRODUKTÓW DO GRUPY WARIANTOWEJ
addProductToVariantGroup(createProductData(
    
"Body z samochodem. Niebieskie. Rozmiar - 56",
    
"{$sellerProductId}_1",
    
"https://e-kidi.pl/13014-medium_default/nicola-body-autko-dlugi-rekaw-11610-11-b-56-68.jpg",
    
1// $variantThumbnail - identyfikator po, ktorym będą łączone zdjęcia. Gdy go brak tej wartości, to jest ona nadawana automatycznie na podstawie image url.
    
34309// $attribiuteValueId - id rozmiaru 56
    
$variantGroupId
), $guzzle);

addProductToVariantGroup(createProductData(
    
"Body z samochodem. Niebieskie. Rozmiar - 68",
    
"{$sellerProductId}_2",
    
"https://e-kidi.pl/13015-medium_default/nicola-body-autko-dlugi-rekaw-11610-11-b-74-86.jpg",
    
1// obrazek jest inny niż poprzedni, ale przedstawia ten sam produkt (mniejszy rozmiar, ma inne zapięcie)
    
34313// rozmiar 68
    
$variantGroupId
), $guzzle);

addProductToVariantGroup(createProductData(
    
"Body z samochodem. Brązowe. Rozmiar - 74",
    
"{$sellerProductId}_3",
    
"https://e-kidi.pl/13019-medium_default/nicola-body-autko-dlugi-rekaw-11610-22-b-74-86.jpg",
    
2,
    
30209// rozmiar 74
    
$variantGroupId
), $guzzle);

addProductToVariantGroup(createProductData(
    
"Body z samochodem. Brązowe. Rozmiar - 86",
    
"{$sellerProductId}_4",
    
"https://e-kidi.pl/13019-medium_default/nicola-body-autko-dlugi-rekaw-11610-22-b-74-86.jpg",
    
2,
    
33024,
    
$variantGroupId
), $guzzle);

addProductToVariantGroup(createProductData(
    
"Body z samochodem. Brązowe. Rozmiar - 62",
    
"{$sellerProductId}_6",
    
"https://e-kidi.pl/13018-medium_default/nicola-body-autko-dlugi-rekaw-11610-22-b-56-68.jpg",
    
2,
    
34311// rozmair 62
    
$variantGroupId
), $guzzle);

addProductToVariantGroup(createProductData(
    
"Body z samochodem. Niebieskie. Rozmiar - 62",
    
"{$sellerProductId}_5",
    
"https://e-kidi.pl/13014-medium_default/nicola-body-autko-dlugi-rekaw-11610-11-b-56-68.jpg",
    
1,
    
34311// rozmiar 62
    
$variantGroupId
), $guzzle);

// SPRAWDZENIE POPRAWNIOŚCI UTWORZONEJ GRUPY WARIANTOWEJ I JEJ AKTYWACJA
enableVariantGroupAndCheckIsValid($variantGroupId$guzzle);
widok stworzonej grupy w panelu: widok stworzonej grupy w serwisie:
Jak robić requesty przez stronę z dokumentacją?
  • Należy kliknąć na dowolną kłódkę po prawej stronie.
  • Wpisać nazwę sklepu i klucz API.
  • Zamknąć okienko, NIE klikać na przycisk Logout.
  • Kliknąć na np. GET /api/v4/categories/{id}, tak aby wysunęły się szczegóły danej akcji.
  • Kliknąć po prawej Try it out
  • Wpisać 1 w pole Category Id
  • Kliknąć Execute
  • Poniżej można obejrzeć przygotowane poleceniu Curl odpowiadające wykonanemu żądaniu
  • Poniżej można obejrzeć wynik
Jak obserwować najnowsze zmiany w zamówieniach?
Zachęcamy do wyszukania wszystkich zamówień utworzonych w minionym miesiącu używając POST orders/search. Aktualnie NIE można polegać na polu updatedAt w modelu ORDER.
Co zrobić gdy serwer zwraca błąd?

Błędy o kodzie z zakresu 500-599 to błędy po stronie serwera, w przypadku ich napotkania warto kilka razy jest powtórzyć request później.

Błąd 429 Too Many Requests oznacza, że wykonano zbyt wiele zapytań do API Arena.pl lub realizacja zapytań była zbyt czasochłonna. Wtedy należy poczekać chwilę przed wykonaniem kolejnych żądań.
Ilość dozwolonych zapytań zmienia się w zależności od obciążenia serwisu.
W przypadku prostych requestów (np. GET/PATCH pojedynczego produktu) zalecany bezpieczny limit to 15 requestów na sekundę.

Inne błędy o kodzie z zakresu 400-499 to błędy związane z nieprawidłowym zapytaniem, np. problemy z autoryzacją lub formatem przesłanych pól - nie należy ich ponawiać, trzeba najpierw rozwiązać dany problem.

Warto zobaczyć przykład z grupami wariantowymi.

Jakie są różnice pomiędzy APIv3 i v4?
Zmiany:
  • są nowe metody dostawy;
  • metody PUT zostały zastąpione metodami PATCH;
  • jest nowy format zamówień: pozycje są pogrupowane w paczki;
  • perPage jest pisane zawsze przy użyciu camel-case;
  • w modelu Product pole variantGroupId przyjmuje id grupy wariantowej z serwisu ARENA.pl, wcześniej to był id grypy wariantowej w sklepie sprzedawcy.
Dodatkowo dostępne są nowe funkcjonalności:
  • możliwość przypisywania kategorii i atrybutów do produktu;
  • tworzenie grup wariantowych i przypisywanie do nich produktów;
  • endpointy /products/bySellerProductId/{sellerProductId};
  • w dokumentacji oznaczyliśmy pola tylko do odczytu (zamiast osobnych modeli np. Product i PostProduct).
Autoryzacja i większość 'modeli' pozostały takie same.
Jakie są różnice pomiędzy APIv1/v2 i v4?
APIv3 zostało napisane od nowa, więc zmieniło się wszystko. Sklep musi sobie także skonfigurować nowe metody dostawy.
Dlaczego kategoria produktu nie zmienia się na przesłaną nową wartość?
W Panelu Sprzedawcy istnieje możliwość zablokowania zmiany kategorii dla danego produktu w celu zapobiegania błędnemu nadpisywaniu przez API np. podczas automatycznego importu z systemów sklepowych. Przesyłany wówczas parametr categoryId jest ignorowany, a odpowiedzią na operację PATCH jest faktyczny stan produktu po aktualizacji.