class ActiveMerchant::Billing::AuthorizeNetGateway

Constants

APPLE_PAY_DATA_DESCRIPTOR
AVS_ERRORS
AVS_REASON_CODES
CARD_CODE_ERRORS
FRAUD_REVIEW
MARKET_TYPE
STANDARD_ERROR_CODE_MAPPING
TRACKS
TRANSACTION_ALREADY_ACTIONED

Public Class Methods

new(options={}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 59
def initialize(options={})
  requires!(options, :login, :password)
  super
end

Public Instance Methods

authorize(amount, payment, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 81
def authorize(amount, payment, options={})
  commit("AUTH_ONLY") do |xml|
    add_order_id(xml, options)
    xml.transactionRequest do
      xml.transactionType('authOnlyTransaction')
      xml.amount(amount(amount))

      add_payment_source(xml, payment)
      add_invoice(xml, options)
      add_customer_data(xml, payment, options)
      add_market_type(xml, payment)
      add_settings(xml, payment, options)
      add_user_fields(xml, amount, options)
    end
  end
end
capture(amount, authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 98
def capture(amount, authorization, options={})
  commit("PRIOR_AUTH_CAPTURE") do |xml|
    add_order_id(xml, options)
    xml.transactionRequest do
      xml.transactionType('priorAuthCaptureTransaction')
      xml.amount(amount(amount))
      xml.refTransId(split_authorization(authorization)[0])

      add_invoice(xml, options)
      add_user_fields(xml, amount, options)
    end
  end
end
credit(amount, payment, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 144
def credit(amount, payment, options={})
  if payment.is_a?(String)
    raise ArgumentError, "Reference credits are not supported. Please supply the original credit card or use the #refund method."
  end

  commit("CREDIT") do |xml|
    add_order_id(xml, options)
    xml.transactionRequest do
      xml.transactionType('refundTransaction')
      xml.amount(amount(amount))

      add_payment_source(xml, payment)
      add_invoice(xml, options)
      add_customer_data(xml, payment, options)
      add_settings(xml, payment, options)
      add_user_fields(xml, amount, options)
    end
  end
end
purchase(amount, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 64
def purchase(amount, payment, options = {})
  commit("AUTH_CAPTURE") do |xml|
    add_order_id(xml, options)
    xml.transactionRequest do
      xml.transactionType('authCaptureTransaction')
      xml.amount(amount(amount))

      add_payment_source(xml, payment)
      add_invoice(xml, options)
      add_customer_data(xml, payment, options)
      add_market_type(xml, payment)
      add_settings(xml, payment, options)
      add_user_fields(xml, amount, options)
    end
  end
end
refund(amount, authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 112
def refund(amount, authorization, options={})
  transaction_id, card_number = split_authorization(authorization)
  commit("CREDIT") do |xml|
    xml.transactionRequest do
      xml.transactionType('refundTransaction')
      xml.amount(amount.nil? ? 0 : amount(amount))
      xml.payment do
        xml.creditCard do
          xml.cardNumber(card_number || options[:card_number])
          xml.expirationDate('XXXX')
        end
      end
      xml.refTransId(transaction_id)

      add_customer_data(xml, nil, options)
      add_user_fields(xml, amount, options)
    end
  end
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 175
def scrub(transcript)
  transcript.
    gsub(%r((<cardNumber>).+(</cardNumber>)), '\1[FILTERED]\2').
    gsub(%r((<cardCode>).+(</cardCode>)), '\1[FILTERED]\2')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 171
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 164
def verify(credit_card, options = {})
  MultiResponse.run(:use_first_response) do |r|
    r.process { authorize(100, credit_card, options) }
    r.process(:ignore_result) { void(r.authorization, options) }
  end
end
void(authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 132
def void(authorization, options={})
  commit("VOID") do |xml|
    add_order_id(xml, options)
    xml.transactionRequest do
      xml.transactionType('voidTransaction')
      xml.refTransId(split_authorization(authorization)[0])

      add_user_fields(xml, nil, options)
    end
  end
end

Private Instance Methods

add_apple_pay_payment_token(xml, apple_pay_payment_token) click to toggle source

developer.authorize.net/api/reference/#apple-pay-transactions

# File lib/active_merchant/billing/gateways/authorize_net.rb, line 268
def add_apple_pay_payment_token(xml, apple_pay_payment_token)
  xml.payment do
    xml.opaqueData do
      xml.dataDescriptor(APPLE_PAY_DATA_DESCRIPTOR)
      xml.dataValue(Base64.strict_encode64(apple_pay_payment_token.payment_data.to_json))
    end
  end
end
add_check(xml, check) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 294
def add_check(xml, check)
  xml.payment do
    xml.bankAccount do
      xml.routingNumber(check.routing_number)
      xml.accountNumber(check.account_number)
      xml.nameOnAccount(check.name)
      xml.echeckType("WEB")
      xml.bankName(check.bank_name)
      xml.checkNumber(check.number)
    end
  end
end
add_credit_card(xml, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 235
def add_credit_card(xml, credit_card)
  if credit_card.track_data
    add_swipe_data(xml, credit_card)
  else
    xml.payment do
      xml.creditCard do
        xml.cardNumber(truncate(credit_card.number, 16))
        xml.expirationDate(format(credit_card.month, :two_digits) + '/' + format(credit_card.year, :four_digits))
        if credit_card.valid_card_verification_value?(credit_card.verification_value, credit_card.brand)
          xml.cardCode(credit_card.verification_value)
        end
        if credit_card.is_a?(NetworkTokenizationCreditCard)
          xml.cryptogram(credit_card.payment_cryptogram)
        end
      end
    end
  end
end
add_customer_data(xml, payment_source, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 307
def add_customer_data(xml, payment_source, options)
  billing_address = options[:billing_address] || options[:address] || {}
  shipping_address = options[:shipping_address] || options[:address] || {}

  xml.customer do
    xml.id(options[:customer]) unless empty?(options[:customer]) || options[:customer] !~ /^\d+$/
    xml.email(options[:email]) unless empty?(options[:email])
  end

  xml.billTo do
    first_name, last_name = names_from(payment_source, billing_address, options)
    xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
    xml.lastName(truncate(last_name, 50)) unless empty?(last_name)

    xml.company(truncate(billing_address[:company], 50)) unless empty?(billing_address[:company])
    xml.address(truncate(billing_address[:address1], 60))
    xml.city(truncate(billing_address[:city], 40))
    xml.state(empty?(billing_address[:state]) ? 'n/a' : truncate(billing_address[:state], 40))
    xml.zip(truncate((billing_address[:zip] || options[:zip]), 20))
    xml.country(truncate(billing_address[:country], 60))
    xml.phoneNumber(truncate(billing_address[:phone], 25)) unless empty?(billing_address[:phone])
    xml.faxNumber(truncate(billing_address[:fax], 25)) unless empty?(billing_address[:fax])
  end

  unless shipping_address.blank?
    xml.shipTo do
      (first_name, last_name) = if shipping_address[:name]
        shipping_address[:name].split
      else
        [shipping_address[:first_name], shipping_address[:last_name]]
      end
      xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
      xml.lastName(truncate(last_name, 50)) unless empty?(last_name)

      xml.company(truncate(shipping_address[:company], 50)) unless empty?(shipping_address[:company])
      xml.address(truncate(shipping_address[:address1], 60))
      xml.city(truncate(shipping_address[:city], 40))
      xml.state(truncate(shipping_address[:state], 40))
      xml.zip(truncate(shipping_address[:zip], 20))
      xml.country(truncate(shipping_address[:country], 60))
    end
  end

  xml.customerIP(options[:ip]) unless empty?(options[:ip])

  xml.cardholderAuthentication do
    xml.authenticationIndicator(options[:authentication_indicator])
    xml.cardholderAuthenticationValue(options[:cardholder_authentication_value])
  end
end
add_invoice(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 362
def add_invoice(xml, options)
  xml.order do
    xml.invoiceNumber(truncate(options[:order_id], 20))
    xml.description(truncate(options[:description], 255))
  end
end
add_market_type(xml, payment) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 277
def add_market_type(xml, payment)
  return if card_brand(payment) == 'check' or card_brand(payment) == 'apple_pay'
  if valid_track_data
    xml.retail do
      xml.marketType(MARKET_TYPE[:retail])
    end
  elsif payment.manual_entry
    xml.retail do
      xml.marketType(MARKET_TYPE[:moto])
    end
  end
end
add_order_id(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 358
def add_order_id(xml, options)
  xml.refId(truncate(options[:order_id], 20))
end
add_payment_source(xml, source) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 183
def add_payment_source(xml, source)
  return unless source
  if card_brand(source) == 'check'
    add_check(xml, source)
  elsif card_brand(source) == 'apple_pay'
    add_apple_pay_payment_token(xml, source)
  else
    add_credit_card(xml, source)
  end
end
add_settings(xml, source, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 194
def add_settings(xml, source, options)
  xml.transactionSettings do
    if card_brand(source) == "check" && options[:recurring]
      xml.setting do
        xml.settingName("recurringBilling")
        xml.settingValue("true")
      end
    end
    if options[:duplicate_window]
      set_duplicate_window(xml, options[:duplicate_window])
    elsif self.class.duplicate_window
      ActiveMerchant.deprecated "Using the duplicate_window class_attribute is deprecated. Use the transaction options hash instead."
      set_duplicate_window(xml, self.class.duplicate_window)
    end
  end
end
add_swipe_data(xml, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 254
def add_swipe_data(xml, credit_card)
  TRACKS.each do |key, regex|
    if regex.match(credit_card.track_data)
      @valid_track_data = true
      xml.payment do
        xml.trackData do
          xml.public_send(:"track#{key}", credit_card.track_data)
        end
      end
    end
  end
end
add_user_fields(xml, amount, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 218
def add_user_fields(xml, amount, options)
  xml.userFields do
    if currency = (options[:currency] || currency(amount))
      xml.userField do
        xml.name("x_currency_code")
        xml.value(currency)
      end
    end
    if application_id.present? && application_id != "ActiveMerchant"
      xml.userField do
        xml.name("x_solution_id")
        xml.value(application_id)
      end
    end
  end
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 487
def authorization_from(response)
  [response[:transaction_id], response[:account_number]].join("#")
end
commit(action, &payload) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 378
def commit(action, &payload)
  url = (test? ? test_url : live_url)
  response = parse(action, ssl_post(url, post_data(&payload), 'Content-Type' => 'text/xml'))

  avs_result = AVSResult.new(code: response[:avs_result_code])
  cvv_result = CVVResult.new(response[:card_code])
  if using_live_gateway_in_test_mode?(response)
    Response.new(false, "Using a live Authorize.net account in Test Mode is not permitted.")
  else
    Response.new(
      success_from(response),
      message_from(response, avs_result, cvv_result),
      response,
      authorization: authorization_from(response),
      test: test?,
      avs_result: avs_result,
      cvv_result: cvv_result,
      fraud_review: fraud_review?(response),
      error_code: map_error_code(response[:response_code], response[:response_reason_code])
    )
  end
end
fraud_review?(response) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 496
def fraud_review?(response)
  (response[:response_code] == FRAUD_REVIEW)
end
map_error_code(response_code, response_reason_code) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 505
def map_error_code(response_code, response_reason_code)
  STANDARD_ERROR_CODE_MAPPING[response_code.to_s << response_reason_code.to_s]
end
message_from(response, avs_result, cvv_result) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 475
def message_from(response, avs_result, cvv_result)
  if response[:response_code] == DECLINED
    if CARD_CODE_ERRORS.include?(cvv_result.code)
      return cvv_result.message
    elsif(AVS_REASON_CODES.include?(response[:response_reason_code]) && AVS_ERRORS.include?(avs_result.code))
      return avs_result.message
    end
  end

  response[:response_reason_text]
end
names_from(payment_source, address, options) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 369
def names_from(payment_source, address, options)
  if payment_source && !payment_source.is_a?(PaymentToken)
    first_name, last_name = (address[:name] || "").split
    [(payment_source.first_name || first_name), (payment_source.last_name || last_name)]
  else
    [options[:first_name], options[:last_name]]
  end
end
parse(action, body) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 413
def parse(action, body)
  doc = Nokogiri::XML(body)
  doc.remove_namespaces!

  response = {action: action}

  response[:response_code] = if(element = doc.at_xpath("//transactionResponse/responseCode"))
    (empty?(element.content) ? nil : element.content.to_i)
  end

  if(element = doc.at_xpath("//errors/error"))
    response[:response_reason_code] = element.at_xpath("errorCode").content[/0*(\d+)$/, 1]
    response[:response_reason_text] = element.at_xpath("errorText").content.chomp('.')
  elsif(element = doc.at_xpath("//transactionResponse/messages/message"))
    response[:response_reason_code] = element.at_xpath("code").content[/0*(\d+)$/, 1]
    response[:response_reason_text] = element.at_xpath("description").content.chomp('.')
  elsif(element = doc.at_xpath("//messages/message"))
    response[:response_reason_code] = element.at_xpath("code").content[/0*(\d+)$/, 1]
    response[:response_reason_text] = element.at_xpath("text").content.chomp('.')
  else
    response[:response_reason_code] = nil
    response[:response_reason_text] = ""
  end

  response[:avs_result_code] = if(element = doc.at_xpath("//avsResultCode"))
    (empty?(element.content) ? nil : element.content)
  end

  response[:transaction_id] = if(element = doc.at_xpath("//transId"))
    (empty?(element.content) ? nil : element.content)
  end

  response[:card_code] = if(element = doc.at_xpath("//cvvResultCode"))
    (empty?(element.content) ? nil : element.content)
  end

  response[:authorization_code] = if(element = doc.at_xpath("//authCode"))
    (empty?(element.content) ? nil : element.content)
  end

  response[:cardholder_authentication_code] = if(element = doc.at_xpath("//cavvResultCode"))
    (empty?(element.content) ? nil : element.content)
  end

  response[:account_number] = if(element = doc.at_xpath("//accountNumber"))
    (empty?(element.content) ? nil : element.content[-4..-1])
  end

  response[:test_request] = if(element = doc.at_xpath("//testRequest"))
    (empty?(element.content) ? nil : element.content)
  end

  response
end
post_data() { |xml| ... } click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 401
def post_data
  Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
    xml.createTransactionRequest('xmlns' => 'AnetApi/xml/v1/schema/AnetApiSchema.xsd') do
      xml.merchantAuthentication do
        xml.name(@options[:login])
        xml.transactionKey(@options[:password])
      end
      yield(xml)
    end
  end.to_xml(indent: 0)
end
set_duplicate_window(xml, value) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 211
def set_duplicate_window(xml, value)
  xml.setting do
    xml.settingName("duplicateWindow")
    xml.settingValue(value)
  end
end
split_authorization(authorization) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 491
def split_authorization(authorization)
  transaction_id, card_number = authorization.split("#")
  [transaction_id, card_number]
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 468
def success_from(response)
  (
    response[:response_code] == APPROVED &&
    TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
  )
end
using_live_gateway_in_test_mode?(response) click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 501
def using_live_gateway_in_test_mode?(response)
  !test? && response[:test_request] == "1"
end
valid_track_data() click to toggle source
# File lib/active_merchant/billing/gateways/authorize_net.rb, line 290
def valid_track_data
  @valid_track_data ||= false
end