lib/active_merchant/billing/gateways/adyen.rb in activemerchant-1.91.0 vs lib/active_merchant/billing/gateways/adyen.rb in activemerchant-1.92.0
- old
+ new
@@ -7,11 +7,11 @@
self.test_url = 'https://pal-test.adyen.com/pal/servlet/Payment/v18'
self.live_url = 'https://pal-live.adyen.com/pal/servlet/Payment/v18'
self.supported_countries = ['AT', 'AU', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HK', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SG', 'SK', 'SI', 'US']
self.default_currency = 'USD'
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover]
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover, :elo]
self.money_format = :cents
self.homepage_url = 'https://www.adyen.com/'
self.display_name = 'Adyen'
@@ -36,62 +36,71 @@
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, options) }
+ r.process { capture(money, r.authorization, capture_options(options)) }
end
end
end
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_shopper_interaction(post, payment, options)
+ add_stored_credentials(post, payment, options)
add_address(post, options)
add_installments(post, options) if options[:installments]
add_3ds(post, options)
- commit('authorise', post)
+ commit('authorise', post, options)
end
def capture(money, authorization, options={})
post = init_post(options)
add_invoice_for_modification(post, money, options)
add_reference(post, authorization, options)
- commit('capture', post)
+ commit('capture', post, options)
end
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)
+ commit('refund', post, options)
end
def void(authorization, options={})
post = init_post(options)
add_reference(post, authorization, options)
- commit('cancel', post)
+ commit('cancel', post, options)
end
+ def adjust(money, authorization, options={})
+ post = init_post(options)
+ add_invoice_for_modification(post, money, options)
+ add_reference(post, authorization, options)
+ commit('adjustAuthorisation', post, options)
+ end
+
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)
+ commit('authorise', post, options)
end
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
def supports_scrubbing?
@@ -149,42 +158,61 @@
'android_pay' => 'androidpay',
'google_pay' => 'paywithgoogle'
}
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[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
add_risk_data(post, options)
end
def add_risk_data(post, options)
- risk_data = {}
- risk_data.merge!(options[:risk_data]) if options[:risk_data]
+ if (risk_data = options[:risk_data])
+ risk_data = Hash[risk_data.map { |k, v| ["riskdata.#{k}", v] }]
+ post[:additionalData].merge!(risk_data)
+ end
+ end
- post[:additionalData][:riskData] = risk_data unless risk_data.empty?
+ def add_stored_credentials(post, payment, options)
+ add_shopper_interaction(post, payment, options)
+ add_recurring_processing_model(post, options)
end
def add_shopper_interaction(post, payment, options={})
- if (payment.respond_to?(:verification_value) && payment.verification_value) || payment.is_a?(NetworkTokenizationCreditCard)
+ 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
+ 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
+
def add_address(post, options)
return unless post[:card]&.kind_of?(Hash)
if (address = options[:billing_address] || options[:address]) && address[:country]
post[:card][:billingAddress] = {}
post[:card][:billingAddress][:street] = address[:address1] || 'N/A'
@@ -200,11 +228,10 @@
amount = {
value: amount(money),
currency: options[:currency] || currency(money)
}
post[:amount] = amount
- post[:recurringProcessingModel] = options[:recurring_processing_model] if options[:recurring_processing_model]
end
def add_invoice_for_modification(post, money, options)
amount = {
value: amount(money),
@@ -237,10 +264,15 @@
card[:holderName] ||= 'Not Provided' if credit_card.is_a?(NetworkTokenizationCreditCard)
requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
post[:card] = card
end
+ def capture_options(options)
+ return options.merge(idempotency_key: "#{options[:idempotency_key]}-cap") if options[:idempotency_key]
+ options
+ end
+
def add_reference(post, authorization, options = {})
_, psp_reference, _ = authorization.split('#')
post[:originalReference] = single_reference(authorization) || psp_reference
end
@@ -284,13 +316,13 @@
def parse(body)
return {} if body.blank?
JSON.parse(body)
end
- def commit(action, parameters)
+ def commit(action, parameters, options)
begin
- raw_response = ssl_post("#{url}/#{action}", post_data(action, parameters), request_headers)
+ 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
@@ -327,21 +359,23 @@
def basic_auth
Base64.strict_encode64("#{@username}:#{@password}")
end
- def request_headers
- {
+ 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
def success_from(action, response)
case action.to_s
when 'authorise', 'authorise3d'
['Authorised', 'Received', 'RedirectShopper'].include?(response['resultCode'])
- when 'capture', 'refund', 'cancel'
+ when 'capture', 'refund', 'cancel', 'adjustAuthorisation'
response['response'] == "[#{action}-received]"
else
false
end
end