Thursday 8 May 2014

Integrate active merchant in rails 3.2

Github repository:--

https://github.com/shamsulsham89/active_merchant_rails3.2
 
http://railscasts.com/episodes/145-integrating-active-merchant

Create a new rails 3.2 app as:-

  rails new active_merchant_payment

Generate scaffold as:-

rails g scaffold product name price:decimal

rails g scaffold cart purchased_at:datetime

rails g scaffold line_item unit_price:decimal product_id:integer cart_id:integer quantity:integer

rails g scaffold order cart_id:integer ip_address:string first_name:string last_name:string card_type:string card_expires_on:date

rails g model order_transaction order_id:integer  action:string amount:integer success:boolean authorization:string message:string params:text

Add in Gemfile:-

gem 'activemerchant'

In routes.rb:-

root :to => 'products#index'

In app/models/cart.rb:--

class Cart < ActiveRecord::Base
  attr_accessible :purchased_at
  has_many :line_items
  has_one :order
 
  def total_price
    # convert to array so it doesn't try to do sum on database directly
    line_items.to_a.sum(&:full_price)
  end
end

In app/models/line_item.rb:--

class LineItem < ActiveRecord::Base
  attr_accessible :cart_id, :product_id, :quantity, :unit_price
  belongs_to :cart
  belongs_to :product
 
  def full_price
    unit_price * quantity
  end
end


In app/views/products/index.html.erb:-

<h1>Listing products</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Price</th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @products.each do |product| %>
  <tr>
    <td><%= product.name %></td>
    <td><%= product.price %></td>
    <td><%= link_to "Add to Cart", line_items_path(:product_id => product), :method => :post %></td>
    <td><%= link_to 'Show', product %></td>
    <td><%= link_to 'Edit', edit_product_path(product) %></td>
    <td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Product', new_product_path %>


In application_controller.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 


In line_items_controller.rb:-


def create
    @product = Product.find(params[:product_id])
    @line_item = LineItem.create!(:cart_id => current_cart.id, :product_id =>   @product.id, :quantity => 1, :unit_price => @product.price)
    flash[:notice] = "Added #{@product.name} to cart."
    redirect_to cart_url(:current)
  end

In carts_controller.rb:--


def show
    @cart = current_cart
 end

In app/views/carts/show.html.erb:-

<p id="notice"><%= notice %></p>


<h4>Products in Your cart</h4>
<table>
  <tr>
    <th>Product Name</th>
    <th>Unit Price</th>
    <th>Full Price</th>
  </tr>
  <% for line_item in @cart.line_items %>
    <tr>
      <td><%= line_item.product.name %></td>
      <td><%= line_item.unit_price %></td>
      <td><%= line_item.full_price %></td>
    </tr>
  <% end %>
</table>
<h3>Total: <%= number_to_currency @cart.total_price %> </h3>

<p>
  <%= link_to "Continue Shopping", products_path %> 
  <% if  @cart.line_items.first %>
    |
    <%= link_to "Checkout", new_order_url %>
  <% end %>
</p>

In app/models/order.rb:--

class Order < ActiveRecord::Base
  attr_accessible :card_expires_on, :card_type, :cart_id, :first_name, :ip_address, :last_name
  attr_accessible :card_number, :card_verification
  belongs_to :cart
 
  has_many :transactions, :class_name => "OrderTransaction"
  attr_accessor :card_number, :card_verification

  validate :validate_card, :on => :create
 
  validates :first_name,  presence: true
  validates :last_name,  presence: true
  validates :card_number,  presence: true
  validates :card_verification,  presence: true

  def purchase
    response = GATEWAY.purchase(price_in_cents, credit_card, purchase_options) rescue false
    unless response
      self.errors.add("base", "Unable to make payment")
      return false
    end

    if response.success? == false  
      errors[:base] << response.message
      return false      
    end

    transactions.create!(:action => "purchase", :amount => price_in_cents, :response => response)
    cart.update_attribute(:purchased_at, Time.now) if response.success?
    response.success?
  end
  def authorize


    response = GATEWAY.authorize(price_in_cents, credit_card, purchase_options) rescue false  
    puts "*******************"  
    puts response.inspect   
    puts "************************"
    unless response  
      errors[:base] << "Unable to authorize credit card"
      return false
    end
     
   
    if response.success? == false  
      if response.avs_result['code'] != "P" 
        errors[:base] << response.avs_result['message']
        return false  
      end
      errors[:base] << response.message
      return false      
    end       

    transactions.create!(:action => "authorize", :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     => "Shamsul Haque",
        :address1 => "Rajiv Chowk",
        :city     => "New Delhi",
        :state    => "Delhi",
        :country  => "India",
        :zip      => "110027"
      },
      :x_test_request => false
    }
  end 

  def validate_card
    unless credit_card.valid?
      credit_card.errors.full_messages.each do |message|
        errors[:base] << message
      end
    end
  end

  def credit_card
    @credit_card ||= ActiveMerchant::Billing::CreditCard.new(
      :brand               => 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

In app/models/order_transaction.rb:-

class OrderTransaction < ActiveRecord::Base
  attr_accessible :action, :amount, :authorization, :message, :order_id, :params, :success, :response
  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

In app/controllers/orders_controller.rb:--

def new
    @order = Order.new
    @cart = current_cart
   
    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @order }
    end
  end

  def create
    @cart = current_cart
    @order = Order.new(params[:order]) 
    @order.cart_id = current_cart.id    
    session[:cart_id] = nil
    @order.ip_address = request.remote_ip
   
    if @order.valid?
      if @order.save && @order.authorize == true
        if @order.purchase
          render :action => "success"
        else
          render :action => "failure"
        end
      else
        render :action => 'new' 
      end
    else
      render :action => 'new'
    end
   end

In app/views/orders:--

Create a new page as  success.html.erb and put the line as:-


SUCCESS!


Create a new page as  failure.html.erb and put the line as:-

FAILURE!

In config/environments/development.rb:--

ActiveMerchantPayment::Application.configure do
  # Settings specified here will take precedence over those in config/application.rb

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Log error messages when you accidentally call methods on nil.
  config.whiny_nils = true

  # Show full error reports and disable caching
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger
  config.active_support.deprecation = :log

  # Only use best-standards-support built into browsers
  config.action_dispatch.best_standards_support = :builtin

  # Raise exception on mass assignment protection for Active Record models
  config.active_record.mass_assignment_sanitizer = :strict

  # Log the query plan for queries taking more than this (works
  # with SQLite, MySQL, and PostgreSQL)
  config.active_record.auto_explain_threshold_in_seconds = 0.5

  # Do not compress assets
  config.assets.compress = false

  # Expands the lines which load the assets
  config.assets.debug = true
end

ActiveMerchant::Billing::Base.mode = :test

::GATEWAY = ActiveMerchant::Billing::Base.gateway(:authorize_net).new(
  :login => "TestMerchant",
  :password => "password", :test => 'true')