class ActiveMerchant::Billing::IxopayGateway

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/ixopay.rb, line 17
def initialize(options = {})
  requires!(options, :username, :password, :secret, :api_key)
  @secret = options[:secret]
  super
end

Public Instance Methods

authorize(money, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 32
def authorize(money, payment_method, options = {})
  request = build_xml_request do |xml|
    add_card_data(xml, payment_method)
    add_preauth(xml, money, options)
  end

  commit(request)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 41
def capture(money, authorization, options = {})
  request = build_xml_request do |xml|
    add_capture(xml, money, authorization, options)
  end

  commit(request)
end
purchase(money, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 23
def purchase(money, payment_method, options = {})
  request = build_xml_request do |xml|
    add_card_data(xml, payment_method)
    add_debit(xml, money, options)
  end

  commit(request)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 49
def refund(money, authorization, options = {})
  request = build_xml_request do |xml|
    add_refund(xml, money, authorization, options)
  end

  commit(request)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 76
def scrub(transcript)
  clean_transcript = remove_invalid_utf_8_byte_sequences(transcript)

  clean_transcript.
    gsub(%r((Authorization: Gateway )(.*)(:)), '\1[FILTERED]\3').
    gsub(%r((<password>)(.*)(</password>)), '\1[FILTERED]\3').
    gsub(%r((<pan>)(.*)(</pan>)), '\1[FILTERED]\3').
    gsub(%r((<cvv>)\d+(</cvv>)), '\1[FILTERED]\2')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 72
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 65
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/ixopay.rb, line 57
def void(authorization, options = {})
  request = build_xml_request do |xml|
    add_void(xml, authorization)
  end

  commit(request)
end

Private Instance Methods

add_billing_address(xml, address) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 222
def add_billing_address(xml, address)
  if address[:name]
    xml.firstName split_names(address[:name])[0]
    xml.lastName  split_names(address[:name])[1]
  end

  xml.billingAddress1 address[:address1]
  xml.billingAddress2 address[:address2]
  xml.billingCity     address[:city]
  xml.billingPostcode address[:zip]
  xml.billingState    address[:state]
  xml.billingCountry  address[:country]
  xml.billingPhone    address[:phone]
end
add_capture(xml, money, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 198
def add_capture(xml, money, authorization, options)
  currency = options[:currency] || currency(money)

  xml.capture_ do
    xml.transactionId new_transaction_id
    add_extra_data(xml, options[:extra_data]) if options[:extra_data]
    xml.referenceTransactionId authorization&.split('|')&.first
    xml.amount                 localized_amount(money, currency)
    xml.currency               currency
  end
end
add_card_data(xml, payment_method) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 131
def add_card_data(xml, payment_method)
  xml.cardData do
    xml.cardHolder      payment_method.name
    xml.pan             payment_method.number
    xml.cvv             payment_method.verification_value
    xml.expirationMonth format(payment_method.month, :two_digits)
    xml.expirationYear  format(payment_method.year, :four_digits)
  end
end
add_customer_data(xml, options) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 210
def add_customer_data(xml, options)
  # Ixopay returns an error if the elements are not added in the order used here.
  xml.customer do
    add_billing_address(xml,  options[:billing_address])  if options[:billing_address]
    add_shipping_address(xml, options[:shipping_address]) if options[:shipping_address]

    xml.company options[:billing_address][:company] if options.dig(:billing_address, :company)
    xml.email options[:email]
    xml.ipAddress(options[:ip] || '127.0.0.1')
  end
end
add_debit(xml, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 141
def add_debit(xml, money, options)
  currency    = options[:currency] || currency(money)
  description = options[:description].blank? ? 'Purchase' : options[:description]

  xml.debit do
    xml.transactionId new_transaction_id

    add_customer_data(xml, options)
    add_extra_data(xml, options[:extra_data]) if options[:extra_data]

    xml.amount      localized_amount(money, currency)
    xml.currency    currency
    xml.description description
    xml.callbackUrl(options[:callback_url])
    add_stored_credentials(xml, options)
  end
end
add_extra_data(xml, extra_data) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 276
def add_extra_data(xml, extra_data)
  extra_data.each do |k, v|
    xml.extraData(v, key: k)
  end
end
add_preauth(xml, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 159
def add_preauth(xml, money, options)
  description  = options[:description].blank? ? 'Preauthorize' : options[:description]
  currency     = options[:currency] || currency(money)
  callback_url = options[:callback_url]

  xml.preauthorize do
    xml.transactionId new_transaction_id

    add_customer_data(xml, options)
    add_extra_data(xml, options[:extra_data]) if options[:extra_data]

    xml.amount      localized_amount(money, currency)
    xml.currency    currency
    xml.description description
    xml.callbackUrl callback_url
    add_stored_credentials(xml, options)
  end
end
add_refund(xml, money, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 178
def add_refund(xml, money, authorization, options)
  currency = options[:currency] || currency(money)

  xml.refund do
    xml.transactionId new_transaction_id
    add_extra_data(xml, options[:extra_data]) if options[:extra_data]
    xml.referenceTransactionId authorization&.split('|')&.first
    xml.amount                 localized_amount(money, currency)
    xml.currency               currency
  end
end
add_shipping_address(xml, address) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 237
def add_shipping_address(xml, address)
  if address[:name]
    xml.shippingFirstName split_names(address[:name])[0]
    xml.shippingLastName  split_names(address[:name])[1]
  end

  xml.shippingCompany   address[:company]
  xml.shippingAddress1  address[:address1]
  xml.shippingAddress2  address[:address2]
  xml.shippingCity      address[:city]
  xml.shippingPostcode  address[:zip]
  xml.shippingState     address[:state]
  xml.shippingCountry   address[:country]
  xml.shippingPhone     address[:phone]
end
add_stored_credentials(xml, options) click to toggle source

Ixopay does not pass any parameters for cardholder/merchant initiated. Ixopay also doesn’t support installment transactions, only recurring (“RECURRING”) and unscheduled (“CARDONFILE”).

Furthermore, Ixopay is slightly unusual in its application of stored credentials in that the gateway does not return a true network_transaction_id that can be sent on subsequent transactions.

# File lib/active_merchant/billing/gateways/ixopay.rb, line 264
def add_stored_credentials(xml, options)
  return unless stored_credential = options[:stored_credential]

  if stored_credential[:initial_transaction]
    xml.transactionIndicator 'INITIAL'
  elsif stored_credential[:reason_type] == 'recurring'
    xml.transactionIndicator 'RECURRING'
  elsif stored_credential[:reason_type] == 'unscheduled'
    xml.transactionIndicator 'CARDONFILE'
  end
end
add_void(xml, authorization) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 190
def add_void(xml, authorization)
  xml.void do
    xml.transactionId new_transaction_id
    add_extra_data(xml, options[:extra_data]) if options[:extra_data]
    xml.referenceTransactionId authorization&.split('|')&.first
  end
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 311
def authorization_from(response)
  response[:reference_id] ? "#{response[:reference_id]}|#{response[:purchase_id]}" : nil
end
build_xml_request() { |xml| ... } click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 119
def build_xml_request
  builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
    xml.transactionWithCard 'xmlns' => 'http://secure.ixopay.com/Schema/V2/TransactionWithCard' do
      xml.username @options[:username]
      xml.password Digest::SHA1.hexdigest(@options[:password])
      yield(xml)
    end
  end

  builder.to_xml
end
commit(request) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 282
def commit(request)
  url = (test? ? test_url : live_url)

  # ssl_post raises an exception for any non-2xx HTTP status from the gateway
  response =
    begin
      parse(ssl_post(url, request, headers(request)))
    rescue StandardError => e
      parse(e.response.body)
    end

  Response.new(
    success_from(response),
    message_from(response),
    response,
    authorization: authorization_from(response),
    test: test?,
    error_code: error_code_from(response)
  )
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 315
def error_code_from(response)
  response.dig(:errors, 'error', 'code') unless success_from(response)
end
generate_signature(http_method, xml, timestamp) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 103
def generate_signature(http_method, xml, timestamp)
  content_type = 'text/xml; charset=utf-8'
  message = "#{http_method}\n#{Digest::MD5.hexdigest(xml)}\n#{content_type}\n#{timestamp}\n\n/transaction"
  digest = OpenSSL::Digest.new('sha512')
  hmac = OpenSSL::HMAC.digest(digest, @secret, message)

  Base64.encode64(hmac).delete("\n")
end
headers(xml) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 92
def headers(xml)
  timestamp = Time.now.httpdate
  signature = generate_signature('POST', xml, timestamp)

  {
    'Authorization' => "Gateway #{options[:api_key]}:#{signature}",
    'Date' => timestamp,
    'Content-Type' => 'text/xml; charset=utf-8'
  }
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 307
def message_from(response)
  response.dig(:errors, 'error', 'message') || response[:return_type]
end
new_transaction_id() click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 253
def new_transaction_id
  SecureRandom.uuid
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 112
def parse(body)
  xml = Nokogiri::XML(body)
  response = Hash.from_xml(xml.to_s)['result']

  response.deep_transform_keys(&:underscore).transform_keys(&:to_sym)
end
remove_invalid_utf_8_byte_sequences(text) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 88
def remove_invalid_utf_8_byte_sequences(text)
  text.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ixopay.rb, line 303
def success_from(response)
  response[:success] == 'true'
end