class ActiveMerchant::Billing::DecidirPlusGateway

Public Class Methods

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

Public Instance Methods

authorize(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 26
def authorize(money, payment, options = {})
  post = {}
  build_purchase_authorize_request(post, money, payment, options)

  commit(:post, 'payments', post)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 33
def capture(money, authorization, options = {})
  post = {}
  post[:amount] = money

  commit(:put, "payments/#{add_reference(authorization)}", post)
end
purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 19
def purchase(money, payment, options = {})
  post = {}
  build_purchase_authorize_request(post, money, payment, options)

  commit(:post, 'payments', post)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 40
def refund(money, authorization, options = {})
  post = {}
  post[:amount] = money

  commit(:post, "payments/#{add_reference(authorization)}/refunds", post)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 74
def scrub(transcript)
  transcript.
    gsub(%r((Apikey: )\w+), '\1[FILTERED]').
    gsub(%r(("card_number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
    gsub(%r(("security_code\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
end
store(payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 59
def store(payment, options = {})
  post = {}
  add_payment(post, payment, options)

  commit(:post, 'tokens', post)
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 70
def supports_scrubbing?
  true
end
unstore(customer_token) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 66
def unstore(customer_token)
  commit(:delete, "cardtokens/#{customer_token}")
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 51
def verify(credit_card, options = {})
  MultiResponse.run(:use_first_response) do |r|
    r.process { store(credit_card, options) }
    r.process { authorize(100, r.authorization, options) }
    r.process(:ignore_result) { void(r.authorization, options) }
  end
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 47
def void(authorization, options = {})
  commit(:post, "payments/#{add_reference(authorization)}/refunds")
end

Private Instance Methods

add_aggregate_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 138
def add_aggregate_data(post, options)
  aggregate_data = {}
  data = options[:aggregate_data]
  aggregate_data[:indicator] = data[:indicator] if data[:indicator]
  aggregate_data[:identification_number] = data[:identification_number] if data[:identification_number]
  aggregate_data[:bill_to_pay] = data[:bill_to_pay] if data[:bill_to_pay]
  aggregate_data[:bill_to_refund] = data[:bill_to_refund] if data[:bill_to_refund]
  aggregate_data[:merchant_name] = data[:merchant_name] if data[:merchant_name]
  aggregate_data[:street] = data[:street] if data[:street]
  aggregate_data[:number] = data[:number] if data[:number]
  aggregate_data[:postal_code] = data[:postal_code] if data[:postal_code]
  aggregate_data[:category] = data[:category] if data[:category]
  aggregate_data[:channel] = data[:channel] if data[:channel]
  aggregate_data[:geographic_code] = data[:geographic_code] if data[:geographic_code]
  aggregate_data[:city] = data[:city] if data[:city]
  aggregate_data[:merchant_id] = data[:merchant_id] if data[:merchant_id]
  aggregate_data[:province] = data[:province] if data[:province]
  aggregate_data[:country] = data[:country] if data[:country]
  aggregate_data[:merchant_email] = data[:merchant_email] if data[:merchant_email]
  aggregate_data[:merchant_phone] = data[:merchant_phone] if data[:merchant_phone]
  post[:aggregate_data] = aggregate_data
end
add_csmdds(hsh, fraud_detection) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 226
def add_csmdds(hsh, fraud_detection)
  return unless fraud_detection[:csmdds]

  csmdds_arr = []
  fraud_detection[:csmdds].each do |csmdds|
    csmdds_hsh = {}
    csmdds_hsh[:code] = csmdds[:code].to_i
    csmdds_hsh[:description] = csmdds[:description]
    csmdds_arr.append(csmdds_hsh)
  end
  hsh[:csmdds] = csmdds_arr unless csmdds_arr.empty?
end
add_customer_data(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 117
def add_customer_data(post, options = {})
  return unless customer = options[:customer]

  post[:customer] = {}
  post[:customer][:id] = customer[:id] if customer[:id]
  post[:customer][:email] = customer[:email] if customer[:email]
end
add_fraud_detection(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 213
def add_fraud_detection(post, options)
  return unless fraud_detection = options[:fraud_detection]

  {}.tap do |hsh|
    hsh[:send_to_cs] = fraud_detection[:send_to_cs] == 'true' # true/false
    hsh[:channel] = fraud_detection[:channel] if fraud_detection[:channel]
    hsh[:dispatch_method] = fraud_detection[:dispatch_method] if fraud_detection[:dispatch_method]
    add_csmdds(hsh, fraud_detection)

    post[:fraud_detection] = hsh
  end
end
add_payment(post, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 96
def add_payment(post, payment, options = {})
  if payment.is_a?(String)
    token, bin = payment.split('|')
    post[:token] = token
    post[:bin] = bin
  else
    post[:card_number] = payment.number
    post[:card_expiration_month] = format(payment.month, :two_digits)
    post[:card_expiration_year] = format(payment.year, :two_digits)
    post[:security_code] = payment.verification_value.to_s
    post[:card_holder_name] = payment.name.empty? ? options[:name_override] : payment.name
    post[:card_holder_identification] = {}
    post[:card_holder_identification][:type] = options[:card_holder_identification_type] if options[:card_holder_identification_type]
    post[:card_holder_identification][:number] = options[:card_holder_identification_number] if options[:card_holder_identification_number]

    # additional data used for Visa transactions
    post[:card_holder_door_number] = options[:card_holder_door_number].to_i if options[:card_holder_door_number]
    post[:card_holder_birthday] = options[:card_holder_birthday] if options[:card_holder_birthday]
  end
end
add_payment_method_id(options) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 177
def add_payment_method_id(options)
  return options[:payment_method_id].to_i if options[:payment_method_id]

  if options[:debit]
    case options[:card_brand]
    when 'visa'
      31
    when 'master'
      105
    when 'maestro'
      106
    when 'cabal'
      108
    else
      31
    end
  else
    case options[:card_brand]
    when 'visa'
      1
    when 'master'
      104
    when 'american_express'
      65
    when 'american_express_prisma'
      111
    when 'cabal'
      63
    when 'diners_club'
      8
    else
      1
    end
  end
end
add_purchase_data(post, money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 125
def add_purchase_data(post, money, payment, options = {})
  post[:site_transaction_id] = options[:site_transaction_id] || SecureRandom.hex
  post[:payment_method_id] = add_payment_method_id(options)
  post[:amount] = money
  post[:currency] = options[:currency] || self.default_currency
  post[:installments] = options[:installments] || 1
  post[:payment_type] = options[:payment_type] || 'single'
  post[:establishment_name] = options[:establishment_name] if options[:establishment_name]

  add_aggregate_data(post, options) if options[:aggregate_data]
  add_sub_payments(post, options)
end
add_reference(authorization) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 90
def add_reference(authorization)
  return unless authorization

  authorization.split('|')[0]
end
add_sub_payments(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 161
def add_sub_payments(post, options)
  # sub_payments field is required for purchase transactions, even if empty
  post[:sub_payments] = []

  return unless sub_payments = options[:sub_payments]

  sub_payments.each do |sub_payment|
    sub_payment_hash = {
      site_id: sub_payment[:site_id],
      installments: sub_payment[:installments].to_i,
      amount: sub_payment[:amount].to_i
    }
    post[:sub_payments] << sub_payment_hash
  end
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 289
def authorization_from(response)
  return nil unless response.dig('id') || response.dig('bin')

  "#{response.dig('id')}|#{response.dig('bin')}"
end
build_purchase_authorize_request(post, money, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 83
def build_purchase_authorize_request(post, money, payment, options)
  add_customer_data(post, options)
  add_payment(post, payment, options)
  add_purchase_data(post, money, payment, options)
  add_fraud_detection(post, options)
end
commit(method, endpoint, parameters = {}, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 245
def commit(method, endpoint, parameters = {}, options = {})
  begin
    raw_response = ssl_request(method, url(endpoint), post_data(parameters), headers(endpoint))
    response = parse(raw_response)
  rescue ResponseError => e
    raw_response = e.response.body
    response = parse(raw_response)
  end

  Response.new(
    success_from(response),
    message_from(response),
    response,
    authorization: authorization_from(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)
  )
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 299
def error_code_from(response)
  return if success_from(response)

  error_code = nil
  if error = response.dig('status_details', 'error')
    error_code = error.dig('reason', 'id') || error['type']
  elsif response['error_type']
    error_code = response['error_type']
  elsif response.dig('error', 'validation_errors')
    error = response.dig('error')
    validation_errors = error.dig('validation_errors', 0)
    code = validation_errors['code'] if validation_errors && validation_errors['code']
    param = validation_errors['param'] if validation_errors && validation_errors['param']
    error_code = "#{error['error_type']} | #{code} | #{param}" if error['error_type']
  elsif error = response.dig('error')
    error_code = error.dig('reason', 'id')
  end

  error_code
end
error_message(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 320
def error_message(response)
  return error_code_from(response) unless validation_errors = response.dig('validation_errors')

  validation_errors = validation_errors[0]
  message = "#{validation_errors&.dig('code')}: #{validation_errors&.dig('param')}"
  return message unless message == ': '

  errors = response['validation_errors'].map { |k, v| "#{k}: #{v}" }.join(', ')
  "#{response['error_type']} - #{errors}"
end
headers(endpoint) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 266
def headers(endpoint)
  {
    'Content-Type' => 'application/json',
    'apikey' => endpoint == 'tokens' ? @options[:public_key] : @options[:private_key]
  }
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 283
def message_from(response)
  return '' if response.empty?

  rejected?(response) ? message_from_status_details(response) : response.dig('status') || error_message(response) || response.dig('message')
end
message_from_fraud_detection(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 342
def message_from_fraud_detection(response)
  return error_message(response.dig('fraud_detection', 'status', 'details'))
end
message_from_status_details(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 335
def message_from_status_details(response)
  return unless error = response.dig('status_details', 'error')
  return message_from_fraud_detection(response) if error.dig('type') == 'cybersource_error'

  "#{error.dig('type')}: #{error.dig('reason', 'description')}"
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 239
def parse(body)
  return {} if body.nil?

  JSON.parse(body)
end
post_data(parameters = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 295
def post_data(parameters = {})
  parameters.to_json
end
rejected?(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 331
def rejected?(response)
  return response.dig('status') == 'rejected'
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 279
def success_from(response)
  response.dig('status') == 'approved' || response.dig('status') == 'active' || response.dig('status') == 'pre_approved' || response.empty?
end
url(action, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/decidir_plus.rb, line 273
def url(action, options = {})
  base_url = (test? ? test_url : live_url)

  return "#{base_url}/#{action}"
end