Kluczową kwestią jest to, że wszelkie operacje związane z logiką działania danej usługi są wykonywane na zdalnym serwerze, w naszym przypadku jest to serwer Allegro. W samym pisaniu kodu nie robi to różnicy, ale przy projektowaniu aplikacji nie można o tym zapomnieć - oznacza to, że nie mamy wpływu na samą bibliotekę. Trzeba też pamiętać, że pod każdym wywołaniem metody WebAPI w rzeczywistości kryje się cały proces komunikacji klient-serwer, musimy zatem uwzględnić czas potrzebny na przesłanie danych (co nierzadko, szczególnie przy dużych porcjach, stanowi czas o wiele większy, niż same operacje na tych danych).
Ostatnią rzeczą, o którą musimy się zatroszczyć przed rozpoczęciem pracy z Allegro WebAPI jest nasz klucz WebAPI. Będzie on nam potrzebny praktycznie w każdej linijce kodu, w której kierujemy jakieś zapytanie do serwera Allegro – to nasz klucz do całej usługi. Klucz jest przypisany do naszego konta i jest niezmienny (posiada czas ważności, ale nie ulega zmianie po przedłużeniu). Aby otrzymać nasz klucz należy skorzystać z formularza na stronie Allegro pod adresem
http://www.allegro.pl/contact/contact.php?topic=288 i czekać na maila zwrotnego od obsługi serwisu. Nie powinno to trwać zbyt długo, zazwyczaj kilkanaście minut, jednak należy mieć na uwadze godziny pracy i dni wolne: jeśli zgłosimy się po klucz w piątek wieczorem, to klucz dostaniemy w poniedziałek późnym rankiem.
Organizacja kodu
Nie chcę nikomu narzucać stylu programowania, ani tym bardziej samego kodu, ale myślę, że pewne rzeczy, nawet jeśli nie będą koniecznie w takim kształcie wykorzystane, mogą się okazać pomocne. Pierwszy aspekt, o który warto zadbać od razu to dostosowanie kodu do naszych potrzeb. Pamiętajmy, że dostępne biblioteki do obsługi SOAP nie dostosowują się do żadnej konkretnej usługi, jedynie umożliwiają korzystanie z niej. Dlatego warto samemu stworzyć chociaż najbardziej podstawowe udogodnienia. WebAPI w bardzo wielu miejscach wymaga użycia stałych wartości, więc dobrze jest mieć je posegregowane i ponazywane odpowiednio. Jako iż przykłady kodu będą prezentowane z użyciem PHP5, proponuję stworzyć sobie prostą (na razie) klasę dziedziczącą po SoapClient (o kwestiach związanych z konkretnymi elementami kod później), która zautomatyzuje wykonywanie niektórych czynności. Do klasy tej z czasem pewnie zaczniemy dodawać kolejne funkcjonalne elementy, jednak na początek ograniczę się tylko do prostej konstrukcji:
- class AllegroWebAPISoapClient extends SoapClient
- {
- // jedynka to kod kraju dla Polski, o tym dalej
- const COUNTRY_PL = 1;
-
- const QUERY_ALLEGROWEBAPI = 1;
-
- public function __construct()
- {
- parent::__construct('http://webapi.allegro.pl/uploader.php?wsdl');
- }
- }
Podobnie pracować możemy w przypadku języków kompilowanych - na przykład po wygenerowaniu kodu wyjściowego przez program gSOAP możemy dodać definicje stałych do jego plików nagłówkowych.
Druga sprawa to organizacja naszych danych dostępowych. Najwygodniej jest je trzymać w osobnym pliku konfiguracyjnym, albo nagłówkowym, bądź też na początku pliku z kodem wykonawczym. Najlepiej, aby były to stałe lub jeśli mamy zamiar wczytywać dane w czasie pracy programu - zmienne pogrupowane na przykład w formie tablicy asocjacyjnej. Przyjmijmy, że w dalszej części poradnika będziemy korzystać z następującego nazewnictwa:
- define('ALLEGRO_LOGIN', 'login');
- define('ALLEGRO_PASSWORD', 'hasło');
- define('ALLEGRO_KEY', 'klucz WebAPI');
- define('ALLEGRO_COUNTRY', AllegroWebAPISoapClient::COUNTRY_PL);
Obsługa błędówZanim przejdziemy do kolejnych etapów, ostatnim elementem dotyczącym podstaw komunikacji z samym WebAPI, a także sposobem tworzenia dobrego kodu jest obsługa błędów. Usługa Allegro wykorzystuje do tego SOAP w zakresie wyjątków. Dobrze jest zawczasu zaopatrzyć nasz kod w obsługę wyjątków, gdyż są one tutaj wyrzucane przy najmniejszych problemach/potknięciach (co jest zrozumiałe, gdyż taka, a nie inna filozofia działania usług sieciowych uniemożliwia nam ingerencje w przebieg procesu po stronie serwera, a więc o najmniejszych nieprawidłowościach chcemy być informowani - naprawdę chcemy). Błędy zwracane za pośrednictwem SOAP będą w przypadku wielu środowisk propagowane od najniższej warstwy obsługi tego protokołu bez żadnego obsłużenia aż do naszego kodu, tak więc jeśli nie będziemy pilnować obsługi wyjątków, nasza aplikacja będzie się "wykrzaczać" przy każdym potknięciu niezależnie, czy to naszym, czy po stronie Allegro, czy też "natury rzeczy martwych". O samą obsługę błędów będziemy się martwić w konkretnych przypadkach, a filozofia obsługi wyjątków to temat rozległy na kilka ładnych tomów projektowania oprogramowania, więc tutaj go wykładać nie będę. Przestawię za to podstawową składnie, która będzie potrzebna do obsługi nieprzewidzianych sytuacji (na razie pomińmy istotę samych metod WebAPI):
- try
- {
- $client = new SoapClient('http://webapi.allegro.pl/uploader.php?wsdl');
- $version = $client->doQuerySysStatus(1, 1, 'klucz WebAPI');
- $session = $client->doLoginEnc('login', base64_encode( hash('sha256', 'hasło', true) ), 1, 'klucz WebAPI', $version['ver-key']);
- }
- catch(SoapFault $error)
- {
- echo 'Błąd ', $error->faultcode, ': ', $error->faultstring, "n";
- }
Jak widać błędy wyrzucane są jako obiekty klasy
SoapFault. Konkretne kody błędów, czyli wartości pola
$error->faultcode widoczne są w dokumentacji przy każdej metodzie WebAPI. Pole to zawiera łańcuch znaków, a więc nie należy szukać wartości liczbowych odpowiednich kodów – jeżeli na przykład podamy błędny klucz dostępu do WebAPI otrzymamy dokładnie
ERR_WEBAPI_KEY.
Pierwsze kroki
PołączenieNo dobra, czas na pierwsze kroki – patrzcie uważnie i powtarzajcie za mną. Ten krok może się różnić najbardziej w przypadku różnych środowisk ze względu właśnie na to, że mają one różnie zrealizowaną obsługę SOAP i WSDL. Na przykład w
C++ możemy wygenerować sobie gotową bibliotekę za pomocą oprogramowania
gSOAP:
wsdl2h -o allegro.h http://webapi.allegro.pl/uploader.php?wsdl
soap2cpp allegro.hTutaj mała uwaga dotycząca powyższego polecenia – aby wykonać je w dokładnie takiej postaci jak powyżej, musimy skopiować do swojego katalogu plik
stlvector.h, który jest dostarczany razem z oprogramowanie gSOAP.
A jak to będzie wyglądać w naszym przykładowym PHP? Tutaj wszystko dzieje się w czasie wykonywania (runtime), jako że PHP jest językiem bardzo dynamicznym. Do obsługi Allegro WebAPI posługiwać się będziemy klasą
SoapClient, która posiada również obsługę WSDL, a w użyciu jest bardzo prosta – sprowadza się to do jednej linijki kodu:
- $client = new SoapClient('http://webapi.allegro.pl/uploader.php?wsdl');
Jeśli dobrze popatrzymy, to zauważymy, że dokładnie taki kod jest w konstruktorze naszej klasy
AllegroWebAPISoapClient. Dlatego aby uczynić nasz kod bardziej czytelnym przyjmijmy, że używamy wszędzie już naszych własnych narzędzi:
- $client = new AllegroWebAPISoapClient();
Klucz wersjiSkoro tak już nam dobrze idzie to od razu przejdziemy do wywoływania metod Allegro WebAPI. Szczególnie takiej jednej, co to ją często wywoływać będziemy. Chodzi o metodę
doQuerySysStatus i jej "hurtowy" odpowiednik
doQueryAllSysStatus - służą one do pobierania informacji o aktualnych wersjach komponentów Allegro WebAPI. Aby pobrać aktualny numer wersji za pomocą pierwszej z nich musimy określić, o jaki serwis nam chodzi i podać pole, o które pytamy:
- $version = $client->doQuerySysStatus(AllegroWebAPISoapClient::QUERY_ALLEGROWEBAPI, ALLEGRO_COUNTRY, ALLEGRO_KEY);
Kod kraju to identyfikator serwisu, do którego chcemy się połączyć. Za pośrednictwem Allegro WebAPI mamy dostęp nie tylko do serwisu allegro.pl, ale również do wszystkich innych serwisów pokrewnych takich jak aukro.cz (Czechy, kod 56) i wiele innych. Jedyne co musimy zmienić w naszym kodzie, aby z nich korzystać, to właśnie identyfikator kraju, a cała reszta nie zmienia się ani trochę. Aby pobrać kody wszystkich dostępnych krajów należy posłużyć się metodą doGetCountries.
Ramka 2: Kod kraju
|
Pierwszy parametr to element, o który pytamy:
1. wersja samego WebAPI,
2. wersja WSDL,
3. wersja spisu kategorii,
4. wersja formularza sprzedaży i jego pól,
5. informacje o stronie.
Drugi parametr to numer serwisu (kraju) – spis wszystkich dostępny jest w dokumentacji, a my będziemy korzystać raczej tylko z wartości, która oznacza Polskę (Allegro.pl). Ostatnim parametrem jest nasz klucz WebAPI.
Zwrócone zostają dwie wartości – wersja zadanego elementu, oraz klucz wersji, który będzie potrzebny do dalszych operacji. Dane te przechowywane są w zmiennej
$version jako pola tablicy asocjacyjnej -
$version['info'] to wersja danego elementu w formacie
X.Y.Z natomiast potrzebny nam klucz wersji to
$version['ver-key'] (ma on postać liczbową).
W przypadku zapytania drugą metodą otrzymujemy wersje wszystkich komponentów ze wszystkich serwisów (jednak nadal musimy podawać swój identyfikator kraju):
- $versions = $client->doQueryAllSysStatus(ALLEGRO_COUNTRY, ALLEGRO_KEY);
Zapytanie to zwraca tablicę obiektów, z których każdy zawiera identyfikator kraju, poszczególne wersje, oraz klucz wersji. Nie będę tutaj opisywał wszystkich pól, gdyż nie jest to aż tak istotne, jednak możemy zobaczyć, w jaki sposób dostać się do pól składowych obiektów. Oto jak wypisać klucze wersji wszystkich dostępnych serwisów:
- foreach($versions as $version)
- {
- echo $version->{'country-id'}, ': ', $version->{'ver-key'}, "n";
- }
Po co nam w ogóle klucz wersji? Jego wartość zmienia się za każdym razem, kiedy zmienia się wersja któregokolwiek z elementów wymienionych przedtem (w praktyce zmieniają się wersje jedynie drzewa kategorii i pól formularza sprzedaży). Ponieważ klucz ten jest potrzebny do niemal każdej operacji przez WebAPI, jeśli nie będziemy znali jego aktualnej wartości, nie zrobimy praktycznie nic. W momencie przydzielania nam klucza WebAPI otrzymujemy również klucze wersji wszystkich serwisów. Gdy wartość klucza serwisu, w którym się poruszamy ulegnie zmianie nie będziemy mogli korzystać z WebAPI do czasu, aż go nie uaktualnimy. Błąd klucza zostanie nam zgłoszony w kodzie jako wyjątek, a o tym w jaki sposób sobie z nimi radzić napiszę w dalszej części.
LogowanieCzas przejść do konkretnych działań – logowanie się do systemu jest chyba dość konkretne na początek. Do tego służy metoda
doLogin, która jest dość prosta w użyciu:
- $session = $client->doLogin(ALLEGRO_LOGIN, ALLEGRO_PASSWORD, ALLEGRO_COUNTRY, ALLEGRO_KEY, $version['ver-key']);
Kolejne parametry są raczej jasne, więc tłumaczyć nie będę. Ostatni parametr to opisywany powyżej klucz wersji. Zwrócone zostają nam dwie dane -
$session['session-handle-part'] to klucz sesji (kolejna ważna zmienna, wszystkie operacje wymagające logowania będziemy potwierdzać właśnie kluczem sesji),
$session['server-time'] - aktualny czas z punktu widzenia serwera Allegro (w razie potrzeby możemy zawsze pobrać aktualny czas również wywołaniem
doGetSystemTime). Jest jeszcze trzecie pole, ale nie jest one wykorzystywane na obecną chwilę.
Funkcja skrótu - algorytm generujący krótki ciąg znaków, w dużym stopniu jednoznaczny (trudno o wygenerowanie takiego samego skrótu dla różnych danych), a odczytanie informacji ze skrótu jest niemożliwe. SHA256 - 256-bitowa funkcja skrótu. Na chwilę obecną nie znaleziono dla niej kolizji, a jej długość powoduje, że nawet metody brute-force takie jak tęczowe tablice są praktycznie nieskuteczne. Base64 - mechanizm kodowania danych w taki sposób, aby były one zapisane wyłącznie znakami alfanumerycznymi oraz +, - i =. Jest to szczególnie przydatne w przypadku transmisji binarnych danych, które zawierają wiele niedozwolonych dla danego protokołu symboli.
Ramka 3: Kodowanie i szyfry
|
Jak jednak widać na pierwszy rzut oka taki kod rodzi pewien niepokój u każdego, kto chociaż raz stracił hasło do jakiejkolwiek usługi w Internecie. Przesyłanie hasła w formie jawnej nie jest nigdy dobrym rozwiązaniem. Można temu zaradzić korzystając z metody
doLoginEnc - jest ona taka sama, jak jej odpowiednik z akapitu powyżej, ale hasło jest tutaj przesyłane jako skrót
SHA256. Jest to bardzo skuteczne zabezpieczenie, które uniemożliwia odczytanie przesyłanego hasła. Jest to skrót z rodziny
SHA2, dlatego w wielu środowiskach może on być niedostępny. Na przykład w PHP można z niego korzystać jako domyślnie dostępnego dopiero od wersji 5.1.2 (wcześniej potrzebne było dodatkowe rozszerzenie o nazwie
hash). Ale jeśli tylko mamy taką możliwość, powinniśmy to robić, gdyż jest to rozwiązanie o wiele bezpieczniejsze. W użyciu metoda bezpiecznego logowania wygląda identycznie, tyle, że skrót hasła musi zostać wysłany w formie binarnej zakodowanej w
base64:
- $session = $client->doLoginEnc(ALLEGRO_LOGIN, base64_encode( hash('sha256', ALLEGRO_PASSWORD, true) ), ALLEGRO_COUNTRY, ALLEGRO_KEY, $version['ver-key']);
Zbierając wszystko w całość
Spróbujmy zatem wykorzystać zdobytą wiedzę do stworzenia czegoś konkretnego. Napiszmy program, który będzie się logował na nasze konto, a przy tym automatycznie uaktualniał się w przypadku modyfikacji w serwisie. Przyjmijmy, że klucz wersji trzymamy w pliku
.verkey i nie znajduje się w nim nic ponadto:
- /* tutaj definicje naszych stałych i klasy */
-
- // pobieramy nasz klucz wersji
- $allegroVerKey = file_get_contents('.verkey');
-
- // łączymy się z Allegro WebAPI
- $client = new AllegroWebAPISoapClient();
-
- // w ten sposób zadbamy, aby ewentualny błąd nie narobił szkód
- try
- {
- try
- {
- // próba logowania
- $session = $client->doLoginEnc(ALLEGRO_LOGIN, base64_encode( hash('sha256', ALLEGRO_PASSWORD, true) ), ALLEGRO_COUNTRY, ALLEGRO_KEY, $allegroVerKey);
- }
- catch(SoapFault $error)
- {
- // błąd niepoprawnego klucza wersji pozwala nam zauważyć fakt iż coś w serwisie się zmieniło
- if($error->faultcode == 'ERR_INVALID_VERSION_CAT_SELL_FIELDS')
- {
- // pobieramy aktualny klucz wersji
- $version = $client->doQuerySysStatus(AllegroWebAPISoapClient::QUERY_ALLEGROWEBAPI, ALLEGRO_COUNTRY, ALLEGRO_KEY);
- $allegroVerKey = $version['ver-key'];
-
- /* tutaj wykonujemy swoje operacje uaktualniające */
-
- // zapisujemy klucz wersji do pliku
- file_put_contents('.verkey', $allegroVerKey);
-
- // ponowna próba logowania, już z nowym kluczem
- $session = $client->doLoginEnc(ALLEGRO_LOGIN, base64_encode( hash('sha256', ALLEGRO_PASSWORD, true) ), ALLEGRO_COUNTRY, ALLEGRO_KEY, $allegroVerKey);
- }
- // każdy inny błąd to już poważny problem
- else
- {
- throw $error;
- }
- }
-
- // udało nam się zalogować
- echo 'Logowanie poprawne. Uzyskany klucz sesji to: ', $session['session-handle-part'];
- }
- catch(SoapFault $error)
- {
- echo 'Błąd ', $error->faultcode, ': ', $error->faultstring, "n";
- }
Klucze
Ucz się, ucz, bo nauka to potęgi klucz. A jak będziesz miał dużo kluczy do zostaniesz woźnym. Jako iż dziś dużo się nauczyliśmy i zdobyliśmy dużo kluczy, to trzeba sobie sporządzić jakiś przybornik woźnego-programisty. Więc jeśli podczas czytania zaczęły Ci się mylić pojęcia, to na koniec powtarzam, jakie klucze są potrzebne i skąd je brać:
- Klucz Allegro WebAPI - to nasz klucz dostępowy. Jest on przypisywany do naszego konta. Otrzymujemy go od Allegro. Potrzebny jest niemalże do każdej operacji w obrębie usługi. Klucz ten jest niezmienny - raz przyznany pozostaje zawsze taki sam (po przedłużeniu ważności również nie zmienia się).
- Klucz wersji - to klucz aktualnej wersji systemu. Zmienia się za każdą zmianą w WebAPI. Klucz ten dostajemy w momencie zgłoszenia do usługi WebAPI wraz z naszym kluczem dostępu, a klucz aktualnie obowiązujący możemy pobrać zapytaniem doQuery(All)SysStatus. Wymagany jest do wszystkich operacji dotyczących konkretnego serwisu. Ulega on zmianie w momencie wprowadzania zmian w usłudze, w szczególności zmian w liście kategorii i liście pól formularza.
- Klucz sesji - to klucz identyfikujący naszą sesję logowania. Dostajemy go po wywołaniu metody doLogin(Enc). Jest wymagany do wszystkich operacji dotyczących konta. Jest to klucz krótko trwały (czas trwania sesji wynosi 3 godziny) i generowany jest każdorazowo podczas logowania.
Podsumowanie
Czego się nauczyliśmy? Przede wszystkim tego czym jest Allegro WebAPI i jak z niego korzystać w naszym programie. Nauczyliśmy się też podstawowych wywołań, które pozwolą nam w pełni wykorzystać potencjał WebAPI w przyszłości.
Opracował:
Rafał Wrzeszcz z Chillout Development