class ActiveMerchant::Billing::IpgGateway

Constants

ACTION_REQUEST_ITEMS
CURRENCY_CODES
NAMESPACE_BASE_URL

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/ipg.rb, line 20
def initialize(options = {})
  requires!(options, :user_id, :password, :pem, :pem_password)
  @credentials = options
  @hosted_data_id = nil
  super
end

Public Instance Methods

authorize(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 33
def authorize(money, payment, options = {})
  xml = build_purchase_and_authorize_request(money, payment, options)

  commit('preAuth', xml, options)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 39
def capture(money, authorization, options = {})
  xml = build_capture_and_refund_request(money, authorization, options)

  commit('postAuth', xml, options)
end
purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 27
def purchase(money, payment, options = {})
  xml = build_purchase_and_authorize_request(money, payment, options)

  commit('sale', xml, options)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 45
def refund(money, authorization, options = {})
  xml = build_capture_and_refund_request(money, authorization, options)

  commit('return', xml, options)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 85
def scrub(transcript)
  transcript.
    gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
    gsub(%r((<v1:CardNumber>).+(</v1:CardNumber>)), '\1[FILTERED]\2').
    gsub(%r((<v1:CardCodeValue>).+(</v1:CardCodeValue>)), '\1[FILTERED]\2')
end
store(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 58
def store(credit_card, options = {})
  @hosted_data_id = options[:hosted_data_id] || generate_unique_id
  xml = Builder::XmlMarkup.new(indent: 2)
  add_storage_item(xml, credit_card, options)

  commit('vault', xml, options)
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 81
def supports_scrubbing?
  true
end
unstore(hosted_data_id) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 66
def unstore(hosted_data_id)
  xml = Builder::XmlMarkup.new(indent: 2)
  add_unstore_item(xml, hosted_data_id)

  commit('unstore', xml)
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 73
def verify(credit_card, options = {})
  options[:currency] = self.default_currency unless options[:currency] && !options[:currency].empty?
  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/ipg.rb, line 51
def void(authorization, options = {})
  xml = Builder::XmlMarkup.new(indent: 2)
  add_transaction_details(xml, options.merge!({ order_id: authorization }))

  commit('void', xml, options)
end

Private Instance Methods

add_address(xml, address) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 216
def add_address(xml, address)
  xml.tag!('v1:Address') do
    xml.tag!('v1:Address1', address[:address1]) if address[:address1]
    xml.tag!('v1:Address2', address[:address2]) if address[:address2]
    xml.tag!('v1:Zip', address[:zip]) if address[:zip]
    xml.tag!('v1:City', address[:city]) if address[:city]
    xml.tag!('v1:State', address[:state]) if address[:state]
    xml.tag!('v1:Country', address[:country]) if address[:country]
  end
end
add_billing(xml, billing) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 281
def add_billing(xml, billing)
  xml.tag!('v1:Billing') do
    xml.tag!('v1:CustomerID', billing[:customer_id]) if billing[:customer_id]
    xml.tag!('v1:Name', billing[:name]) if billing[:name]
    xml.tag!('v1:Company', billing[:company]) if billing[:company]
    xml.tag!('v1:Address1', billing[:address_1]) if billing[:address_1]
    xml.tag!('v1:Address2', billing[:address_2]) if billing[:address_2]
    xml.tag!('v1:City', billing[:city]) if billing[:city]
    xml.tag!('v1:State', billing[:state]) if billing[:state]
    xml.tag!('v1:Zip', billing[:zip]) if billing[:zip]
    xml.tag!('v1:Country', billing[:country]) if billing[:country]
    xml.tag!('v1:Phone', billing[:phone]) if billing[:phone]
    xml.tag!('v1:Fax', billing[:fax]) if billing[:fax]
    xml.tag!('v1:Email', billing[:email]) if billing[:email]
  end
end
add_credit_card(xml, payment, options = {}, credit_envelope = 'v1') click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 180
def add_credit_card(xml, payment, options = {}, credit_envelope = 'v1')
  if payment&.is_a?(CreditCard)
    requires!(options.merge!({ card_number: payment.number, month: payment.month, year: payment.year }), :card_number, :month, :year)

    xml.tag!("#{credit_envelope}:CreditCardData") do
      xml.tag!('v1:CardNumber', payment.number) if payment.number
      xml.tag!('v1:ExpMonth', format(payment.month, :two_digits)) if payment.month
      xml.tag!('v1:ExpYear', format(payment.year, :two_digits)) if payment.year
      xml.tag!('v1:CardCodeValue', payment.verification_value) if payment.verification_value
      xml.tag!('v1:Brand', options[:brand]) if options[:brand]
    end
  end

  if options[:card_function_type]
    xml.tag!('v1:cardFunction') do
      xml.tag!('v1:Type', options[:card_function_type])
    end
  end

  if options[:track_data]
    xml.tag!("#{credit_envelope}:CreditCardData") do
      xml.tag!('v1:TrackData', options[:track_data])
    end
  end
end
add_document(xml, document) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 227
def add_document(xml, document)
  xml.tag!('v1:Document') do
    xml.tag!('v1:Type', document[:type]) if document[:type]
    xml.tag!('v1:Number', document[:number]) if document[:number]
  end
end
add_payment(xml, money, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 266
def add_payment(xml, money, payment, options)
  requires!(options.merge!({ money: money }), :currency, :money)
  xml.tag!('v1:Payment') do
    xml.tag!('v1:HostedDataID', payment) if payment&.is_a?(String)
    xml.tag!('v1:HostedDataStoreID', options[:hosted_data_store_id]) if options[:hosted_data_store_id]
    xml.tag!('v1:DeclineHostedDataDuplicates', options[:decline_hosted_data_duplicates]) if options[:decline_hosted_data_duplicates]
    xml.tag!('v1:SubTotal', options[:sub_total]) if options[:sub_total]
    xml.tag!('v1:ValueAddedTax', options[:value_added_tax]) if options[:value_added_tax]
    xml.tag!('v1:DeliveryAmount', options[:delivery_amount]) if options[:delivery_amount]
    xml.tag!('v1:ChargeTotal', amount(money))
    xml.tag!('v1:Currency', CURRENCY_CODES[options[:currency]])
    xml.tag!('v1:numberOfInstallments', options[:number_of_installments]) if options[:number_of_installments]
  end
end
add_shipping(xml, shipping) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 298
def add_shipping(xml, shipping)
  xml.tag!('v1:Shipping') do
    xml.tag!('v1:Type', shipping[:type]) if shipping[:type]
    xml.tag!('v1:Name', shipping[:name]) if shipping[:name]
    xml.tag!('v1:Address1', shipping[:address_1]) if shipping[:address_1]
    xml.tag!('v1:Address2', shipping[:address_2]) if shipping[:address_2]
    xml.tag!('v1:City', shipping[:city]) if shipping[:city]
    xml.tag!('v1:State', shipping[:state]) if shipping[:state]
    xml.tag!('v1:Zip', shipping[:zip]) if shipping[:zip]
    xml.tag!('v1:Country', shipping[:country]) if shipping[:country]
  end
end
add_storage_item(xml, credit_card, options) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 152
def add_storage_item(xml, credit_card, options)
  requires!(options.merge!({ credit_card: credit_card, hosted_data_id: @hosted_data_id }), :credit_card, :hosted_data_id)
  xml.tag!('ns2:StoreHostedData') do
    xml.tag!('ns2:DataStorageItem') do
      add_credit_card(xml, credit_card, {}, 'ns2')
      add_three_d_secure(xml, options[:three_d_secure]) if options[:three_d_secure]
      xml.tag!('ns2:HostedDataID', @hosted_data_id) if @hosted_data_id
    end
  end
end
add_stored_credentials(xml, params) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 146
def add_stored_credentials(xml, params)
  recurring_type = params[:stored_credential][:initial_transaction] ? 'FIRST' : 'REPEAT' if params[:stored_credential]
  recurring_type = params[:recurring_type] if params[:recurring_type]
  xml.tag!('v1:recurringType', recurring_type)
end
add_sub_merchant(xml, submerchant) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 206
def add_sub_merchant(xml, submerchant)
  xml.tag!('v1:SubMerchant') do
    xml.tag!('v1:Mcc', submerchant[:mcc]) if submerchant[:mcc]
    xml.tag!('v1:LegalName', submerchant[:legal_name]) if submerchant[:legal_name]
    add_address(xml, submerchant[:address]) if submerchant[:address]
    add_document(xml, submerchant[:document]) if submerchant[:document]
    xml.tag!('v1:MerchantID', submerchant[:merchant_id]) if submerchant[:merchant_id]
  end
end
add_three_d_secure(xml, three_d_secure) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 234
def add_three_d_secure(xml, three_d_secure)
  xml.tag!('v1:CreditCard3DSecure') do
    xml.tag!('v1:AuthenticationValue', three_d_secure[:cavv]) if three_d_secure[:cavv]
    xml.tag!('v1:XID', three_d_secure[:xid]) if three_d_secure[:xid]
    xml.tag!('v1:Secure3D2TransactionStatus', three_d_secure[:directory_response_status]) if three_d_secure[:directory_response_status]
    xml.tag!('v1:Secure3D2AuthenticationResponse', three_d_secure[:authentication_response_status]) if three_d_secure[:authentication_response_status]
    xml.tag!('v1:Secure3DProtocolVersion', three_d_secure[:version]) if three_d_secure[:version]
    xml.tag!('v1:DirectoryServerTransactionId', three_d_secure[:ds_transaction_id]) if three_d_secure[:ds_transaction_id]
  end
end
add_transaction_details(xml, options, pre_order = false) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 245
def add_transaction_details(xml, options, pre_order = false)
  requires!(options, :order_id) if pre_order
  xml.tag!('v1:TransactionDetails') do
    xml.tag!('v1:OrderId', options[:order_id]) if options[:order_id]
    xml.tag!('v1:MerchantTransactionId', options[:merchant_transaction_id]) if options[:merchant_transaction_id]
    xml.tag!('v1:Ip', options[:ip]) if options[:ip]
    xml.tag!('v1:Tdate', options[:t_date]) if options[:t_date]
    xml.tag!('v1:IpgTransactionId', options[:ipg_transaction_id]) if options[:ipg_transaction_id]
    xml.tag!('v1:ReferencedMerchantTransactionId', options[:referenced_merchant_transaction_id]) if options[:referenced_merchant_transaction_id]
    xml.tag!('v1:TransactionOrigin', options[:transaction_origin]) if options[:transaction_origin]
    xml.tag!('v1:InvoiceNumber', options[:invoice_number]) if options[:invoice_number]
    xml.tag!('v1:DynamicMerchantName', options[:dynamic_merchant_name]) if options[:dynamic_merchant_name]
    xml.tag!('v1:Comments', options[:comments]) if options[:comments]
    if options[:terminal_id]
      xml.tag!('v1:Terminal') do
        xml.tag!('v1:TerminalID', options[:terminal_id]) if options[:terminal_id]
      end
    end
  end
end
add_transaction_type(xml, type) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 173
def add_transaction_type(xml, type)
  xml.tag!('v1:CreditCardTxType') do
    xml.tag!('v1:StoreId', @credentials[:store_id])
    xml.tag!('v1:Type', type)
  end
end
add_unstore_item(xml, hosted_data_id) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 163
def add_unstore_item(xml, hosted_data_id)
  requires!({}.merge!({ hosted_data_id: hosted_data_id }), :hosted_data_id)
  xml.tag!('ns2:StoreHostedData') do
    xml.tag!('ns2:DataStorageItem') do
      xml.tag!('ns2:Function', 'delete')
      xml.tag!('ns2:HostedDataID', hosted_data_id)
    end
  end
end
authorization_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 406
def authorization_from(action, response)
  return (action == 'vault' ? response[:hosted_data_id] : response[:OrderId])
end
build_action_request(xml, action, body) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 126
def build_action_request(xml, action, body)
  xml.tag!('ns4:IPGApiActionRequest', ipg_action_namespaces) do
    xml.tag!('ns2:Action') do
      xml << body.target!
    end
  end
end
build_capture_and_refund_request(money, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 110
def build_capture_and_refund_request(money, authorization, options)
  xml = Builder::XmlMarkup.new(indent: 2)
  add_payment(xml, money, nil, options)
  add_transaction_details(xml, options.merge!({ order_id: authorization }), true)
  xml
end
build_header() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 311
def build_header
  {
    'Content-Type' => 'text/xml; charset=utf-8',
    'Authorization' => "Basic #{encoded_credentials}"
  }
end
build_order_request(xml, action, body) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 117
def build_order_request(xml, action, body)
  xml.tag!('ipg:IPGApiOrderRequest') do
    xml.tag!('v1:Transaction') do
      add_transaction_type(xml, action)
      xml << body.target!
    end
  end
end
build_purchase_and_authorize_request(money, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 96
def build_purchase_and_authorize_request(money, payment, options)
  xml = Builder::XmlMarkup.new(indent: 2)

  add_credit_card(xml, payment, options)
  add_sub_merchant(xml, options[:submerchant]) if options[:submerchant]
  add_three_d_secure(xml, options[:three_d_secure]) if options[:three_d_secure]
  add_stored_credentials(xml, options) if options[:stored_credential] || options[:recurring_type]
  add_payment(xml, money, payment, options)
  add_transaction_details(xml, options)
  add_billing(xml, options[:billing]) if options[:billing]
  add_shipping(xml, options[:shipping]) if options[:shipping]
  xml
end
build_soap_request(action, body) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 134
def build_soap_request(action, body)
  xml = Builder::XmlMarkup.new(indent: 2)
  xml.tag!('soapenv:Envelope', envelope_namespaces) do
    xml.tag!('soapenv:Header')
    xml.tag!('soapenv:Body') do
      build_order_request(xml, action, body) unless ACTION_REQUEST_ITEMS.include?(action)
      build_action_request(xml, action, body) if ACTION_REQUEST_ITEMS.include?(action)
    end
  end
  xml.target!
end
commit(action, request, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 354
def commit(action, request, options = {})
  override_store_id(options)
  url = (test? ? test_url : live_url)
  soap_request = build_soap_request(action, request)
  response = parse(ssl_post(url, soap_request, build_header))
  Response.new(
    response[:success],
    message_from(response),
    response,
    authorization: authorization_from(action, response),
    avs_result: AVSResult.new(code: response[:AVSResponse]),
    cvv_result: CVVResult.new(response[:ProcessorCCVResponse]),
    test: test?,
    error_code: error_code_from(response)
  )
end
encoded_credentials() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 318
def encoded_credentials
  # We remove 'WS' and add it back on the next line because the ipg docs are a little confusing.
  # Some merchants will likely add it to their user_id and others won't.
  user_id = @credentials[:user_id].sub(/^WS/, '')
  Base64.encode64("WS#{user_id}:#{@credentials[:password]}").delete("\n")
end
envelope_namespaces() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 325
def envelope_namespaces
  {
    'xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/',
    'xmlns:ipg' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi",
    'xmlns:v1' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1"
  }
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 410
def error_code_from(response)
  response[:ErrorMessage]&.split(':')&.first unless response[:success]
end
handle_response(response) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 414
def handle_response(response)
  case response.code.to_i
  when 200...300, 500
    response.body
  else
    raise ResponseError.new(response)
  end
end
ipg_action_namespaces() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 340
def ipg_action_namespaces
  {
    'xmlns:ns4' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi",
    'xmlns:ns2' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/a1",
    'xmlns:ns3' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1"
  }
end
ipg_order_namespaces() click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 333
def ipg_order_namespaces
  {
    'xmlns:v1' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/v1",
    'xmlns:ipgapi' => "#{NAMESPACE_BASE_URL}/ipgapi/schemas/ipgapi"
  }
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 402
def message_from(response)
  [response[:TransactionResult], response[:ErrorMessage]&.split(':')&.last&.strip].compact.join(', ')
end
override_store_id(options) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 348
def override_store_id(options)
  raise ArgumentError, 'store_id must be provieded' if @credentials[:store_id].blank? && options[:store_id].blank?

  @credentials[:store_id] = options[:store_id] if options[:store_id].present?
end
parse(xml) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 371
def parse(xml)
  reply = {}
  xml = REXML::Document.new(xml)
  root = REXML::XPath.first(xml, '//ipgapi:IPGApiOrderResponse') || REXML::XPath.first(xml, '//ipgapi:IPGApiActionResponse') || REXML::XPath.first(xml, '//SOAP-ENV:Fault') || REXML::XPath.first(xml, '//ns4:IPGApiActionResponse')
  reply[:success] = REXML::XPath.first(xml, '//faultcode') ? false : true
  if REXML::XPath.first(xml, '//ns4:IPGApiActionResponse')
    reply[:tpv_error_code] = REXML::XPath.first(root, '//ns2:Error').attributes['Code']
    reply[:tpv_error_msg] = REXML::XPath.first(root, '//ns2:ErrorMessage').text
    reply[:success] = false
  end
  root.elements.to_a.each do |node|
    parse_element(reply, node)
  end
  reply[:hosted_data_id] = @hosted_data_id if @hosted_data_id
  return reply
end
parse_element(reply, node) click to toggle source
# File lib/active_merchant/billing/gateways/ipg.rb, line 388
def parse_element(reply, node)
  if node.has_elements?
    node.elements.each { |e| parse_element(reply, e) }
  else
    if /item/.match?(node.parent.name)
      parent = node.parent.name
      parent += '_' + node.parent.attributes['id'] if node.parent.attributes['id']
      parent += '_'
    end
    reply["#{parent}#{node.name}".to_sym] ||= node.text
  end
  return reply
end