class ActiveMerchant::Billing::AdyenGateway

Constants

API_VERSION
AVS_MAPPING
CVC_MAPPING
NETWORK_TOKENIZATION_CARD_SOURCE
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/adyen.rb, line 32
def initialize(options={})
  requires!(options, :username, :password, :merchant_account)
  @username, @password, @merchant_account = options.values_at(:username, :password, :merchant_account)
  super
end

Public Instance Methods

adjust(money, authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 83
def adjust(money, authorization, options={})
  post = init_post(options)
  add_invoice_for_modification(post, money, options)
  add_reference(post, authorization, options)
  add_extra_data(post, nil, options)
  commit('adjustAuthorisation', post, options)
end
authorize(money, payment, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 49
def authorize(money, payment, options={})
  requires!(options, :order_id)
  post = init_post(options)
  add_invoice(post, money, options)
  add_payment(post, payment)
  add_extra_data(post, payment, options)
  add_stored_credentials(post, payment, options)
  add_address(post, options)
  add_installments(post, options) if options[:installments]
  add_3ds(post, options)
  add_3ds_authenticated_data(post, options)
  commit('authorise', post, options)
end
capture(money, authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 63
def capture(money, authorization, options={})
  post = init_post(options)
  add_invoice_for_modification(post, money, options)
  add_reference(post, authorization, options)
  commit('capture', post, options)
end
purchase(money, payment, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 38
def purchase(money, payment, options={})
  if options[:execute_threed] || options[:threed_dynamic]
    authorize(money, payment, options)
  else
    MultiResponse.run do |r|
      r.process { authorize(money, payment, options) }
      r.process { capture(money, r.authorization, capture_options(options)) }
    end
  end
end
refund(money, authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 70
def refund(money, authorization, options={})
  post = init_post(options)
  add_invoice_for_modification(post, money, options)
  add_original_reference(post, authorization, options)
  commit('refund', post, options)
end
scrub(transcript) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 115
def scrub(transcript)
  transcript.
    gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
    gsub(%r(("number\\?":\\?")[^"]*)i, '\1[FILTERED]').
    gsub(%r(("cvc\\?":\\?")[^"]*)i, '\1[FILTERED]').
    gsub(%r(("cavv\\?":\\?")[^"]*)i, '\1[FILTERED]')
end
store(credit_card, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 91
def store(credit_card, options={})
  requires!(options, :order_id)
  post = init_post(options)
  add_invoice(post, 0, options)
  add_payment(post, credit_card)
  add_extra_data(post, credit_card, options)
  add_stored_credentials(post, credit_card, options)
  add_recurring_contract(post, options)
  add_address(post, options)
  commit('authorise', post, options)
end
supports_scrubbing?() click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 111
def supports_scrubbing?
  true
end
verify(credit_card, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 103
def verify(credit_card, options={})
  MultiResponse.run(:use_first_response) do |r|
    r.process { authorize(0, credit_card, options) }
    options[:idempotency_key] = nil
    r.process(:ignore_result) { void(r.authorization, options) }
  end
end
void(authorization, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 77
def void(authorization, options={})
  post = init_post(options)
  add_reference(post, authorization, options)
  commit('cancel', post, options)
end

Private Instance Methods

add_3ds(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 330
def add_3ds(post, options)
  if three_ds_2_options = options[:three_ds_2]
    device_channel = three_ds_2_options[:channel]
    if device_channel == 'app'
      post[:threeDS2RequestData] = { deviceChannel: device_channel }
    else
      add_browser_info(three_ds_2_options[:browser_info], post)
      post[:threeDS2RequestData] = { deviceChannel: device_channel, notificationURL: three_ds_2_options[:notification_url] }
    end
  else
    return unless options[:execute_threed] || options[:threed_dynamic]
    post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
    post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
  end
end
add_3ds1_authenticated_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 354
def add_3ds1_authenticated_data(post, options)
  three_d_secure_options = options[:three_d_secure]
  post[:mpiData] = {
    cavv: three_d_secure_options[:cavv],
    cavvAlgorithm: three_d_secure_options[:cavv_algorithm],
    eci: three_d_secure_options[:eci],
    xid: three_d_secure_options[:xid],
    directoryResponse: three_d_secure_options[:directory_response_status],
    authenticationResponse: three_d_secure_options[:authentication_response_status]
  }
end
add_3ds2_authenticated_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 366
def add_3ds2_authenticated_data(post, options)
  three_d_secure_options = options[:three_d_secure]
  # If the transaction was authenticated in a frictionless flow, send the transStatus from the ARes.
  if(three_d_secure_options[:authentication_response_status].nil?)
    authentication_response = three_d_secure_options[:directory_response_status]
  else
    authentication_response = three_d_secure_options[:authentication_response_status]
  end
  post[:mpiData] = {
    threeDSVersion: three_d_secure_options[:version],
    eci: three_d_secure_options[:eci],
    cavv: three_d_secure_options[:cavv],
    dsTransID: three_d_secure_options[:ds_transaction_id],
    directoryResponse: three_d_secure_options[:directory_response_status],
    authenticationResponse: authentication_response
  }
end
add_3ds_authenticated_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 346
def add_3ds_authenticated_data(post, options)
  if options[:three_d_secure] && options[:three_d_secure][:eci] && options[:three_d_secure][:xid]
    add_3ds1_authenticated_data(post, options)
  elsif options[:three_d_secure]
    add_3ds2_authenticated_data(post, options)
  end
end
add_address(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 228
def add_address(post, options)
  return unless post[:card]&.kind_of?(Hash)
  if (address = options[:billing_address] || options[:address]) && address[:country]
    post[:billingAddress] = {}
    post[:billingAddress][:street] = address[:address1] || 'N/A'
    post[:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
    post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
    post[:billingAddress][:city] = address[:city] || 'N/A'
    post[:billingAddress][:stateOrProvince] = get_state(address)
    post[:billingAddress][:country] = address[:country] if address[:country]
  end
end
add_browser_info(browser_info, post) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 488
def add_browser_info(browser_info, post)
  return unless browser_info
  post[:browserInfo] = {
    acceptHeader: browser_info[:accept_header],
    colorDepth: browser_info[:depth],
    javaEnabled: browser_info[:java],
    language: browser_info[:language],
    screenHeight: browser_info[:height],
    screenWidth: browser_info[:width],
    timeZoneOffset: browser_info[:timezone],
    userAgent: browser_info[:user_agent]
  }
end
add_card(post, credit_card) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 274
def add_card(post, credit_card)
  card = {
    expiryMonth: credit_card.month,
    expiryYear: credit_card.year,
    holderName: credit_card.name,
    number: credit_card.number,
    cvc: credit_card.verification_value
  }

  card.delete_if { |k, v| v.blank? }
  card[:holderName] ||= 'Not Provided' if credit_card.is_a?(NetworkTokenizationCreditCard)
  requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
  post[:card] = card
end
add_extra_data(post, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 171
def add_extra_data(post, payment, options)
  post[:telephoneNumber] = options[:billing_address][:phone] if options.dig(:billing_address, :phone)
  post[:shopperEmail] = options[:shopper_email] if options[:shopper_email]
  post[:shopperIP] = options[:shopper_ip] if options[:shopper_ip]
  post[:shopperReference] = options[:shopper_reference] if options[:shopper_reference]
  post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement]
  post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset]
  post[:selectedBrand] = options[:selected_brand] if options[:selected_brand]
  post[:selectedBrand] ||= NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
  post[:deliveryDate] = options[:delivery_date] if options[:delivery_date]
  post[:merchantOrderReference] = options[:merchant_order_reference] if options[:merchant_order_reference]
  post[:additionalData] ||= {}
  post[:additionalData][:overwriteBrand] = normalize(options[:overwrite_brand]) if options[:overwrite_brand]
  post[:additionalData][:customRoutingFlag] = options[:custom_routing_flag] if options[:custom_routing_flag]
  post[:additionalData]['paymentdatasource.type'] = NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
  post[:additionalData][:authorisationType] = options[:authorisation_type] if options[:authorisation_type]
  post[:additionalData][:adjustAuthorisationData] = options[:adjust_authorisation_data] if options[:adjust_authorisation_data]
  post[:additionalData][:industryUsage] = options[:industry_usage] if options[:industry_usage]
  post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
  post[:additionalData][:RequestedTestAcquirerResponseCode] = options[:requested_test_acquirer_response_code] if options[:requested_test_acquirer_response_code] && test?
  post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
  add_risk_data(post, options)
end
add_installments(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 324
def add_installments(post, options)
  post[:installments] = {
    value: options[:installments]
  }
end
add_invoice(post, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 245
def add_invoice(post, money, options)
  currency = options[:currency] || currency(money)
  amount = {
    value: localized_amount(money, currency),
    currency: currency
  }
  post[:amount] = amount
end
add_invoice_for_modification(post, money, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 254
def add_invoice_for_modification(post, money, options)
  currency = options[:currency] || currency(money)
  amount = {
    value: localized_amount(money, currency),
    currency: currency
  }
  post[:modificationAmount] = amount
end
add_mpi_data_for_network_tokenization_card(post, payment) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 304
def add_mpi_data_for_network_tokenization_card(post, payment)
  post[:mpiData] = {}
  post[:mpiData][:authenticationResponse] = 'Y'
  post[:mpiData][:cavv] = payment.payment_cryptogram
  post[:mpiData][:directoryResponse] = 'Y'
  post[:mpiData][:eci] = payment.eci || '07'
end
add_original_reference(post, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 299
def add_original_reference(post, authorization, options = {})
  original_psp_reference, _, _ = authorization.split('#')
  post[:originalReference] = single_reference(authorization) || original_psp_reference
end
add_payment(post, payment) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 263
def add_payment(post, payment)
  if payment.is_a?(String)
    _, _, recurring_detail_reference = payment.split('#')
    post[:selectedRecurringDetailReference] = recurring_detail_reference
    add_recurring_contract(post, options)
  else
    add_mpi_data_for_network_tokenization_card(post, payment) if payment.is_a?(NetworkTokenizationCreditCard)
    add_card(post, payment)
  end
end
add_recurring_contract(post, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 316
def add_recurring_contract(post, options = {})
  recurring = {
    contract: 'RECURRING'
  }

  post[:recurring] = recurring
end
add_recurring_processing_model(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 217
def add_recurring_processing_model(post, options)
  return unless options.dig(:stored_credential, :reason_type) || options[:recurring_processing_model]
  if options.dig(:stored_credential, :reason_type) && options[:stored_credential][:reason_type] == 'unscheduled'
    recurring_processing_model = 'CardOnFile'
  else
    recurring_processing_model = 'Subscription'
  end

  post[:recurringProcessingModel] = options[:recurring_processing_model] || recurring_processing_model
end
add_reference(post, authorization, options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 294
def add_reference(post, authorization, options = {})
  _, psp_reference, _ = authorization.split('#')
  post[:originalReference] = single_reference(authorization) || psp_reference
end
add_risk_data(post, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 195
def add_risk_data(post, options)
  if (risk_data = options[:risk_data])
    risk_data = Hash[risk_data.map { |k, v| ["riskdata.#{k}", v] }]
    post[:additionalData].merge!(risk_data)
  end
end
add_shopper_interaction(post, payment, options={}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 207
def add_shopper_interaction(post, payment, options={})
  if options.dig(:stored_credential, :initial_transaction) || (payment.respond_to?(:verification_value) && payment.verification_value) || payment.is_a?(NetworkTokenizationCreditCard)
    shopper_interaction = 'Ecommerce'
  else
    shopper_interaction = 'ContAuth'
  end

  post[:shopperInteraction] = options[:shopper_interaction] || shopper_interaction
end
add_stored_credentials(post, payment, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 202
def add_stored_credentials(post, payment, options)
  add_shopper_interaction(post, payment, options)
  add_recurring_processing_model(post, options)
end
authorization_from(action, parameters, response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 467
def authorization_from(action, parameters, response)
  return nil if response['pspReference'].nil?
  recurring = response['additionalData']['recurring.recurringDetailReference'] if response['additionalData']
  "#{parameters[:originalReference]}##{response['pspReference']}##{recurring}"
end
authorize_message_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 459
def authorize_message_from(response)
  if response['refusalReason'] && response['additionalData'] && response['additionalData']['refusalReasonRaw']
    "#{response['refusalReason']} | #{response['additionalData']['refusalReasonRaw']}"
  else
    response['refusalReason'] || response['resultCode'] || response['message']
  end
end
avs_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 410
def avs_code_from(response)
  AVS_MAPPING[response['additionalData']['avsResult'][0..1].strip] if response.dig('additionalData', 'avsResult')
end
basic_auth() click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 428
def basic_auth
  Base64.strict_encode64("#{@username}:#{@password}")
end
capture_options(options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 289
def capture_options(options)
  return options.merge(idempotency_key: "#{options[:idempotency_key]}-cap") if options[:idempotency_key]
  options
end
commit(action, parameters, options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 389
def commit(action, parameters, options)
  begin
    raw_response = ssl_post("#{url}/#{action}", post_data(action, parameters), request_headers(options))
    response = parse(raw_response)
  rescue ResponseError => e
    raw_response = e.response.body
    response = parse(raw_response)
  end
  success = success_from(action, response)
  Response.new(
    success,
    message_from(action, response),
    response,
    authorization: authorization_from(action, parameters, response),
    test: test?,
    error_code: success ? nil : error_code_from(response),
    avs_result: AVSResult.new(:code => avs_code_from(response)),
    cvv_result: CVVResult.new(cvv_result_from(response))
  )
end
cvv_result_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 414
def cvv_result_from(response)
  CVC_MAPPING[response['additionalData']['cvcResult'][0]] if response.dig('additionalData', 'cvcResult')
end
error_code_from(response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 484
def error_code_from(response)
  STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
end
get_state(address) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 241
def get_state(address)
  address[:state] && !address[:state].blank? ? address[:state] : 'N/A'
end
init_post(options = {}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 473
def init_post(options = {})
  post = {}
  post[:merchantAccount] = options[:merchant_account] || @merchant_account
  post[:reference] = options[:order_id] if options[:order_id]
  post
end
message_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 454
def message_from(action, response)
  return authorize_message_from(response) if action.to_s == 'authorise'
  response['response'] || response['message']
end
parse(body) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 384
def parse(body)
  return {} if body.blank?
  JSON.parse(body)
end
post_data(action, parameters = {}) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 480
def post_data(action, parameters = {})
  JSON.generate(parameters)
end
request_headers(options) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 432
def request_headers(options)
  headers = {
    'Content-Type' => 'application/json',
    'Authorization' => "Basic #{basic_auth}"
  }
  headers['Idempotency-Key'] = options[:idempotency_key] if options[:idempotency_key]
  headers
end
single_reference(authorization) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 312
def single_reference(authorization)
  authorization if !authorization.include?('#')
end
success_from(action, response) click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 441
def success_from(action, response)
  case action.to_s
  when 'authorise', 'authorise3d'
    ['Authorised', 'Received', 'RedirectShopper'].include?(response['resultCode'])
  when 'capture', 'refund', 'cancel'
    response['response'] == "[#{action}-received]"
  when 'adjustAuthorisation'
    response['response'] == 'Authorised' || response['response'] == '[adjustAuthorisation-received]'
  else
    false
  end
end
url() click to toggle source
# File lib/active_merchant/billing/gateways/adyen.rb, line 418
def url
  if test?
    "#{test_url}#{API_VERSION}"
  elsif @options[:subdomain]
    "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/#{API_VERSION}"
  else
    "#{live_url}#{API_VERSION}"
  end
end