Obsługa API Allegro w PHP
UWAGA! Artykuł poniższy ma status „w budowie” co oznacza, że jest w trakcie pisania / weryfikowania.
Allegro to najpopularniejsza platforma sprzedażowa w Polsce, działająca od 1999 roku. Liczba użytkowników serwisu to ponad 18 milionów z czego ponad 13 milionów aktywnych. Większość firm prowadzących sprzedaż on-line oferuje swoje usługi na platformie.
W poniższym artykule przyjrzymy się bliżej API Allegro, które na przestrzeni lat przechodziło wiele dosyć drastycznych zmian. Aktualna wersja wydaje się być w końcu tą ostateczną, choć wciąż jest intensywnie rozwijana.
Środowiska
Allegro udostępnia dwa środowiska:
Środowisko testowe (sandbox)
Aby z niego korzystać musimy założyć konto na https://allegro.pl.allegrosandbox.pl/rejestracja. Następnie dodajemy nową aplikację pod adresem https://apps.developer.allegro.pl.allegrosandbox.pl/new. Dostęp do zasobów Allegro API poprzedzony jest zaś przedrostkiem https://api.allegro.pl.allegrosandbox.pl/. W przypadku sandbox-a, korzystanie z niego nie wymaga włączenia dwustopniowego logowania.
Środowisko produkcyjne
Po przetestowaniu aplikacji możemy przejść na środowisko produkcyjne. Oczywiście musimy mieć konto na https://allegro.pl/rejestracja. Aplikację dodajemy pod adresem https://apps.developer.allegro.pl/., a metody wywołujemy podając przedrostek https://api.allegro.pl/.
Autoryzacja
Korzystanie z API wymaga autoryzacji kluczem i włączonego dwustopniowego logowania na koncie. Po udanym „logowaniu” (autoryzacji) pobieramy specjalny klucz (token), którym będziemy posługiwać się do pobierania danych z Allegro. Token jest ważny określony czas (np. 12 godzin) i musimy go odświeżać zanim straci ważność.
W pierwszej kolejności musimy utworzyć aplikację w Allegro (linki powyżej wopisie środowisk).
Podajemy unikatową nazwę, rodzaj aplikacji i w zależności od wybranej rodzaju – ścieżkę do niej. Na koniec ustawiamy uprawnienia, czyli metody z jakich będzie mógł korzystać nasz program. Uprawnienia są jasne.
Przyjrzyjmy się rodzajowi aplikacji. Mamy dwa podstawowe – z dostępem do przeglądarki i bez dostępu (aplikacja konsolowa).
Autoryzacja z dostępem do przeglądarki
Udostępniamy specjalny link wskazujący na serwis Allegro w którym wskazujemy dane i adres naszej aplikacji. Użytkownik wchodzi w link i potwierdza. Po zatwierdzeniu aplikacji, Allegro wysyła tymczasowy token pod wskazany adres w ścieżce (kolejne pole). Pod tym adresem musi znaleźć się skrypt, który pobierze wspomniany (tymczasowy) token i wygeneruje nowy.
Autoryzacja bez dostępu do przeglądarki
Pobieramy z Allegro link, który następnie przekazujemy użytkownikowi w celu potwierdzenia autoryzacji. Następnie generujemy token, który będziemy wykorzystywać w trakcie korzystania z aplikacji.
Pozostałe rodzaje autoryzacji
Allegro udostępnia jeszcze dwie metody logowania. Jedna służy do pobierania publicznych danych, druga zaś do tworzenia instancji naszej aplikacji.
Wszystko to jest całkiem nieźle napisane w oficjalnym artykule Allegro – Uwierzytelnianie i autoryzacja. Podany jest również kod w PHP i Pythonie.
Po utworzeniu aplikacji pobierzmy odpowiednie dane i zapiszmy je w pliku konfiguracyjnym związanym z danym kontem Allegro
return [
'client_id' => 'XXX',
'client_secret' => 'XXX'
];
Pobieranie danych z Allegro
Po autoryzacji otrzymujemy token dostępowy, którym będziemy się posługiwać do wywoływania API Allegro. Należy pamiętać, aby odświeżać go co pewien czas, gdyż inaczej wygaśnie po 12 godzinach. Allegro obsługuje kilka sposobów pobierania danych GET
, POST
, PUT
, PATCH
i DELETE
. Są to standardy HTTP.
Generalna różnica między nimi jest taka, że przy użyciu GET
tworzymy po prostu link z danymi. Przykładowo w WordPress przy edycji artykuły w pasku przeglądarki jest na końcu adresu ?post=102354&action=edit
co oznacza wysłanie dwóch parametrów o nazwie post
i action
. Oba mają podane wartości (102354
i edit
). Ma ona również ograniczenie w długości wysyłanych danych (link nie może być „nieskończony” – najpewniej przyjąć limit 2000 znaków, choć według standardu minimum to 8000 znaków)
W przypadku pozostałych zapytań wysyłane dane są „ukryte” (możemy podejrzeć je korzystając z „narzędzi dla webmasterów”. Ich rola sprowadza się do nazewnictwa – POST
służy do pobierania danych, PUT
i PATCH
do aktualizacji, DELETE
zaś do usuwania.
Zdecydowana liczba innych API (szczególnie starszych) udostępnia tylko dwie metody do operowania na danych: GET
(pobieranie) i POST
(tworzenie, zmiana).
cURL
cURL
jest bardzo popularną biblioteką do obsługi komunikacji między klientem, a serwerem. Korzysta z niej wiele programów i języków. Pozwala na wysyłanie i odbieranie danych, wysyłanie plików, nawiązywanie połączenia itd.
Korzystanie z niej w PHP jest względnie proste. Spójrzmy na przykład wysyłania danych metodą POST
.
$curl = curl_init();
curl_setopt_array ($curl, [
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => $header,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $fields
]);
$result = curl_exec ($curl);
curl_close ($curl);
return $result;
Na początek inicjujemy sesję cURL
, później ustawiamy i wysyłamy zapytanie do serwera. W ustawieniach mamy:
URL
– adres pod który ma być wysłane zapytanieHTTPHEADER
– nagłówek w formie tablicy dołączany do zapytania (ustawiamy w nim np. token)SSL_VERIFYPEER
– sprawdzenie certyfikatu SSL. Problemy z tym parametrem są związane z brakiem aktualnej bazy certyfikatów na serwerze.RETURNTRANSFER
– czy ma zostać zwrócona odpowiedźPOST
– ustawienie metody POST (domyślnie jest GET)POSTFIELDS
– wysyłane dane – w przypadku Allegro muszą być zakodowane w formacieJSON
np. za pomocą funkcjijson_encode
(wyjątkiem jest tutaj proces autoryzacji).
Allegro wysyła odpowiedzi w formacie JSON
, które musimy „rozpakować” przy pomocy funkcji json_decode
.
W przypadku metody GET
nie uzupełniamy dwóch ostatnich pól, a parametry przekazujemy w przygotowanym adresie URL.
if ( !empty($get) ) $url .= '?'.http_build_query ($get);
Jeżeli są podane parametry (w tablicy) to dodaj do adresu znak zapytania, a po nim odpowiednio sformatowane parametry przy użyciu funkcji http_build_query
.
Dla pozostałych typów PUT
, PATCH
, DELETE
korzystamy z niestandardowego ustawienia, a parametry podajemy w POSTFIELDS
//...
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => $fields
]
Stwórzmy kod do pobierania danych, którym będziemy posługiwać się w dalszej części poradnika.
define ('ALLEGRO_HTTP_GET', 1);
define ('ALLEGRO_HTTP_POST', 2);
define ('ALLEGRO_HTTP_PUT', 3);
define ('ALLEGRO_HTTP_PATCH', 4);
define ('ALLEGRO_HTTP_DELETE', 5);
define ('ALLEGRO_API_URL', 'https://api.allegro.pl.allegrosandbox.pl/');
define ('ALLEGRO_CODE_URL', 'https://allegro.pl/auth/oauth/device');
define ('ALLEGRO_TOKEN_URL', 'https://allegro.pl/auth/oauth/token');
class Allegro {
private $config;
function _construct($config) {
$this->config = $config;
}
function request($type, $method, $params = null, $beta = false) {
if ($beta) {
$header => [
'Accept: application/vnd.allegro.beta.v1+json',
'Accept-Language: pl-PL',
'Authorization: Bearer '.$this->config['token'],
'Content-Type: application/vnd.allegro.beta.v1+json'
];
} else {
$header => [
'Accept: application/vnd.allegro.public.v1+json',
'Accept-Language: pl-PL',
'Authorization: Bearer '.$this->config['token'],
'Content-Type: application/vnd.allegro.public.v1+json'
];
}
$curl = curl_init();
curl_setopt_array ($curl, [
CURLOPT_URL => ALLEGRO_API_URL . $method,
CURLOPT_HTTPHEADER => $header,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_RETURNTRANSFER => true,
]);
if ($type == ALLEGRO_HTTP_GET) {
if ($params != null) curl_setopt ($url.'?'.http_build_query ($get) );
} else {
curl_setopt( $curl, CURLOPT_POST, json_encode ($params) );
if ($params != null) curl_setopt( $curl, CURLOPT_POSTFIELDS, json_encode ($params) );
if ($type != ALLEGRO_HTTP_POST) {
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $type);
}
}
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($result === false || $code >= 400) {
throw new Exception ("$code $result");
}
$result = curl_exec ($curl);
curl_close ($curl);
return json_decode ($result);
}
function get($method, $params = null, $beta=false) {
return request(ALLEGRO_HTTP_GET, $method, $params, $beta);
}
function post($method, $params = null, $beta=false) {
return request(ALLEGRO_HTTP_POST, $method, $params, $beta);
}
function put($method, $params = null, $beta=false) {
return request(ALLEGRO_HTTP_PUT, $method, $params, $beta);
}
function patch($method, $params = null, $beta=false) {
return request(ALLEGRO_HTTP_PATCH, $method, $params, $beta);
}
function delete($method, $params = null, $beta=false) {
return request(ALLEGRO_HTTP_DELETE, $method, $params, $beta);
}
}
W powyższym kodzie utworzyliśmy jedną uniwersalną metodę request do obsługi wszystkich zapytań. Dane (za wyjątkiem GET) są kodowane na format JSON przed wysłaniem i odbierane również w tym formacie – dekodujemy je. Dodatkowo dodaliśmy obsługę błędów oraz przenieśliśmy część konfiguracji na zewnątrz klasy (dodajemy ją w konstruktorze). Niektóre metody w Allegro są udostępniane w wersji beta i aby z nich skorzystać musimy zmodyfikować nieco nagłówek (elementy Accept i Content-Type).
W nagłówkach brakuje po jeszcze jednym wierszu, który dodamy później jak zdobędziemy token dostępowy – parametru Authorization
.
Tokeny
Jak wspomniałem, do korzystania z API musimy najpierw utworzyć aplikację, a następnie pobrać link do autoryzacji i wysłać go użytkownikowi (w modelu bez dostępu do przeglądarki)
Moglibyśmy rozszerzyć metodę request, ale ze względu na większą ilość różnic, skomplikuje to nieco całą operację. Napiszmy nową funkcję request
function requestAuth($url, $params) {
$auth = base64_encode($this->config['clientId'] . ':' . $this->config['client_secret']);
$header => [
"Authorization: Basic {$auth}",
"Content-Type: application/x-www-form-urlencoded"
];
$curl = curl_init();
curl_setopt_array ($curl, [
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => $header,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $params
]
);
$result = curl_exec ($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($result === false || $code != 200) {
throw new Exception ("$code $result");
}
curl_close ($curl);
return $result;
}
function code() {
$params = [
'client_id' => $this->config['client_id'];
];
return $this->requestAuth (ALLEGRO_CODE_URL, $params);
}
Po wywołaniu metody code
, otrzymamy w odpowiedzi m.in. link oraz kod urządzenia (device_code
). Musimy wejść w link, potwierdzić i pobrać token, którym będziemy posługiwać się korzystając z API.
function accessToken ($device_code) {
$params = [
'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
'device_code' => $device_code
];
$result = $this->requestAuth (ALLEGRO_TOKEN_URL, $params);
return $result;
}
function refreshToken ($refresh_token) {
$params = [
'grant_type' => 'refresh_token',
'refresh_token' => $refresh_token
];
$result = $this->requestAuth (ALLEGRO_TOKEN_URL, $params);
return $result;
}
Wystawianie aukcji
W zeszłym roku Allegro wprowadziło obowiązek korzystania z katalogu produktów przy wystawianiu – dla większości kategorii. Wiąże się to z tym, że aby wystawić ofertę, musimy najpierw znaleźć istniejący produkt lub dodać nowy, a dopiero pod niego podpiąć aukcję. Nie dotyczy to wszystkich kategorii, ale zdecydowanej większości.
Wyszukiwanie produktu w katalogu
Do wyszukiwania produktów w katalogu służy zasób /sale/products
. Nie będę szczegółowo omawiał tej funkcji, dla ułatwienia napiszemy tylko wyszukiwanie po numerze EAN, ISBN, UPC.
function getProductByGTIN ($gtin) {
$params = [
'mode' => $gtin
];
return $this->get ('sale/products', $params);
}
Wystawianie produktu
Do wystawiania produktu służy zasób /sale/product-offers
. Jeżeli produkt istnieje to możemy się pod niego podpiąć podając numer produktu lub kod GTIN
.
function addProduct ($product) {
$params = [
'external' => [
'id' => $product['id']
],
'name' => $product['title'],
'productSet' => [
'product' => $product['name'],
],
'payments' => [
'invoice' => 'VAT'
],
'stock' => [
'available' => $product['quantity'],
'unit' => 'UNIT'
],
'delivery' => [
'handlingTime' => 'PT24H',
'shippingRates' => $product['shipping_rates']
],
'publication' => [
'status' => 'ACTIVE',
],
'afterSalesServices' => [
'impliedWarranty' => [
'id' => $product['impliedWarrantyId']
],
'returnPolicy' => [
'id' => $product['returnPolicyId'],
],
'warranty' => [
'id' => $product['warrantyId']
]
]
];
if ( isset($product['category_id']) ) $params['productSet']['category']['id'] = $product['category_id'];
if ( isset($product['parameters']) ) $params['productSet']['parameters'] = $product['parameters'];
if ( isset($product['allegro_product_id']) ) $params['productSet']['id'] = $product['allegro_product_id'];
return $this->post('sale/offers', $params);
}
Jest to tylko wycinek (pól jest więcej). Omówimy najważniejsze pola:
external id
– nasz własny kod produktu (maksymalnie 100 znaków) – nie jest widoczny dla kupującegoname
– tytuł aukcjiproductSet
– tablica z danymi produktu – nazwa, kategoria, parametry, zdjęcia itd. Uzupełniając poleid
(czyli identyfikator produktu) nie musimy ich podawać. W tym polu można również podać kod GTIN / MPN, ale najpierw należy wskazać to w poluidType
.payments invoice
– czyli faktura – możliwe wartości toVAT
,VAT_MARGIN
,WITHOUT_VAT
,NO_INVOICE
stock available
– stan produktushippingRates
– identyfikator metod dostawy, które utworzymy / pobierzemy osobnymi metodamiafterSalesServices
– gwarancje, polityka zwrotów, reklamacje – podobnie jak z metodami dostawypublication status
– czy oferta ma być aktywna po wystawieniu (możemy na przykład dodatkowo weryfikować aukcję po wystawieniu i dopiero jak wszystko jest OK to ją aktywować)
Jak już wspomniałem – pól jest więcej. Za pomocą tej metody możemy dodać np. załącznik do wystawianej aukcji czy też powiązać produkt. Produkt możemy wystawiać, pobierać, zmieniać i wykonywać na nim różne inne operacje. Z tego powodu najlepiej jest utworzyć osobną klasę, która będzie go reprezentowała – opisze jego strukturę.
Cenniki dostawy
Aby móc wystawić produkt musimy utworzyć cennik dostawy. Możemy to zrobić w Allegro, albo skorzystać z API.