class ActiveMerchant::Billing::CardStreamGateway

Constants

AVS_POSTAL_MATCH

0 - No additional information available. 1 - Postcode not checked. 2 - Postcode matched. 4 - Postcode not matched. 8 - Postcode partially matched.

AVS_STREET_MATCH

0 - No additional information available. 1 - Address numeric not checked. 2 - Address numeric matched. 4 - Address numeric not matched. 8 - Address numeric partially matched.

CURRENCY_CODES
CVV_CODE
THREEDSECURE_REQUIRED_DEPRECATION_MESSAGE

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method ActiveMerchant::Billing::Gateway::new
# File lib/active_merchant/billing/gateways/card_stream.rb, line 141
def initialize(options = {})
  requires!(options, :login, :shared_secret)
  @threeds_required = false
  if options[:threeDSRequired]
    ActiveMerchant.deprecated(THREEDSECURE_REQUIRED_DEPRECATION_MESSAGE)
    @threeds_required = options[:threeDSRequired]
  end
  super
end

Public Instance Methods

authorize(money, credit_card_or_reference, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 151
def authorize(money, credit_card_or_reference, options = {})
  post = {}
  add_auth_purchase(post, -1, money, credit_card_or_reference, options)
  commit('SALE', post)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 163
def capture(money, authorization, options = {})
  post = {}
  add_pair(post, :xref, authorization)
  add_pair(post, :amount, localized_amount(money, options[:currency] || currency(money)), required: true)
  add_remote_address(post, options)

  commit('CAPTURE', post)
end
purchase(money, credit_card_or_reference, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 157
def purchase(money, credit_card_or_reference, options = {})
  post = {}
  add_auth_purchase(post, 0, money, credit_card_or_reference, options)
  commit('SALE', post)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 172
def refund(money, authorization, options = {})
  post = {}
  add_pair(post, :xref, authorization)
  add_amount(post, money, options)
  add_remote_address(post, options)
  add_country_code(post, options)
  response = commit('REFUND_SALE', post)

  return response if response.success?
  return response unless options[:force_full_refund_if_unsettled]

  if response.params['responseCode'] == '65541'
    void(authorization, options)
  else
    response
  end
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 208
def scrub(transcript)
  transcript.
    gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
    gsub(%r((cardNumber=)\d+), '\1[FILTERED]').
    gsub(%r((CVV=)\d+), '\1[FILTERED]')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 204
def supports_scrubbing?
  true
end
verify(creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 197
def verify(creditcard, options = {})
  MultiResponse.run(:use_first_response) do |r|
    r.process { authorize(100, creditcard, options) }
    r.process(:ignore_result) { void(r.authorization, options) }
  end
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 190
def void(authorization, options = {})
  post = {}
  add_pair(post, :xref, authorization)
  add_remote_address(post, options)
  commit('CANCEL', post)
end

Private Instance Methods

add_amount(post, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 228
def add_amount(post, money, options)
  currency = options[:currency] || currency(money)
  add_pair(post, :amount, localized_amount(money, currency), required: true)
  add_pair(post, :currencyCode, currency_code(currency))
end
add_auth_purchase(post, pair_value, money, credit_card_or_reference, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 217
def add_auth_purchase(post, pair_value, money, credit_card_or_reference, options)
  add_pair(post, :captureDelay, pair_value)
  add_amount(post, money, options)
  add_invoice(post, credit_card_or_reference, money, options)
  add_credit_card_or_reference(post, credit_card_or_reference)
  add_customer_data(post, options)
  add_remote_address(post, options)
  add_country_code(post, options)
  add_threeds_fields(post, options)
end
add_country_code(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 303
def add_country_code(post, options)
  post[:countryCode] = options[:country_code] || self.supported_countries[0]
end
add_credit_card(post, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 273
def add_credit_card(post, credit_card)
  add_pair(post, :customerName, credit_card.name, required: true)
  add_pair(post, :cardNumber, credit_card.number, required: true)
  add_pair(post, :cardExpiryMonth, format(credit_card.month, :two_digits), required: true)
  add_pair(post, :cardExpiryYear, format(credit_card.year, :two_digits), required: true)
  add_pair(post, :cardCVV, credit_card.verification_value)
end
add_credit_card_or_reference(post, credit_card_or_reference) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 261
def add_credit_card_or_reference(post, credit_card_or_reference)
  if credit_card_or_reference.respond_to?(:number)
    add_credit_card(post, credit_card_or_reference)
  else
    add_reference(post, credit_card_or_reference.to_s)
  end
end
add_customer_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 234
def add_customer_data(post, options)
  add_pair(post, :customerEmail, options[:email])
  if (address = options[:billing_address] || options[:address])
    add_pair(post, :customerAddress, "#{address[:address1]} #{address[:address2]}".strip)
    add_pair(post, :customerPostCode, address[:zip])
    add_pair(post, :customerPhone, options[:phone])
    add_pair(post, :customerCountryCode, address[:country] || 'GB')
  else
    add_pair(post, :customerCountryCode, 'GB')
  end
end
add_hmac(post) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 311
def add_hmac(post)
  result = post.sort.collect { |key, value| "#{key}=#{normalize_line_endings(CGI.escape(value.to_s))}" }.join('&')
  result = Digest::SHA512.hexdigest("#{result}#{@options[:shared_secret]}")

  add_pair(post, :signature, result)
end
add_invoice(post, credit_card_or_reference, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 246
def add_invoice(post, credit_card_or_reference, money, options)
  add_pair(post, :transactionUnique, options[:order_id], required: true)
  add_pair(post, :orderRef, options[:description] || options[:order_id], required: true)
  add_pair(post, :statementNarrative1, options[:merchant_name]) if options[:merchant_name]
  add_pair(post, :statementNarrative2, options[:dynamic_descriptor]) if options[:dynamic_descriptor]
  if credit_card_or_reference.respond_to?(:number) && %w[american_express diners_club].include?(card_brand(credit_card_or_reference).to_s)
    add_pair(post, :item1Quantity, 1)
    add_pair(post, :item1Description, (options[:description] || options[:order_id]).slice(0, 15))
    add_pair(post, :item1GrossValue, localized_amount(money, options[:currency] || currency(money)))
  end

  add_pair(post, :type, options[:type] || '1')
  add_threeds_required(post, options)
end
add_pair(post, key, value, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 379
def add_pair(post, key, value, options = {})
  post[key] = value if !value.blank? || options[:required]
end
add_reference(post, reference) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 269
def add_reference(post, reference)
  add_pair(post, :xref, reference, required: true)
end
add_remote_address(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 299
def add_remote_address(post, options = {})
  add_pair(post, :remoteAddress, options[:ip] || '1.1.1.1')
end
add_threeds_fields(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 285
def add_threeds_fields(post, options)
  return unless three_d_secure = options[:three_d_secure]

  add_pair(post, :threeDSEnrolled, formatted_enrollment(three_d_secure[:enrolled]))
  if three_d_secure[:enrolled] == 'true'
    add_pair(post, :threeDSAuthenticated, three_d_secure[:authentication_response_status])
    if three_d_secure[:authentication_response_status] == 'Y'
      post[:threeDSECI]  = three_d_secure[:eci]
      post[:threeDSCAVV] = three_d_secure[:cavv]
      post[:threeDSXID] = three_d_secure[:xid] || three_d_secure[:ds_transaction_id]
    end
  end
end
add_threeds_required(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 281
def add_threeds_required(post, options)
  add_pair(post, :threeDSRequired, options[:threeds_required] || @threeds_required ? 'Y' : 'N')
end
avs_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 350
def avs_from(response)
  postal_match = AVS_POSTAL_MATCH[response[:avscv2ResponseCode].to_s[1, 1]]
  street_match = AVS_STREET_MATCH[response[:avscv2ResponseCode].to_s[2, 1]]

  code = if postal_match == 'Y' && street_match == 'Y'
           'M'
         elsif postal_match == 'Y'
           'P'
         elsif street_match == 'Y'
           'A'
         else
           'I'
         end

  AVSResult.new({
    code: code,
    postal_match: postal_match,
    street_match: street_match
  })
end
commit(action, parameters) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 329
def commit(action, parameters)
  parameters.update(
    merchantID: @options[:login],
    action: action
  )
  # adds a signature to the post hash/array
  add_hmac(parameters)

  response = parse(ssl_post(self.live_url, post_data(action, parameters)))

  Response.new(
    response[:responseCode] == '0',
    response[:responseCode] == '0' ? 'APPROVED' : response[:responseMessage],
    response,
    test: test?,
    authorization: response[:xref],
    cvv_result: CVV_CODE[response[:avscv2ResponseCode].to_s[0, 1]],
    avs_result: avs_from(response)
  )
end
currency_code(currency) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 371
def currency_code(currency)
  CURRENCY_CODES[currency]
end
formatted_enrollment(val) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 383
def formatted_enrollment(val)
  case val
  when 'Y', 'N', 'U' then val
  when true, 'true' then 'Y'
  when false, 'false' then 'N'
  end
end
normalize_line_endings(str) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 307
def normalize_line_endings(str)
  str.gsub(/%0D%0A|%0A%0D|%0D/, '%0A')
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 318
def parse(body)
  result = {}
  pairs = body.split('&')
  pairs.each do |pair|
    a = pair.split('=')
    # because some value pairs don't have a value
    result[a[0].to_sym] = a[1] == nil ? '' : CGI.unescape(a[1])
  end
  result
end
post_data(action, parameters = {}) click to toggle source
# File lib/active_merchant/billing/gateways/card_stream.rb, line 375
def post_data(action, parameters = {})
  parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
end