class ActiveMerchant::Billing::DeepstackGateway

Constants

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/deepstack.rb, line 17
def initialize(options = {})
  requires!(options, :publishable_api_key, :app_id, :shared_secret)
  @publishable_api_key, @app_id, @shared_secret = options.values_at(:publishable_api_key, :app_id, :shared_secret)
  super
end

Public Instance Methods

authorize(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 33
def authorize(money, payment, options = {})
  post = {}
  add_payment(post, payment, options)
  add_order(post, money, options)
  add_address(post, payment, options)
  add_customer_data(post, options)

  commit('auth', post)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 43
def capture(money, authorization, options = {})
  post = {}
  add_invoice(post, money, authorization, options)

  commit('capture', post)
end
get_token(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 69
def get_token(credit_card, options = {})
  post = {}
  add_payment_instrument(post, credit_card, options)
  add_address_payment_instrument(post, credit_card, options)
  commit('gettoken', post)
end
purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 23
def purchase(money, payment, options = {})
  post = {}
  add_payment(post, payment, options)
  add_order(post, money, options)
  add_purchase_capture(post)
  add_address(post, payment, options)
  add_customer_data(post, options)
  commit('sale', post)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 50
def refund(money, authorization, options = {})
  post = {}
  add_invoice(post, money, authorization, options)
  commit('refund', post)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 80
def scrub(transcript)
  transcript.
    gsub(%r((Authorization: Bearer )\w+), '\1[FILTERED]').
    gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
    gsub(%r((Hmac: )[\w=]+), '\1[FILTERED]').
    gsub(%r((\\"account_number\\":\\")[\w*]+), '\1[FILTERED]').
    gsub(%r((\\"cvv\\":\\")\w+), '\1[FILTERED]').
    gsub(%r((\\"expiration\\":\\")\w+), '\1[FILTERED]')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 76
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 62
def verify(credit_card, options = {})
  MultiResponse.run(:use_first_response) do |r|
    r.process { authorize(100, credit_card, options) }
    r.process(:ignore_result) { void(0, r.authorization, options) }
  end
end
void(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 56
def void(money, authorization, options = {})
  post = {}
  add_invoice(post, money, authorization, options)
  commit('void', post)
end

Private Instance Methods

add_address(post, creditcard, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 103
def add_address(post, creditcard, options)
  return post unless options.key?(:address) || options.key?(:billing_address)

  billing_address = options[:address] || options[:billing_address]
  post[:source] ||= {}

  post[:source][:billing_contact] = {}
  post[:source][:billing_contact][:first_name] = billing_address[:first_name] if billing_address[:first_name]
  post[:source][:billing_contact][:last_name] = billing_address[:last_name] if billing_address[:last_name]
  post[:source][:billing_contact][:phone] = billing_address[:phone] if billing_address[:phone]
  post[:source][:billing_contact][:email] = options[:email] if options[:email]
  post[:source][:billing_contact][:address] = {}
  post[:source][:billing_contact][:address][:line_1] = billing_address[:address1] if billing_address[:address1]
  post[:source][:billing_contact][:address][:line_2] = billing_address[:address2] if billing_address[:address2]
  post[:source][:billing_contact][:address][:city] = billing_address[:city] if billing_address[:city]
  post[:source][:billing_contact][:address][:state] = billing_address[:state] if billing_address[:state]
  post[:source][:billing_contact][:address][:postal_code] = billing_address[:zip] if billing_address[:zip]
  post[:source][:billing_contact][:address][:country_code] = billing_address[:country] if billing_address[:country]
end
add_address_payment_instrument(post, creditcard, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 123
def add_address_payment_instrument(post, creditcard, options)
  return post unless options.key?(:address) || options.key?(:billing_address)

  billing_address = options[:address] || options[:billing_address]
  post[:source] = {} unless post.key?(:payment_instrument)

  post[:payment_instrument][:billing_contact] = {}
  post[:payment_instrument][:billing_contact][:first_name] = billing_address[:first_name] if billing_address[:first_name]
  post[:payment_instrument][:billing_contact][:last_name] = billing_address[:last_name] if billing_address[:last_name]
  post[:payment_instrument][:billing_contact][:phone] = billing_address[:phone] if billing_address[:phone]
  post[:payment_instrument][:billing_contact][:email] = billing_address[:email] if billing_address[:email]
  post[:payment_instrument][:billing_contact][:address] = {}
  post[:payment_instrument][:billing_contact][:address][:line_1] = billing_address[:address1] if billing_address[:address1]
  post[:payment_instrument][:billing_contact][:address][:line_2] = billing_address[:address2] if billing_address[:address2]
  post[:payment_instrument][:billing_contact][:address][:city] = billing_address[:city] if billing_address[:city]
  post[:payment_instrument][:billing_contact][:address][:state] = billing_address[:state] if billing_address[:state]
  post[:payment_instrument][:billing_contact][:address][:postal_code] = billing_address[:zip] if billing_address[:zip]
  post[:payment_instrument][:billing_contact][:address][:country_code] = billing_address[:country] if billing_address[:country]
end
add_customer_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 92
def add_customer_data(post, options)
  post[:meta] ||= {}

  add_shipping(post, options) if options.key?(:shipping_address)
  post[:meta][:client_customer_id] = options[:customer] if options[:customer]
  post[:meta][:client_transaction_id] = options[:order_id] if options[:order_id]
  post[:meta][:client_transaction_description] = options[:description] if options[:description]
  post[:meta][:client_invoice_id] = options[:invoice] if options[:invoice]
  post[:meta][:card_holder_ip_address] = options[:ip] if options[:ip]
end
add_invoice(post, money, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 161
def add_invoice(post, money, authorization, options)
  post[:amount] = amount(money)
  post[:charge] = authorization
end
add_order(post, amount, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 201
def add_order(post, amount, options)
  post[:transaction] ||= {}

  post[:transaction][:amount] = amount
  post[:transaction][:cof_type] = options.key?(:cof_type) ? options[:cof_type].upcase : 'UNSCHEDULED_CARDHOLDER'
  post[:transaction][:capture] = false # Change this in the request (auth/charge)
  post[:transaction][:currency_code] = (options[:currency] || currency(amount).upcase)
  post[:transaction][:avs] = options[:avs] || true # default avs to true unless told otherwise
  post[:transaction][:save_payment_instrument] = options[:save_payment_instrument] || false
end
add_payment(post, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 166
def add_payment(post, payment, options)
  if payment.kind_of?(String)
    post[:source] = {}
    post[:source][:type] = 'card_on_file'
    post[:source][:card_on_file] = {}
    post[:source][:card_on_file][:id] = payment
    post[:source][:card_on_file][:cvv] = options[:verification_value] || ''
    post[:source][:card_on_file][:customer_id] = options[:customer_id] || ''
  # credit card object
  elsif payment.respond_to?(:number)
    post[:source] = {}
    post[:source][:type] = 'credit_card'
    post[:source][:credit_card] = {}
    post[:source][:credit_card][:account_number] = payment.number
    post[:source][:credit_card][:cvv] = payment.verification_value || ''
    post[:source][:credit_card][:expiration] = '%02d%02d' % [payment.month, payment.year % 100]
    post[:source][:credit_card][:customer_id] = options[:customer_id] || ''
  end
end
add_payment_instrument(post, creditcard, options) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 186
def add_payment_instrument(post, creditcard, options)
  if creditcard.kind_of?(String)
    post[:source] = creditcard
    return post
  end
  return post unless creditcard.respond_to?(:number)

  post[:payment_instrument] = {}
  post[:payment_instrument][:type] = 'credit_card'
  post[:payment_instrument][:credit_card] = {}
  post[:payment_instrument][:credit_card][:account_number] = creditcard.number
  post[:payment_instrument][:credit_card][:expiration] = '%02d%02d' % [creditcard.month, creditcard.year % 100]
  post[:payment_instrument][:credit_card][:cvv] = creditcard.verification_value
end
add_purchase_capture(post) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 212
def add_purchase_capture(post)
  post[:transaction] ||= {}
  post[:transaction][:capture] = true
end
add_shipping(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 143
def add_shipping(post, options = {})
  return post unless options.key?(:shipping_address)

  shipping = options[:shipping_address]
  post[:meta][:shipping_info] = {}
  post[:meta][:shipping_info][:first_name] = shipping[:first_name] if shipping[:first_name]
  post[:meta][:shipping_info][:last_name] = shipping[:last_name] if shipping[:last_name]
  post[:meta][:shipping_info][:phone] = shipping[:phone] if shipping[:phone]
  post[:meta][:shipping_info][:email] = shipping[:email] if shipping[:email]
  post[:meta][:shipping_info][:address] = {}
  post[:meta][:shipping_info][:address][:line_1] = shipping[:address1] if shipping[:address1]
  post[:meta][:shipping_info][:address][:line_2] = shipping[:address2] if shipping[:address2]
  post[:meta][:shipping_info][:address][:city] = shipping[:city] if shipping[:city]
  post[:meta][:shipping_info][:address][:state] = shipping[:state] if shipping[:state]
  post[:meta][:shipping_info][:address][:postal_code] = shipping[:zip] if shipping[:zip]
  post[:meta][:shipping_info][:address][:country_code] = shipping[:country] if shipping[:country]
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 313
def authorization_from(response)
  response['id']
end
commit(action, parameters, method = 'POST') click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 223
def commit(action, parameters, method = 'POST')
  url = (test? ? test_url : live_url)
  if no_hmac(action)
    request_headers = headers.merge(create_basic(parameters, action))
  else
    request_headers = headers.merge(create_hmac(parameters, method))
  end
  request_url = url + get_url(action)
  begin
    response = parse(ssl_post(request_url, post_data(action, parameters), request_headers))
    Response.new(
      success_from(response),
      message_from(response),
      response,
      authorization: authorization_from(response),
      avs_result: AVSResult.new(code: response['avs_result']),
      cvv_result: CVVResult.new(response['cvv_result']),
      test: test?,
      error_code: error_code_from(response)
    )
  rescue ResponseError => e
    Response.new(
      false,
      message_from_error(e.response.body),
      response_error(e.response.body)
    )
  rescue JSON::ParserError
    Response.new(
      false,
      message_from(response),
      json_error(response)
    )
  end
end
create_basic(post, method) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 363
def create_basic(post, method)
  return { 'Authorization' => "Bearer #{@publishable_api_key}" }
end
create_hmac(post, method) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 367
def create_hmac(post, method)
  # Need requestDate, requestMethod, Nonce, AppIDKey
  app_id_key = @app_id
  request_method = method.upcase
  uuid = SecureRandom.uuid
  request_time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')

  string_to_hash = "#{app_id_key}|#{request_method}|#{request_time}|#{uuid}|#{JSON.generate(post)}"
  signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), Base64.strict_decode64(@shared_secret), string_to_hash)
  base64_signature = Base64.strict_encode64(signature)
  hmac_header = Base64.strict_encode64("#{app_id_key}|#{request_method}|#{request_time}|#{uuid}|#{base64_signature}")
  return { 'hmac' => hmac_header }
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 321
def error_code_from(response)
  error_code = nil
  error_code = response['response_code'] unless success_from(response)
  if error = response.dig('detail')
    error_code = error
  elsif error = response.dig('error')
    error_code = error.dig('reason', 'id')
  end
  error_code
end
get_url(action) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 332
def get_url(action)
  base = '/api/v1/'
  case action
  when 'sale'
    return base + 'payments/charge'
  when 'auth'
    return base + 'payments/charge'
  when 'capture'
    return base + 'payments/capture'
  when 'void'
    return base + 'payments/refund'
  when 'refund'
    return base + 'payments/refund'
  when 'gettoken'
    return base + 'vault/token'
  when 'vault'
    return base + 'vault/payment-instrument/token'
  else
    return base + 'noaction'
  end
end
headers() click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 258
def headers
  {
    'Accept' => 'text/plain',
    'Content-Type' => 'application/json'
  }
end
json_error(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 271
def json_error(response)
  msg = 'Invalid response received from the Conekta API.'
  msg += "  (The raw response returned by the API was #{response.inspect})"
  {
    'message' => msg
  }
end
message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 291
def message_from(response)
  response = JSON.parse(response) if response.is_a?(String)
  if response.key?('message')
    return response['message']
  elsif response.key?('detail')
    return response['detail']
  end
end
message_from_error(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 300
def message_from_error(response)
  if response.is_a?(String)
    response.gsub!('\\"', '"')
    response = JSON.parse(response)
  end

  if response.key?('detail')
    return response['detail']
  elsif response.key?('message')
    return response['message']
  end
end
no_hmac(action) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 354
def no_hmac(action)
  case action
  when 'gettoken'
    return true
  else
    return false
  end
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 217
def parse(body)
  return {} if !body || body.empty?

  JSON.parse(body)
end
post_data(action, parameters = {}) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 317
def post_data(action, parameters = {})
  return JSON.generate(parameters)
end
response_error(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 265
def response_error(response)
  parse(response)
rescue JSON::ParserError
  json_error(response)
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/deepstack.rb, line 279
def success_from(response)
  success = false
  if response.key?('response_code')
    success = response['response_code'] == '00'
  # Hack because token/payment instrument methods do not return a response_code
  elsif response.key?('id')
    success = true if response['id'].start_with?('tok', 'card')
  end

  return success
end