class ActiveMerchant::Billing::Realex3dsGateway

For more information on the Realex Payment Gateway visit their site realexpayments.com. Realex is the leading gateway in Ireland

Merchant ID and Password

To be able to use this library you will need to obtain an account from Realex, you can find contact them via their website.

Caveats

Realex requires that you specify the account to which your transactions are made.

gateway = ActiveMerchant::Billing::Realex3dsGateway.new(:login => 'xxx', :password => 'xxx', :acction => 'xxx')

If you wish to accept multiple currencies, you need to create an account per currency. This you would need to handle within your application logic. Again, contact Realex for more information.

They also require accepting payment from a Diners card (Mastercard) go through a different account.

Realex also requires that you send several (extra) required identifiers with credit and void methods

The pasref can be accessed from the response params. i.e.

response.params['pasref']

Testing

Realex provide test card numbers on a per-account basis, you will need to request these. Then if you copy the fixtures file that comes with this library to ~/.active_merchant/fixtures.yml you can add in the required card number (and account) fixtures.

Constants

BANK_ERROR
CARD_MAPPING
DECLINED
ERROR
RECURRING_PAYMENTS_URL
THREE_D_SECURE_URL
URL

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 69
def initialize(options = {})
  requires!(options, :login, :password)
  options[:refund_hash] = Digest::SHA1.hexdigest(options[:rebate_secret]) if options.has_key?(:rebate_secret)
  @options = options
  super
end

Private Class Methods

timestamp() click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 605
def self.timestamp
  Time.now.strftime('%Y%m%d%H%M%S')
end

Public Instance Methods

authorize(money, creditcard, options = {}) click to toggle source

Performs an authorization, which reserves the funds on the customer’s credit card, but does not charge the card.

Parameters

  • money – The amount to be authorized. Either an Integer value in cents or a Money object.

  • creditcard – The CreditCard details for the transaction.

  • options – A hash of optional parameters.

Options

  • :order_id – The application generated order identifier. (REQUIRED)

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 89
def authorize(money, creditcard, options = {})
  requires!(options, :order_id)

  if options[:three_d_secure]
    three_d_secure_request = build_3d_secure_verify_signature_or_enrolled_request("3ds-verifyenrolled", money, creditcard, options)
    three_d_secure_response = commit(three_d_secure_request, :three_d_secure)
    return three_d_secure_response if three_d_secure_response.enrolled?
  end
  
  request = build_purchase_or_authorization_request(:authorization, money, creditcard, options) 
  commit(request)
end
capture(money, authorization, options = {}) click to toggle source

Captures the funds from an authorized transaction.

Parameters

  • money – The amount to be captured. Either an Integer value in cents or a Money object.

  • authorization – The authorization returned from the previous authorize request.

Options

  • :order_id – The application generated order identifier. (REQUIRED)

  • :pasref – The realex payments reference of the original transaction. (REQUIRED)

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 174
def capture(money, authorization, options = {})
  requires!(options, :pasref)
  requires!(options, :order_id)
  
  request = build_capture_request(authorization, options) 
  commit(request)
end
credit(money, authorization, options = {}) click to toggle source

Credit an account.

This transaction is also referred to as a Refund (or Rebate) and indicates to the gateway that money should flow from the merchant to the customer.

Parameters

  • money – The amount to be credited to the customer. Either an Integer value in cents or a Money object.

  • authorization - The authorization returned from the previous authorize request.

  • options – A hash of parameters.

Options

  • :order_id – The application generated order identifier. (REQUIRED)

  • :pasref – The realex payments reference of the original transaction. (REQUIRED)

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 198
def credit(money, authorization, options = {})
  requires!(options, :order_id)
  requires!(options, :pasref)
  
  request = build_credit_request(money, authorization, options)
  commit(request)
end
purchase(money, creditcard, options = {}) click to toggle source

Perform a purchase, which is essentially an authorization and capture in a single operation.

Parameters

  • money – The amount to be purchased. Either an Integer value in cents or a Money object.

  • creditcard – The CreditCard details for the transaction.

  • options – A hash of optional parameters.

Options

  • :order_id – The application generated order identifier. (REQUIRED)

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 114
def purchase(money, creditcard, options = {})
  requires!(options, :order_id)

  if options[:three_d_secure_auth]
    three_d_secure_request = build_3d_secure_verify_signature_or_enrolled_request("3ds-verifysig", money, creditcard, options)
    three_d_secure_response = commit(three_d_secure_request, :three_d_secure)
    result = three_d_secure_response.params['result']
    if result  == '00'
      status = three_d_secure_response.params['threedsecure_status'] 
      # success
      if status == 'Y' || status == 'A'
        # Y: 3d Secure complete.
        # A: ACS service aknowledges.
        # not-liable. continue
        options[:three_d_secure_sig] = {}
        options[:three_d_secure_sig][:eci]  = three_d_secure_response.params['threedsecure_eci']
        options[:three_d_secure_sig][:xid]  = three_d_secure_response.params['threedsecure_xid']
        options[:three_d_secure_sig][:cavv] = three_d_secure_response.params['threedsecure_cavv']
        # TODO add option[:accept_liability_authentication_failed]
        # TODO add option[:accept_liability_acs_failure]

      elsif status == 'N'
        # password entered incorrectly
        # liable. abort?
        return Response.new(false, "3DSecure password entered incorrectly. Aborting transaction.",{},{})
      elsif status == 'U'
        # Bank ACS service having dificulty.
        # liable. abort?
        return Response.new(false, "3DSecure Bank ACS service 500 errors. Aborting transaction.",{},{})
      end  
    elsif result == "110"
      # fail, message tampered with.
      return Response.new(false, "3DSecure message tampered. Aborting transaction.",{},{})
    else
      return Response.new(false, "Unknown 3DSecure Error.",{},{})
    end
  end
  
  if options[:three_d_secure] && !options[:three_d_secure_auth]
    three_d_secure_request = build_3d_secure_verify_signature_or_enrolled_request("3ds-verifyenrolled", money, creditcard, options)
    three_d_secure_response = commit(three_d_secure_request, :three_d_secure)
    return three_d_secure_response if three_d_secure_response.enrolled?
  end
  
  request = build_purchase_or_authorization_request(:purchase, money, creditcard, options)
  commit(request)
end
recurring(money, credit_card, options = {}) click to toggle source

Recurring Payments

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 227
def recurring(money, credit_card, options = {})
  requires!(options, :order_id)

  request = build_receipt_in_request(money, credit_card, options) 
  commit(request, :recurring)
end
store(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 234
def store(credit_card, options = {})
  requires!(options, :order_id)
  request = build_new_card_request(credit_card, options)
  commit(request, :recurring)
end
store_user(options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 245
def store_user(options = {})
  requires!(options, :order_id)
  request = build_new_payee_request(options)
  commit(request, :recurring)
end
unstore(creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 240
def unstore(creditcard, options = {})     
  request = build_cancel_card_request(creditcard, options)
  commit(request, :recurring)
end
void(authorization, options = {}) click to toggle source

Void a previous transaction

Parameters

  • authorization - The authorization returned from the previous authorize request.

Options

  • :order_id – The application generated order identifier. (REQUIRED)

  • :pasref – The realex payments reference of the original transaction. (REQUIRED)

# File lib/active_merchant/billing/gateways/realex3ds.rb, line 217
def void(authorization, options = {})
  requires!(options, :order_id)
  requires!(options, :pasref)
  
  request = build_void_request(authorization, options) 
  commit(request)
end

Private Instance Methods

add_address_and_customer_info(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 460
def add_address_and_customer_info(xml, options)
  billing_address = options[:billing_address] || options[:address]
  shipping_address = options[:shipping_address]
  
  return unless billing_address || shipping_address || options[:customer] || options[:invoice] || options[:ip]
  
  xml.tag! 'tssinfo' do
    
    xml.tag! 'custnum', options[:customer] if options[:customer]
    xml.tag! 'prodid', options[:invoice] if options[:invoice]
    xml.tag! 'custipaddress', options[:ip] if options[:ip]
    # xml.tag! 'varref'
    
    if billing_address
      xml.tag! 'address', 'type' => 'billing' do
        xml.tag! 'code', avs_input_code_or_zip( billing_address, options )
        xml.tag! 'country', billing_address[:country]
      end
    end
    
    if shipping_address
      xml.tag! 'address', 'type' => 'shipping' do
        xml.tag! 'code', shipping_address[:zip]
        xml.tag! 'country', shipping_address[:country]
      end
    end
    
  end
end
add_ammount(xml, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 514
def add_ammount(xml, money, options)
  xml.tag! 'amount', amount(money), 'currency' => options[:currency] || currency(money)
end
add_card(xml, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 518
def add_card(xml, credit_card)
  xml.tag! 'card' do
    xml.tag! 'number', credit_card.number
    xml.tag! 'expdate', expiry_date(credit_card)
    xml.tag! 'chname', credit_card.name
    xml.tag! 'type', CARD_MAPPING[card_brand(credit_card).to_s]
    xml.tag! 'issueno', credit_card.issue_number
    xml.tag! 'cvn' do
      xml.tag! 'number', credit_card.verification_value
      xml.tag! 'presind', (options['presind'] || (credit_card.verification_value? ? 1 : nil))
    end
  end
end
add_comments(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 507
def add_comments(xml, options)
  return unless options[:description]
  xml.tag! 'comments' do
    xml.tag! 'comment', options[:description], 'id' => 1 
  end
end
add_merchant_details(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 494
def add_merchant_details(xml, options)
  xml.tag! 'merchantid', @options[:login] 
  if options[:account] || @options[:account]
    xml.tag! 'account', options[:account] || @options[:account]
  end
end
add_signed_digest(xml, *values) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 550
def add_signed_digest(xml, *values)
  string = stringify_values(values)
  xml.tag! 'sha1hash', sha1from(string)
end
add_three_d_secure(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 433
def add_three_d_secure(xml, options)
  if options[:three_d_secure_sig]
    xml.tag! 'mpi' do
      xml.tag! 'cavv', options[:three_d_secure_sig][:cavv]
      xml.tag! 'xid', options[:three_d_secure_sig][:xid]
      xml.tag! 'eci', options[:three_d_secure_sig][:eci]
    end
  end
end
add_transaction_identifiers(xml, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 501
def add_transaction_identifiers(xml, authorization, options)
  xml.tag! 'orderid', sanitize_order_id(options[:order_id])
  xml.tag! 'pasref', options[:pasref]
  xml.tag! 'authcode', authorization
end
auto_settle_flag(action) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 555
def auto_settle_flag(action)
  action == :authorization ? '0' : '1'
end
avs_input_code(address) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 532
def avs_input_code(address)
  address.values_at(:zip, :address1).map{ |v| extract_digits(v) }.join('|')
end
avs_input_code_or_zip(address, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 490
def avs_input_code_or_zip(address, options)
  options[ :skip_avs_check ] ? address[ :zip ] : avs_input_code( address )
end
build_3d_secure_verify_signature_or_enrolled_request(action, money, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 418
def build_3d_secure_verify_signature_or_enrolled_request(action, money, credit_card, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => action do
    add_merchant_details(xml, options)
    xml.tag! 'orderid', sanitize_order_id(options[:order_id])
    add_ammount(xml, money, options)
    add_card(xml, credit_card)
    xml.tag!('pares', options[:three_d_secure_auth][:pa_res]) if(action == '3ds-verifysig' && options[:three_d_secure_auth] )
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], amount(money), (options[:currency] || currency(money)), credit_card.number)
    add_comments(xml, options)
  end
  xml.target!
end
build_cancel_card_request(creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 363
def build_cancel_card_request(creditcard, options = {})
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'card-cancel-card' do
    add_merchant_details(xml, options)
    xml.tag! 'card' do          
      xml.tag! 'ref', options[:payment_method]
      xml.tag! 'payerref', options[:user][:id]
      xml.tag! 'expdate', expiry_date(creditcard)
    end
    # TODO userid . card ref . expiry date
    add_signed_digest(xml, timestamp, @options[:login], options[:user][:id], options[:payment_method])
  end
end
build_capture_request(authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 324
def build_capture_request(authorization, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'settle' do
    add_merchant_details(xml, options)
    add_transaction_identifiers(xml, authorization, options)
    add_comments(xml, options)
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], '', '', '')
  end
  xml.target!
end
build_credit_request(money, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 336
def build_credit_request(money, authorization, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'rebate' do
    add_merchant_details(xml, options)
    add_transaction_identifiers(xml, authorization, options)
    xml.tag! 'amount', amount(money), 'currency' => options[:currency] || currency(money)
    xml.tag! 'refundhash', @options[:refund_hash] if @options[:refund_hash]
    xml.tag! 'autosettle', 'flag' => 1          
    add_comments(xml, options)
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], amount(money), (options[:currency] || currency(money)), '')
  end
  xml.target!
end
build_new_card_request(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 378
def build_new_card_request(credit_card, options = {})
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'card-new' do
    add_merchant_details(xml, options)
    xml.tag! 'orderid', sanitize_order_id(options[:order_id])
    xml.tag! 'card' do
      xml.tag! 'ref', options[:payment_method]
      xml.tag! 'payerref', options[:user][:id]
      xml.tag! 'number', credit_card.number
      xml.tag! 'expdate', expiry_date(credit_card)
      xml.tag! 'chname', credit_card.name
      xml.tag! 'type', CARD_MAPPING[card_brand(credit_card).to_s]
      xml.tag! 'issueno', credit_card.issue_number
      xml.tag! 'cvn' do
        xml.tag! 'number', credit_card.verification_value
        xml.tag! 'presind', (options['presind'] || (credit_card.verification_value? ? 1 : nil))
      end
    end
    # timestamp.merchantid.orderid.amount.currency.payerref.chname.(card)number
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], '', '', options[:user][:id], credit_card.name, credit_card.number)
  end
  xml.target!
end
build_new_payee_request(options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 403
def build_new_payee_request(options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'payer-new' do
    add_merchant_details(xml, options)
    xml.tag! 'orderid', sanitize_order_id(options[:order_id])
    xml.tag! 'payer', 'type' => 'Business', 'ref' => options[:user][:id] do
      xml.tag! 'firstname', options[:user][:first_name]
      xml.tag! 'surname', options[:user][:last_name]
    end
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], '', '', options[:user][:id])
  end
  xml.target!
end
build_purchase_or_authorization_request(action, money, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 307
def build_purchase_or_authorization_request(action, money, credit_card, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'auth' do
    add_merchant_details(xml, options)
    xml.tag! 'orderid', sanitize_order_id(options[:order_id])
    add_ammount(xml, money, options)
    add_card(xml, credit_card)
    xml.tag! 'autosettle', 'flag' => auto_settle_flag(action)
    add_three_d_secure(xml, options) if options[:three_d_secure_sig]
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], amount(money), (options[:currency] || currency(money)), credit_card.number)
    add_comments(xml, options)
    add_address_and_customer_info(xml, options)
  end
  xml.target!
end
build_receipt_in_request(money, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 443
def build_receipt_in_request(money, credit_card, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'receipt-in' do
    add_merchant_details(xml, options)
    xml.tag! 'orderid', sanitize_order_id(options[:order_id])
    add_ammount(xml, money, options)
    xml.tag! 'payerref', options[:user][:id]
    xml.tag! 'paymentmethod', options[:payment_method]
    xml.tag! 'autosettle', 'flag' => '1'
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], amount(money), (options[:currency] || currency(money)), options[:user][:id])
    add_comments(xml, options)
    add_address_and_customer_info(xml, options)
  end
  xml.target!
end
build_void_request(authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 351
def build_void_request(authorization, options)
  timestamp = self.class.timestamp
  xml = Builder::XmlMarkup.new :indent => 2
  xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'void' do
    add_merchant_details(xml, options)
    add_transaction_identifiers(xml, authorization, options)
    add_comments(xml, options)
    add_signed_digest(xml, timestamp, @options[:login], options[:order_id], '', '', '')
  end
  xml.target!
end
commit(request, endpoint=:default) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 252
def commit(request, endpoint=:default)
  url = URL
  url = THREE_D_SECURE_URL if endpoint == :three_d_secure
  url = RECURRING_PAYMENTS_URL if endpoint == :recurring
  
  response = ssl_post(url, request)
  parsed = parse(response)

  options = {
    :test => parsed[:message] =~ /\[ test system \]/,
    :authorization => parsed[:authcode],
    :cvv_result => parsed[:cvnresult],
    :body => response,
    :avs_result => {
      :street_match => parsed[:avsaddressresponse],
      :postal_match => parsed[:avspostcoderesponse]
    }
  }

  if endpoint == :three_d_secure
    options.merge!({
      :pa_req => parsed[:pareq],
      :acs_url => parsed[:url],
      :three_d_secure => true,
      :xid => parsed[:xid],
      :three_d_secure_enrolled => parsed[:enrolled] == "Y" ? true : false
    })
  end

  Response.new(parsed[:result] == "00", message_from(parsed), parsed, options)
end
expiry_date(credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 559
def expiry_date(credit_card)
  "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
end
extract_digits(string) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 536
def extract_digits(string)
  return "" if string.nil?
  string.gsub(/[\D]/,'')
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 577
def message_from(response)
  message = nil
  case response[:result]                
  when "00"
    message = SUCCESS
  when "101"
    message = response[:message]
  when "102", "103"
    message = DECLINED
  when /^2[0-9][0-9]/
    message = BANK_ERROR
  when /^3[0-9][0-9]/
    message = REALEX_ERROR
  when /^5[0-9][0-9]/
    message = response[:message]
  when "600", "601", "603"
    message = ERROR
  when "666"
    message = CLIENT_DEACTIVATED
  else
    message = DECLINED
  end  
end
normalize(field) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 567
def normalize(field)
  case field
  when "true"   then true
  when "false"  then false
  when ""       then nil
  when "null"   then nil
  else field
  end        
end
parse(xml) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 284
def parse(xml)
  response = {}
          
  xml = REXML::Document.new(xml)
  
  return response unless xml.root

  xml.elements.each('//response/*') do |node|

    if (node.elements.size == 0)
      response[node.name.downcase.to_sym] = normalize(node.text)
    else
      node.elements.each do |childnode|
        name = "#{node.name.downcase}_#{childnode.name.downcase}"
        response[name.to_sym] = normalize(childnode.text)
      end              
    end

  end
  
  response
end
sanitize_order_id(order_id) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 601
def sanitize_order_id(order_id)
  order_id.to_s.gsub(/[^a-zA-Z0-9\-_]/, '')
end
sha1from(string) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 563
def sha1from(string)
  Digest::SHA1.hexdigest("#{Digest::SHA1.hexdigest(string)}.#{@options[:password]}")
end
stringify_values(values) click to toggle source
# File lib/active_merchant/billing/gateways/realex3ds.rb, line 541
def stringify_values(values)
  string = ""
  values.each do |val|
    string << "#{val}"
    string << "." unless val.equal?(values.last)
  end
  string
end