lib/active_merchant/billing/gateways/adyen.rb in activemerchant-1.126.0 vs lib/active_merchant/billing/gateways/adyen.rb in activemerchant-1.129.0

- old
+ new

@@ -15,16 +15,20 @@ self.money_format = :cents self.homepage_url = 'https://www.adyen.com/' self.display_name = 'Adyen' - PAYMENT_API_VERSION = 'v64' - RECURRING_API_VERSION = 'v49' + PAYMENT_API_VERSION = 'v68' + RECURRING_API_VERSION = 'v68' STANDARD_ERROR_CODE_MAPPING = { + '0' => STANDARD_ERROR_CODE[:processing_error], + '10' => STANDARD_ERROR_CODE[:config_error], + '100' => STANDARD_ERROR_CODE[:invalid_amount], '101' => STANDARD_ERROR_CODE[:incorrect_number], '103' => STANDARD_ERROR_CODE[:invalid_cvc], + '104' => STANDARD_ERROR_CODE[:incorrect_address], '131' => STANDARD_ERROR_CODE[:incorrect_address], '132' => STANDARD_ERROR_CODE[:incorrect_address], '133' => STANDARD_ERROR_CODE[:incorrect_address], '134' => STANDARD_ERROR_CODE[:incorrect_address], '135' => STANDARD_ERROR_CODE[:incorrect_address] @@ -60,19 +64,22 @@ add_3ds_authenticated_data(post, options) add_splits(post, options) add_recurring_contract(post, options) add_network_transaction_reference(post, options) add_application_info(post, options) + add_level_2_data(post, options) + add_level_3_data(post, options) 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) add_splits(post, options) add_network_transaction_reference(post, options) + add_shopper_statement(post, options) commit('capture', post, options) end def refund(money, authorization, options = {}) post = init_post(options) @@ -115,11 +122,11 @@ add_invoice(post, 0, options) add_payment(post, credit_card, options) add_extra_data(post, credit_card, options) add_stored_credentials(post, credit_card, options) add_address(post, options) - + add_network_transaction_reference(post, options) options[:recurring_contract_type] ||= 'RECURRING' add_recurring_contract(post, options) action = options[:tokenize_only] ? 'storeToken' : 'authorise' @@ -214,11 +221,11 @@ } NETWORK_TOKENIZATION_CARD_SOURCE = { 'apple_pay' => 'applepay', 'android_pay' => 'androidpay', - 'google_pay' => 'paywithgoogle' + 'google_pay' => 'googlepay' } def add_extra_data(post, payment, options) post[:telephoneNumber] = (options[:billing_address][:phone_number] if options.dig(:billing_address, :phone_number)) || (options[:billing_address][:phone] if options.dig(:billing_address, :phone)) || '' post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset] @@ -240,19 +247,69 @@ add_risk_data(post, options) add_shopper_reference(post, options) add_merchant_data(post, options) end + def extract_and_transform(mapper, from) + mapper.each_with_object({}) do |key_map, hsh| + key, item_key = key_map[0], key_map[1] + hsh[key] = from[item_key.to_sym] + end + end + + def add_level_2_data(post, options) + return unless options[:level_2_data].present? + + mapper = { + "enhancedSchemeData.totalTaxAmount": 'total_tax_amount', + "enhancedSchemeData.customerReference": 'customer_reference' + } + post[:additionalData].merge!(extract_and_transform(mapper, options[:level_2_data])) + end + + def add_level_3_data(post, options) + return unless options[:level_3_data].present? + + mapper = { "enhancedSchemeData.freightAmount": 'freight_amount', + "enhancedSchemeData.destinationStateProvinceCode": 'destination_state_province_code', + "enhancedSchemeData.shipFromPostalCode": 'ship_from_postal_code', + "enhancedSchemeData.orderDate": 'order_date', + "enhancedSchemeData.destinationPostalCode": 'destination_postal_code', + "enhancedSchemeData.destinationCountryCode": 'destination_country_code', + "enhancedSchemeData.dutyAmount": 'duty_amount' } + + post[:additionalData].merge!(extract_and_transform(mapper, options[:level_3_data])) + + item_detail_keys = %w[description product_code quantity unit_of_measure unit_price discount_amount total_amount commodity_code] + if options[:level_3_data][:items].present? + options[:level_3_data][:items].last(9).each.with_index(1) do |item, index| + mapper = item_detail_keys.each_with_object({}) do |key, hsh| + hsh["enhancedSchemeData.itemDetailLine#{index}.#{key.camelize(:lower)}"] = key + end + post[:additionalData].merge!(extract_and_transform(mapper, item)) + end + end + post[:additionalData].compact! + end + def add_shopper_data(post, options) post[:shopperEmail] = options[:email] if options[:email] post[:shopperEmail] = options[:shopper_email] if options[:shopper_email] post[:shopperIP] = options[:ip] if options[:ip] post[:shopperIP] = options[:shopper_ip] if options[:shopper_ip] post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement] post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement] end + def add_shopper_statement(post, options) + return unless options[:shopper_statement] + + post[:additionalData] = { + shopperStatement: options[:shopper_statement] + } + end + def add_merchant_data(post, options) post[:additionalData][:subMerchantID] = options[:sub_merchant_id] if options[:sub_merchant_id] post[:additionalData][:subMerchantName] = options[:sub_merchant_name] if options[:sub_merchant_name] post[:additionalData][:subMerchantStreet] = options[:sub_merchant_street] if options[:sub_merchant_street] post[:additionalData][:subMerchantCity] = options[:sub_merchant_city] if options[:sub_merchant_city] @@ -385,20 +442,20 @@ post[:selectedRecurringDetailReference] = recurring_detail_reference options[:recurring_contract_type] ||= 'RECURRING' elsif payment.is_a?(Check) add_bank_account(post, payment, options, action) else - add_mpi_data_for_network_tokenization_card(post, payment) if payment.is_a?(NetworkTokenizationCreditCard) + add_mpi_data_for_network_tokenization_card(post, payment, options) if payment.is_a?(NetworkTokenizationCreditCard) add_card(post, payment) end end def add_bank_account(post, bank_account, options, action) bank = { bankAccountNumber: bank_account.account_number, ownerName: bank_account.name, - countryCode: options[:billing_address][:country] + countryCode: options[:billing_address].try(:[], :country) } action == 'refundWithData' ? bank[:iban] = bank_account.routing_number : bank[:bankLocationId] = bank_account.routing_number requires!(bank, :bankAccountNumber, :ownerName, :countryCode) @@ -436,11 +493,13 @@ def add_reference(post, authorization, options = {}) original_reference = authorization.split('#').reject(&:empty?).first post[:originalReference] = original_reference end - def add_mpi_data_for_network_tokenization_card(post, payment) + def add_mpi_data_for_network_tokenization_card(post, payment, options) + return if options[:skip_mpi_data] == 'Y' + post[:mpiData] = {} post[:mpiData][:authenticationResponse] = 'Y' post[:mpiData][:cavv] = payment.payment_cryptogram post[:mpiData][:directoryResponse] = 'Y' post[:mpiData][:eci] = payment.eci || '07' @@ -614,10 +673,10 @@ headers end def success_from(action, response, options) if %w[RedirectShopper ChallengeShopper].include?(response.dig('resultCode')) && !options[:execute_threed] && !options[:threed_dynamic] - response['refusalReason'] = 'Received unexpected 3DS authentication response. Use the execute_threed and/or threed_dynamic options to initiate a proper 3DS flow.' + response['refusalReason'] = 'Received unexpected 3DS authentication response, but a 3DS initiation flag was not included in the request.' return false end case action.to_s when 'authorise', 'authorise3d' %w[Authorised Received RedirectShopper].include?(response['resultCode'])