class ActiveMerchant::Billing::DLocalGateway

Public Class Methods

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

Public Instance Methods

authorize(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 27
def authorize(money, payment, options = {})
  post = {}
  add_auth_purchase_params(post, money, payment, 'authorize', options)
  add_three_ds(post, options)
  post[:card][:verify] = true if options[:verify].to_s == 'true'

  commit('authorize', post, options)
end
capture(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 36
def capture(money, authorization, options = {})
  post = {}
  post[:authorization_id] = authorization
  add_invoice(post, money, options) if money
  commit('capture', post, options)
end
inquire(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 61
def inquire(authorization, options = {})
  post = {}
  post[:payment_id] = authorization
  action = authorization ? 'status' : 'orders'
  commit(action, post, options)
end
purchase(money, payment, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 19
def purchase(money, payment, options = {})
  post = {}
  add_auth_purchase_params(post, money, payment, 'purchase', options)
  add_three_ds(post, options)

  commit('purchase', post, options)
end
refund(money, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 43
def refund(money, authorization, options = {})
  post = {}
  post[:payment_id] = authorization
  post[:notification_url] = options[:notification_url]
  add_invoice(post, money, options) if money
  commit('refund', post, options)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 76
def scrub(transcript)
  transcript.
    gsub(%r((X-Trans-Key: )\w+), '\1[FILTERED]').
    gsub(%r((\"number\\\":\\\")\d+), '\1[FILTERED]').
    gsub(%r((\"cvv\\\":\\\")\d+), '\1[FILTERED]')
end
supports_network_tokenization?() click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 72
def supports_network_tokenization?
  true
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 68
def supports_scrubbing?
  true
end
verify(credit_card, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 57
def verify(credit_card, options = {})
  authorize(0, credit_card, options.merge(verify: 'true'))
end
void(authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 51
def void(authorization, options = {})
  post = {}
  post[:authorization_id] = authorization
  commit('void', post, options)
end

Private Instance Methods

add_additional_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 103
def add_additional_data(post, options)
  post[:additional_risk_data] = options[:additional_data]
end
add_address(post, card, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 136
def add_address(post, card, options)
  return unless address = options[:billing_address] || options[:address]

  address_object = {}
  address_object[:state] = address[:state] if address[:state]
  address_object[:city] = address[:city] if address[:city]
  address_object[:zip_code] = address[:zip] if address[:zip]
  address_object[:street] = address[:street] || parse_street(address) if parse_street(address)
  address_object[:number] = address[:number] || parse_house_number(address) if parse_house_number(address)
  address_object
end
add_auth_purchase_params(post, money, card, action, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 85
def add_auth_purchase_params(post, money, card, action, options)
  add_invoice(post, money, options)
  post[:payment_method_id] = 'CARD'
  post[:payment_method_flow] = 'DIRECT'
  add_country(post, card, options)
  add_payer(post, card, options)
  add_card(post, card, action, options)
  add_additional_data(post, options)
  post[:order_id] = options[:order_id] || generate_unique_id
  post[:original_order_id] = options[:original_order_id] if options[:original_order_id]
  post[:description] = options[:description] if options[:description]
end
add_card(post, card, action, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 162
def add_card(post, card, action, options = {})
  post[:card] = {}
  if card.is_a?(NetworkTokenizationCreditCard)
    post[:card][:network_token] = card.number
    post[:card][:cryptogram] = card.payment_cryptogram
    post[:card][:eci] = card.eci
  else
    post[:card][:number] = card.number
    post[:card][:cvv] = card.verification_value
  end

  if options[:stored_credential]
    # required for MC debit recurrent in BR 'USED'(subsecuence Payments) . 'FIRST' an inital payment
    post[:card][:stored_credential_usage] = (options[:stored_credential][:initial_transaction] ? 'FIRST' : 'USED')
    post[:card][:network_payment_reference] = options[:stored_credential][:network_transaction_id] if options[:stored_credential][:network_transaction_id]
    # used case of Network Token: 'CARD_ON_FILE', 'SUBSCRIPTION', 'UNSCHEDULED_CARD_ON_FILE'
    post[:card][:stored_credential_type] = fetch_stored_credential_type(options[:stored_credential])
  end

  post[:card][:holder_name] = card.name
  post[:card][:expiration_month] = card.month
  post[:card][:expiration_year] = card.year
  post[:card][:descriptor] = options[:dynamic_descriptor] if options[:dynamic_descriptor]
  post[:card][:capture] = (action == 'purchase')
  post[:card][:installments] = options[:installments] if options[:installments]
  post[:card][:installments_id] = options[:installments_id] if options[:installments_id]
  post[:card][:force_type] = options[:force_type].to_s.upcase if options[:force_type]
  post[:card][:save] = options[:save] if options[:save]
end
add_country(post, card, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 107
def add_country(post, card, options)
  return unless address = options[:billing_address] || options[:address]

  post[:country] = lookup_country_code(address[:country])
end
add_invoice(post, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 98
def add_invoice(post, money, options)
  post[:amount] = amount(money)
  post[:currency] = (options[:currency] || currency(money))
end
add_payer(post, card, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 119
def add_payer(post, card, options)
  address = options[:billing_address] || options[:address]
  phone_number = address[:phone] || address[:phone_number] if address

  post[:payer] = {}
  post[:payer][:name] = card.name
  post[:payer][:email] = options[:email] if options[:email]
  post[:payer][:birth_date] = options[:birth_date] if options[:birth_date]
  post[:payer][:phone] = phone_number if phone_number
  post[:payer][:document] = options[:document] if options[:document]
  post[:payer][:document2] = options[:document2] if options[:document2]
  post[:payer][:user_reference] = options[:user_reference] if options[:user_reference]
  post[:payer][:event_uuid] = options[:device_id] if options[:device_id]
  post[:payer][:ip] = options[:ip] if options[:ip]
  post[:payer][:address] = add_address(post, card, options)
end
add_three_ds(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 318
def add_three_ds(post, options)
  return unless three_d_secure = options[:three_d_secure]

  post[:three_dsecure] = {
    mpi: true,
    three_dsecure_version: three_d_secure[:version],
    cavv: three_d_secure[:cavv],
    eci: three_d_secure[:eci],
    enrollment_response: formatted_enrollment(three_d_secure[:enrolled]),
    authentication_response: three_d_secure[:authentication_response_status]
  }.merge(xid_or_ds_trans_id(three_d_secure))
end
authorization_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 248
def authorization_from(response)
  response['id']
end
commit(action, parameters, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 204
def commit(action, parameters, options = {})
  three_ds_errors = validate_three_ds_params(parameters[:three_dsecure]) if parameters[:three_dsecure].present?
  return three_ds_errors if three_ds_errors

  url = url(action, parameters, options)
  post = post_data(action, parameters)
  begin
    raw = if %w(status orders).include?(action)
            ssl_get(url, headers(nil, options))
          else
            ssl_post(url, post, headers(post, options))
          end
    response = parse(raw)
  rescue ResponseError => e
    raw = e.response.body
    response = parse(raw)
  end

  Response.new(
    success_from(action, response),
    message_from(action, response),
    response,
    authorization: authorization_from(response),
    network_transaction_id: network_transaction_id_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(action, response)
  )
end
endpoint(action, parameters, options) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 267
def endpoint(action, parameters, options)
  case action
  when 'purchase'
    'secure_payments'
  when 'authorize'
    'secure_payments'
  when 'refund'
    'refunds'
  when 'capture'
    'payments'
  when 'void'
    "payments/#{parameters[:authorization_id]}/cancel"
  when 'status'
    "payments/#{parameters[:payment_id]}/status"
  when 'orders'
    "orders/#{options[:order_id]}"
  end
end
error_code_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 256
def error_code_from(action, response)
  return if success_from(action, response)

  code = response['status_code'] || response['code']
  code&.to_s
end
fetch_stored_credential_type(stored_credential) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 192
def fetch_stored_credential_type(stored_credential)
  if stored_credential[:reason_type] == 'unscheduled'
    stored_credential[:initiator] == 'merchant' ? 'UNSCHEDULED_CARD_ON_FILE' : 'CARD_ON_FILE'
  else
    'SUBSCRIPTION'
  end
end
formatted_enrollment(val) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 345
def formatted_enrollment(val)
  case val
  when 'Y', 'N', 'U' then val
  when true, 'true' then 'Y'
  when false, 'false' then 'N'
  end
end
headers(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 286
def headers(post, options = {})
  timestamp = Time.now.utc.iso8601
  headers = {
    'Content-Type' => 'application/json',
    'X-Date' => timestamp,
    'X-Login' => @options[:login],
    'X-Trans-Key' => @options[:trans_key],
    'X-Version' => '2.1',
    'Authorization' => signature(post, timestamp)
  }
  headers['X-Idempotency-Key'] = options[:idempotency_key] if options[:idempotency_key]
  headers
end
lookup_country_code(country_field) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 113
def lookup_country_code(country_field)
  Country.find(country_field).code(:alpha2).value
rescue InvalidCountryCodeError
  nil
end
message_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 244
def message_from(action, response)
  response['status_detail'] || response['message']
end
network_transaction_id_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 252
def network_transaction_id_from(response)
  response.dig('card', 'network_tx_reference')
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 200
def parse(body)
  JSON.parse(body)
end
parse_house_number(address) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 155
def parse_house_number(address)
  return unless address[:address1]

  house = address[:address1].split(/\s+/).keep_if { |x| x =~ /\d/ }.join(' ')
  house.empty? ? nil : house
end
parse_street(address) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 148
def parse_street(address)
  return unless address[:address1]

  street = address[:address1].split(/\s+/).keep_if { |x| x !~ /\d/ }.join(' ')
  street.empty? ? nil : street
end
post_data(action, parameters = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 306
def post_data(action, parameters = {})
  parameters.to_json
end
signature(post, timestamp) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 300
def signature(post, timestamp)
  content = "#{@options[:login]}#{timestamp}#{post}"
  digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @options[:secret_key], content)
  "V2-HMAC-SHA256, Signature: #{digest}"
end
success_from(action, response) click to toggle source

A refund may not be immediate, and return a status_code of 100, “Pending”. Since we aren’t handling async notifications of eventual success, we count 100 as a success.

# File lib/active_merchant/billing/gateways/d_local.rb, line 238
def success_from(action, response)
  return false unless response['status_code']

  %w[100 200 400 600 700].include? response['status_code'].to_s
end
url(action, parameters, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 263
def url(action, parameters, options = {})
  "#{test? ? test_url : live_url}/#{endpoint(action, parameters, options)}/"
end
validate_three_ds_params(three_ds) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 331
def validate_three_ds_params(three_ds)
  errors = {}
  supported_version = %w{1.0 2.0 2.1.0 2.2.0}.include?(three_ds[:three_dsecure_version])
  supported_enrollment = ['Y', 'N', 'U', nil].include?(three_ds[:enrollment_response])
  supported_auth_response = ['Y', 'N', 'U', nil].include?(three_ds[:authentication_response])

  errors[:three_ds_version] = 'ThreeDs version not supported' unless supported_version
  errors[:enrollment] = 'Enrollment value not supported' unless supported_enrollment
  errors[:auth_response] = 'Authentication response value not supported' unless supported_auth_response
  errors.compact!

  errors.present? ? Response.new(false, 'ThreeDs data is invalid', errors) : nil
end
xid_or_ds_trans_id(three_d_secure) click to toggle source
# File lib/active_merchant/billing/gateways/d_local.rb, line 310
def xid_or_ds_trans_id(three_d_secure)
  if three_d_secure[:version].to_f >= 2
    { ds_transaction_id: three_d_secure[:ds_transaction_id] }
  else
    { xid: three_d_secure[:xid] }
  end
end