class ActiveMerchant::Billing::FirstdataE4Gateway

Constants

BRANDS
DEFAULT_ECI
E4_BRANDS
POST_HEADERS
SENSITIVE_FIELDS
STANDARD_ERROR_CODE_MAPPING
SUCCESS
TRANSACTIONS

Public Class Methods

new(options = {}) click to toggle source

Create a new FirstdataE4Gateway

The gateway requires that a valid login and password be passed in the options hash.

Options

  • :login – The EXACT ID. Also known as the Gateway ID.

    (Found in your administration terminal settings)
  • :password – The terminal password (not your account password)

Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 76
def initialize(options = {})
  requires!(options, :login, :password)
  @options = options

  super
end

Public Instance Methods

authorize(money, credit_card_or_store_authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 83
def authorize(money, credit_card_or_store_authorization, options = {})
  commit(:authorization, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 91
def capture(money, authorization, options = {})
  commit(:capture, build_capture_or_credit_request(money, authorization, options))
end
purchase(money, credit_card_or_store_authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 87
def purchase(money, credit_card_or_store_authorization, options = {})
  commit(:sale, build_sale_or_authorization_request(money, credit_card_or_store_authorization, options))
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 99
def refund(money, authorization, options = {})
  commit(:credit, build_capture_or_credit_request(money, authorization, options))
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 143
def scrub(transcript)
  transcript.
    gsub(%r((<Card_Number>).+(</Card_Number>)), '\1[FILTERED]\2').
    gsub(%r((<VerificationStr2>).+(</VerificationStr2>)), '\1[FILTERED]\2').
    gsub(%r((<Password>).+(</Password>))i, '\1[FILTERED]\2').
    gsub(%r((<CAVV>).+(</CAVV>)), '\1[FILTERED]\2').
    gsub(%r((Card Number : ).*\d)i, '\1[FILTERED]')
end
store(credit_card, options = {}) click to toggle source

Tokenize a credit card with TransArmor

The TransArmor token and other card data necessary for subsequent transactions is stored in the response's authorization attribute. The authorization string may be passed to authorize and purchase instead of a ActiveMerchant::Billing::CreditCard instance.

TransArmor support must be explicitly activated on your gateway account by FirstData. If your authorization string is empty, contact FirstData support for account setup assistance.

Example

# Generate token
result = gateway.store(credit_card)
if result.success?
  my_record.update_attributes(:authorization => result.authorization)
end

# Use token
result = gateway.purchase(1000, my_record.authorization)

firstdata.zendesk.com/entries/21303361-transarmor-tokenization

# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 130
def store(credit_card, options = {})
  commit(:store, build_store_request(credit_card, options), credit_card)
end
supports_network_tokenization?() click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 152
def supports_network_tokenization?
  true
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 139
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 103
def verify(credit_card, options = {})
  commit(:verify, build_sale_or_authorization_request(0, credit_card, options))
end
verify_credentials() click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 134
def verify_credentials
  response = void('0')
  response.message != 'Unauthorized Request. Bad or missing credentials.'
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 95
def void(authorization, options = {})
  commit(:void, build_capture_or_credit_request(money_from_authorization(authorization), authorization, options))
end

Private Instance Methods

add_address(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 319
def add_address(xml, options)
  if address = (options[:billing_address] || options[:address])
    xml.tag! 'ZipCode', address[:zip]
  end
end
add_amount(xml, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 226
def add_amount(xml, money, options)
  currency_code = options[:currency] || default_currency
  xml.tag! 'DollarAmount', localized_amount(money, currency_code)
  xml.tag! 'Currency', currency_code
end
add_card_authentication_data(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 292
def add_card_authentication_data(xml, options)
  xml.tag! 'CAVV', options[:cavv]
  xml.tag! 'XID', options[:xid]
end
add_credentials(xml) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 210
def add_credentials(xml)
  xml.tag! 'ExactID', @options[:login]
  xml.tag! 'Password', @options[:password]
end
add_credit_card(xml, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 232
def add_credit_card(xml, credit_card, options)
  if credit_card.respond_to?(:track_data) && credit_card.track_data.present?
    xml.tag! 'Track1', credit_card.track_data
    xml.tag! 'Ecommerce_Flag', 'R'
  else
    xml.tag! 'Card_Number', credit_card.number
    xml.tag! 'Expiry_Date', expdate(credit_card)
    xml.tag! 'CardHoldersName', credit_card.name
    xml.tag! 'CardType', card_type(credit_card.brand)

    add_credit_card_eci(xml, credit_card, options)
    add_credit_card_verification_strings(xml, credit_card, options)
  end
end
add_credit_card_eci(xml, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 247
def add_credit_card_eci(xml, credit_card, options)
  eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == 'discover'
          # Discover requires any Apple Pay transaction, regardless of in-app
          # or web, and regardless of the ECI contained in the PKPaymentToken,
          # to have an ECI value explicitly of 04.
          '04'
        else
          (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
  end

  xml.tag! 'Ecommerce_Flag', eci.to_s =~ /^[0-9]+$/ ? eci.to_s.rjust(2, '0') : eci
end
add_credit_card_token(xml, store_authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 297
def add_credit_card_token(xml, store_authorization, options)
  params = store_authorization.split(';')
  credit_card = CreditCard.new(
    :brand      => params[1],
    :first_name => params[2],
    :last_name  => params[3],
    :month      => params[4],
    :year       => params[5])

  xml.tag! 'TransarmorToken', params[0]
  xml.tag! 'Expiry_Date', expdate(credit_card)
  xml.tag! 'CardHoldersName', credit_card.name
  xml.tag! 'CardType', card_type(credit_card.brand)
  add_card_authentication_data(xml, options)
end
add_credit_card_verification_strings(xml, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 260
def add_credit_card_verification_strings(xml, credit_card, options)
  address = options[:billing_address] || options[:address]
  if address
    address_values = []
    [:address1, :zip, :city, :state, :country].each { |part| address_values << address[part].to_s }
    xml.tag! 'VerificationStr1', address_values.join('|')
  end

  if credit_card.is_a?(NetworkTokenizationCreditCard)
    add_network_tokenization_credit_card(xml, credit_card)
  else
    if credit_card.verification_value?
      xml.tag! 'CVD_Presence_Ind', '1'
      xml.tag! 'VerificationStr2', credit_card.verification_value
    end

    add_card_authentication_data(xml, options)
  end
end
add_customer_data(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 313
def add_customer_data(xml, options)
  xml.tag! 'Customer_Ref', options[:customer] if options[:customer]
  xml.tag! 'Client_IP', options[:ip] if options[:ip]
  xml.tag! 'Client_Email', options[:email] if options[:email]
end
add_identification(xml, identification) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 219
def add_identification(xml, identification)
  authorization_num, transaction_tag, _ = identification.split(';')

  xml.tag! 'Authorization_Num', authorization_num
  xml.tag! 'Transaction_Tag', transaction_tag
end
add_invoice(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 325
def add_invoice(xml, options)
  xml.tag! 'Reference_No', options[:order_id]
  xml.tag! 'Reference_3',  options[:description] if options[:description]
end
add_level_3(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 335
def add_level_3(xml, options)
  xml.tag!('Level3') { |x| x << options[:level_3] } if options[:level_3]
end
add_network_tokenization_credit_card(xml, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 280
def add_network_tokenization_credit_card(xml, credit_card)
  case card_brand(credit_card).to_sym
  when :american_express
    cryptogram = Base64.decode64(credit_card.payment_cryptogram)
    xml.tag!('XID', Base64.encode64(cryptogram[20...40]))
    xml.tag!('CAVV', Base64.encode64(cryptogram[0...20]))
  else
    xml.tag!('XID', credit_card.transaction_id) if credit_card.transaction_id
    xml.tag!('CAVV', credit_card.payment_cryptogram)
  end
end
add_tax_fields(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 330
def add_tax_fields(xml, options)
  xml.tag! 'Tax1Amount',  options[:tax1_amount] if options[:tax1_amount]
  xml.tag! 'Tax1Number',  options[:tax1_number] if options[:tax1_number]
end
add_transaction_type(xml, action) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 215
def add_transaction_type(xml, action)
  xml.tag! 'Transaction_Type', TRANSACTIONS[action]
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 376
def authorization_from(response)
  if response[:authorization_num] && response[:transaction_tag]
    [
      response[:authorization_num],
      response[:transaction_tag],
      (response[:dollar_amount].to_f * 100).round
    ].join(';')
  else
    ''
  end
end
build_capture_or_credit_request(money, identification, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 190
def build_capture_or_credit_request(money, identification, options)
  xml = Builder::XmlMarkup.new

  add_identification(xml, identification)
  add_amount(xml, money, options)
  add_customer_data(xml, options)
  add_card_authentication_data(xml, options)

  xml.target!
end
build_request(action, body) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 158
def build_request(action, body)
  xml = Builder::XmlMarkup.new

  xml.instruct!
  xml.tag! 'Transaction', xmlns: 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes' do
    add_credentials(xml)
    add_transaction_type(xml, action)
    xml << body
  end

  xml.target!
end
build_sale_or_authorization_request(money, credit_card_or_store_authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 171
def build_sale_or_authorization_request(money, credit_card_or_store_authorization, options)
  xml = Builder::XmlMarkup.new

  add_amount(xml, money, options)

  if credit_card_or_store_authorization.is_a? String
    add_credit_card_token(xml, credit_card_or_store_authorization, options)
  else
    add_credit_card(xml, credit_card_or_store_authorization, options)
  end

  add_customer_data(xml, options)
  add_invoice(xml, options)
  add_tax_fields(xml, options)
  add_level_3(xml, options)

  xml.target!
end
build_store_request(credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 201
def build_store_request(credit_card, options)
  xml = Builder::XmlMarkup.new

  add_credit_card(xml, credit_card, options)
  add_customer_data(xml, options)

  xml.target!
end
card_type(credit_card_brand) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 343
def card_type(credit_card_brand)
  E4_BRANDS[credit_card_brand.to_sym] if credit_card_brand
end
commit(action, request, credit_card = nil) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 347
def commit(action, request, credit_card = nil)
  url = (test? ? self.test_url : self.live_url)
  begin
    response = parse(ssl_post(url, build_request(action, request), POST_HEADERS))
  rescue ResponseError => e
    response = parse_error(e.response)
  end

  Response.new(successful?(response), message_from(response), response,
    :test => test?,
    :authorization => successful?(response) ? response_authorization(action, response, credit_card) : '',
    :avs_result => {:code => response[:avs]},
    :cvv_result => response[:cvv2],
    :error_code => standard_error_code(response)
  )
end
expdate(credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 339
def expdate(credit_card)
  "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 408
def message_from(response)
  if(response[:faultcode] && response[:faultstring])
    response[:faultstring]
  elsif(response[:error_number] && response[:error_number] != '0')
    response[:error_description]
  else
    result = (response[:exact_message] || '')
    result << " - #{response[:bank_message]}" if response[:bank_message].present?
    result
  end
end
money_from_authorization(auth) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 403
def money_from_authorization(auth)
  _, _, amount = auth.split(/;/, 3)
  amount.to_i
end
parse(xml) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 433
def parse(xml)
  response = {}
  xml = REXML::Document.new(xml)

  if root = REXML::XPath.first(xml, '//TransactionResult')
    parse_elements(response, root)
  end

  response.delete_if { |k, v| SENSITIVE_FIELDS.include?(k) }
end
parse_elements(response, root) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 444
def parse_elements(response, root)
  root.elements.to_a.each do |node|
    response[node.name.gsub(/EXact/, 'Exact').underscore.to_sym] = (node.text || '').strip
  end
end
parse_error(error) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 420
def parse_error(error)
  {
    :transaction_approved => 'false',
    :error_number => error.code,
    :error_description => error.body,
    :ecommerce_error_code => error.body.gsub(/[^\d]/, '')
  }
end
response_authorization(action, response, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 368
def response_authorization(action, response, credit_card)
  if action == :store
    store_authorization_from(response, credit_card)
  else
    authorization_from(response)
  end
end
standard_error_code(response) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 429
def standard_error_code(response)
  STANDARD_ERROR_CODE_MAPPING[response[:bank_resp_code] || response[:ecommerce_error_code]]
end
store_authorization_from(response, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 388
def store_authorization_from(response, credit_card)
  if response[:transarmor_token].present?
    [
      response[:transarmor_token],
      credit_card.brand,
      credit_card.first_name,
      credit_card.last_name,
      credit_card.month,
      credit_card.year
    ].map { |value| value.to_s.gsub(/;/, '') }.join(';')
  else
    raise StandardError, "TransArmor support is not enabled on your #{display_name} account"
  end
end
successful?(response) click to toggle source
# File lib/active_merchant/billing/gateways/firstdata_e4.rb, line 364
def successful?(response)
  response[:transaction_approved] == SUCCESS
end