Paypal – wstęp 12
Do napisania tego oto tutorialu zmotywowały mnie wiele rzeczy po pierwsze w polskim internecie nie ma zbyt wiele na temat płatności paypal’em. Znalazłem na kilku forach zapytania na ten temat. Z czego sam jedno napisałem na forum.php.pl. Drugą przesłanką która uświadomiła, że paypal zaczyna zdobywać polski rynek płatności online jest fakt umowy jaką paypal.com podpisał z płatności.pl i już niedługo będziemy mogli się cieszyć płaceniem za usługi i produkty w ten wygodny sposób. Kolejną sprawą, która pokazała mi jak bardzo szkodliwą rzeczą może być nieumiejętne korzystanie z “Buy Now” (tych nie kodowanych) gdzie wystarczy ze źródła strony wyciąć:
<input type="hidden" name="return" value="url_po_udanej_transakcji" />
by uzyskać dostęp do zasobu który chcemy kupić. Następny fakt wypłynął na światło dzienne gdy sam pisałem obsługę IPN na Paypal’u i zobaczyłem, że nawet tutorial’e i przykłady na stronie developer center @ paypal mają błędy(mnie szczególnie ineteresował ten do php4). A Koniec końców w poszukiwaniu gotowych rozwiązań tj. klas, funkcji które mogły by zaspokoić moje potrzeby. Znalazłem dwa takie rozwiązania. Pierwsze z nich na phpclasses.org było mocno rozbudowane i zabezpieczone szyfrowaniem. Jednak miało jedną podstawową wadę wymagało “doklejenia” kolejnych 3 dużych bibliotek. Natomiast drugie z nich po wstępnych testach okazało się nie działać tak jak bym chciał, więc postanowiłem napisać coś własnego.
Tyle słowem wstępu teraz przedstawie czego będziemy potrzebować do wdrożenia płatności paypal’em na naszej stronie.
Po pierwsze do testów paypal udostępnia nam sandbox. Żeby do owej “piaskownicy” uzyskać dostęp trzeba założyć sobie konto na: developer.paypal.com . Po założeniu konta i potwierdzeniu prawdziwości skrzynki e-mail. Mamy możliwość założenia kont testowych. Będą nam potrzebne takie dwa: pierwsze buissnes account do testowania płatności i wygenerowania kodu buttonów na, których będziemy testowali nasze rozwiązania, drugie natomiast kupującego po to by zbadać sam proces dokonywania płatności.
Uwaga! ważne do testowania płatności na sandbox’ie wymaga bycia zalogowanym na developer.paypal.com
Aby zrozumieć temat bardziej spójrzmy na poniższy schemat.
Dla klienta zasada jest prosta. Rejestruje się na naszej stronie. Opłaca usługę np. abonament na dostęp do poprzednich wydań naszego magazynu w plikach pdf po przez kliknięcie Buy Now. Jest przekierowany na strone paypal’a gdzie loguje się na swoje konto lub płaci kartą płatniczą. Po pozytywnym zakończeniu transakcji wraca na naszą stronę z podziękowaniami tzw “return”. Paypal gubi każdą sesje więc nie ma co liczyć na jej odzyskanie po powrocie. Rozwiązaniem najprostszym jest odesłanie klienta po podziękowaniu do formularza logowania.
Serwer paypala oprócz tego. że przeprowadza całą transakcje i odpowiedzialność za wpisane tam poufne dane takie jak numer karty kredytowej są po jego stronie. To jeszcze przekazuje nam dane na temat transakcji za pośrednictwem IPN(instant payment notification). Dzieje się to w następujący sposób. Po dokonaniu wpłaty na paypalu wysyła do naszego skryptu na adres podany w:
<input type="hidden" name="notify_url" value="url_do_skryptu_obsługującego_ipn" />
przekazuje nam pule zmiennych metodą POST min.
$invoice = $_POST['invoice']; $receiver_email = $_POST['receiver_email']; $item_name = $_POST['item_name']; $item_number = $_POST['item_number']; $quantity= $_POST['invoice']; $payment_status = $_POST['payment_status']; $pending_reason = $_POST['pending_reason']; $payment_date = $_POST['payment_date']; $payment_gross = $_POST['payment_gross']; $payment_fee = $_POST['payment_fee']; $payment_amount = $_POST['mc_gross']; $payment_currency = $_POST['mc_currency']; $txn_id = $_POST['txn_id']; $txn_type = $_POST['txn_type']; $first_name = $_POST['first_name']; $last_name = $_POST['last_name']; $address_street = $_POST['address_street']; $address_city = $_POST['address_city']; $address_state = $_POST['address_state']; $address_zip = $_POST['address_zip']; $address_country = $_POST['address_country']; $payer_email = $_POST['payer_email']; $address_status = $_POST['address_status']; $payment_type = $_POST['payment_type']; $notify_version = $_POST['notify_version']; $verify_sign =$_POST['verify_sign'];
Dzięki tym zmiennym możemy po pierwsze rozpoznać czy płatność została zakończona powodzeniem:
$_POST['payment_status'] == "succes"
Oraz poznać bliżej dane naszego klienta. Jego dane jakie podał na naszej stronie mogą się różnić od tych danych jakie widnieją na paypalu. Możemy je wykorzystać jako powiększenie naszej bazy do mailingów z promocjami, bądź zachować je tylko po to by rejestrować sprzedaż prowadzoną na naszej stronie.
Jednak zanim obsłużymy otrzymane żądanie musimy sprawdzić czy aby na pewno pochodzi z paypal’a. Wystarczy złożyć wszystkie zmienne przekazane postem w ciąg znaków typu zmienna1=5&zmienna2=6&….. itd. Jednak zanim to jednak zrobimy musimy dodać na samym początku komendę, która poinformuje paypal, że chcemy dokonać potwierdzenia czy takie żądanie zostało wysłane przez paypal czy też nie. Zrobimy to następującym kodem:
$req = 'cmd=_notify-validate'; //dokładamy komendę z prośbą o potwierdzenie tego powiadomienia o płatności
foreach ($_POST as $key => $value) {
$value = urlencode($value);
$req .= "&$key=$value";
}
//zmienna req teraz przechowuje wszystkie zmienne z $_POST w postaci którą opisywałem wcześniej
Następnie wystarczy wysłać takie zapytanie do serwera paypala. Są dwie szkoły wysyłania, albo fsockopen() albo za pomocą klasy cURL. Oba są poprawne. Ja zaprezentuje fragment kodu wykorzystujący pierwszą z nich.
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n"; // ustawiamy nagłówki zapytania. $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . strlen($req) . "\r\n\r\n"; // w tej linii zamieszczamy długość naszego zapytania $fp = fsockopen (paypal_adr, 80, $errno, $errstr, 30); //stała paypal_adr jest adresem na który wysyłamy nasze zapytanie z prośbą o validacje
Stała paypal_adr może mieć dwie wartości: pierwszą testową https://www.sandbox.paypal.com lub drugą już w pełni funkcjonalną https://www.paypal.com czasami pojawia się problem z https:// ponieważ niektóre serwery nie mają skonfigurowanego portu.
Otwarliśmy deskryptor portu $fp. Teraz musimy wysłać nasze dane do validacji.
if (!$fp) { // sprawdzamy czy wszystko ok z naszym połączeniem jeżeli nie jest to deskryptor przyjmuje wartość false
// tutaj możemy napisać obsługę HTTP ERROR
} else {
fputs ($fp, $header . $req); // jeżeli deskryptor jest ok to wysyłamy mu nasze nagłówki połączone z zapytaniem o validacje powiadomienia
Jeżeli już wysłaliśmy naszą prośbe o potwierdzenie żądania teraz musimy obsłużyć odpowiedź serwera paypal, który zwróci nam string z informacją czy wszystko poszło tak jak powinno czy też to odwołanie było tylko próbą wyłudzenia od nas towaru.
Zatem zaczynamy od odczytania odpowiedzi do póty do póki nie dostaniemy znaku końca pliku. Kod który się tym zajmie może wyglądać tak:
while (!feof($fp)) {
$res = fgets ($fp, 1024);
Otrzymaliśmy więc już całą zawartość odpowiedzi i przechowujemy ją w zmiennej $res. Teraz trzeba by było sprawdzić czy ta płatność jest potwierdzona czy też nie. Do tego posłuży nam funkcja która porównuje binarnie dwa ciągi znaków strcmp(). Jeżeli oba porównywane ciągi są identyczne to funkcja zwraca 0. Kod, który sprawdzi odpowiedź mógł by wyglądać tak:
if (strcmp($res, "VERIFIED")==0) {
//Sprawdzamy czy transakcja ma status jako zakończona
if (strcmp ($payment_status, "Completed") == 0) {
//jeżeli tak to możemy spokojnie dalej przetwarzać dane
}
}else if(strcmp($res, "INVALID")==0){
// możemy zarejstrować do pliku wszystkie próby ataków na nasz serwis np. tak:
$fplog = fopen('ipnlog.txt','a');
fwrite($fplog, gmstrftime ("%b %d %Y %H:%M:%S", time())." -- ".$res." -- ".$req."\n");
fclose($fplog);
}
Zatem wiecie już na jakiej zasadzie można obsługiwać IPN otrzymany od paypal’a. Nie taki diabeł straszny jak się okazuje. Jednak mi on napsuł sporo krwi. Ciąg dalszy wkrótce nastąpi jak tylko znajdę trochę czasu.
hafciarnia