#145 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:
- source codeProject Files in Zip (108 KB)
- mp4Full Size H.264 Video (30.1 MB)
- m4vSmaller H.264 Video (19.9 MB)
- webmFull Size VP8 Video (50 MB)
- ogvFull Size Theora Video (39.9 MB)
Resources
- Episode 144: Active Merchant Basics
- Active Merchant
- Active Merchant PeepCode PDF
- PayPal's Developer Site
- PayPal's Website Payment Pro Guide PDF
- Nifty Generators
- Full Episode Source Code
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.
bash
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
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
filter_parameter_logging :card_number, :card_verification
config/environment.rb
config.gem "activemerchant", :lib => "active_merchant", :version => "1.4.1"
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.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.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
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
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
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
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
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 %>
<% 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!
SUCCESS!
views/orders/failure.html.erb
FAILURE
FAILURE

