class ActiveMerchant::Billing::BarclaycardSmartpayGateway

Constants

API_VERSION
AVS_MAPPING

Smartpay may return AVS codes not covered by standard AVSResult codes. Smartpay's descriptions noted below.

Public Class Methods

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

Public Instance Methods

authorize(money, creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 32
def authorize(money, creditcard, options = {})
  requires!(options, :order_id)

  post = payment_request(money, options)
  post[:amount] = amount_hash(money, options[:currency])
  post[:card] = credit_card_hash(creditcard)
  post[:billingAddress] = billing_address_hash(options) if options[:billing_address]
  post[:deliveryAddress] = shipping_address_hash(options) if options[:shipping_address]
  post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement]

  add_3ds(post, options)
  commit('authorise', post)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 46
def capture(money, authorization, options = {})
  requires!(options, :order_id)

  post = modification_request(authorization, options)
  post[:modificationAmount] = amount_hash(money, options[:currency])

  commit('capture', post)
end
credit(money, creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 64
def credit(money, creditcard, options = {})
  post = payment_request(money, options)
  post[:amount] = amount_hash(money, options[:currency])
  post[:card] = credit_card_hash(creditcard)
  post[:dateOfBirth] = options[:date_of_birth] if options[:date_of_birth]
  post[:entityType]  = options[:entity_type] if options[:entity_type]
  post[:nationality] = options[:nationality] if options[:nationality]
  post[:shopperName] = options[:shopper_name] if options[:shopper_name]

  if options[:third_party_payout]
    post[:recurring] = options[:recurring_contract] || {contract: 'PAYOUT'}
    MultiResponse.run do |r|
      r.process {
        commit(
          'storeDetailAndSubmitThirdParty',
          post,
          @options[:store_payout_account],
          @options[:store_payout_password])
      }
      r.process {
        commit(
          'confirmThirdParty',
          modification_request(r.authorization, @options),
          @options[:review_payout_account],
          @options[:review_payout_password])
      }
    end
  else
    commit('refundWithData', post)
  end
end
purchase(money, creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 23
def purchase(money, creditcard, options = {})
  requires!(options, :order_id)

  MultiResponse.run do |r|
    r.process { authorize(money, creditcard, options) }
    r.process { capture(money, r.authorization, options) }
  end
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 55
def refund(money, authorization, options = {})
  requires!(options, :order_id)

  post = modification_request(authorization, options)
  post[:modificationAmount] = amount_hash(money, options[:currency])

  commit('refund', post)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 120
def scrub(transcript)
  transcript.
    gsub(%r(((?:\r\n)?Authorization: Basic )[^\r\n]+(\r\n)?), '\1[FILTERED]').
    gsub(%r((card.number=)\d+), '\1[FILTERED]').
    gsub(%r((card.cvc=)\d+), '\1[FILTERED]')
end
store(creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 108
def store(creditcard, options = {})
  post = store_request(options)
  post[:card] = credit_card_hash(creditcard)
  post[:recurring] = {:contract => 'RECURRING'}

  commit('store', post)
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 116
def supports_scrubbing?
  true
end
verify(creditcard, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 104
def verify(creditcard, options = {})
  authorize(0, creditcard, options)
end
void(identification, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 96
def void(identification, options = {})
  requires!(options, :order_id)

  post = modification_request(identification, options)

  commit('cancel', post)
end

Private Instance Methods

add_3ds(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 353
def add_3ds(post, options)
  if three_ds_2_options = options[:three_ds_2]
    device_channel = three_ds_2_options[:channel]
    if device_channel == 'app'
      post[:threeDS2RequestData] = { deviceChannel: device_channel }
    else
      add_browser_info(three_ds_2_options[:browser_info], post)
      post[:threeDS2RequestData] = { deviceChannel: device_channel, notificationURL: three_ds_2_options[:notification_url] }
    end
  else
    return unless options[:execute_threed] || options[:threed_dynamic]
    post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
    post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
  end
end
add_browser_info(browser_info, post) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 369
def add_browser_info(browser_info, post)
  return unless browser_info
  post[:browserInfo] = {
    acceptHeader: browser_info[:accept_header],
    colorDepth: browser_info[:depth],
    javaEnabled: browser_info[:java],
    language: browser_info[:language],
    screenHeight: browser_info[:height],
    screenWidth: browser_info[:width],
    timeZoneOffset: browser_info[:timezone],
    userAgent: browser_info[:user_agent]
  }
end
amount_hash(money, currency) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 304
def amount_hash(money, currency)
  currency = currency || currency(money)
  hash = {}
  hash[:currency] = currency
  hash[:value]    = localized_amount(money, currency) if money
  hash
end
authorization_from(parameters, response) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 183
def authorization_from(parameters, response)
  authorization = [parameters[:originalReference], response['pspReference']].compact

  return nil if authorization.empty?
  return authorization.join('#')
end
billing_address_hash(options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 265
def billing_address_hash(options)
  address = options[:address] || options[:billing_address] if options[:address] || options[:billing_address]
  street = options[:street] || parse_street(address)
  house = options[:house_number] || parse_house_number(address)

  create_address_hash(address, house, street)
end
build_url(action) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 252
def build_url(action)
  case action
  when 'store'
    "#{test? ? self.test_url : self.live_url}/Recurring/#{API_VERSION}/storeToken"
  when 'finalize3ds'
    "#{test? ? self.test_url : self.live_url}/Payment/#{API_VERSION}/authorise3d"
  when 'storeDetailAndSubmitThirdParty', 'confirmThirdParty'
    "#{test? ? self.test_url : self.live_url}/Payout/#{API_VERSION}/#{action}"
  else
    "#{test? ? self.test_url : self.live_url}/Payment/#{API_VERSION}/#{action}"
  end
end
commit(action, post, account = 'ws', password = @options[:password]) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 153
def commit(action, post, account = 'ws', password = @options[:password])
  request = post_data(flatten_hash(post))
  request_headers = headers(account, password)
  raw_response = ssl_post(build_url(action), request, request_headers)
  response = parse(raw_response)

  Response.new(
    success_from(response),
    message_from(response),
    response,
    test: test?,
    avs_result: AVSResult.new(:code => parse_avs_code(response)),
    authorization: response['recurringDetailReference'] || authorization_from(post, response)
  )
rescue ResponseError => e
  case e.response.code
  when '401'
    return Response.new(false, 'Invalid credentials', {}, :test => test?)
  when '403'
    return Response.new(false, 'Not allowed', {}, :test => test?)
  when '422', '500'
    if e.response.body.split(/\W+/).any? { |word| %w(validation configuration security).include?(word) }
      error_message = e.response.body[/#{Regexp.escape('message=')}(.*?)#{Regexp.escape('&')}/m, 1].tr('+', ' ')
      error_code = e.response.body[/#{Regexp.escape('errorCode=')}(.*?)#{Regexp.escape('&')}/m, 1]
      return Response.new(false, error_code + ': ' + error_message, {}, :test => test?)
    end
  end
  raise
end
create_address_hash(address, house, street) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 293
def create_address_hash(address, house, street)
  hash = {}
  hash[:houseNumberOrName] = house
  hash[:street]            = street
  hash[:city]              = address[:city]
  hash[:stateOrProvince]   = address[:state]
  hash[:postalCode]        = address[:zip]
  hash[:country]           = address[:country]
  hash.keep_if { |_, v| v }
end
credit_card_hash(creditcard) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 312
def credit_card_hash(creditcard)
  hash = {}
  hash[:cvc]         = creditcard.verification_value if creditcard.verification_value
  hash[:expiryMonth] = format(creditcard.month, :two_digits) if creditcard.month
  hash[:expiryYear]  = format(creditcard.year, :four_digits) if creditcard.year
  hash[:holderName]  = creditcard.name if creditcard.name
  hash[:number]      = creditcard.number if creditcard.number
  hash
end
flatten_hash(hash, prefix = nil) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 194
def flatten_hash(hash, prefix = nil)
  flat_hash = {}
  hash.each_pair do |key, val|
    conc_key = prefix.nil? ? key : "#{prefix}.#{key}"
    if val.is_a?(Hash)
      flat_hash.merge!(flatten_hash(val, conc_key))
    else
      flat_hash[conc_key] = val
    end
  end
  flat_hash
end
headers(account, password) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 207
def headers(account, password)
  {
    'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
    'Authorization' => 'Basic ' + Base64.strict_encode64("#{account}@Company.#{@options[:company]}:#{password}").strip
  }
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 237
def message_from(response)
  return response['resultCode'] if response.has_key?('resultCode') # Payment request
  return response['response'] if response['response'] # Modification request
  return response['result'] if response.has_key?('result') # Store/Recurring request
  'Failure' # Negative fallback in case of error
end
modification_request(reference, options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 322
def modification_request(reference, options)
  hash = {}
  hash[:merchantAccount]    = @options[:merchant]
  hash[:originalReference]  = psp_reference_from(reference)
  hash.keep_if { |_, v| v }
end
parse(response) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 214
def parse(response)
  parsed_response = {}
  params = CGI.parse(response)
  params.each do |key, value|
    parsed_key = key.split('.', 2)
    if parsed_key.size > 1
      parsed_response[parsed_key[0]] ||= {}
      parsed_response[parsed_key[0]][parsed_key[1]] = value[0]
    else
      parsed_response[parsed_key[0]] = value[0]
    end
  end
  parsed_response
end
parse_avs_code(response) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 190
def parse_avs_code(response)
  AVS_MAPPING[response['additionalData']['avsResult'][0..1].strip] if response.dig('additionalData', 'avsResult')
end
parse_house_number(address) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 287
def parse_house_number(address)
  address_to_parse = "#{address[:address1]} #{address[:address2]}"
  house = address[:houseNumberOrName] || address_to_parse.split(/\s+/).keep_if { |x| x =~ /\d/ }.join(' ')
  house.empty? ? 'Not Provided' : house
end
parse_street(address) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 281
def parse_street(address)
  address_to_parse = "#{address[:address1]} #{address[:address2]}"
  street = address[:street] || address_to_parse.split(/\s+/).keep_if { |x| x !~ /\d/ }.join(' ')
  street.empty? ? 'Not Provided' : street
end
payment_request(money, options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 333
def payment_request(money, options)
  hash = {}
  hash[:merchantAccount]    = @options[:merchant]
  hash[:reference]          = options[:order_id]
  hash[:shopperEmail]       = options[:email]
  hash[:shopperIP]          = options[:ip]
  hash[:shopperReference]   = options[:customer]
  hash[:shopperInteraction] = options[:shopper_interaction]
  hash[:deviceFingerprint]  = options[:device_fingerprint]
  hash.keep_if { |_, v| v }
end
post_data(data) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 229
def post_data(data)
  data.map do |key, val|
    "#{key}=#{CGI.escape(val.to_s)}"
  end.reduce do |x, y|
    "#{x}&#{y}"
  end
end
psp_reference_from(authorization) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 329
def psp_reference_from(authorization)
  authorization.nil? ? nil : authorization.split('#').first
end
shipping_address_hash(options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 273
def shipping_address_hash(options)
  address = options[:shipping_address]
  street = options[:shipping_street] || parse_street(address)
  house = options[:shipping_house_number] || parse_house_number(address)

  create_address_hash(address, house, street)
end
store_request(options) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 345
def store_request(options)
  hash = {}
  hash[:merchantAccount]  = @options[:merchant]
  hash[:shopperEmail]     = options[:email]
  hash[:shopperReference] = options[:customer] if options[:customer]
  hash.keep_if { |_, v| v }
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/barclaycard_smartpay.rb, line 244
def success_from(response)
  return true if response['result'] == 'Success'

  successful_results = %w(Authorised Received [payout-submit-received])
  successful_responses = %w([capture-received] [cancel-received] [refund-received] [payout-confirm-received])
  successful_results.include?(response['resultCode']) || successful_responses.include?(response['response'])
end