Sunday, 1 April 2012

Realex RealVault ActiveMerchant implementation

The following is the extension of the ActiveMerchant gem that will allow the system to store information about the customer and its credit card in realex, typically this is used for recurring payments (in fact it's the name of the pdf provided by Realex), but in our case the need was for charging customers that don't show up at the hotel. It's not the final refactory.

# see: RealVault - Recurring Payments.pdf
module ActiveMerchant::Billing

  class RealVault < RealexGateway
    # 7. on page 24
    URL = 'https://check-doc.cgi'

    # 7.1 (Setup a new Payer)
    # Create a Profile for a payer using payer‐new
    # If the payer exists another one will be stored
    # payer is a hash (see self.test)
    def send_payer(payer, options = {})
      requires! options, :order_id
      request = build_payer_new_request payer, options
      commit request, URL
    end
  
    # 7.2 (Payment method setup)
    # Store the Card details using Card‐New
    # If the card exists another one will be stored
    # card is a ActiveMerchant::Billing::CreditCard
    # options is a hash
    def send_card(card, options)
      requires! options, :order_id
      request = build_card_new_request card, options
      commit request, URL
    end

    # see self.test or send_x methods
    def store!(payer, card, options)
      send_payer_response = send_payer(payer, options)
      send_card_response  = send_card(card, options) if send_payer_response.success?
      
      { :success? => (send_payer_response.success? && send_card_response.success?),
        :send_payer_response => send_payer_response,
        :send_card_response  => send_card_response }
    end

    # ActiveMerchant::Billing::RealVault.test
    # will store the payer and the card in the test account
    # makes a purchase before as suggested by the doc. to make sure auth is ok
    # works out of the box
    def self.test
      timestamp = Time.new.to_s :number
      currency  = 'EUR'
      firstname = 'John'
      surname   = 'Smith'
      payerref  = timestamp
      orderid   = timestamp
      
      rv = ActiveMerchant::Billing::RealVault.new({:password => "pwd",
                                                   :currency => currency,
                                                   :rebate   => "rebate",
                                                   :account  => "internettest",
                                                   :login    => "merchant_id",
                                                   :gateway  => "Realex"})

      card = ActiveMerchant::Billing::CreditCard.new(
                    :number             => 'test_cc_number',
                    :month              => '12',
                    :year               => '2012',
                    :type               => 'visa',
                    :first_name         => firstname,
                    :last_name          => surname,
                    :verification_value => '123')

      payer = { :type      => 'Business',
                :ref       => payerref, # 'smithj01'
                :title     => 'Mr',
                :firstname => firstname,
                :surname   => surname,
                :email     => 'jsmith@acme.com',
                :company   => 'Acme Inc',
                :address   => {
                  :line1   => '123 Fake St.',
                  # :line2
                  # :line3
                  :postcode     => '3',
                  :city         => 'Hytown',
                  :county       => 'Dunham',
                  :country      => 'Ireland',
                  :country_code => 'IE'
                },
                :phonenumbers => {
                  :home   => '55555555',
                  :work   => '+35317433923',
                  :fax    => '+35317893248',
                  :mobile => '+353873748392',
                }
              }

      options = {:order_id => orderid,
                 :currency => currency,
                 :payerref => payer[:ref]}

      rv.purchase 777, card, options
      rv.store! payer, card, options
      # rv.send_payer payer, options
      # rv.send_card card, options
    end

    private

    def build_card_new_request(card, options)
      action    = 'card-new'
      timestamp = Time.now.strftime '%Y%m%d%H%M%S' # same as to_s(:number), but not subjected to override
      orderid   = sanitize_order_id options[:order_id]
      chname    = "#{card.first_name}#{card.last_name}"
      expdate   = card.year.to_s[2..3] + card.month.to_s
      
      sha1      = sha1from "#{timestamp}.#{@options[:login]}.#{orderid}...#{options[:payerref]}.#{chname}.#{card.number}"

      xml       = Builder::XmlMarkup.new :indent => 2
      
      xml.tag! :request, :timestamp => timestamp, :type => action do
        xml.tag! :merchantid, @options[:login]
        xml.tag! :orderid   , orderid
        xml.tag! :card do
          xml.tag! :ref     , card.type + timestamp[3..14]
          xml.tag! :payerref, options[:payerref]
          xml.tag! :number  , card.number
          xml.tag! :expdate , expdate
          xml.tag! :chname  , chname
          xml.tag! :type    , CARD_MAPPING[card_brand(card).to_s]
          xml.tag! :issueno
        end
        xml.tag! :sha1hash, sha1
      end
    end
    
    
    def build_payer_new_request(payer, options)
      action    = 'payer-new'
      timestamp = Time.now.strftime '%Y%m%d%H%M%S' # same as to_s(:number), but not subjected to override
      orderid   = sanitize_order_id options[:order_id]
      sha1      = sha1from "#{timestamp}.#{@options[:login]}.#{orderid}...#{payer[:ref]}"

      xml       = Builder::XmlMarkup.new :indent => 2

      xml.tag! :request, :timestamp => timestamp, :type => action do
    
        xml.tag! :merchantid, @options[:login] 
        xml.tag! :account   , @options[:account]
        xml.tag! :orderid   , orderid

        xml.tag! :payer, :type => payer[:type], :ref => payer[:ref] do
          [:title, :firstname, :surname, :company].each do |tag|
            xml.tag! tag, payer.send('[]', tag)
          end
          xml.tag! :address do
            address = payer[:address]
            [:line1, :line2, :line3, :city, :county, :postcode].each do |tag|
              xml.tag! tag, address.send('[]', tag)
            end
            xml.tag! :country, address[:country], :code => address[:country_code]
          end
          xml.tag! :phonenumbers do
            phonenumbers = payer[:phonenumbers]
            [:home, :work, :fax, :mobile].each do |tag|
              xml.tag! tag, phonenumbers[tag]
            end
          end
          xml.tag! :email, payer[:email]
          xml.tag! :comments do
            xml.tag! :comment, nil, :id => 1 
            xml.tag! :comment, nil, :id => 2 
          end
        end
        xml.tag! :sha1hash, sha1
        xml.tag! :comments do
            xml.tag! :comment, nil, :id => 1 
            xml.tag! :comment, nil, :id => 2 
        end
      end

      xml.target!
    end


  end


end
Unfortunately even the gem itself needed a slight change, due to constant priority order in Ruby (this wouldn't be necessary in java for example):
  # Realex.rb in ActiveMerchant 1.9.4
  # commit method in private section
-      def commit(request)
+      def commit(request, url = URL)

No comments:

Post a Comment