Blog jak widać już nie jest aktualizowany. I raczej nie będzie. Wisi sobie ot tak, ze względów historycznych :)

Paypal - Instant Payment Notification

Krzysztof Rygielski | 2009-04-10 11:22 | Kategorie: Paypal, Rails

Poniższy post powstał dzięki informacjom z bloga Bseanvt. Following post was made with use of information from Bseanvt blog, so all credit goes to Sean.

Ostatnio pracujÄ™ nad aplikacjÄ… na Rails, obsÅ‚ugujÄ…cÄ… pÅ‚atnoÅ›ci Paypal. Nie byÅ‚oby to nic specjalnie skomplikowanego, gdyby nie to, że potrzebne mi jest powiadomienie, kiedy zostanie dokonany transfer pieniÄ™dzy z konta klienta na konto sprzedawcy. Paypal udostÄ™pnia narzÄ™dzie o nazwie IPN (Instant Payment Notification), które wysyÅ‚a powiadomienia pod url zdefiniowany w profilu sprzedawcy.

Na poczÄ…tku zaÅ‚ozyÅ‚em sobie, że zrobiÄ™ prosty skrypt, który zwyczajnie zapisze przekazywane od Paypala parametry. PostanowiÅ‚em skorzystać z narzÄ™dzia IPN simulator dostÄ™pnego w Paypal Sandbox. Niestety, za każdym razem przy próbie wysÅ‚ania powiadomienia pod wskazany adres otrzymywaÅ‚em komunikat:

IPN delivery failed. Unable to connect to the specified URL. Please verify the URL and try again.

Dziwna sprawa, ponieważ URL byÅ‚ bez problemu dostÄ™pny przez przeglÄ…darkÄ™. DÅ‚ugo szukaÅ‚em rozwiÄ…zania, zarówno w dokumentacji (która to, nawiasem mówiÄ…c, jest chaotyczna i rozgrzebana) jak i na forach Paypala. Dopiero na blogu Bseanvt znalazÅ‚em rozwiÄ…zanie. OkazaÅ‚o siÄ™, że Paypal, bez wzglÄ™du na to czy url jest bÅ‚Ä™dny, czy poprawny, ale tylko nie poprawnie obsÅ‚uguje wymianÄ™ danych, zwraca ten sam komunikat. Zamiast mówić "Twój URL jest BE" powinno mówić "Twój URL jest cacy, tylko Twoja obsÅ‚uga IPN jest BE". Cóż, widać developerzy Paypala nie widzÄ… różnicy. Na wspomnianym blogu opisano rozwiÄ…zanie, jednak przytoczÄ™ je też tutaj.

Do kontrolera należy dołączyć następujące rzeczy:

require 'uri'
require 'net/http'
require 'net/https'
#aby nie byÅ‚o problemów z autoryzacjÄ…
#i powiadomienie było przyjęte
protect_from_forgery :except=>[:ipn]

Następnie funkcja obsługująca powiadomienie IPN musi powinna wyglądać mniej więcej tak:

def ipn
  begin
    if request.post?
      #po  otrzymaniu powiadomienia, dane należy odesłać
      #w tym samym porządku, dodając na końcu cmd=_notify-validate
     
      from_pp = request.raw_post
      data = from_pp + "&cmd=_notify-validate"
      url = URI.parse 'https://sandbox.paypal.com/cgi-bin/webscr'
      http = Net::HTTP.new url.host, url.port
      http.use_ssl = true

      response, data = http.post url.path, data, { 'Content-Type' => 'application/x-www-for-urlencoded' }
    end
  rescue Exception => e
    logger.info("Error: paypal transaction #{e.message}")
  end
end

Żeby obsłużyć dalsze przetwarzanie danych otrzymanych w powiadomieniu należy napisać już własny kod. Aktualnie sam nad tym pracuję :-)

Jeszcze raz podziękowania dla Sean'a z Bseanvt.