Wrzasq.pl

Allegro WebAPI tutorial vol. 2

Monday, 11 August 2008, 09:44

Co będziemy robić dziś wieczorem?

To co zwykle - opanowywać świat. A dokonamy tego zasypując Allegro automatycznie wygenerowanymi aukcjami wykorzystując Allegro WebAPI. Jednak nie przyjdzie nam to tak łatwo, jakbyśmy się spodziewali. Trudności nie ma zbyt wiele, ale są raczej bym to nazwał - niedogodności, albo niedociągnięcia. Jak już wspominałem w poprzedniej części Allegro WebAPI niestety nie jest napisane w jakiś bardziej intuicyjny sposób, niż obsługa strony. Tworząc nową aukcję po prostu wysyłamy listę pól, a nie jakby to się mogło wydawać w takim przypadku bardziej naturalną drogą na przykład obiekt aukcji z odpowiednio ustawionymi zmiennymi składowymi. Na dodatek obiekty pól są kolejną kłodą pod nogi. Nie wiem, czy w jakimkolwiek szczerzej używanym języku programowania można używać znaku minus jako elementu identyfikatora, ale żaden mi na myśl nie przychodzi, a troche ich znam, tymczasem w WebAPI niemal wszystko, co w nazwie zawiera więcej niż jeden wyraz, zawiera również znak minus (myślnik).

No ale na temat - do tworzenia samej aukcji wykorzystujemy metodę doNewAuctionExt() (lub w wersji bez dodatkowych pól doNewAuction()). Jeśli tworzymy aplikację bardziej rozbudowaną warto najpierw pobrać listę pól formularza metodą doGetSellFormFieldsExt(), jednak ponieważ podstawowe pola formularza się nie zmieniają, a tutaj staram się opisać jedynie podstawowe mechanizmy, to pominę to w przykładzie. Należy jednak pamiętać, że różne kategorie mają czasem różne pola i jeśli chcemy wstawić przedmiot w konkretnej kategorii, to pobranie pól będzie konieczne przynajmniej raz, żeby poznać ich ID. Omawiana przeze mnie metoda tworząca nową aukcję przyjmuje cztery argumenty - pierwszym jest identyfikator sesji logowania, które omówiłem w poprzedniej części tutoriala. Drugim jest lista pól formularza, czyli zawartość właściwa naszej aukcji. Trzecim argumentem jest flaga prywatności - 0 dla aukcji normalnych, 1 jeśli chcemy aukcję ograniczyć do formy "aukcji prywatnej". Jako ostatni przekazujemy unikalny identyfikator, który aukcji nadamy. Możemy go potem wykorzystać do weryfikacji aukcji (taki mały myk, opiszę pod koniec).

Pola formularza

Logowanie mamy, flaga prywatności to po prostu zero, albo jeden, generowanie identyfikatora ogranicza się do wywołania funkcji uniqid(). Jedyną bardziej skomplikowaną czynnością jest tworzenie listy pól formularza. Nie pierwszy raz to powiem, ale powiem - nie jest to w Allegro WebAPI zbyt logiczne, a już na pewno nie "naturalne". Każde pole jest obiektem, które posiada osobną składową na każdy rodzaj wartości - wartość liczbową, tekstową, logiczną i jeszcze kilka innych chociaż każde pole zawsze zawiera tylko jeden rodzaj wartości. Ze względu na to, że wszystkie pola każdego z takich obiektów muszą być ustawione i to na domyślne wartości, ja tworzę sobie jeden obiekt pustego pola i dalej go po prostu klonuję - oszczędza to mnóstwo pisania i zapewnia ustawienie wszystkich pól w każdym obiekcie. Przedstawię zatem listę pól, które na pewno musimy dodać do listy (są one zawsze obowiązkowe - o tym czy pole jest obowiązkowe możemy się dowiedzieć wykorzystując doGetSellFormFieldsExt() i sprawdzają opcje ustawione przez flagi dla poszczególnych pól):

ID: 1 (tekstowe)
Nazwa aukcji.
ID: 2 (liczbowe)
Numer kategorii.
ID: 3 (data/czas)
Czas rozpoczęcia.
ID: 4 (wyliczeniowe)
Czas trwania, wartość jest jedną ze stałych, których wartości można odczytać z informacji o polach.
ID: 5 (liczbowe)
Ilość sztuk (1 - 1000).
ID: 6 (zmiennoprzecinkowe)
Cena wywoławcza w walucie używanej w danym serwisie (w przypadku Allegro to PLN). Uwaga - mimo, iż to pole jest oznaczone jako wymagane, w przypadku sklepów, czyli ofert "Kup Teraz" (a do tego przeważnie się używa WebAPI) używamy pole "Cena Kup Teraz" o ID 8, a to pole jest nieużywane!
ID: 9 (liczbowe)
Numer kraju, pokrywa się z numerem kraju używanym do wybierania wersji serwisu.
ID: 10 (wyliczeniowe)
Jedno z województw, ich listę można pobrać z informacji o tym polu.
ID: 11 (tekstowe)
Miejscowość.
ID: 12 (wyliczeniowe)
Kto pokrywa koszty transportu. W zasadzie to pole logiczne, przeliczalne wartości tego pola to tylko 0 jeśli koszty pokrywa sprzedający i 1 jeśli kupujący.
ID: 13 (liczbowe)
Opcje transportu. Wartość to nałożenie flag, kolejne bity odpowiadają kolejnym wartością z listy opcji, a opcje kolejno to: "Przesyłka pocztowa", "Przesyłka pocztowa priorytetowa", "Przesyłka kurierska", "Odbiór osobisty", "Inne", "Zgadzam się na wysłanie przedmiotu za granicę". Szkoda, że w manualu WebAPI nie napisali, że tego typu pola są właśnie złożeniem flag…
ID: 14 (liczbowe)
Opcje płatności. Wartość jest obliczana jak w przypadku pola "Opcje transportu", dostępne flagi to kolejno: "Wpłata na konto", "Pobranie", "Płatności Allegro", "Płatności Allegro Escrow", "Inne".
ID: 24 (tekstowe)
Opis przedmiotu.

Jak nie trudno zauważyć wszystkie obowiązkowe pola są standardowymi polami przy generowaniu aucji na Allegro, więc możemy ten sam schemat wykorzystywać w każdym przypadku - żadne pola specyficzne dla kategorii nie są obowiązkowe. Pola zawierające typ data/czas są polami liczbowymi - zawierają zwykły timestamp, jednak w ich przypadku używane jest inne pole przesyłanego obiektu.

Inne przydatne pola to:

ID: 7 (zmiennoprzecinkowe)
Cena minimalna.
ID: 8 (zmiennoprzecinkowe)
Cena "Kup Teraz!".
ID: 15 (liczbowe)
Opcje dodatkowe. Ponownie złożenie flag, którymi są kolejno: "Pogrubienie", "Miniaturka", "Podświetlenie", "Wyróżnienie", "Strona kategorii", "Strona główna". Miniaturka jest generowana z pierwszego na liście zdjęcia (patrz niżej).
ID: 16 - 23 (binarne)
Obrazki.

Ale od razu uprzedzę o kilku szczegółach:

  • Obrazy przesyłamy jako zawartość pliku obrazka (czyli na przykład pobieramy ją używając file_get_contents() i nie kodujemy ręcznie w Base64 - tak jak to napisałem w poprzedniej części biblioteka SOAP sama przepuszcza obrazki i inne dane binarne przez Base64 korzystając z definicji WSDL. Jednak limit długości pliku jest narzucony na długość tego pola, zatem przy sprawdzaniu wielkości zmiennej musimy już użyć wartości po zakodowaniu, a więc robimy ro ręcznie (ale tylko przy sprawdzaniu długości).
  • Opis (i wszelkie inne zmienne tekstowe) pozostawiamy w "surowej" wersji HTMLa, nie podmieniamy żadnych encji, ani nie żadnych innych znaków - tak jak to wpiszemy do zmiennej, tak zostanie to pokazane na stronie Allegro. Tutaj ponownie wszystko robi za nas biblioteka SOAP z PHP.
  • Aby wstawić do aukcji galerię wykorzystujemy, tak samo jak na stronie Allegro, tak <GALERIA>, ale tutaj kolejny myk - otóż (co mnie trochę zdziwiło) ten tag Allegro tłumaczy na różne języki i tak na przykład w przypadku serwisu testowego musimy zamiennie użyć <GALLERY>.

Stage 2

Zanim przestawię kod tworzący aukcję, oto rozszerzona klasa z poprzedniej części tutoriala wzbogacona o wartości odpowiednich flag, oraz o funkcję do przycinania obrazków do odpowiednich wielkości. Ponieważ w większości formatów mamy doczynienia z kompresją, wielkość w bajtach nie jest zazwyczaj wprost proporcjonalna do wymiarów graficznych, zatem stopniowo zmniejszam wymiary aż obrazek będzie się nadawał do przesłania - oczywiście to tylko najprostsza metoda, w tym artykule nie omawiam obróbki obrazów. Na dodatek według informacji o polach, obrazki mogą mieć do 25kB (25000 bajtów) jednak w praktyce system dopuszczał mi tylko obrazki do 20kB (20000 bajtów) - tutaj nadal nie rozgryzłem Allegro WebAPI.

/**
 * @see http://www.php.net/manual/en/book.soap.php
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 (C) by Wrzasq
 */
class WebAPISoapClient extends SoapClient
{
/**
 * Kraj - Polska.
 */
    const COUNTRY_PL = 1;
/**
 * Logowanie do serwisu testwebapi.pl.
 */
    const COUNTRY_TESTWEBAPI = 228;

/**
 * Zapytanie o wersję Allegro WebAPI.
 */
    const QUERY_ALLEGROWEBAPI = 1;

/**
 * Czas trwania - 3 dni.
 */
    const LIFETIME_3DAYS = 0;
/**
 * Czas trwania - 5 dni.
 */
    const LIFETIME_5DAYS = 1;
/**
 * Czas trwania - 7 dni.
 */
    const LIFETIME_7DAYS = 2;
/**
 * Czas trwania - 10 dni.
 */
    const LIFETIME_10DAYS = 3;
/**
 * Czas trwania - 14 dni.
 */
    const LIFETIME_14DAYS = 4;

/**
 * Koszty pokrywa sprzedający.
 */
    const TRANSPORT_COST_SELLER = 0;
/**
 * Koszty pokrywa kupujący.
 */
    const TRANSPORT_COST_BUYER = 1;

/**
 * Przesyłka pocztowa (polecona/paczka).
 */
    const TRANSPORT_OPTION_POST = 1;
/**
 * Przesyłka pocztowa priorytetowa (polecona/paczka).
 */
    const TRANSPORT_OPTION_POSTPRIORITY = 2;
/**
 * Przesyłka kurierska.
 */
    const TRANSPORT_OPTION_COURIER = 4;
/**
 * Odbiór osobisty.
 */
    const TRANSPORT_OPTION_PERSONAL = 8;
/**
 * Inne.
 */
    const TRANSPORT_OPTION_OTHER = 16;
/**
 * Zgadzam się na przesłanie przedmiotu za granicę.
 */
    const TRANSPORT_OPTION_ABROAD = 32;

/**
 * Przedpłata.
 */
    const PAYMENT_OPTION_PREPAID = 1;
/**
 * Pobranie.
 */
    const PAYMENT_OPTION_POSTPAID = 2;
/**
 * Płatności Allegro.
 */
    const PAYMENT_OPTION_ALLEGRO = 4;
/**
 * Bezpieczne Płatności Allegro z Escrow.
 */
    const PAYMENT_OPTION_ESCROW = 8;
/**
 * Bezpieczne Płatności Allegro z Escrow.
 */
    const PAYMENT_OPTION_OTHER = 16;

/**
 * Pogrubienie.
 */
    const OPTION_BOLD = 1;
/**
 * Miniaturka.
 */
    const OPTION_THUMB = 2;
/**
 * Podświetlenie.
 */
    const OPTION_HIGHLIGHT = 4;
/**
 * Wyróżnienie.
 */
    const OPTION_PREMIUM = 8;
/**
 * Strona kategorii.
 */
    const OPTION_CATEGORY = 16;
/**
 * Strona główna.
 */
    const OPTION_HOME = 32;

/**
 * Automatycznie tworzy klienta dla interfejsu Allegro WebAPI.
 */
    public function __construct()
    {
        parent::__construct('http://webapi.allegro.pl/uploader.php?wsdl');
    }

/**
 * Redukuje obraz do wielkości nadającej się do przesyłu.
 * 
 * @param string $url URL obrazka (lokalne, albo sieciowe).
 * @return string Binarna zawartość obrazka w formacie JPEG.
 */
    public static function resize($url)
    {
        $image = file_get_contents($url);

        // właśnie tutaj używamy Base64 ręcznie, ale nigdzie indziej!
        while( strlen( base64_encode($image) ) > 200000)
        {
            $temp = imagecreatefromstring($image);
            $x = ceil(0.9 * imagesx($temp) );
            $y = ceil(0.9 * imagesy($temp) );

            $image = imagecreatetruecolor($x, $y);
            imagecopyresized($image, $temp, 0, 0, 0, 0, $x, $y, imagesx($temp), imagesy($temp) );

            imagejpeg($image, 'temp.jpg', 75);
            $image = file_get_contents('temp.jpg');
            unlink('temp.jpg');
        }

        return $image;
    }
}

A więc jedyne, co nam pozostaje to wykorzystać ową klasę do tworzenia aukcji. Żeby nie powtarzać kodu, nie będę przepisywał fragmentu z poprzedniej części tutoriala, poniższy kod powinien się znaleźć wewnątrz bloku try{} po udanym logowaniu. Na początku (tak jak to wcześniej zapowiadałem) tworzymy puste pole, a następnie klonujemy je. Mimo iż wartość fid można odczytać i zapisać normalnie, to aby ujednolicić kod zastosowałem konstrukcję {'fid'}, ale jest to zabieg czysto kosmetyczny - te dwa zapisy są równoważne. Na końcu kodu sprawdzamy, czy aukcja została utworzona poprawnie. Tyle, że dokumentacja techniczna nie podaje żadnych szczegółów na temat tego, jak ewentualne nieprawidłowości miałyby być zgłąszana i kiedy mogłyby zaistnieć - w końcu ewentualne błędy są na bierząco raportowane już w czasie zakładania aukcji. Dlatego prezentuje rozwiązanie, które logika nakazała mi przyjąć za właściwe, jak do tej pory toporna współpraca z Allegro WebAPI mi się udaje, więc mam nadzieję, że i tym razem dobrze to rozgryzłem ;).

    $empty = new stdClass();
    $empty->{'fvalue-string'} = '';
    $empty->{'fvalue-int'} = 0;
    $empty->{'fvalue-float'} = 0;
    // to pole w formie pustej ma zawierać spację
    $empty->{'fvalue-image'} = ' ';
    $empty->{'fvalue-datetime'} = 0;
    $empty->{'fvalue-boolean'} = false;

    $form = array();

    // pamiętaj, że maksymalna długość 50 "znaków" liczona jest w bajtach, dlatego polskie znaki, czy encje HTMLa liczone są za kilka bajtów
    $field = clone $empty;
    $field->{'fid'} = 1;
    $field->{'fvalue-string'} = 'Aukcja FooBar';
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 2;
    $field->{'fvalue-int'} = 48878;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 3;
    $field->{'fvalue-datetime'} = time();
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 4;
    $field->{'fvalue-int'} = WebAPISOAPClient::LIFETIME_7DAYS;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 5;
    $field->{'fvalue-int'} = 69;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 8;
    $field->{'fvalue-float'} = 666;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 9;
    $field->{'fvalue-int'} = WebAPISOAPClient::COUNTRY_PL;
    $form[] = $field;

    // 16 to województwo zachodniopomorskie, numer województwa można pobrać z listy opisu pola
    $field = clone $empty;
    $field->{'fid'} = 10;
    $field->{'fvalue-int'} = 16;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 11;
    $field->{'fvalue-string'} = 'Szczecin';
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 12;
    $field->{'fvalue-int'} = WebAPISOAPClient::TRANSPORT_COST_BUYER;
    $form[] = $field;

    // flagi składamy przez ich logiczne sumowanie
    $field = clone $empty;
    $field->{'fid'} = 13;
    $field->{'fvalue-int'} = WebAPISOAPClient::TRANSPORT_OPTION_POST | WebAPISOAPClient::TRANSPORT_OPTION_POSTPRIORITY;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 14;
    $field->{'fvalue-int'} = WebAPISOAPClient::PAYMENT_OPTION_PREPAID | WebAPISOAPClient::PAYMENT_OPTION_ALLEGRO;
    $form[] = $field;

    $field = clone $empty;
    $field->{'fid'} = 15;
    $field->{'fvalue-int'} = WebAPISOAPClient::OPTION_BOLD | WebAPISOAPClient::OPTION_THUMB | WebAPISOAPClient::OPTION_HIGHLIGHT;
    $form[] = $field;

    $i = 0;

    // maksymalnie 8 zdjęć!
    foreach( array('zdjecie1.jpg', 'zdjecie2.png', '/var/www/zdjecie3.gif', 'http://example.com/zdjecie4.bmp') as $image)
    {
        $field = clone $empty;
        $field->{'fid'} = 16 + $i;
        $field->{'fvalue-image'} = WebAPISoapClient::resize($image);
        $form[] = $field;
        $i++;
    }

    $field = clone $empty;
    $field->{'fid'} = 24;
    $field->{'fvalue-string'} = '<h1>Opis</h1>

<p>Nasza aukcja - powered by <a href="https://wrzasq.pl/" title="Tworzenie stron i aplikacji internetowych">Wrzasq.pl</a>.</p>

<div><GALERIA></div>.';
    $form[] = $field;

    $local = uniqid();
    $item = $client->doNewAuctionExt($session['session-handle-part'], $form, 0, $local);
    $check = $client->doVerifyItem($session['session-handle-part'], $local);

    if($item['item-id'] == $check)
    {
        echo '<p>Wystawiono przedmiot <a href="http://allegro.pl/item' . $item['item-id'] . '.html">' . $item['item-id'] . '</a>.</p>';
    }
    else
    {
        echo '<p class="error">Coś poszło nie tak.</p>';
    }

Z mojej strony to na razie tyle, tworzenie aukcji to chyba najważniejsze zastosowanie Allegro WebAPI. W przyszłości może opiszę jakieś inne jego użycie. Pytania odnośnie WebAPI polecam kierować na forum.

Część IV
Część V
Część VI

Tags: , , , , ,