#145
Jan 19, 2009

Integrating Active Merchant

In this episode I show how to add Active Merchant's functionality to a Rails application to make a fully-functional checkout process.
Download (31.4 MB, 18:15)
alternative download for iPod & Apple TV (24.5 MB, 18:15)

Resources

Update: As Pawel pointed out in the comments, make sure to add the card numbers to the filter_parameter_logging call in your application.rb file so they are not logged.

rake gems:install

script/generate nifty_scaffold order new cart_id:integer ip_address:string first_name:string last_name:string card_type:string card_expires_on:date

script/generate model order_transaction order_id:integer action:string amount:integer success:boolean authorization:string message:string params:text
# controllers/application.rb
filter_parameter_logging :card_number, :card_verification

# config/environment.rb
config.gem "activemerchant", :lib => "active_merchant", :version => "1.4.1"

# config/environment/development.rb
config.after_initialize do
  ActiveMerchant::Billing::Base.mode = :test
  ::GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(
    :login => "seller_1229899173_biz_api1.railscasts.com",
    :password => "FXWU58S7KXFC6HBE",
    :signature => "AGjv6SW.mTiKxtkm6L9DcSUCUgePAUDQ3L-kTdszkPG8mRfjaRZDYtSu"
  )
end

# config/environment/test.rb
config.after_initialize do
  ActiveMerchant::Billing::Base.mode = :test
  ::GATEWAY = ActiveMerchant::Billing::BogusGateway.new
end

# config/environment/production.rb
config.after_initialize do
  ActiveMerchant::Billing::Base.mode = :production
  ::GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(
    :login => "seller_1229899173_biz_api1.railscasts.com",
    :password => "FXWU58S7KXFC6HBE",
    :signature => "AGjv6SW.mTiKxtkm6L9DcSUCUgePAUDQ3L-kTdszkPG8mRfjaRZDYtSu"
  )
end

# orders_controller.rb
def create
  @order = current_cart.build_order(params[:order])
  @order.ip_address = request.remote_ip
  if @order.save
    if @order.purchase
      render :action => "success"
    else
      render :action => "failure"
    end
  else
    render :action => 'new'
  end
end

# models/cart.rb
has_one :order

# models/order.rb
class Order < ActiveRecord::Base
  belongs_to :cart
  has_many :transactions, :class_name => "OrderTransaction"
  
  attr_accessor :card_number, :card_verification
  
  validate_on_create :validate_card
  
  def purchase
    response = GATEWAY.purchase(price_in_cents, credit_card, purchase_options)
    transactions.create!(:action => "purchase", :amount => price_in_cents, :response => response)
    cart.update_attribute(:purchased_at, Time.now) if response.success?
    response.success?
  end
  
  def price_in_cents
    (cart.total_price*100).round
  end

  private
  
  def purchase_options
    {
      :ip => ip_address,
      :billing_address => {
        :name     => "Ryan Bates",
        :address1 => "123 Main St.",
        :city     => "New York",
        :state    => "NY",
        :country  => "US",
        :zip      => "10001"
      }
    }
  end
  
  def validate_card
    unless credit_card.valid?
      credit_card.errors.full_messages.each do |message|
        errors.add_to_base message
      end
    end
  end
  
  def credit_card
    @credit_card ||= ActiveMerchant::Billing::CreditCard.new(
      :type               => card_type,
      :number             => card_number,
      :verification_value => card_verification,
      :month              => card_expires_on.month,
      :year               => card_expires_on.year,
      :first_name         => first_name,
      :last_name          => last_name
    )
  end
end

# models/order_transaction.rb
class OrderTransaction < ActiveRecord::Base
  belongs_to :order
  serialize :params
  
  def response=(response)
    self.success       = response.success?
    self.authorization = response.authorization
    self.message       = response.message
    self.params        = response.params
  rescue ActiveMerchant::ActiveMerchantError => e
    self.success       = false
    self.authorization = nil
    self.message       = e.message
    self.params        = {}
  end
end
<!-- views/orders/new.html.erb -->
<% form_for @order do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :first_name %><br />
    <%= f.text_field :first_name %>
  </p>
  <p>
    <%= f.label :last_name %><br />
    <%= f.text_field :last_name %>
  </p>
  <p>
    <%= f.label :card_type %><br />
    <%= f.select :card_type, [["Visa", "visa"], ["MasterCard", "master"], ["Discover", "discover"], ["American Express", "american_express"]] %>
  </p>
  <p>
    <%= f.label :card_number %><br />
    <%= f.text_field :card_number %>
  </p>
  <p>
    <%= f.label :card_verification, "Card Verification Value (CVV)" %><br />
    <%= f.text_field :card_verification %>
  </p>
  <p>
    <%= f.label :card_expires_on %><br />
    <%= f.date_select :card_expires_on, :discard_day => true, :start_year => Date.today.year, :end_year => (Date.today.year+10), :add_month_numbers => true %>
  </p>
  <p><%= f.submit "Submit" %></p>
<% end %>

<!-- views/orders/success.html.erb -->
SUCCESS!

<!-- views/orders/failure.html.erb -->
FAILURE

RSS Feed for Episode Comments 46 comments

1. jflcooper Jan 19, 2009 at 03:12

Awesome as usual...

I'm always checking out the site for new screen casts and i am never disappointed.

can't wait for the others.


2. pimpmaster Jan 19, 2009 at 03:49

Sweeeeeeeet!


3. Branden Jan 19, 2009 at 04:10

It's out of my skill range in Ruby on Rails but I look forward to implementing some of this stuff when I get around to it. Still learning Illustrator and After Effects at the moment :/


4. Vinay Jan 19, 2009 at 04:49

Ryan,

Great stuff!
I was wondering if you could do an episode in this series for a SaaS model subscription or a one time payment instead of the shopping cart model.
Im trying to implement this and its really hard to get my head around the variables that PayPal requires for this.
Its always easier when someone explains them so.. :D
Hope you can meet me somewhere half way on that at least.
Thanks!


5. Martin Strom Jan 19, 2009 at 04:53

Thanks for an incredibly useful screencast. Not only the Active Merchant stuff but how you organize your models, controllers and so on.

Super!


6. Ray Rogers Jan 19, 2009 at 05:26

Thank for this series on PayPal and Active Merchant. Can't wait until next week episode, please keep them coming. I'm starting my first e-commerce project with Rails for a friend. This is a great look inside laying that important foundation.

One more note: Thank you for the screen casts over at Pragmatic Studio. I have learned a lot from both of them.


7. Jonas Schneider Jan 19, 2009 at 06:02

Best episode ever in the series.

Go for recurring billing please!


8. Jack VandaL Jan 19, 2009 at 09:59

So great episode. Just wanted to mention that I couldn't get this via iTunes. I keep getting an unknown error (-50) when I try to get this episode under the (iPod &Apple TV) group. Thanks again for all the great info.


9. azazeal Jan 19, 2009 at 10:37

GG once again


10. Gustavo Scanferla Jan 19, 2009 at 11:07

Magic!


11. Reuben Jan 19, 2009 at 11:18

"So great episode. Just wanted to mention that I couldn't get this via iTunes. I keep getting an unknown error (-50) when I try to get this episode under the (iPod &Apple TV) group. Thanks again for all the great info."

+1


12. Pawel Jan 19, 2009 at 11:48

I would suggest using filter_parameter_logging to prevent credit card information to be stored in logs.

Anyway, nice episode -- as always!


13. Ryan Bates Jan 19, 2009 at 13:16

Thanks for the feedback guys. The iTunes problem should be fixed shortly.

@Pawel, great suggestion. I'll add it to the show notes.


14. Ray Jan 19, 2009 at 19:14

Hi Ryan,

Great episode!
Do you think it will be possible to show the different configurations which will need to be done to use the other gateways?

I would like to see how to do this with 2checkout (since I'm from Trinidad and Tobago, and PayPal isn't an option since we can't withdraw funds)

Thanks.


15. Dillo Jan 19, 2009 at 19:21

Hi Ryan,

Awesome screen cast as usual! I'd also like to point out that some credit card companies like Visa prohibit storage of CVV values in any form or manner. Not sure if this is company policy or actual law though.

I've seen this value getting stored in the logs of some of the applications I've worked on in the past, and thought that it might be great if you can point this out to some of the new comers to ecommerce.


16. John Northrup Jan 19, 2009 at 21:32

@Dillo

It's not just CVV that Visa cares about, it's what is called PII (Person Identifiable Information). Anyone that codifies applications that process credit card data should familiarize themselves with what is known as the PCI DSS Security Standards found over at https://www.pcisecuritystandards.org


17. Adam Jan 20, 2009 at 11:26

Brilliant stuff, really.

This series is absolutely fantastic. CC processing is just one of those black arts that nobody ever discusses well, so your episodes here are very valuable.
I'm especially looking forward to the recurring billing episode so I can implement it within my own rails app that I want to start charging a subscription for.

I'll be donating something your way shortly to say thanks for your hard work.


18. Patrick Leahy Jan 20, 2009 at 20:51

Hey Ryan,
This is good stuff but I did notice a problem. It happened when I used attr_accessor myself. At 9:25, you submit the form and validator displays the errors - but it doesn't highlight the problem fields. How can we fix this? It's the one thing that's keeping my application from going into production.

Thanks!


19. Ryan Wilson Jan 20, 2009 at 21:16

Great article, especially the layout of the models. Could you do one on how to extend active merchant so that we can all contribute to the code base? I'd love to learn how to write another gateway, or extend AM to do recurring billing or tagged transactions.

Thanks!


20. Chip Castle Jan 21, 2009 at 12:28

Great screencast, as usual!

I have already implemented ActiveMerchant on my site using the Braintree gateway, but I think a separate screencast on recurring billing using their vault would be especially helpful, even if it is with a different gateway.

BTW: Your code is super CLEAN. Very readable. You always produce the BEST screencasts.

Thanks Ryan!
Chip


21. Alex Jan 21, 2009 at 14:01

Great stuff Ryan! Another vote here for SaaS app recurring billing :)


22. Justin Turney Jan 21, 2009 at 14:13

Excellent work! Can't wait for the next.


23. Michael Graff Jan 24, 2009 at 22:48

As a suggestion for future episodes, one thing that is rather near and dear to me right now is that activerecord is actually getting in my way.

What I mean by that specifically is, I want to use native data types in a PostgreSQL database. However, it seems Activerecord is written to make this not only difficult, but more or less impossible without major changes throughout the database layer.

Why would I want to break database agnostic activerecord? Efficiency, both in terms of processing, searching the database, and the ability to use user-defined data types for speed and space improvements.

I'm currently storing around 30 million records in a single table, and without using the cidr and inet types it would be a nightmare.


24. tranquiliste Feb 03, 2009 at 13:26

Hello,

Great episodes on payment, they are very useful. I have just one question you may have address in other rails cast:

How do you do to have https form?


25. Sean Schofield Feb 04, 2009 at 18:30

For Rails people interested in commerce you should check out Spree which is an open source rails commerce platform. It uses ActiveMechant.


26. Scott Feb 11, 2009 at 17:39

I have the same problem as Patrick above. When you submit the form and the validations fails, none of the Credit Card fields get tagged with class="fieldWithErrors". Does anyone know how to get them to?


27. ttyl Feb 17, 2009 at 08:40

Hi,

How do i go about adding invoice ID. and Item Title to the gateway option?


28. Richard Genthner Feb 17, 2009 at 14:31

have you tired this with authorize.net? I'm trying to customize it to make it work with the authorize.net. Ideas?


29. william lai Feb 23, 2009 at 11:04

1. Richard: I believe active merchant already works with authorize.net. The gateway you need to instantiate will need to be different.

2. Did anyone else run into problems with this error on rake gems:install :-

/Library/Ruby/Site/1.8/rubygems.rb:149:in `activate': can't activate
activesupport (>= 1.4.1, runtime), already activated
activesupport-2.1.0 (Gem::Exception)

I searched and searched but found no solution.


30. Edgar J. Suárez Mar 03, 2009 at 11:59

Excellent!

+1 recurring billing!


31. Alex Mar 04, 2009 at 08:46

Hi,
I have a question about the fields.

I need to create a payment form which have the "name on card" field and I don't have the field "CVV", can the field "name on card" be sent with the payment and also can the field "CVV" be discarted?


32. Dave Reid Apr 23, 2009 at 13:07

I'm trying to implement much of this code into an application I'm working on and I get this error:
The error occurred while evaluating nil.month
When I try to place an order... Any thoughts?


33. Tim Matheson Apr 23, 2009 at 20:45

@Dave Reid
You are calling the method month an an object that contains nil class. Try using debugger to find out where your object becomes nil or call debugger conditional.

if object.nil?
  debugger
end

Good luck.


34. Chip May 06, 2009 at 11:34

My transactions suddenly seem to be going through to PayPal's development sandbox without any Billing information.

Do other gateways allow actual transactions without billing info (now possibly PayPal again)?


35. Dave Reid May 07, 2009 at 13:34

@Tim thanks I figured that one out...

Any thoughts on why the code doesn't pass the purchase_options to paypal?


36. Sergei May 22, 2009 at 02:50

How to download source code from github?


37. شات صوتي Jun 04, 2009 at 22:36

thx............


38. Jade Jun 08, 2009 at 08:29

that's rather hard to unerstand


39. pjammer Aug 01, 2009 at 07:54

Great tutorial as always. Quick question regarding second submittals. If a user gets the error message (which i've added) but then needs to resubmit their order again, I assume they go to the new action again. but doesn't this create another Order record in the DB? should we redirect them to an EDIT method, so we can reuse that same line in the Order table?

or am i missing something?


40. Tony Primerano Aug 26, 2009 at 05:05

Excellent railscast. Thanks for doing these. I was up and running with Linkpoint/FirstData in no time.

I had the same question as pjammer. If the transaction fails I guess we need to implement the update action for the order controller. My only concern with this is that it might inhibit debugging of transaction problems since the order database will change as the user makes corrections.. but then again most of the information they are sending that would cause an error is private so probably not an issue.

Thanks again.

My linkpoint setup notes are here
http://tonycode.com/wiki/index.php?title=First_Data_Ruby_on_Rails_GEM


41. linoj Sep 24, 2009 at 19:41

fyi, I recently posted a screencast on my new SaasRamp plugin for doing recurring billing for software as a service (saas)
http://www.vaporbase.com/postings/SaasRamp_Screencast


42. Josh Dec 25, 2009 at 06:49

Great screencast. One small point / question for Ryan.

You are using a CART model for recording a line item selected for purchase. I am assuming you would have a CART -> LINE_ITEM type relationship.

You are then basically recording that "this cart has been purchased" with the relationship to the ORDER table.

Im curious if this is your preferred structure for an eCommerce app? Spree, for example, does not have a cart. Everything goes into an ORDER -> LINE_ITEM type relationship and then you eventually mark an order as "complete" when purchased.

Im really REALLY struggling with the best eCommerce structure and was hoping you could provide some direction towards what you've done.


43. Yuval Jan 17, 2010 at 21:55

Josh, that's a great question. I've had the exact same one in scouring the web for rails ecom solutions. Right now I have a single cart per user, and multiple orders which are created based on the cart's contents. Once an order is complete, the cart is cleared.

However, now that I'm actually integrating the paypal code, this isn't going to work, as a unique "invoice id" is required for every express checkout. Which leaves me wondering if I should be creating a new cart every time, and making that the order, or as you say, starting with orders which seems to be a cleaner solution rather than having bizarre order <-> cart relationships.


44. Yuval Jan 18, 2010 at 21:43

Hey guys. Hoping someone might still be reading these comments. I am not having any luck getting through to paypal and its driving me nuts. Everything works perfectly if I use the railscasts paypal_options from github (username, password, api sig). But if I use my own, I get "This transaction cannot be processed due to an invalid merchant configuration."

I have no idea what's broken. I have a valid sandbox account with a test seller account, which has a bank account and API credentials. Anyone?


45. Yuval Jan 19, 2010 at 05:37

Whew, figured it out. Lets just say the paypal sandbox has become a good deal more onnerous since this railscast was first posted. Be sure and create a test seller account and a test website payments pro account from the same screen. Then log in to your seller account, click on profile to get the API sig, then make sure you agree to the wpp billing agreement. Otherwise your transactions will fail. Oh, and ignore every instance where paypal says it will set up api creds/bak accounts/etc for you automatically. It never works.


46. uggs online Jan 29, 2010 at 21:30

Hey, there I am impressed the way you have gathered and articulated such information, it was a nice read


47. Ram Feb 03, 2010 at 02:48

Good one. As usual ! Keep them coming :o)

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