class ActiveMerchant::Billing::ReachGateway

Constants

API_VERSION
PAYMENT_METHOD_MAP
STANDARD_ERROR_CODE_MAPPING

Public Class Methods

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

Public Instance Methods

authorize(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 45
def authorize(money, payment, options = {})
  request = build_checkout_request(money, payment, options)
  add_custom_fields_data(request, options)
  add_customer_data(request, options, payment)
  add_stored_credentials(request, options)
  post = { request: request, card: add_payment(payment, options) }
  if options[:stored_credential]
    MultiResponse.run(:use_first_response) do |r|
      r.process { commit('checkout', post) }
      r.process do
        r2 = get_network_payment_reference(r.responses[0])
        r.params[:network_transaction_id] = r2.message
        r2
      end
    end
  else
    commit('checkout', post)
  end
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 70
def capture(money, authorization, options = {})
  post = { request: { MerchantId: @options[:merchant_id], OrderId: authorization } }
  commit('capture', post)
end
purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 65
def purchase(money, payment, options = {})
  options[:capture] = true
  authorize(money, payment, options)
end
refund(amount, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 87
def refund(amount, authorization, options = {})
  currency = options[:currency] || currency(options[:amount])
  post = {
    request: {
      MerchantId: @options[:merchant_id],
      OrderId: authorization,
      ReferenceId: options[:order_id] || options[:reference_id],
      Amount: localized_amount(amount, currency)
    }
  }
  commit('refund', post)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 79
def scrub(transcript)
  transcript.
    gsub(%r(((MerchantId)[% \w]+%\d{2})[\w -]+), '\1[FILTERED]').
    gsub(%r((signature=)[\w%]+), '\1[FILTERED]\2').
    gsub(%r((Number%22%3A%22)\d+), '\1[FILTERED]\2').
    gsub(%r((VerificationCode%22%3A)\d+), '\1[FILTERED]\2')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 75
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 111
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/reach.rb, line 100
def void(authorization, options = {})
  post = {
    request: {
      MerchantId: @options[:merchant_id],
      OrderId: authorization
    }
  }

  commit('cancel', post)
end

Private Instance Methods

add_custom_fields_data(request, options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 192
def add_custom_fields_data(request, options)
  add_shipping_data(request, options) if options[:taxes].present?
  request[:RateOfferId] = options[:rate_offer_id] if options[:rate_offer_id].present?
  request[:Items] = options[:items] if options[:items].present?
end
add_customer_data(request, options, payment) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 146
def add_customer_data(request, options, payment)
  address = options[:billing_address] || options[:address]

  return if address.blank?

  request[:Consumer] = {
    Name: payment.respond_to?(:name) ? payment.name : address[:name],
    Email: options[:email],
    Address: address[:address1],
    City: address[:city],
    Country: address[:country]
  }.compact
end
add_payment(payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 136
def add_payment(payment, options)
  ntid = options.dig(:stored_credential, :network_transaction_id)
  cvv_or_previos_reference = (ntid ? { PreviousNetworkPaymentReference: ntid } : { VerificationCode: payment.verification_value })
  {
    Name: payment.name,
    Number: payment.number,
    Expiry: { Month: payment.month, Year: payment.year }
  }.merge!(cvv_or_previos_reference)
end
add_shipping_data(request, options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 198
def add_shipping_data(request, options)
  request[:Shipping] = {
    ConsumerPrice: options[:price],
    ConsumerTaxes: options[:taxes],
    ConsumerDuty: options[:duty]
  }
  request[:Consignee] = {
    Name: options[:consignee_name],
    Address: options[:consignee_address],
    City: options[:consignee_city],
    Country: options[:consignee_country]
  }
end
add_stored_credentials(request, options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 160
def add_stored_credentials(request, options)
  request[:PaymentModel] = payment_model(options) || ''
  request[:DeviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 267
def authorization_from(response)
  response['OrderId']
end
build_checkout_request(amount, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 120
def build_checkout_request(amount, payment, options)
  currency = options[:currency] || currency(options[:amount])
  {
    MerchantId: @options[:merchant_id],
    ReferenceId: options[:order_id],
    ConsumerCurrency: currency,
    Capture: options[:capture] || false,
    PaymentMethod: PAYMENT_METHOD_MAP.fetch(payment.brand.to_sym, 'unsupported'),
    Items: [
      Sku: options[:item_sku] || SecureRandom.alphanumeric,
      ConsumerPrice: localized_amount(amount, currency),
      Quantity: (options[:item_quantity] || 1)
    ]
  }
end
commit(action, parameters) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 240
def commit(action, parameters)
  body = post_data format_and_sign(parameters)
  raw_response = ssl_post url(action), body
  response = parse(raw_response)

  Response.new(
    success_from(response),
    message_from(response) || '',
    response,
    authorization: authorization_from(response['response']),
    # avs_result: AVSResult.new(code: response['some_avs_response_key']),
    # cvv_result: CVVResult.new(response['some_cvv_response_key']),
    test: test?,
    error_code: error_code_from(response)
  )
rescue ActiveMerchant::ResponseError => e
  Response.new(false, (e.response.body.present? ? e.response.body : e.response.msg), {}, test: test?)
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 275
def error_code_from(response)
  response['response']['Error']['Code'] unless success_from(response)
end
format_and_sign(post) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 223
def format_and_sign(post)
  post[:request] = post[:request].to_json
  post[:card] = post[:card].to_json if post[:card].present?
  post[:signature] = sign_body(post[:request])
  post
end
get_network_payment_reference(response) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 230
def get_network_payment_reference(response)
  parameters = { request: { MerchantId: @options[:merchant_id], OrderId: response.params['response']['OrderId'] } }
  body = post_data format_and_sign(parameters)

  raw_response = ssl_request :post, url('query'), body, {}
  response = parse(raw_response)
  message = response.dig('response', 'Payment', 'NetworkPaymentReference')
  Response.new(true, message, {})
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 263
def message_from(response)
  success_from(response) ? '' : response.dig('response', 'Error', 'ReasonCode')
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 216
def parse(body)
  hash_response = URI.decode_www_form(body).to_h
  hash_response['response'] = JSON.parse(hash_response['response'])

  hash_response
end
payment_model(options) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 165
def payment_model(options)
  stored_credential = options[:stored_credential]
  return options[:payment_model] if options[:payment_model]
  return 'CIT-One-Time' unless stored_credential

  payment_model_options = {
    initial_transaction: {
      'cardholder' => {
        'installment' => 'CIT-Setup-Scheduled',
        'unscheduled' => 'CIT-Setup-Unscheduled-MIT',
        'recurring' => 'CIT-Setup-Unscheduled'
      }
    },
    no_initial_transaction: {
      'cardholder' => {
        'unscheduled' => 'CIT-Subsequent-Unscheduled'
      },
      'merchant' => {
        'recurring' => 'MIT-Subsequent-Scheduled',
        'unscheduled' => 'MIT-Subsequent-Unscheduled'
      }
    }
  }
  initial = stored_credential[:initial_transaction] ? :initial_transaction : :no_initial_transaction
  payment_model_options[initial].dig(stored_credential[:initiator], stored_credential[:reason_type])
end
post_data(params) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 271
def post_data(params)
  params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
end
sign_body(body) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 212
def sign_body(body)
  Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', @options[:secret].encode('utf-8'), body.encode('utf-8')))
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 259
def success_from(response)
  response.dig('response', 'Error').blank?
end
url(action) click to toggle source
# File lib/active_merchant/billing/gateways/reach.rb, line 279
def url(action)
  "#{test? ? test_url : live_url}#{API_VERSION}/#{action}"
end