#142
Dec 29, 2008

PayPal Notifications

PayPal's IPN (Instant Payment Notification) service allows your app to get confirmation when an order is processed. In this episode I use IPN to mark a cart as purchased.
Download (16.4 MB, 10:46)
alternative download for iPod & Apple TV (12.8 MB, 10:46)

Note: As mentioned at the end of this episode, this code should not be used in production as-is due to security problems. I will address these in the next episode.

Resources

script/generate nifty_scaffold payment_notification params:text cart_id:integer status:string transaction_id:string create
rake db:migrate
curl -d "txn_id=3XC103945N720211C&invoice=923204115&payment_status=Completed" http://localhost:3000/payment_notifications
# payment_notifications_controller.rb
protect_from_forgery :except => [:create]

def create
  PaymentNotification.create!(:params => params, :cart_id => params[:invoice], :status => params[:payment_status], :transaction_id => params[:txn_id])
  render :nothing => true
end

# models/payment_notification.rb
belongs_to :cart
serialize :params
after_create :mark_cart_as_purchased

private

def mark_cart_as_purchased
  if status == "Completed"
    cart.update_attribute(:purchased_at, Time.now)
  end
end

# controllers/application.rb
def current_cart
  if session[:cart_id]
    @current_cart ||= Cart.find(session[:cart_id])
    session[:cart_id] = nil if @current_cart.purchased_at
  end
  if session[:cart_id].nil?
    @current_cart = Cart.create!
    session[:cart_id] = @current_cart.id
  end
  @current_cart
end

# models/cart.rb
def paypal_url(return_url, notify_url)
  values = {
    :business => 'seller_1229899173_biz@railscasts.com',
    :cmd => '_cart',
    :upload => 1,
    :return => return_url,
    :invoice => id,
    :notify_url => notify_url
  }
  line_items.each_with_index do |item, index|
    values.merge!({
      "amount_#{index+1}" => item.unit_price,
      "item_name_#{index+1}" => item.product.name,
      "item_number_#{index+1}" => item.id,
      "quantity_#{index+1}" => item.quantity
    })
  end
  "https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
<!-- carts/show.html.erb -->
<%= link_to "Checkout", @cart.paypal_url(products_url, payment_notifications_url) %>

RSS Feed for Episode Comments 36 comments

1. MTH Dec 29, 2008 at 01:52

It's annoying to wait until next episode for production use! :-)

Thanks, Ryan!


2. Dave Spurr Dec 29, 2008 at 04:05

To test this locally you could quite easily setup a dynamic dns to point a public domain name to your local environment, such as dyndns.

I've done that on a couple of occasions when working with external services which need to hit my machine as part of the applications natural processes.


3. Hamza Khan-Cheema Dec 29, 2008 at 05:50

It is interesting how other people do these kind of things.

However I am still a bit unsure why you are not using ActiveMerchant ?

Don't you find it makes things easier ?

Thanks, and keep up the good work!

Hamza


4. MTH Dec 29, 2008 at 06:46

@Trasbo You are not right:

1. don't looks like a spam site
2. didn't spam comments.

Anyway, thanks for suggestions, I'll think about it.

@Hamza, agree,
Ryan, please, show us how to use ActiveMerchant

Thanks


5. MTH Dec 29, 2008 at 07:41

David,
I'll respond to you by email.

I'm very appreciate Ryan, and don't want to start flame here.
Thanks.


6. Liam Monahan Dec 29, 2008 at 09:34

I think recurring billing would be an interesting and useful topic. I know that certain payment gateways support it, but it can be complicated. You would need a background task to scan periodically and make charges...

Thanks Ryan and keep up the good work.


7. zero0x Dec 29, 2008 at 09:50

Hey,

I'm working on an e-shop (php but nevermind), and wanna ask something.

Is it safe enough to check the notification for the amount paid to ensure that payment was not tampered?


8. John Dec 29, 2008 at 15:01

Hi Ryan,

Paypal's Order Management integration guide says you need to use a "shared secret" or a "postback". I did not see you use either.

See page 26th of the guide: "After your server receives an Instant Payment Notification, you must confirm that the notification is authentic. This is known as notification validation."

Can you comment on this? Am I missing something?

Thanks,

John


9. Josh Dec 30, 2008 at 09:02

No questions, no suggestions, no complaints, just THANKS!


10. Mark Dec 30, 2008 at 11:09

Thanks for sharing!


11. RailsCasts Fan Dec 30, 2008 at 13:16

Ryan President ! :)


12. QuBiT Dec 31, 2008 at 01:15

Great Tutorial Again!

@Ryan:
If you continue to support us with this high quality screencasts, I'll maybe use the learned things to open my own business ;)

Happy New Year to everyone! (not yet but in a few hours ;) )


13. MTH Dec 31, 2008 at 02:05

Happy New Year, Ryan!

Thank you for all your work this year.
Please, continue on the next year.
Thanks thanks thanks!!!


14. Mitja Dec 31, 2008 at 06:45

Hello!

Thanks for this tutorial, it is really great, but i am somehow stuck on the serialize part....

In a LOG i can see i get all the information from PayPal, but serialize is somehow failing.

Anyone else with a same problem?
What could be a trick?

Thanks


15. Josh Jan 01, 2009 at 10:03

Hey Ryan,

I have taken what you have done here (great workup, by the way!) and have translated it to use Google Checkout using the google4r-checkout gem. It is really great! Let me know if you want me to pastie you the source for a Railscast episode in this series :)

Thanks again!


16. Patrick Jan 02, 2009 at 02:31

Hey Ryan:
Would you mind addressing how to validate non-model backed form components? I'm specifically talking about forms where you use a model to save stuff like the customer's name and phone number, but you don't want to save sensitive data like their credit card number to the database for security reasons.

The same question was raised in a different context by creatop in the comments of Episode 37: "Nice, but what about more complicated non-model forms with many fields with validation required?"

It seems like a pretty common scenario to me - but for some reason there is absolutely no documentation on it (or if there is, I've wasted the last five hours for nothing...haha).


17. elise Jan 02, 2009 at 07:37

Happy New Year !
I owe you not a beer, but a bottle of fine single malt. Or i might just go and buy a few of your commercial screencasts.
Keep up the good work !


18. Jacko Jan 02, 2009 at 12:42

Big thanks. I was looking all the time for this. Greetings, Jacko


19. Ryan Bates Jan 02, 2009 at 14:54

@Hamza, I plan to cover ActiveMerchant in future episodes.

@zero0x, right, this is one of the security issues I plan to cover in the next episode.

@Josh, that sounds great. I don't plan to cover Google Checkout in this series, but maybe if there's enough demand I'll make an episode for it later.

@Patrick, using ActiveRecord's validations on a non-database backed model can be difficult. I am delaying talking about this issue until Rails has some better support for this built in. But in the meantime you may want to check out some external validation plugins. Sorry I don't know of the best one off the top of my head.

I personally do not run into this issue because I prefer to keep a record of user input. So, every model that has a decent size form is backed by a database.


20. Dan Pickett Jan 02, 2009 at 16:03

Great screencast, Ryan!

Paypal kind of bums me out. I dislike the idea of ipn. For MassPay transactions, I really wish there was a query/request you could send to get the status of payment, but unfortunately I haven't been able to find it.


21. Thomas Jan 03, 2009 at 05:37

@Dan: it exists and it is compulsory for validating a purchase! It's just that Ryan didn't get into that yet. I guess he'll talk about it in the next episode.

At this stage of the app you still cannot mark a cart as being purchased/paid.

FYI Paypal provides on their developer website, a Ruby on Rails app in which you can dive in to understand the payment process, but beware it requires a lot of cleaning up.


22. Sean Schofield Jan 08, 2009 at 08:30

People interested in PayPal for Rails may want to check out the Spree[1] commerce project which has excellent PayPal support.

There's also a nice extension[2] that provides integrates support for Paypal Website Standard.

[1] http://spreehq.org
[2] http://github.com/Gregg/spree-pp-website-standard/tree/master


23. Jane Chen Nov 10, 2009 at 07:55

Josh or Ryan,

Can you PLEASE post a tutorial using google4r-checkout? Thanks!

- Jane


24. pandorajewelry Nov 16, 2009 at 18:14

<a href="http://www.hotsalesshop.com/ghd-iv-mk4-dark-hair-straightener-flat-p-13162.html" target="_blank">GHD IV MK4 Dark styler</a>
<a href="http://www.newshopstyle.co.uk/chi-blue-camo-ceramic-flat-iron-p-3804.html" target="_blank">CHI Flat Irons</a>
<a href="http://www.pandorajewelrycom.com/match-earrings-c-27.html" target="_blank">Pandora Diamond Earrings</a>


25. artemave Dec 17, 2009 at 07:10

Before considering PayPal, consider this:
http://blog.apparentsoft.com/business/124/is-paypal-good-for-your-microisv-business-a-short-paypal-horror-story/


26. John Dec 28, 2009 at 20:34

I also would love to see a Railscast on google4r-checkout. It seems like a great plug-in and not nearly as klunky as working with PayPal. However, I still have a lot of questions after reading through the google4r-checkout documentation. I'm sure Ryan would answer most of them with his excellent Railscast presentations.


27. Henk Meijer Feb 22, 2010 at 02:40

Thanks Ryan! Love your screencasts. I followed all your PayPal tutorials and everything works as expected. But when I test my code live, the IPN doesn't seem to get through . I keep getting error messages in the PayPal IPN-log:
IPN delivery failed. HTTP error code 500: Internal Server Error

Any ideas?


28. Henk Meijer Feb 23, 2010 at 00:36

Update: I found my problem. There was a http-user-agent check in my application controller, which caused the IPN to bounce.
Again: thanks for your amazing screencasts.


29. Shreyans Mar 05, 2010 at 15:19

Hi Ryan,
Based on the information you provided, I am trying to implement paypal IPN and save the donors (non-profit website) information to my database. To accomplish this- after paypal sends IPN(tranx is complete) I am saving the params (some of them) to the db. But the fact is that paypal sends IPN multiple times and every time it saves the params to the database making duplicate entries. Is there any way to control this?


30. johndiny Apr 25, 2010 at 10:40

Interesting post!!!!sxafsf


31. Andrew Apr 27, 2010 at 15:45

Good video, one thing that might trip people up is that in the after_create method inside the payment_notification model the cart was not able to be found, it always returned as nil for me so the whole process would fail since it couldn't update the attribute.

To fix this I just defined a cart variable and find the cart by id using the params[:invoice] as we set this in the user model at the start. All worked well then.


32. Carel Jonkhout Apr 29, 2010 at 17:33

Thank for this very nice railscast. I have one little critisism however. I think is it better to send a different secret with every paypal checkout. It's not hard to do and much more secure.


33. teresa May 28, 2010 at 02:00

I really appreciate what you post.


34. Jet May 30, 2010 at 14:35

I get an error:

TypeError (can't convert HashWithIndifferentAccess into String)

from serialize :params, which is generated from

def create
  PaymentNotification.create!(:params => params, :cart_id => params[:invoice], :status => params[:payment_status], :transaction_id => params[:txn_id])
  render :nothing => true
end

Any ideas?


35. Jose Jul 14, 2010 at 18:29

What's the difference between the return_url and notify_url?
The return_url was demonstrated in the previous episode but why can't the notify_url be shown working in this episode, with localhost? Wouldn't Paypal be doing the same thing for both - FORM HTTP POST?


36. w.a. Jul 16, 2010 at 06:39

Usefull code will try it!


37. Aaron M Jul 18, 2010 at 17:18

In rails 3, i had to change the protect_from_forgery call to: skip_before_filter :verify_authenticity_token, :only => [:create]


38. bamboo flooring Jul 27, 2010 at 22:30

I really appreciate what you post.


40. Converse all star Aug 02, 2010 at 19:14

fghfghfghf


41. replicawatches Aug 04, 2010 at 19:13

 comes housed in a black plastic case with stainless steel lugs at each side. The black

wristband is nicely crafted from leather, and in case you plan on banging your new watch

around a bit, the display is protected by a durable, scratch-resistant mineral crystal

face.


42. w.a. Aug 11, 2010 at 03:52

can't just take the code without words of thank YOU!


43. free directory list Aug 11, 2010 at 22:32

Thanks for screencast


44. Electrical fan Aug 19, 2010 at 01:48

Having a electric fan in a stuffy office, home or business alike, can be a godsend on a hot summer's day. Heat like nothing else can befuddle the mind, making it hard to concentrate on your all important office work.


45. PVC Vane Aug 19, 2010 at 01:48

Our Vertical Blinds System ranged from Low profile system, High Profile system. mini System. Slim system,Chain control system and wand control system. cassette system etc.


46. China Fitness Equipment Aug 19, 2010 at 01:49

As a branch factory of China Peteresa Group, Peteresa Fitness Equipment Co.,LTD was established in the year 2008, which is dedicated to design and manufacturer high quality home fitness equipments and machines. Our factory mainly supply:
Abdominal exercise equipments such as AB Rocket, AB circle, AB coaster, AB lounge, AB flyer, Total core, Balance power;


47. Body Exercise Machines Aug 19, 2010 at 01:49

We are now experiencing annual sales turnovers worth in excess of USD 5,000,000. Outputing around 100,000,000 fitness equipment annually, our scientific management and strict operating systems ensure that all items meet the standards required for export into countries worldwide.


48. Foil balloon Aug 19, 2010 at 01:58

Foil balloon is an ideal products for paties and events, and fesitivals, you can use them in fun parties, birthday parties, and decorate you Chirstamas day on Christmas festival.
many differents kinds shape.if interest pls do not heistate to contact us.
usual size: 18, 24, 30, 36, inches


49. ceramic sanitary ware Aug 19, 2010 at 02:00

Vacuuming and high-pressure injection technologies are used during shaping process. It helps to eliminate the possible errors may caused by manual operations. Besides, it reduced the possibilities of leaking because of the reduction of internal air bubbles.


50. China Hydraulic Tools Aug 19, 2010 at 02:02

Ningbo Sunter is a recognized manufacture who specializes in kinds of hydraulic tools, including hydraulic cylinder, double-acting cylinder, single-acting clyinder, hydraulic pumps,hydraulic wrenches,hydraulic pusher and Hydraulic Accessories.


51. replicahandbags Aug 20, 2010 at 00:33

I have always liked Outdoor movies, a child standing at the window, looked out from home to

the following. Will be able to see the staff busy figure, a huge white cloth has a child

hang up and soon will be able to see the movie.


52. wholesale new era caps Aug 20, 2010 at 20:50

It's funny how we adopt words and adapt our lexicon to the times. This is a very useful slant on things.


53. cheap air jordans Aug 23, 2010 at 23:21

Thank you for another essential article. Thanks for the article. I'm a complete newb to everything including Ruby but was able to follow along and where it differed from the current version of cucumber or Ruby it actually forced me how to find the info myself.


54. louis vuitton shoes Aug 26, 2010 at 23:14

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


55. Wholesale Electronics Aug 27, 2010 at 00:20

Discount Wholesale Electronics, Wholesale Cell Phones, Electronic Gadgets and More from the Best Dropship Wholesaler


56. snow boots Aug 30, 2010 at 21:15

Ruby but was able to follow along and where it differed from the current version of cucumber or Ruby it actually forced me how to find the info myself.


57. levis belts Sep 01, 2010 at 21:10

I feel like I’m often looking for interesting things to read about a variety of subjects, but I manage to include your blog among my reads every day because you have interesting entries that I look forward to. Here’s hoping there’s a lot more great material coming!

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source