class ActiveMerchant::Billing::KushkiGateway

Constants

ENDPOINT

Public Class Methods

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

Public Instance Methods

authorize(amount, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 27
def authorize(amount, payment_method, options = {})
  MultiResponse.run() do |r|
    r.process { tokenize(amount, payment_method, options) }
    r.process { preauthorize(amount, r.authorization, options, payment_method) }
  end
end
capture(amount, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 34
def capture(amount, authorization, options = {})
  action = 'capture'

  post = {}
  post[:ticketNumber] = authorization
  add_invoice(action, post, amount, options)
  add_full_response(post, options)

  commit(action, post)
end
purchase(amount, payment_method, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 20
def purchase(amount, payment_method, options = {})
  MultiResponse.run() do |r|
    r.process { tokenize(amount, payment_method, options) }
    r.process { charge(amount, r.authorization, options, payment_method) }
  end
end
refund(amount, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 45
def refund(amount, authorization, options = {})
  action = 'refund'

  post = {}
  post[:ticketNumber] = authorization
  add_full_response(post, options)
  add_invoice(action, post, amount, options)

  commit(action, post, options)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 70
def scrub(transcript)
  transcript.
    gsub(%r((Private-Merchant-Id: )\d+), '\1[FILTERED]').
    gsub(%r((\"card\\\":{\\\"number\\\":\\\")\d+), '\1[FILTERED]').
    gsub(%r((\"cvv\\\":\\\")\d+), '\1[FILTERED]')
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 66
def supports_scrubbing?
  true
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 56
def void(authorization, options = {})
  action = 'void'

  post = {}
  post[:ticketNumber] = authorization
  add_full_response(post, options)

  commit(action, post)
end

Private Instance Methods

add_amount_by_country(sum, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 147
def add_amount_by_country(sum, options)
  if amount = options[:amount]
    sum[:subtotalIva] = amount[:subtotal_iva].to_f if amount[:subtotal_iva]
    sum[:iva] = amount[:iva].to_f if amount[:iva]
    sum[:subtotalIva0] = amount[:subtotal_iva_0].to_f if amount[:subtotal_iva_0]
    sum[:ice] = amount[:ice].to_f if amount[:ice]
    if (extra_taxes = amount[:extra_taxes])
      sum[:extraTaxes] ||= Hash.new
      sum[:extraTaxes][:propina] = extra_taxes[:propina].to_f if extra_taxes[:propina]
      sum[:extraTaxes][:tasaAeroportuaria] = extra_taxes[:tasa_aeroportuaria].to_f if extra_taxes[:tasa_aeroportuaria]
      sum[:extraTaxes][:agenciaDeViaje] = extra_taxes[:agencia_de_viaje].to_f if extra_taxes[:agencia_de_viaje]
      sum[:extraTaxes][:iac] = extra_taxes[:iac].to_f if extra_taxes[:iac]
    end
  end
end
add_amount_defaults(sum, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 139
def add_amount_defaults(sum, money, options)
  sum[:subtotalIva] = 0
  sum[:iva] = 0
  sum[:subtotalIva0] = amount(money).to_f

  sum[:ice] = 0 if sum[:currency] != 'COP'
end
add_contact_details(post, contact_details_options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 177
def add_contact_details(post, contact_details_options)
  contact_details = {}
  contact_details[:documentType] = contact_details_options[:document_type] if contact_details_options[:document_type]
  contact_details[:documentNumber] = contact_details_options[:document_number] if contact_details_options[:document_number]
  contact_details[:email] = contact_details_options[:email] if contact_details_options[:email]
  contact_details[:firstName] = contact_details_options[:first_name] if contact_details_options[:first_name]
  contact_details[:lastName] = contact_details_options[:last_name] if contact_details_options[:last_name]
  contact_details[:secondLastName] = contact_details_options[:second_last_name] if contact_details_options[:second_last_name]
  contact_details[:phoneNumber] = contact_details_options[:phone_number] if contact_details_options[:phone_number]
  post[:contactDetails] = contact_details
end
add_deferred(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 202
def add_deferred(post, options)
  return unless options[:deferred_grace_months] && options[:deferred_credit_type] && options[:deferred_months]

  post[:deferred] = {
    graceMonths: options[:deferred_grace_months],
    creditType: options[:deferred_credit_type],
    months: options[:deferred_months]
  }
end
add_full_response(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 189
def add_full_response(post, options)
  # this is the only currently accepted value for this field, previously it was 'true'
  post[:fullResponse] = 'v2' unless options[:full_response] == 'false' || options[:full_response].blank?
end
add_invoice(action, post, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 125
def add_invoice(action, post, money, options)
  if action == 'tokenize'
    post[:totalAmount] = amount(money).to_f
    post[:currency] = options[:currency] || currency(money)
    post[:isDeferred] = false
  else
    sum = {}
    sum[:currency] = options[:currency] || currency(money)
    add_amount_defaults(sum, money, options)
    add_amount_by_country(sum, options)
    post[:amount] = sum
  end
end
add_metadata(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 194
def add_metadata(post, options)
  post[:metadata] = options[:metadata] if options[:metadata]
end
add_months(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 198
def add_months(post, options)
  post[:months] = options[:months] if options[:months]
end
add_payment_method(post, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 163
def add_payment_method(post, payment_method, options)
  card = {}
  card[:number] = payment_method.number
  card[:cvv] = payment_method.verification_value
  card[:expiryMonth] = format(payment_method.month, :two_digits)
  card[:expiryYear] = format(payment_method.year, :two_digits)
  card[:name] = payment_method.name
  post[:card] = card
end
add_product_details(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 212
def add_product_details(post, options)
  return unless options[:product_details]

  product_items_array = []
  options[:product_details].each do |item|
    product_items_obj = {}

    product_items_obj[:id] = item[:id] if item[:id]
    product_items_obj[:title] = item[:title] if item[:title]
    product_items_obj[:price] = item[:price].to_i if item[:price]
    product_items_obj[:sku] = item[:sku] if item[:sku]
    product_items_obj[:quantity] = item[:quantity].to_i if item[:quantity]

    product_items_array << product_items_obj
  end

  product_items = {
    product: product_items_array
  }

  post[:productDetails] = product_items
end
add_reference(post, authorization, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 173
def add_reference(post, authorization, options)
  post[:token] = authorization
end
add_three_d_secure(post, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 235
def add_three_d_secure(post, payment_method, options)
  three_d_secure = options[:three_d_secure]
  return unless three_d_secure.present?

  post[:threeDomainSecure] = {
    eci: three_d_secure[:eci],
    specificationVersion: three_d_secure[:version]
  }

  if payment_method.brand == 'master'
    post[:threeDomainSecure][:acceptRisk] = three_d_secure[:eci] == '00'
    post[:threeDomainSecure][:ucaf] = three_d_secure[:cavv]
    post[:threeDomainSecure][:directoryServerTransactionID] = three_d_secure[:ds_transaction_id]
    case three_d_secure[:eci]
    when '07'
      post[:threeDomainSecure][:collectionIndicator] = '0'
    when '06'
      post[:threeDomainSecure][:collectionIndicator] = '1'
    else
      post[:threeDomainSecure][:collectionIndicator] = '2'
    end
  elsif payment_method.brand == 'visa'
    post[:threeDomainSecure][:acceptRisk] = three_d_secure[:eci] == '07'
    post[:threeDomainSecure][:cavv] = three_d_secure[:cavv]
    post[:threeDomainSecure][:xid] = three_d_secure[:xid] if three_d_secure[:xid].present?
  else
    raise ArgumentError.new 'Kushki supports 3ds2 authentication for only Visa and Mastercard brands.'
  end
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 348
def authorization_from(response)
  response['token'] || response['ticketNumber']
end
charge(amount, authorization, options, payment_method = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 93
def charge(amount, authorization, options, payment_method = {})
  action = 'charge'

  post = {}
  add_reference(post, authorization, options)
  add_invoice(action, post, amount, options)
  add_contact_details(post, options[:contact_details]) if options[:contact_details]
  add_full_response(post, options)
  add_metadata(post, options)
  add_months(post, options)
  add_deferred(post, options)
  add_three_d_secure(post, payment_method, options)
  add_product_details(post, options)

  commit(action, post)
end
commit(action, params, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 274
def commit(action, params, options = {})
  response =
    begin
      parse(ssl_invoke(action, params, options))
    rescue ResponseError => e
      parse(e.response.body)
    end

  success = success_from(response)

  Response.new(
    success,
    message_from(success, response),
    response,
    authorization: success ? authorization_from(response) : nil,
    error_code: success ? nil : error_from(response),
    test: test?
  )
end
error_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 352
def error_from(response)
  response['code']
end
headers(action) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 304
def headers(action)
  hfields = {}
  hfields['Public-Merchant-Id'] = @options[:public_merchant_id] if action == 'tokenize'
  hfields['Private-Merchant-Id'] = @options[:private_merchant_id] unless action == 'tokenize'
  hfields['Content-Type'] = 'application/json'
  hfields
end
message_from(succeeded, response) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 340
def message_from(succeeded, response)
  if succeeded
    'Succeeded'
  else
    response['message']
  end
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 326
def parse(body)
  JSON.parse(body)
rescue JSON::ParserError
  message = 'Invalid JSON response received from KushkiGateway. Please contact KushkiGateway if you continue to receive this message.'
  message += " (The raw response returned by the API was #{body.inspect})"
  {
    'message' => message
  }
end
post_data(params) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 312
def post_data(params)
  params.to_json
end
preauthorize(amount, authorization, options, payment_method = {}) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 110
def preauthorize(amount, authorization, options, payment_method = {})
  action = 'preAuthorization'

  post = {}
  add_reference(post, authorization, options)
  add_invoice(action, post, amount, options)
  add_full_response(post, options)
  add_metadata(post, options)
  add_months(post, options)
  add_deferred(post, options)
  add_three_d_secure(post, payment_method, options)

  commit(action, post)
end
ssl_invoke(action, params, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 294
def ssl_invoke(action, params, options)
  if %w[void refund].include?(action)
    # removes ticketNumber from request for partial refunds because gateway will reject if included in request body
    data = options[:partial_refund] == true ? post_data(params.except(:ticketNumber)) : nil
    ssl_request(:delete, url(action, params), data, headers(action))
  else
    ssl_post(url(action, params), post_data(params), headers(action))
  end
end
success_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 336
def success_from(response)
  return true if response['token'] || response['ticketNumber'] || response['code'] == 'K000'
end
tokenize(amount, payment_method, options) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 79
def tokenize(amount, payment_method, options)
  action = 'tokenize'

  post = {}
  add_invoice(action, post, amount, options)
  add_payment_method(post, payment_method, options)
  add_full_response(post, options)
  add_metadata(post, options)
  add_months(post, options)
  add_deferred(post, options)

  commit(action, post)
end
url(action, params) click to toggle source
# File lib/active_merchant/billing/gateways/kushki.rb, line 316
def url(action, params)
  base_url = test? ? test_url : live_url

  if %w[void refund].include?(action)
    base_url + 'v1/' + ENDPOINT[action] + '/' + params[:ticketNumber].to_s
  else
    base_url + 'card/v1/' + ENDPOINT[action]
  end
end