activemerchant 1.116.0 → 1.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +148 -1
  3. data/README.md +4 -2
  4. data/lib/active_merchant/billing/check.rb +10 -0
  5. data/lib/active_merchant/billing/credit_card.rb +3 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +80 -15
  7. data/lib/active_merchant/billing/gateways/adyen.rb +29 -8
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +37 -1
  9. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
  10. data/lib/active_merchant/billing/gateways/blue_snap.rb +3 -1
  11. data/lib/active_merchant/billing/gateways/braintree_blue.rb +54 -7
  12. data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
  13. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -2
  14. data/lib/active_merchant/billing/gateways/credorax.rb +30 -14
  15. data/lib/active_merchant/billing/gateways/cyber_source.rb +51 -8
  16. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/decidir.rb +22 -2
  18. data/lib/active_merchant/billing/gateways/elavon.rb +54 -2
  19. data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
  20. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
  21. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  22. data/lib/active_merchant/billing/gateways/global_collect.rb +25 -6
  23. data/lib/active_merchant/billing/gateways/hps.rb +65 -2
  24. data/lib/active_merchant/billing/gateways/litle.rb +21 -5
  25. data/lib/active_merchant/billing/gateways/mercado_pago.rb +2 -2
  26. data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
  27. data/lib/active_merchant/billing/gateways/orbital.rb +178 -45
  28. data/lib/active_merchant/billing/gateways/payeezy.rb +53 -11
  29. data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
  30. data/lib/active_merchant/billing/gateways/paymentez.rb +21 -1
  31. data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
  32. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
  33. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  34. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  35. data/lib/active_merchant/billing/gateways/pin.rb +11 -0
  36. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  37. data/lib/active_merchant/billing/gateways/redsys.rb +101 -5
  38. data/lib/active_merchant/billing/gateways/safe_charge.rb +39 -6
  39. data/lib/active_merchant/billing/gateways/stripe.rb +9 -9
  40. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +82 -25
  41. data/lib/active_merchant/billing/gateways/vpos.rb +177 -0
  42. data/lib/active_merchant/billing/gateways/worldpay.rb +31 -14
  43. data/lib/active_merchant/billing/response.rb +2 -1
  44. data/lib/active_merchant/version.rb +1 -1
  45. data/lib/certs/cacert.pem +1582 -2431
  46. metadata +5 -3
@@ -257,6 +257,8 @@ module ActiveMerchant
257
257
  add_settings(xml, payment, options)
258
258
  add_user_fields(xml, amount, options)
259
259
  add_ship_from_address(xml, options)
260
+ add_processing_options(xml, options)
261
+ add_subsequent_auth_information(xml, options)
260
262
  end
261
263
  end
262
264
 
@@ -408,7 +410,7 @@ module ActiveMerchant
408
410
 
409
411
  def add_settings(xml, source, options)
410
412
  xml.transactionSettings do
411
- if options[:recurring]
413
+ if options[:recurring] || subsequent_recurring_transaction?(options)
412
414
  xml.setting do
413
415
  xml.settingName('recurringBilling')
414
416
  xml.settingValue('true')
@@ -702,6 +704,31 @@ module ActiveMerchant
702
704
  xml.extraOptions("x_delim_char=#{options[:delimiter]}") if options[:delimiter]
703
705
  end
704
706
 
707
+ def add_processing_options(xml, options)
708
+ return unless options[:stored_credential]
709
+
710
+ xml.processingOptions do
711
+ if options[:stored_credential][:initial_transaction] && options[:stored_credential][:reason_type] == 'recurring'
712
+ xml.isFirstRecurringPayment 'true'
713
+ elsif options[:stored_credential][:initial_transaction]
714
+ xml.isFirstSubsequentAuth 'true'
715
+ elsif options[:stored_credential][:initiator] == 'cardholder'
716
+ xml.isStoredCredentials 'true'
717
+ else
718
+ xml.isSubsequentAuth 'true'
719
+ end
720
+ end
721
+ end
722
+
723
+ def add_subsequent_auth_information(xml, options)
724
+ return unless options.dig(:stored_credential, :initiator) == 'merchant'
725
+
726
+ xml.subsequentAuthInformation do
727
+ xml.reason options[:stored_credential_reason_type_override] if options[:stored_credential_reason_type_override]
728
+ xml.originalNetworkTransId options[:stored_credential][:network_transaction_id] if options[:stored_credential][:network_transaction_id]
729
+ end
730
+ end
731
+
705
732
  def create_customer_payment_profile(credit_card, options)
706
733
  commit(:cim_store_update, options) do |xml|
707
734
  xml.customerProfileId options[:customer_profile_id]
@@ -764,6 +791,10 @@ module ActiveMerchant
764
791
  end
765
792
  end
766
793
 
794
+ def subsequent_recurring_transaction?(options)
795
+ options.dig(:stored_credential, :reason_type) == 'recurring' && !options.dig(:stored_credential, :initial_transaction)
796
+ end
797
+
767
798
  def headers
768
799
  { 'Content-Type' => 'text/xml' }
769
800
  end
@@ -904,6 +935,11 @@ module ActiveMerchant
904
935
  empty?(element.content) ? nil : element.content
905
936
  end
906
937
 
938
+ response[:network_trans_id] =
939
+ if element = doc.at_xpath('//networkTransId')
940
+ empty?(element.content) ? nil : element.content
941
+ end
942
+
907
943
  response
908
944
  end
909
945
 
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+
2
3
  module ActiveMerchant #:nodoc:
3
4
  module Billing #:nodoc:
4
5
  # ==== Customer Information Manager (CIM)
@@ -562,6 +563,8 @@ module ActiveMerchant #:nodoc:
562
563
 
563
564
  def build_get_customer_profile_request(xml, options)
564
565
  xml.tag!('customerProfileId', options[:customer_profile_id])
566
+ xml.tag!('unmaskExpirationDate', options[:unmask_expiration_date]) if options[:unmask_expiration_date]
567
+ xml.tag!('includeIssuerInfo', options[:include_issuer_info]) if options[:include_issuer_info]
565
568
  xml.target!
566
569
  end
567
570
 
@@ -573,6 +576,7 @@ module ActiveMerchant #:nodoc:
573
576
  xml.tag!('customerProfileId', options[:customer_profile_id])
574
577
  xml.tag!('customerPaymentProfileId', options[:customer_payment_profile_id])
575
578
  xml.tag!('unmaskExpirationDate', options[:unmask_expiration_date]) if options[:unmask_expiration_date]
579
+ xml.tag!('includeIssuerInfo', options[:include_issuer_info]) if options[:include_issuer_info]
576
580
  xml.target!
577
581
  end
578
582
 
@@ -319,7 +319,9 @@ module ActiveMerchant
319
319
  def add_fraud_info(doc, payment_method, options)
320
320
  doc.send('transaction-fraud-info') do
321
321
  doc.send('shopper-ip-address', options[:ip]) if options[:ip]
322
-
322
+ if fraud_info = options[:transaction_fraud_info]
323
+ doc.send('fraud-session-id', fraud_info[:fraud_session_id]) if fraud_info[:fraud_session_id]
324
+ end
323
325
  unless payment_method.is_a? String
324
326
  doc.send('shipping-contact-info') do
325
327
  add_shipping_contact_info(doc, payment_method, options)
@@ -7,7 +7,7 @@ rescue LoadError
7
7
  raise 'Could not load the braintree gem. Use `gem install braintree` to install it.'
8
8
  end
9
9
 
10
- raise "Need braintree gem >= 2.78.0. Run `gem install braintree --version '~>2.78'` to get the correct version." unless Braintree::Version::Major == 2 && Braintree::Version::Minor >= 78
10
+ raise 'Need braintree gem >= 2.0.0.' unless Braintree::Version::Major >= 2 && Braintree::Version::Minor >= 0
11
11
 
12
12
  module ActiveMerchant #:nodoc:
13
13
  module Billing #:nodoc:
@@ -115,10 +115,36 @@ module ActiveMerchant #:nodoc:
115
115
  end
116
116
  end
117
117
 
118
- def verify(credit_card, options = {})
119
- MultiResponse.run(:use_first_response) do |r|
120
- r.process { authorize(100, credit_card, options) }
121
- r.process(:ignore_result) { void(r.authorization, options) }
118
+ def verify(creditcard, options = {})
119
+ if options[:allow_card_verification] == true
120
+ options.delete(:allow_card_verification)
121
+ exp_month = creditcard.month.to_s
122
+ exp_year = creditcard.year.to_s
123
+ expiration = "#{exp_month}/#{exp_year}"
124
+ payload = {
125
+ credit_card: {
126
+ number: creditcard.number,
127
+ expiration_date: expiration,
128
+ cvv: creditcard.verification_value,
129
+ billing_address: {
130
+ postal_code: options[:billing_address][:zip]
131
+ }
132
+ }
133
+ }
134
+ commit do
135
+ result = @braintree_gateway.verification.create(payload)
136
+ response = Response.new(result.success?, message_from_transaction_result(result), response_options(result))
137
+ response.cvv_result['message'] = ''
138
+ response.cvv_result['code'] = response.params['cvv_result']
139
+ response.avs_result['code'] = response.params['avs_result'][:code]
140
+ response
141
+ end
142
+
143
+ else
144
+ MultiResponse.run(:use_first_response) do |r|
145
+ r.process { authorize(100, creditcard, options) }
146
+ r.process(:ignore_result) { void(r.authorization, options) }
147
+ end
122
148
  end
123
149
  end
124
150
 
@@ -368,7 +394,11 @@ module ActiveMerchant #:nodoc:
368
394
 
369
395
  def response_options(result)
370
396
  options = {}
371
- if result.transaction
397
+ if result.credit_card_verification
398
+ options[:authorization] = result.credit_card_verification.id
399
+ options[:avs_result] = { code: avs_code_from(result.credit_card_verification) }
400
+ options[:cvv_result] = result.credit_card_verification.cvv_response_code
401
+ elsif result.transaction
372
402
  options[:authorization] = result.transaction.id
373
403
  options[:avs_result] = { code: avs_code_from(result.transaction) }
374
404
  options[:cvv_result] = result.transaction.cvv_response_code
@@ -583,6 +613,7 @@ module ActiveMerchant #:nodoc:
583
613
  parameters[:device_data] = options[:device_data] if options[:device_data]
584
614
  parameters[:service_fee_amount] = options[:service_fee_amount] if options[:service_fee_amount]
585
615
 
616
+ add_account_type(parameters, options) if options[:account_type]
586
617
  add_skip_options(parameters, options)
587
618
  add_merchant_account_id(parameters, options)
588
619
 
@@ -591,6 +622,7 @@ module ActiveMerchant #:nodoc:
591
622
  add_addresses(parameters, options)
592
623
 
593
624
  add_descriptor(parameters, options)
625
+ add_risk_data(parameters, options)
594
626
  add_travel_data(parameters, options) if options[:travel_data]
595
627
  add_lodging_data(parameters, options) if options[:lodging_data]
596
628
  add_channel(parameters, options)
@@ -609,6 +641,11 @@ module ActiveMerchant #:nodoc:
609
641
  parameters
610
642
  end
611
643
 
644
+ def add_account_type(parameters, options)
645
+ parameters[:options][:credit_card] = {}
646
+ parameters[:options][:credit_card][:account_type] = options[:account_type]
647
+ end
648
+
612
649
  def add_skip_options(parameters, options)
613
650
  parameters[:options][:skip_advanced_fraud_checking] = options[:skip_advanced_fraud_checking] if options[:skip_advanced_fraud_checking]
614
651
  parameters[:options][:skip_avs] = options[:skip_avs] if options[:skip_avs]
@@ -646,6 +683,15 @@ module ActiveMerchant #:nodoc:
646
683
  }
647
684
  end
648
685
 
686
+ def add_risk_data(parameters, options)
687
+ return unless options[:risk_data]
688
+
689
+ parameters[:risk_data] = {
690
+ customer_browser: options[:risk_data][:customer_browser],
691
+ customer_ip: options[:risk_data][:customer_ip]
692
+ }
693
+ end
694
+
649
695
  def add_level_2_data(parameters, options)
650
696
  parameters[:tax_amount] = options[:tax_amount] if options[:tax_amount]
651
697
  parameters[:tax_exempt] = options[:tax_exempt] if options[:tax_exempt]
@@ -755,7 +801,8 @@ module ActiveMerchant #:nodoc:
755
801
  eci_indicator: credit_card_or_vault_id.eci
756
802
  }
757
803
  elsif credit_card_or_vault_id.source == :android_pay || credit_card_or_vault_id.source == :google_pay
758
- parameters[:android_pay_card] = {
804
+ Braintree::Version::Major < 3 ? pay_card = :android_pay_card : pay_card = :google_pay_card
805
+ parameters[pay_card] = {
759
806
  number: credit_card_or_vault_id.number,
760
807
  cryptogram: credit_card_or_vault_id.payment_cryptogram,
761
808
  expiration_month: credit_card_or_vault_id.month.to_s.rjust(2, '0'),
@@ -8,7 +8,7 @@ module ActiveMerchant #:nodoc:
8
8
 
9
9
  self.supported_countries = ['US']
10
10
  self.supported_cardtypes = %i[visa master american_express discover diners_club jcb]
11
- self.homepage_url = 'http://www.higherone.com/'
11
+ self.homepage_url = 'https://transactcampus.com'
12
12
  self.display_name = 'Cashnet'
13
13
  self.money_format = :dollars
14
14
  self.max_retries = 0
@@ -76,7 +76,7 @@ module ActiveMerchant #:nodoc:
76
76
 
77
77
  return unparsable_response(raw_response) unless parsed_response
78
78
 
79
- success = (parsed_response[:result] == '0')
79
+ success = success?(parsed_response)
80
80
  Response.new(
81
81
  success,
82
82
  CASHNET_CODES[parsed_response[:result]],
@@ -86,6 +86,10 @@ module ActiveMerchant #:nodoc:
86
86
  )
87
87
  end
88
88
 
89
+ def success?(response)
90
+ response[:result] == '0'
91
+ end
92
+
89
93
  def post_data(action, parameters = {})
90
94
  post = {}
91
95
  post[:command] = action
@@ -191,6 +195,7 @@ module ActiveMerchant #:nodoc:
191
195
  '215' => 'Old PIN does not validate ',
192
196
  '221' => 'Invalid credit card processor type specified in location or payment code',
193
197
  '222' => 'Credit card processor error',
198
+ '230' => 'Host Error (USE VOID OR REVERSAL TO REFUND UNSETTLED TRANSACTIONS)',
194
199
  '280' => 'SmartPay transaction not posted',
195
200
  '301' => 'Original transaction not found for this customer',
196
201
  '302' => 'Amount to refund exceeds original payment amount or is missing',
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
  self.supported_countries = %w[AD AE AR AT AU BE BG BH BR CH CL CN CO CY CZ DE DK EE EG ES FI FR GB GR HK HR HU IE IS IT JO JP KW LI LT LU LV MC MT MX MY NL NO NZ OM PE PL PT QA RO SA SE SG SI SK SM TR US]
10
10
  self.default_currency = 'USD'
11
11
  self.money_format = :cents
12
- self.supported_cardtypes = %i[visa master american_express diners_club maestro discover]
12
+ self.supported_cardtypes = %i[visa master american_express diners_club maestro discover jcb]
13
13
  self.currencies_without_fractions = %w(BIF DJF GNF ISK KMF XAF CLF XPF JPY PYG RWF KRW VUV VND XOF)
14
14
  self.currencies_with_three_decimal_places = %w(BHD LYD JOD KWD OMR TND)
15
15
 
@@ -37,12 +37,15 @@ module ActiveMerchant #:nodoc:
37
37
  post = {}
38
38
  add_invoice(post, amount, options)
39
39
  add_customer_data(post, options)
40
+ add_metadata(post, options)
40
41
 
41
42
  commit(:capture, post, authorization)
42
43
  end
43
44
 
44
45
  def void(authorization, _options = {})
45
46
  post = {}
47
+ add_metadata(post, options)
48
+
46
49
  commit(:void, post, authorization)
47
50
  end
48
51
 
@@ -50,6 +53,7 @@ module ActiveMerchant #:nodoc:
50
53
  post = {}
51
54
  add_invoice(post, amount, options)
52
55
  add_customer_data(post, options)
56
+ add_metadata(post, options)
53
57
 
54
58
  commit(:refund, post, authorization)
55
59
  end
@@ -79,8 +83,10 @@ module ActiveMerchant #:nodoc:
79
83
  add_invoice(post, amount, options)
80
84
  add_payment_method(post, payment_method, options)
81
85
  add_customer_data(post, options)
86
+ add_stored_credential_options(post, options)
82
87
  add_transaction_data(post, options)
83
88
  add_3ds(post, options)
89
+ add_metadata(post, options)
84
90
  end
85
91
 
86
92
  def add_invoice(post, money, options)
@@ -96,6 +102,11 @@ module ActiveMerchant #:nodoc:
96
102
  post[:metadata][:udf5] = application_id || 'ActiveMerchant'
97
103
  end
98
104
 
105
+ def add_metadata(post, options)
106
+ post[:metadata] = {} unless post[:metadata]
107
+ post[:metadata].merge!(options[:metadata]) if options[:metadata]
108
+ end
109
+
99
110
  def add_payment_method(post, payment_method, options)
100
111
  post[:source] = {}
101
112
  if payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :network_token
@@ -138,12 +149,33 @@ module ActiveMerchant #:nodoc:
138
149
  post[:previous_payment_id] = options[:previous_charge_id] if options[:previous_charge_id]
139
150
  end
140
151
 
152
+ def add_stored_credential_options(post, options = {})
153
+ return unless options[:stored_credential]
154
+
155
+ case options[:stored_credential][:initial_transaction]
156
+ when true
157
+ post[:merchant_initiated] = false
158
+ when false
159
+ post[:'source.stored'] = true
160
+ post[:previous_payment_id] = options[:stored_credential][:network_transaction_id] if options[:stored_credential][:network_transaction_id]
161
+ post[:merchant_initiated] = true
162
+ end
163
+
164
+ case options[:stored_credential][:reason_type]
165
+ when 'recurring' || 'installment'
166
+ post[:payment_type] = 'Recurring'
167
+ when 'unscheduled'
168
+ return
169
+ end
170
+ end
171
+
141
172
  def add_3ds(post, options)
142
173
  if options[:three_d_secure] || options[:execute_threed]
143
174
  post[:'3ds'] = {}
144
175
  post[:'3ds'][:enabled] = true
145
176
  post[:success_url] = options[:callback_url] if options[:callback_url]
146
177
  post[:failure_url] = options[:callback_url] if options[:callback_url]
178
+ post[:'3ds'][:attempt_n3d] = options[:attempt_n3d] if options[:attempt_n3d]
147
179
  end
148
180
 
149
181
  if options[:three_d_secure]
@@ -151,7 +183,6 @@ module ActiveMerchant #:nodoc:
151
183
  post[:'3ds'][:cryptogram] = options[:three_d_secure][:cavv] if options[:three_d_secure][:cavv]
152
184
  post[:'3ds'][:version] = options[:three_d_secure][:version] if options[:three_d_secure][:version]
153
185
  post[:'3ds'][:xid] = options[:three_d_secure][:ds_transaction_id] || options[:three_d_secure][:xid]
154
- post[:'3ds'][:attempt_n3d] = options[:attempt_n3d] if options[:attempt_n3d]
155
186
  end
156
187
  end
157
188
 
@@ -25,7 +25,7 @@ module ActiveMerchant #:nodoc:
25
25
  self.currencies_with_three_decimal_places = %w(BHD IQD JOD KWD LYD OMR TND)
26
26
 
27
27
  self.money_format = :cents
28
- self.supported_cardtypes = %i[visa master maestro]
28
+ self.supported_cardtypes = %i[visa master maestro american_express]
29
29
 
30
30
  RESPONSE_MESSAGES = {
31
31
  '00' => 'Approved or completed successfully',
@@ -117,7 +117,8 @@ module ActiveMerchant #:nodoc:
117
117
  '96' => 'System malfunction',
118
118
  'R0' => 'Stop Payment Order',
119
119
  'R1' => 'Revocation of Authorisation Order',
120
- 'R3' => 'Revocation of all Authorisations Order'
120
+ 'R3' => 'Revocation of all Authorisations Order',
121
+ '1A' => 'Strong Customer Authentication required'
121
122
  }
122
123
 
123
124
  def initialize(options = {})
@@ -324,14 +325,16 @@ module ActiveMerchant #:nodoc:
324
325
  end
325
326
 
326
327
  def add_3d_secure(post, options)
327
- if options[:eci] && options[:xid]
328
+ if (options[:eci] && options[:xid]) || (options[:three_d_secure] && options[:three_d_secure][:version]&.start_with?('1'))
328
329
  add_3d_secure_1_data(post, options)
329
330
  elsif options[:execute_threed] && options[:three_ds_2]
330
331
  three_ds_2_options = options[:three_ds_2]
331
332
  browser_info = three_ds_2_options[:browser_info]
332
333
  post[:'3ds_initiate'] = options[:three_ds_initiate] || '01'
334
+ post[:f23] = options[:f23] if options[:f23]
333
335
  post[:'3ds_purchasedate'] = Time.now.utc.strftime('%Y%m%d%I%M%S')
334
336
  options.dig(:stored_credential, :initiator) == 'merchant' ? post[:'3ds_channel'] = '03' : post[:'3ds_channel'] = '02'
337
+ post[:'3ds_reqchallengeind'] = options[:three_ds_reqchallengeind] if options[:three_ds_reqchallengeind]
335
338
  post[:'3ds_redirect_url'] = three_ds_2_options[:notification_url]
336
339
  post[:'3ds_challengewindowsize'] = options[:three_ds_challenge_window_size] || '03'
337
340
  post[:d5] = browser_info[:user_agent]
@@ -343,22 +346,35 @@ module ActiveMerchant #:nodoc:
343
346
  post[:d6] = browser_info[:language]
344
347
  post[:'3ds_browserjavaenabled'] = browser_info[:java]
345
348
  post[:'3ds_browseracceptheader'] = browser_info[:accept_header]
346
- if (shipping_address = options[:shipping_address])
347
- post[:'3ds_shipaddrstate'] = shipping_address[:state]
348
- post[:'3ds_shipaddrpostcode'] = shipping_address[:zip]
349
- post[:'3ds_shipaddrline2'] = shipping_address[:address2]
350
- post[:'3ds_shipaddrline1'] = shipping_address[:address1]
351
- post[:'3ds_shipaddrcountry'] = shipping_address[:country]
352
- post[:'3ds_shipaddrcity'] = shipping_address[:city]
353
- end
349
+ add_complete_shipping_address(post, options[:shipping_address]) if options[:shipping_address]
354
350
  elsif options[:three_d_secure]
355
351
  add_normalized_3d_secure_2_data(post, options)
356
352
  end
357
353
  end
358
354
 
359
355
  def add_3d_secure_1_data(post, options)
360
- post[:i8] = build_i8(options[:eci], options[:cavv], options[:xid])
361
- post[:'3ds_version'] = options[:three_ds_version].nil? || options[:three_ds_version] == '1' ? '1.0' : options[:three_ds_version]
356
+ if three_d_secure_options = options[:three_d_secure]
357
+ post[:i8] = build_i8(
358
+ three_d_secure_options[:eci],
359
+ three_d_secure_options[:cavv],
360
+ three_d_secure_options[:xid]
361
+ )
362
+ post[:'3ds_version'] = three_d_secure_options[:version]&.start_with?('1') ? '1.0' : three_d_secure_options[:version]
363
+ else
364
+ post[:i8] = build_i8(options[:eci], options[:cavv], options[:xid])
365
+ post[:'3ds_version'] = options[:three_ds_version].nil? || options[:three_ds_version]&.start_with?('1') ? '1.0' : options[:three_ds_version]
366
+ end
367
+ end
368
+
369
+ def add_complete_shipping_address(post, shipping_address)
370
+ return if shipping_address.values.any?(&:blank?)
371
+
372
+ post[:'3ds_shipaddrstate'] = shipping_address[:state]
373
+ post[:'3ds_shipaddrpostcode'] = shipping_address[:zip]
374
+ post[:'3ds_shipaddrline2'] = shipping_address[:address2]
375
+ post[:'3ds_shipaddrline1'] = shipping_address[:address1]
376
+ post[:'3ds_shipaddrcountry'] = shipping_address[:country]
377
+ post[:'3ds_shipaddrcity'] = shipping_address[:city]
362
378
  end
363
379
 
364
380
  def add_normalized_3d_secure_2_data(post, options)
@@ -368,7 +384,7 @@ module ActiveMerchant #:nodoc:
368
384
  three_d_secure_options[:eci],
369
385
  three_d_secure_options[:cavv]
370
386
  )
371
- post[:'3ds_version'] = three_d_secure_options[:version] == '2' ? '2.0' : three_d_secure_options[:version]
387
+ post[:'3ds_version'] = three_d_secure_options[:version]&.start_with?('2') ? '2.0' : three_d_secure_options[:version]
372
388
  post[:'3ds_dstrxid'] = three_d_secure_options[:ds_transaction_id]
373
389
  end
374
390
 
@@ -25,8 +25,8 @@ module ActiveMerchant #:nodoc:
25
25
  self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
26
26
 
27
27
  # Schema files can be found here: https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/
28
- TEST_XSD_VERSION = '1.164'
29
- PRODUCTION_XSD_VERSION = '1.164'
28
+ TEST_XSD_VERSION = '1.181'
29
+ PRODUCTION_XSD_VERSION = '1.181'
30
30
  ECI_BRAND_MAPPING = {
31
31
  visa: 'vbv',
32
32
  master: 'spa',
@@ -256,8 +256,9 @@ module ActiveMerchant #:nodoc:
256
256
 
257
257
  private
258
258
 
259
- # Create all address hash key value pairs so that we still function if we
260
- # were only provided with one or two of them or even none
259
+ # Create all required address hash key value pairs
260
+ # If a value of nil is received, that value will be passed on to the gateway and will not be replaced with a default value
261
+ # Billing address fields received without an override value or with an empty string value will be replaced with the default_address values
261
262
  def setup_address_hash(options)
262
263
  default_address = {
263
264
  address1: 'Unspecified',
@@ -268,10 +269,20 @@ module ActiveMerchant #:nodoc:
268
269
  }
269
270
 
270
271
  submitted_address = options[:billing_address] || options[:address] || default_address
271
- options[:billing_address] = default_address.merge(submitted_address.symbolize_keys) { |_k, default, submitted| submitted.blank? ? default : submitted }
272
+ options[:billing_address] = default_address.merge(submitted_address.symbolize_keys) { |_k, default, submitted| check_billing_field_value(default, submitted) }
272
273
  options[:shipping_address] = options[:shipping_address] || {}
273
274
  end
274
275
 
276
+ def check_billing_field_value(default, submitted)
277
+ if submitted.nil?
278
+ nil
279
+ elsif submitted.blank?
280
+ default
281
+ else
282
+ submitted
283
+ end
284
+ end
285
+
275
286
  def build_auth_request(money, creditcard_or_reference, options)
276
287
  xml = Builder::XmlMarkup.new indent: 2
277
288
  add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
@@ -287,6 +298,8 @@ module ActiveMerchant #:nodoc:
287
298
  add_partner_solution_id(xml)
288
299
  add_stored_credential_options(xml, options)
289
300
  add_merchant_description(xml, options)
301
+ add_sales_slip_number(xml, options)
302
+ add_airline_data(xml, options)
290
303
 
291
304
  xml.target!
292
305
  end
@@ -325,6 +338,8 @@ module ActiveMerchant #:nodoc:
325
338
  add_threeds_2_ucaf_data(xml, payment_method_or_reference, options)
326
339
  add_decision_manager_fields(xml, options)
327
340
  add_mdd_fields(xml, options)
341
+ add_sales_slip_number(xml, options)
342
+ add_airline_data(xml, options)
328
343
  if !payment_method_or_reference.is_a?(String) && card_brand(payment_method_or_reference) == 'check'
329
344
  add_check_service(xml)
330
345
  add_issuer_additional_data(xml, options)
@@ -512,6 +527,18 @@ module ActiveMerchant #:nodoc:
512
527
  end
513
528
  end
514
529
 
530
+ def add_sales_slip_number(xml, options)
531
+ xml.tag! 'salesSlipNumber', options[:sales_slip_number] if options[:sales_slip_number]
532
+ end
533
+
534
+ def add_airline_data(xml, options)
535
+ return unless options[:airline_agent_code]
536
+
537
+ xml.tag! 'airlineData' do
538
+ xml.tag! 'agentCode', options[:airline_agent_code]
539
+ end
540
+ end
541
+
515
542
  def add_purchase_data(xml, money = 0, include_grand_total = false, options = {})
516
543
  xml.tag! 'purchaseTotals' do
517
544
  xml.tag! 'currency', options[:currency] || currency(money)
@@ -520,9 +547,12 @@ module ActiveMerchant #:nodoc:
520
547
  end
521
548
 
522
549
  def add_address(xml, payment_method, address, options, shipTo = false)
550
+ first_name, last_name = address_names(address[:name], payment_method)
551
+ bill_to_merchant_tax_id = options[:merchant_tax_id] unless shipTo
552
+
523
553
  xml.tag! shipTo ? 'shipTo' : 'billTo' do
524
- xml.tag! 'firstName', payment_method.first_name if payment_method
525
- xml.tag! 'lastName', payment_method.last_name if payment_method
554
+ xml.tag! 'firstName', first_name if first_name
555
+ xml.tag! 'lastName', last_name if last_name
526
556
  xml.tag! 'street1', address[:address1]
527
557
  xml.tag! 'street2', address[:address2] unless address[:address2].blank?
528
558
  xml.tag! 'city', address[:city]
@@ -536,9 +566,20 @@ module ActiveMerchant #:nodoc:
536
566
  xml.tag! 'ipAddress', options[:ip] unless options[:ip].blank? || shipTo
537
567
  xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank?
538
568
  xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank?
569
+ xml.tag! 'merchantTaxID', bill_to_merchant_tax_id unless bill_to_merchant_tax_id.blank?
539
570
  end
540
571
  end
541
572
 
573
+ def address_names(address_name, payment_method)
574
+ names = split_names(address_name)
575
+ return names if names.any?(&:present?)
576
+
577
+ [
578
+ payment_method&.first_name,
579
+ payment_method&.last_name
580
+ ]
581
+ end
582
+
542
583
  def add_creditcard(xml, creditcard)
543
584
  xml.tag! 'card' do
544
585
  xml.tag! 'accountNumber', creditcard.number
@@ -581,7 +622,7 @@ module ActiveMerchant #:nodoc:
581
622
  xml.tag! 'merchantDefinedData' do
582
623
  (1..100).each do |each|
583
624
  key = "mdd_field_#{each}".to_sym
584
- xml.tag!("field#{each}", options[key]) if options[key]
625
+ xml.tag!('mddField', options[key], 'id' => each) if options[key]
585
626
  end
586
627
  end
587
628
  end
@@ -830,6 +871,8 @@ module ActiveMerchant #:nodoc:
830
871
 
831
872
  xml.tag! 'installment' do
832
873
  xml.tag! 'totalCount', options[:installment_total_count]
874
+ xml.tag!('planType', options[:installment_plan_type]) if options[:installment_plan_type]
875
+ xml.tag!('firstInstallmentDate', options[:first_installment_date]) if options[:first_installment_date]
833
876
  end
834
877
  end
835
878