module ActiveMerchant::Billing::BeanstreamCore

Constants

AVS_CODES
CVD_CODES
PERIODICITIES
PERIODS
PROFILE_OPERATIONS
RECURRING_OPERATION
RECURRING_URL
SECURE_PROFILE_URL
SP_SERVICE_VERSION
STATES
TRANSACTIONS

Public Class Methods

included(base) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 130
def self.included(base)
  base.default_currency = 'CAD'

  # The countries the gateway supports merchants from as 2 digit ISO country codes
  base.supported_countries = %w[CA US]

  # The card types supported by the payment gateway
  base.supported_cardtypes = %i[visa master american_express discover diners_club jcb]

  # The homepage URL of the gateway
  base.homepage_url = 'http://www.beanstream.com/'
  base.live_url = 'https://api.na.bambora.com/scripts/process_transaction.asp'

  # The name of the gateway
  base.display_name = 'Beanstream.com'
end
new(options = {}) click to toggle source

Only :login is required by default, which is the merchant’s merchant ID. If you’d like to perform void, capture or refund transactions then you’ll also need to add a username and password to your account under administration -> account settings -> order settings -> Use username/password validation

Calls superclass method
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 152
def initialize(options = {})
  requires!(options, :login)
  super
end

Public Instance Methods

capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 157
def capture(money, authorization, options = {})
  reference, = split_auth(authorization)
  post = {}
  add_amount(post, money)
  add_reference(post, reference)
  add_transaction_type(post, :capture)
  add_recurring_payment(post, options)
  commit(post)
end
credit(money, source, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 176
def credit(money, source, options = {})
  ActiveMerchant.deprecated Gateway::CREDIT_DEPRECATION_MESSAGE
  refund(money, source, options)
end
refund(money, source, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 167
def refund(money, source, options = {})
  post = {}
  reference, _, type = split_auth(source)
  add_reference(post, reference)
  add_transaction_type(post, refund_action(type))
  add_amount(post, money)
  commit(post)
end

Private Instance Methods

add_address(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 223
def add_address(post, options)
  post[:ordEmailAddress]  = options[:email] if options[:email]
  post[:shipEmailAddress] = options[:shipping_email] || options[:email] if options[:email]

  prepare_address_for_non_american_countries(options)

  if billing_address = options[:billing_address] || options[:address]
    post[:ordName]          = billing_address[:name]
    post[:ordPhoneNumber]   = billing_address[:phone] || billing_address[:phone_number]
    post[:ordAddress1]      = billing_address[:address1]
    post[:ordAddress2]      = billing_address[:address2]
    post[:ordCity]          = billing_address[:city]
    post[:ordProvince]      = state_for(billing_address)
    post[:ordPostalCode]    = billing_address[:zip]
    post[:ordCountry]       = billing_address[:country]
  end

  if shipping_address = options[:shipping_address]
    post[:shipName]         = shipping_address[:name]
    post[:shipPhoneNumber]  = shipping_address[:phone]
    post[:shipAddress1]     = shipping_address[:address1]
    post[:shipAddress2]     = shipping_address[:address2]
    post[:shipCity]         = shipping_address[:city]
    post[:shipProvince]     = state_for(shipping_address)
    post[:shipPostalCode]   = shipping_address[:zip]
    post[:shipCountry]      = shipping_address[:country]
    post[:shippingMethod]   = shipping_address[:shipping_method]
    post[:deliveryEstimate] = shipping_address[:delivery_estimate]
  end
end
add_amount(post, money) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 211
def add_amount(post, money)
  post[:trnAmount] = amount(money)
end
add_check(post, check) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 298
def add_check(post, check)
  # The institution number of the consumer’s financial institution. Required for Canadian dollar EFT transactions.
  post[:institutionNumber] = check.institution_number

  # The bank transit number of the consumer’s bank account. Required for Canadian dollar EFT transactions.
  post[:transitNumber] = check.transit_number

  # The routing number of the consumer’s bank account.  Required for US dollar EFT transactions.
  post[:routingNumber] = check.routing_number

  # The account number of the consumer’s bank account.  Required for both Canadian and US dollar EFT transactions.
  post[:accountNumber] = check.account_number
end
add_credit_card(post, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 283
def add_credit_card(post, credit_card)
  if credit_card
    post[:trnCardOwner] = credit_card.name
    post[:trnCardNumber] = credit_card.number
    post[:trnExpMonth] = format(credit_card.month, :two_digits)
    post[:trnExpYear] = format(credit_card.year, :two_digits)
    post[:trnCardCvd] = credit_card.verification_value
    if credit_card.is_a?(NetworkTokenizationCreditCard)
      post[:"3DSecureXID"] = credit_card.transaction_id
      post[:"3DSecureECI"] = credit_card.eci
      post[:"3DSecureCAVV"] = credit_card.payment_cryptogram
    end
  end
end
add_customer_ip(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 191
def add_customer_ip(post, options)
  post[:customerIp] = options[:ip] if options[:ip]
end
add_invoice(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 273
def add_invoice(post, options)
  post[:trnOrderNumber]   = options[:order_id]
  post[:trnComments]      = options[:description]
  post[:ordItemPrice]     = amount(options[:subtotal])
  post[:ordShippingPrice] = amount(options[:shipping])
  post[:ordTax1Price]     = amount(options[:tax1] || options[:tax])
  post[:ordTax2Price]     = amount(options[:tax2])
  post[:ref1]             = options[:custom]
end
add_original_amount(post, amount) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 215
def add_original_amount(post, amount)
  post[:trnAmount] = amount
end
add_recurring_amount(post, money) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 324
def add_recurring_amount(post, money)
  post[:amount] = amount(money)
end
add_recurring_invoice(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 328
def add_recurring_invoice(post, options)
  post[:rbApplyTax1] = options[:apply_tax1]
  post[:rbApplyTax2] = options[:apply_tax2]
end
add_recurring_operation_type(post, operation) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 333
def add_recurring_operation_type(post, operation)
  post[:operationType] = RECURRING_OPERATION[operation]
end
add_recurring_payment(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 269
def add_recurring_payment(post, options)
  post[:recurringPayment] = 1 if options[:recurring].to_s == 'true'
end
add_recurring_service(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 337
def add_recurring_service(post, options)
  post[:serviceVersion] = '1.0'
  post[:merchantId]     = @options[:login]
  post[:passCode]       = @options[:recurring_api_key]
  post[:rbAccountId]    = options[:account_id]
end
add_recurring_type(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 344
def add_recurring_type(post, options)
  # XXX requires!
  post[:trnRecurring] = 1
  period, increment = interval(options)
  post[:rbBillingPeriod] = PERIODS[period]
  post[:rbBillingIncrement] = increment

  if options.include? :start_date
    post[:rbCharge] = 0
    post[:rbFirstBilling] = options[:start_date].strftime('%m%d%Y')
  end

  if count = options[:occurrences] || options[:payments]
    post[:rbExpiry] = (options[:start_date] || Date.current).advance(period => count).strftime('%m%d%Y')
  end
end
add_reference(post, reference) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 219
def add_reference(post, reference)
  post[:adjId] = reference
end
add_secure_profile_variables(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 312
def add_secure_profile_variables(post, options = {})
  post[:serviceVersion] = SP_SERVICE_VERSION
  post[:responseFormat] = 'QS'
  post[:cardValidation] = (options[:cardValidation].to_i == 1) || '0'
  post[:operationType] = options[:operationType] || options[:operation] || secure_profile_action(:new)
  post[:customerCode] = options[:billing_id] || options[:vault_id] || false
  post[:status] = options[:status]

  billing_address = options[:billing_address] || options[:address]
  post[:trnCardOwner] = billing_address ? billing_address[:name] : nil
end
add_source(post, source) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 448
def add_source(post, source)
  if source.is_a?(String) || source.is_a?(Integer)
    post[:customerCode] = source
  else
    card_brand(source) == 'check' ? add_check(post, source) : add_credit_card(post, source)
  end
end
add_transaction_type(post, action) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 456
def add_transaction_type(post, action)
  post[:trnType] = TRANSACTIONS[action]
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 432
def authorization_from(response)
  "#{response[:trnId]};#{response[:trnAmount]};#{response[:trnType]}"
end
commit(params, use_profile_api = false) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 405
def commit(params, use_profile_api = false)
  post(post_data(params, use_profile_api), use_profile_api)
end
interval(options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 361
def interval(options)
  if options.include? :periodicity
    requires!(options, [:periodicity, *PERIODICITIES.keys])
    PERIODICITIES[options[:periodicity]]
  elsif options.include? :interval
    interval = options[:interval]
    if interval.respond_to? :parts
      parts = interval.parts
      raise ArgumentError.new("Cannot recur with mixed interval (#{interval}). Use only one of: days, weeks, months or years") if parts.length > 1

      parts.first
    elsif interval.kind_of? Hash
      requires!(interval, :unit)
      unit, length = interval.values_at(:unit, :length)
      length ||= 1
      [unit, length]
    end
  end
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 436
def message_from(response)
  response[:messageText] || response[:responseMessage]
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 381
def parse(body)
  results = {}
  body&.split(/&/)&.each do |pair|
    key, val = pair.split(/\=/)
    results[key.to_sym] = val.nil? ? nil : CGI.unescape(val)
  end

  # Clean up the message text if there is any
  if results[:messageText]
    results[:messageText].gsub!(/<LI>/, '')
    results[:messageText].gsub!(/(\.)?<br>/, '. ')
    results[:messageText].strip!
  end

  results
end
post(data, use_profile_api = nil) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 413
def post(data, use_profile_api = nil)
  response = parse(ssl_post((use_profile_api ? SECURE_PROFILE_URL : self.live_url), data))
  response[:customer_vault_id] = response[:customerCode] if response[:customerCode]
  build_response(
    success?(response),
    message_from(response),
    response,
    test: test? || response[:authCode] == 'TEST',
    authorization: authorization_from(response),
    cvv_result: CVD_CODES[response[:cvdId]],
    avs_result: { code: AVS_CODES.include?(response[:avsId]) ? AVS_CODES[response[:avsId]] : response[:avsId] }
  )
end
post_data(params, use_profile_api) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 460
def post_data(params, use_profile_api)
  params[:requestType] = 'BACKEND'
  if use_profile_api
    params[:merchantId] = @options[:login]
    params[:passCode] = @options[:secure_profile_api_key]
  else
    params[:username] = @options[:user] if @options[:user]
    params[:password] = @options[:password] if @options[:password]
    params[:merchant_id] = @options[:login]
    params[:passcode] = @options[:api_key]
  end
  params[:vbvEnabled] = '0'
  params[:scEnabled] = '0'

  params.reject { |_k, v| v.blank? }.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
end
prepare_address_for_non_american_countries(options) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 258
def prepare_address_for_non_american_countries(options)
  [options[:billing_address], options[:shipping_address]].compact.each do |address|
    next if empty?(address[:country])

    unless %w[US CA].include?(address[:country])
      address[:state] = '--'
      address[:zip]   = '000000' unless address[:zip]
    end
  end
end
purchase_action(source) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 183
def purchase_action(source)
  if source.is_a?(Check)
    :check_purchase
  else
    :purchase
  end
end
recurring_commit(params) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 409
def recurring_commit(params)
  recurring_post(post_data(params, false))
end
recurring_message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 440
def recurring_message_from(response)
  response[:message]
end
recurring_parse(data) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 398
def recurring_parse(data)
  REXML::Document.new(data).root.elements.to_a.inject({}) do |response, element|
    response[element.name.to_sym] = element.text
    response
  end
end
recurring_post(data) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 427
def recurring_post(data)
  response = recurring_parse(ssl_post(RECURRING_URL, data))
  build_response(recurring_success?(response), recurring_message_from(response), response)
end
recurring_success?(response) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 444
def recurring_success?(response)
  response[:code] == '1'
end
refund_action(type) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 199
def refund_action(type)
  type == TRANSACTIONS[:check_purchase] ? :check_refund : :refund
end
secure_profile_action(type) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 203
def secure_profile_action(type)
  PROFILE_OPERATIONS[type] || PROFILE_OPERATIONS[:new]
end
split_auth(string) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 207
def split_auth(string)
  string.split(';')
end
state_for(address) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 254
def state_for(address)
  STATES[address[:state].upcase] || address[:state] if address[:state]
end
void_action(original_transaction_type) click to toggle source
# File lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb, line 195
def void_action(original_transaction_type)
  original_transaction_type == TRANSACTIONS[:refund] ? :void_refund : :void_purchase
end