activemerchant 1.107.3 → 1.107.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07ae67a126faf1c4123540018228b4fa2cde8e60cba7f3ff274b939f8a9cd5f3
4
- data.tar.gz: 8b280d1f7d3ffa183adfe3851afd46362bbd42d8bf8673af9f32ceba92b91084
3
+ metadata.gz: df13edc6e4df78bc1172d0d699d41e21c6b9941ade9b2594602009cc4b78de66
4
+ data.tar.gz: 18c2f0264c1ae1edc34a6da0bf4c1244aa5135dfa72cef2c67f53cc4ea6c43a5
5
5
  SHA512:
6
- metadata.gz: 4735b7fe06d0991807532a7a29d54fb6a189fdb7dc308cc975693877b6ff7cebeafcf61fecd10ca9a733406a5027063bc1396e1f878b6d5d49bd5d2087fc338b
7
- data.tar.gz: 5c793be60a27bf1bdbae18a6a973261ed93a3e1e883ee9af6c7c52f644fdbe46edff6cbe142d0136fcb163a015aecfa3195471e0c184a633cff056b569caa1e2
6
+ metadata.gz: 1e0359cf05cd83b90ef3621b5c9656011e9361f610b7b0b381ee6af749358f2d597c74aace2a63a18d660a7d1842ca8b509b2a18918a637cc2b2ff2206b378e5
7
+ data.tar.gz: eeb3bc4faa2b4f4c25115715c16d520f39b5f8eade99c8dc3091b2347839b9fa9fb785d90bcb0b0851c05282d3bebd2ad4f19b070a1a13f38c43d363d7ea089f
data/CHANGELOG CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.107.4 (Jun 2, 2020)
6
+ * Elavon: Implement true verify action [leila-alderman] #3610
7
+ * Vantiv Express: Implement true verify [leila-alderman] #3617
8
+ * Litle: Pass expiration data for basis payment method [therufs] #3606
9
+ * Stripe Payment Intents: Error handling and backwards compatibility within refund [britth] #3627
10
+ * HPS: Prevent errors when account_type or account_holder_type are nil [britth] #3628
11
+ * D Local: Handle invalid country code errors [curiousepic] #3626
12
+ * Stripe Payment Intents: Utilize execute_threed flag to determine success [britth] #3625
13
+ * Elavon: Add Level 3 fields [leila-alderman] #3632
14
+ * CyberSource: Stored Credential fixes [curiousepic] #3624
15
+ * CyberSource: Fix invalid and missing field tests [curiousepic] #3634
16
+ * CyberSource: Pass stored credentials with purchase [curiousepic] #3636
17
+ * Mercado Pago: Add payment_method_option_id field [schwarzgeist] #3635
18
+ * Stripe: Provide error when attempting an authorize with ACH [britth] #3633
19
+ * EBANX: Send original order id as merchant_payment_code metadata [miguelxpn] #3637
20
+ * Element: Add card_present_code field [schwarzgeist] #3623
21
+ * Orbital: Add support for Level 3 fields [leila-alderman] #3639
22
+ * Firstdata: Strip newline characters from address [bittercoder] #3643
23
+ * Forte: add sec_code attribute for echeck [wsmoak] #3640
24
+
5
25
  == Version 1.107.3 (May 8, 2020)
6
26
  * Realex: Ignore IPv6 unsupported addresses [elfassy] #3622
7
27
  * Cybersource: Set partnerSolutionID after the business rules, fixes 500 error [pi3r] #3621
@@ -29,6 +49,7 @@
29
49
  * iATS Payments: Add support for Customer Code payment method [molbrown] #3611
30
50
  * HPS: Add Google Pay support [MSmedal] #3597
31
51
  * Adyen: Parse appropriate message for 3DS2 authorization calls [britth] #3619
52
+ * CyberSource: Add error details response fields [schwarzgeist] #3629
32
53
 
33
54
  == Version 1.107.1 (Apr 1, 2020)
34
55
  * Add `allowed_push_host` to gemspec [mdeloupy]
@@ -1,4 +1,5 @@
1
1
  require 'active_merchant/billing/gateways/braintree/braintree_common'
2
+ require 'active_support/core_ext/array/extract_options'
2
3
 
3
4
  begin
4
5
  require 'braintree'
@@ -100,7 +101,7 @@ module ActiveMerchant #:nodoc:
100
101
  response = response_from_result(@braintree_gateway.transaction.refund(transaction_id, money))
101
102
 
102
103
  if !response.success? && options[:force_full_refund_if_unsettled] &&
103
- response.message =~ /#{ERROR_CODES[:cannot_refund_if_unsettled]}/
104
+ response.message =~ /#{ERROR_CODES[:cannot_refund_if_unsettled]}/
104
105
  void(transaction_id)
105
106
  else
106
107
  response
@@ -34,6 +34,7 @@ module ActiveMerchant #:nodoc:
34
34
  jcb: 'js',
35
35
  discover: 'pb',
36
36
  }.freeze
37
+ DEFAULT_COLLECTION_INDICATOR = 2
37
38
 
38
39
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro, :elo]
39
40
  self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB PK)
@@ -326,7 +327,9 @@ module ActiveMerchant #:nodoc:
326
327
  add_threeds_services(xml, options)
327
328
  add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
328
329
  add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
330
+ add_stored_credential_options(xml, options)
329
331
  end
332
+
330
333
  add_issuer_additional_data(xml, options)
331
334
  add_merchant_description(xml, options)
332
335
  add_partner_solution_id(xml)
@@ -624,12 +627,13 @@ module ActiveMerchant #:nodoc:
624
627
 
625
628
  xml.tag! 'ucaf' do
626
629
  xml.tag!('authenticationData', options[:three_d_secure][:cavv])
627
- xml.tag!('collectionIndicator', options[:collection_indicator]) if options[:collection_indicator]
630
+ xml.tag!('collectionIndicator', options[:collection_indicator] || DEFAULT_COLLECTION_INDICATOR)
628
631
  end
629
632
  end
630
633
 
631
634
  def stored_credential_commerce_indicator(options)
632
635
  return unless options[:stored_credential]
636
+
633
637
  return if options[:stored_credential][:initial_transaction]
634
638
 
635
639
  case options[:stored_credential][:reason_type]
@@ -644,6 +648,7 @@ module ActiveMerchant #:nodoc:
644
648
 
645
649
  def add_auth_network_tokenization(xml, payment_method, options)
646
650
  return unless network_tokenization?(payment_method)
651
+
647
652
  brand = card_brand(payment_method).to_sym
648
653
 
649
654
  case brand
@@ -656,7 +661,7 @@ module ActiveMerchant #:nodoc:
656
661
  when :master
657
662
  xml.tag! 'ucaf' do
658
663
  xml.tag!('authenticationData', payment_method.payment_cryptogram)
659
- xml.tag!('collectionIndicator', '2')
664
+ xml.tag!('collectionIndicator', DEFAULT_COLLECTION_INDICATOR)
660
665
  end
661
666
  xml.tag! 'ccAuthService', {'run' => 'true'} do
662
667
  xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand])
@@ -815,15 +820,10 @@ module ActiveMerchant #:nodoc:
815
820
 
816
821
  def add_stored_credential_options(xml, options={})
817
822
  return unless options[:stored_credential]
818
-
819
- if options[:stored_credential][:initial_transaction]
820
- xml.tag! 'subsequentAuthFirst', 'true'
821
- elsif options[:stored_credential][:reason_type] == 'unscheduled'
822
- xml.tag! 'subsequentAuth', 'true'
823
- xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id]
824
- else
825
- xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id]
826
- end
823
+ xml.tag! 'subsequentAuth', 'true' if options[:stored_credential][:initiator] == 'merchant'
824
+ xml.tag! 'subsequentAuthFirst', 'true' if options[:stored_credential][:initial_transaction]
825
+ xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id] if options[:stored_credential][:initiator] == 'merchant'
826
+ xml.tag! 'subsequentAuthStoredCredential', 'true' if options[:stored_credential][:initiator] == 'cardholder' && !options[:stored_credential][:initial_transaction] || options[:stored_credential][:initiator] == 'merchant' && options[:stored_credential][:reason_type] == 'unscheduled'
827
827
  end
828
828
 
829
829
  def add_partner_solution_id(xml)
@@ -873,7 +873,7 @@ module ActiveMerchant #:nodoc:
873
873
  end
874
874
 
875
875
  success = success?(response)
876
- message = response[:message]
876
+ message = message_from(response)
877
877
 
878
878
  authorization = success ? authorization_from(response, action, amount, options) : nil
879
879
 
@@ -940,6 +940,16 @@ module ActiveMerchant #:nodoc:
940
940
  def success?(response)
941
941
  response[:decision] == @@decision_codes[:accept]
942
942
  end
943
+
944
+ def message_from(response)
945
+ if response[:reasonCode] == '101' && response[:missingField]
946
+ "#{response[:message]}: #{response[:missingField]}"
947
+ elsif response[:reasonCode] == '102' && response[:invalidField]
948
+ "#{response[:message]}: #{response[:invalidField]}"
949
+ else
950
+ response[:message]
951
+ end
952
+ end
943
953
  end
944
954
  end
945
955
  end
@@ -93,8 +93,10 @@ module ActiveMerchant #:nodoc:
93
93
  post[:country] = lookup_country_code(address[:country])
94
94
  end
95
95
 
96
- def lookup_country_code(country)
97
- Country.find(country).code(:alpha2).value
96
+ def lookup_country_code(country_field)
97
+ Country.find(country_field).code(:alpha2).value
98
+ rescue InvalidCountryCodeError
99
+ nil
98
100
  end
99
101
 
100
102
  def add_payer(post, card, options)
@@ -212,6 +212,8 @@ module ActiveMerchant #:nodoc:
212
212
  def add_additional_data(post, options)
213
213
  post[:device_id] = options[:device_id] if options[:device_id]
214
214
  post[:metadata] = options[:metadata] if options[:metadata]
215
+ post[:metadata] = {} if post[:metadata].nil?
216
+ post[:metadata][:merchant_payment_code] = options[:order_id] if options[:order_id]
215
217
  end
216
218
 
217
219
  def parse(body)
@@ -26,6 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  void: 'CCDELETE',
27
27
  store: 'CCGETTOKEN',
28
28
  update: 'CCUPDATETOKEN',
29
+ verify: 'CCVERIFY'
29
30
  }
30
31
 
31
32
  def initialize(options = {})
@@ -48,6 +49,7 @@ module ActiveMerchant #:nodoc:
48
49
  add_test_mode(form, options)
49
50
  add_ip(form, options)
50
51
  add_ssl_dynamic_dba(form, options)
52
+ add_level_3_fields(form, options) if options[:level_3_data]
51
53
  commit(:purchase, money, form, options)
52
54
  end
53
55
 
@@ -62,6 +64,7 @@ module ActiveMerchant #:nodoc:
62
64
  add_test_mode(form, options)
63
65
  add_ip(form, options)
64
66
  add_ssl_dynamic_dba(form, options)
67
+ add_level_3_fields(form, options) if options[:level_3_data]
65
68
  commit(:authorize, money, form, options)
66
69
  end
67
70
 
@@ -74,6 +77,7 @@ module ActiveMerchant #:nodoc:
74
77
  add_invoice(form, options)
75
78
  add_creditcard(form, options[:credit_card])
76
79
  add_currency(form, money, options)
80
+ add_address(form, options)
77
81
  add_customer_data(form, options)
78
82
  add_test_mode(form, options)
79
83
  else
@@ -113,10 +117,12 @@ module ActiveMerchant #:nodoc:
113
117
  end
114
118
 
115
119
  def verify(credit_card, options = {})
116
- MultiResponse.run(:use_first_response) do |r|
117
- r.process { authorize(100, credit_card, options) }
118
- r.process(:ignore_result) { void(r.authorization, options) }
119
- end
120
+ form = {}
121
+ add_creditcard(form, credit_card)
122
+ add_address(form, options)
123
+ add_test_mode(form, options)
124
+ add_ip(form, options)
125
+ commit(:verify, 0, form, options)
120
126
  end
121
127
 
122
128
  def store(creditcard, options = {})
@@ -255,6 +261,46 @@ module ActiveMerchant #:nodoc:
255
261
  form[:dynamic_dba] = options[:dba] if options.has_key?(:dba)
256
262
  end
257
263
 
264
+ def add_level_3_fields(form, options)
265
+ level_3_data = options[:level_3_data]
266
+ form[:customer_code] = level_3_data[:customer_code] if level_3_data[:customer_code]
267
+ form[:salestax] = level_3_data[:salestax] if level_3_data[:salestax]
268
+ form[:salestax_indicator] = level_3_data[:salestax_indicator] if level_3_data[:salestax_indicator]
269
+ form[:level3_indicator] = level_3_data[:level3_indicator] if level_3_data[:level3_indicator]
270
+ form[:ship_to_zip] = level_3_data[:ship_to_zip] if level_3_data[:ship_to_zip]
271
+ form[:ship_to_country] = level_3_data[:ship_to_country] if level_3_data[:ship_to_country]
272
+ form[:shipping_amount] = level_3_data[:shipping_amount] if level_3_data[:shipping_amount]
273
+ form[:ship_from_postal_code] = level_3_data[:ship_from_postal_code] if level_3_data[:ship_from_postal_code]
274
+ form[:discount_amount] = level_3_data[:discount_amount] if level_3_data[:discount_amount]
275
+ form[:duty_amount] = level_3_data[:duty_amount] if level_3_data[:duty_amount]
276
+ form[:national_tax_indicator] = level_3_data[:national_tax_indicator] if level_3_data[:national_tax_indicator]
277
+ form[:national_tax_amount] = level_3_data[:national_tax_amount] if level_3_data[:national_tax_amount]
278
+ form[:order_date] = level_3_data[:order_date] if level_3_data[:order_date]
279
+ form[:other_tax] = level_3_data[:other_tax] if level_3_data[:other_tax]
280
+ form[:summary_commodity_code] = level_3_data[:summary_commodity_code] if level_3_data[:summary_commodity_code]
281
+ form[:merchant_vat_number] = level_3_data[:merchant_vat_number] if level_3_data[:merchant_vat_number]
282
+ form[:customer_vat_number] = level_3_data[:customer_vat_number] if level_3_data[:customer_vat_number]
283
+ form[:freight_tax_amount] = level_3_data[:freight_tax_amount] if level_3_data[:freight_tax_amount]
284
+ form[:vat_invoice_number] = level_3_data[:vat_invoice_number] if level_3_data[:vat_invoice_number]
285
+ form[:tracking_number] = level_3_data[:tracking_number] if level_3_data[:tracking_number]
286
+ form[:shipping_company] = level_3_data[:shipping_company] if level_3_data[:shipping_company]
287
+ form[:other_fees] = level_3_data[:other_fees] if level_3_data[:other_fees]
288
+ add_line_items(form, level_3_data) if level_3_data[:line_items]
289
+ end
290
+
291
+ def add_line_items(form, level_3_data)
292
+ items = []
293
+ level_3_data[:line_items].each do |line_item|
294
+ item = {}
295
+ line_item.each do |key, value|
296
+ prefixed_key = "ssl_line_Item_#{key}"
297
+ item[prefixed_key.to_sym] = value
298
+ end
299
+ items << item
300
+ end
301
+ form[:LineItemProducts] = { product: items }
302
+ end
303
+
258
304
  def message_from(response)
259
305
  success?(response) ? response['result_message'] : response['errorMessage']
260
306
  end
@@ -284,7 +330,7 @@ module ActiveMerchant #:nodoc:
284
330
  end
285
331
 
286
332
  def post_data_string(key, value, options)
287
- if custom_field?(key, options)
333
+ if custom_field?(key, options) || key == :LineItemProducts
288
334
  "#{key}=#{CGI.escape(value.to_s)}"
289
335
  else
290
336
  "ssl_#{key}=#{CGI.escape(value.to_s)}"
@@ -111,10 +111,18 @@ module ActiveMerchant #:nodoc:
111
111
  end
112
112
 
113
113
  def verify(credit_card, options={})
114
- MultiResponse.run(:use_first_response) do |r|
115
- r.process { authorize(100, credit_card, options) }
116
- r.process(:ignore_result) { void(r.authorization, options) }
114
+ request = build_soap_request do |xml|
115
+ xml.CreditCardAVSOnly(xmlns: 'https://transaction.elementexpress.com') do
116
+ add_credentials(xml)
117
+ add_payment_method(xml, credit_card)
118
+ add_transaction(xml, 0, options)
119
+ add_terminal(xml, options)
120
+ add_address(xml, options)
121
+ end
117
122
  end
123
+
124
+ # send request with the transaction amount set to 0
125
+ commit('CreditCardAVSOnly', request, 0)
118
126
  end
119
127
 
120
128
  def supports_scrubbing?
@@ -186,7 +194,7 @@ module ActiveMerchant #:nodoc:
186
194
  def add_terminal(xml, options)
187
195
  xml.terminal do
188
196
  xml.TerminalID '01'
189
- xml.CardPresentCode 'UseDefault'
197
+ xml.CardPresentCode options[:card_present_code] || 'UseDefault'
190
198
  xml.CardholderPresentCode 'UseDefault'
191
199
  xml.CardInputCode 'UseDefault'
192
200
  xml.CVVPresenceCode 'UseDefault'
@@ -262,7 +262,7 @@ module ActiveMerchant #:nodoc:
262
262
  address = options[:billing_address] || options[:address]
263
263
  if address
264
264
  address_values = []
265
- [:address1, :zip, :city, :state, :country].each { |part| address_values << address[part].to_s }
265
+ [:address1, :zip, :city, :state, :country].each { |part| address_values << address[part].to_s.tr("\r\n", ' ').strip }
266
266
  xml.tag! 'VerificationStr1', address_values.join('|')
267
267
  end
268
268
 
@@ -24,7 +24,7 @@ module ActiveMerchant #:nodoc:
24
24
  post = {}
25
25
  add_amount(post, money, options)
26
26
  add_invoice(post, options)
27
- add_payment_method(post, payment_method)
27
+ add_payment_method(post, payment_method, options)
28
28
  add_billing_address(post, payment_method, options)
29
29
  add_shipping_address(post, options)
30
30
  post[:action] = 'sale'
@@ -36,7 +36,7 @@ module ActiveMerchant #:nodoc:
36
36
  post = {}
37
37
  add_amount(post, money, options)
38
38
  add_invoice(post, options)
39
- add_payment_method(post, payment_method)
39
+ add_payment_method(post, payment_method, options)
40
40
  add_billing_address(post, payment_method, options)
41
41
  add_shipping_address(post, options)
42
42
  post[:action] = 'authorize'
@@ -57,7 +57,7 @@ module ActiveMerchant #:nodoc:
57
57
  post = {}
58
58
  add_amount(post, money, options)
59
59
  add_invoice(post, options)
60
- add_payment_method(post, payment_method)
60
+ add_payment_method(post, payment_method, options)
61
61
  add_billing_address(post, payment_method, options)
62
62
  post[:action] = 'disburse'
63
63
 
@@ -151,21 +151,22 @@ module ActiveMerchant #:nodoc:
151
151
  post[:shipping_address][:physical_address][:locality] = address[:city] if address[:city]
152
152
  end
153
153
 
154
- def add_payment_method(post, payment_method)
154
+ def add_payment_method(post, payment_method, options)
155
155
  if payment_method.respond_to?(:brand)
156
156
  add_credit_card(post, payment_method)
157
157
  else
158
- add_echeck(post, payment_method)
158
+ add_echeck(post, payment_method, options)
159
159
  end
160
160
  end
161
161
 
162
- def add_echeck(post, payment)
162
+ def add_echeck(post, payment, options)
163
163
  post[:echeck] = {}
164
164
  post[:echeck][:account_holder] = payment.name
165
165
  post[:echeck][:account_number] = payment.account_number
166
166
  post[:echeck][:routing_number] = payment.routing_number
167
167
  post[:echeck][:account_type] = payment.account_type
168
168
  post[:echeck][:check_number] = payment.number
169
+ post[:echeck][:sec_code] = options[:sec_code] || "WEB"
169
170
  end
170
171
 
171
172
  def add_credit_card(post, payment)
@@ -201,9 +201,9 @@ module ActiveMerchant #:nodoc:
201
201
  xml.hps :RoutingNumber, check.routing_number
202
202
  xml.hps :AccountNumber, check.account_number
203
203
  xml.hps :CheckNumber, check.number
204
- xml.hps :AccountType, check.account_type.upcase
204
+ xml.hps :AccountType, check.account_type&.upcase
205
205
  end
206
- xml.hps :CheckType, check.account_holder_type.upcase
206
+ xml.hps :CheckType, check.account_holder_type&.upcase
207
207
  end
208
208
 
209
209
  def add_details(xml, options)
@@ -263,6 +263,7 @@ module ActiveMerchant #:nodoc:
263
263
  if payment_method.is_a?(String)
264
264
  doc.token do
265
265
  doc.litleToken(payment_method)
266
+ doc.expDate(format_exp_date(options[:basis_expiration_month], options[:basis_expiration_year])) if options[:basis_expiration_month] && options[:basis_expiration_year]
266
267
  end
267
268
  elsif payment_method.respond_to?(:track_data) && payment_method.track_data.present?
268
269
  doc.card do
@@ -416,7 +417,11 @@ module ActiveMerchant #:nodoc:
416
417
  end
417
418
 
418
419
  def exp_date(payment_method)
419
- "#{format(payment_method.month, :two_digits)}#{format(payment_method.year, :two_digits)}"
420
+ format_exp_date(payment_method.month, payment_method.year)
421
+ end
422
+
423
+ def format_exp_date(month, year)
424
+ "#{format(month, :two_digits)}#{format(year, :two_digits)}"
420
425
  end
421
426
 
422
427
  def parse(kind, xml)
@@ -113,6 +113,7 @@ module ActiveMerchant #:nodoc:
113
113
 
114
114
  post[:processing_mode] = options[:processing_mode]
115
115
  post[:merchant_account_id] = options[:merchant_account_id] if options[:merchant_account_id]
116
+ post[:payment_method_option_id] = options[:payment_method_option_id] if options[:payment_method_option_id]
116
117
  add_merchant_services(post, options)
117
118
  end
118
119
 
@@ -306,7 +306,8 @@ module ActiveMerchant #:nodoc:
306
306
  gsub(%r((<OrbitalConnectionUsername>).+(</OrbitalConnectionUsername>)), '\1[FILTERED]\2').
307
307
  gsub(%r((<OrbitalConnectionPassword>).+(</OrbitalConnectionPassword>)), '\1[FILTERED]\2').
308
308
  gsub(%r((<AccountNum>).+(</AccountNum>)), '\1[FILTERED]\2').
309
- gsub(%r((<CCAccountNum>).+(</CCAccountNum>)), '\1[FILTERED]\2').
309
+ # the response sometimes contains a new line that cuts off the end of the closing tag
310
+ gsub(%r((<CCAccountNum>).+(</CC)), '\1[FILTERED]\2').
310
311
  gsub(%r((<CardSecVal>).+(</CardSecVal>)), '\1[FILTERED]\2').
311
312
  gsub(%r((<MerchantID>).+(</MerchantID>)), '\1[FILTERED]\2').
312
313
  gsub(%r((<CustomerMerchantID>).+(</CustomerMerchantID>)), '\1[FILTERED]\2')
@@ -361,6 +362,15 @@ module ActiveMerchant #:nodoc:
361
362
  end
362
363
  end
363
364
 
365
+ def add_level_3_tax(xml, options={})
366
+ if (level_3 = options[:level_3_data])
367
+ xml.tag! :PC3VATtaxAmt, byte_limit(level_3[:vat_tax], 12) if level_3[:vat_tax]
368
+ xml.tag! :PC3AltTaxAmt, byte_limit(level_3[:alt_tax], 9) if level_3[:alt_tax]
369
+ xml.tag! :PC3VATtaxRate, byte_limit(level_3[:vat_rate], 4) if level_3[:vat_rate]
370
+ xml.tag! :PC3AltTaxInd, byte_limit(level_3[:alt_ind], 15) if level_3[:alt_ind]
371
+ end
372
+ end
373
+
364
374
  def add_level_2_advice_addendum(xml, options={})
365
375
  if (level_2 = options[:level_2_data])
366
376
  xml.tag! :AMEXTranAdvAddn1, byte_limit(level_2[:advice_addendum_1], 40) if level_2[:advice_addendum_1]
@@ -382,6 +392,35 @@ module ActiveMerchant #:nodoc:
382
392
  end
383
393
  end
384
394
 
395
+ def add_level_3_purchase(xml, options={})
396
+ if (level_3 = options[:level_3_data])
397
+ xml.tag! :PC3FreightAmt, byte_limit(level_3[:freight_amount], 12) if level_3[:freight_amount]
398
+ xml.tag! :PC3DutyAmt, byte_limit(level_3[:duty_amount], 12) if level_3[:duty_amount]
399
+ xml.tag! :PC3DestCountryCd, byte_limit(level_3[:dest_country], 3) if level_3[:dest_country]
400
+ xml.tag! :PC3ShipFromZip, byte_limit(level_3[:ship_from_zip], 10) if level_3[:ship_from_zip]
401
+ xml.tag! :PC3DiscAmt, byte_limit(level_3[:discount_amount], 12) if level_3[:discount_amount]
402
+ end
403
+ end
404
+
405
+ def add_line_items(xml, options={})
406
+ xml.tag! :PC3LineItemCount, byte_limit(options[:line_items].count, 2)
407
+ xml.tag! :PC3LineItemArray do
408
+ options[:line_items].each_with_index do |line_item, index|
409
+ xml.tag! :PC3LineItem do
410
+ xml.tag! :PC3DtlIndex, byte_limit(index + 1, 2)
411
+ line_item.each do |key, value|
412
+ if key == :line_tot
413
+ formatted_key = :PC3Dtllinetot
414
+ else
415
+ formatted_key = "PC3Dtl#{key.to_s.camelize}".to_sym
416
+ end
417
+ xml.tag! formatted_key, value
418
+ end
419
+ end
420
+ end
421
+ end
422
+ end
423
+
385
424
  def add_address(xml, creditcard, options)
386
425
  if (address = (options[:billing_address] || options[:address]))
387
426
  avs_supported = AVS_SUPPORTED_COUNTRIES.include?(address[:country].to_s) || empty?(address[:country])
@@ -715,6 +754,9 @@ module ActiveMerchant #:nodoc:
715
754
  end
716
755
 
717
756
  add_level_2_purchase(xml, parameters)
757
+ add_level_3_purchase(xml, parameters)
758
+ add_level_3_tax(xml, parameters)
759
+ add_line_items(xml, parameters) if parameters[:line_items]
718
760
  add_stored_credentials(xml, parameters)
719
761
  add_pymt_brand_program_code(xml, creditcard, three_d_secure)
720
762
  end
@@ -373,7 +373,8 @@ module ActiveMerchant
373
373
 
374
374
  def ipv4?(ip_address)
375
375
  return false if ip_address.nil?
376
- !!ip_address[/\A\d+\.\d+\.\d+\.\d+\z/]
376
+
377
+ !ip_address.match(/\A\d+\.\d+\.\d+\.\d+\z/).nil?
377
378
  end
378
379
  end
379
380
  end
@@ -80,6 +80,11 @@ module ActiveMerchant #:nodoc:
80
80
  end
81
81
 
82
82
  def authorize(money, payment, options = {})
83
+ if ach?(payment)
84
+ direct_bank_error = 'Direct bank account transactions are not supported for authorize.'
85
+ return Response.new(false, direct_bank_error)
86
+ end
87
+
83
88
  MultiResponse.run do |r|
84
89
  if payment.is_a?(ApplePayPaymentToken)
85
90
  r.process { tokenize_apple_pay_token(payment) }
@@ -647,7 +652,7 @@ module ActiveMerchant #:nodoc:
647
652
  add_expand_parameters(parameters, options) if parameters
648
653
  response = api_request(method, url, parameters, options)
649
654
  response['webhook_id'] = options[:webhook_id] if options[:webhook_id]
650
- success = success_from(response)
655
+ success = success_from(response, options)
651
656
 
652
657
  card = card_from_response(response)
653
658
  avs_code = AVS_CODE_TRANSLATOR["line1: #{card["address_line1_check"]}, zip: #{card["address_zip_check"]}"]
@@ -681,7 +686,7 @@ module ActiveMerchant #:nodoc:
681
686
  success ? 'Transaction approved' : response.fetch('error', {'message' => 'No error details'})['message']
682
687
  end
683
688
 
684
- def success_from(response)
689
+ def success_from(response, options)
685
690
  !response.key?('error') && response['status'] != 'failed'
686
691
  end
687
692
 
@@ -112,8 +112,22 @@ module ActiveMerchant #:nodoc:
112
112
  end
113
113
 
114
114
  def refund(money, intent_id, options = {})
115
- intent = commit(:get, "payment_intents/#{intent_id}", nil, options)
116
- charge_id = intent.params.dig('charges', 'data')[0].dig('id')
115
+ if intent_id.include?('pi_')
116
+ intent = api_request(:get, "payment_intents/#{intent_id}", nil, options)
117
+
118
+ return Response.new(false, intent['error']['message'], intent) if intent['error']
119
+
120
+ charge_id = intent.try(:[], 'charges').try(:[], 'data').try(:[], 0).try(:[], 'id')
121
+
122
+ if charge_id.nil?
123
+ error_message = "No associated charge for #{intent['id']}"
124
+ error_message << "; payment_intent has a status of #{intent['status']}" if intent.try(:[], 'status') && intent.try(:[], 'status') != 'succeeded'
125
+ return Response.new(false, error_message, intent)
126
+ end
127
+ else
128
+ charge_id = intent_id
129
+ end
130
+
117
131
  super(money, charge_id, options)
118
132
  end
119
133
 
@@ -281,6 +295,16 @@ module ActiveMerchant #:nodoc:
281
295
 
282
296
  options.merge(idempotency_key: "#{options[:idempotency_key]}-#{suffix}")
283
297
  end
298
+
299
+ def success_from(response, options)
300
+ if response['status'] == 'requires_action' && !options[:execute_threed]
301
+ response['error'] = {}
302
+ response['error']['message'] = 'Received unexpected 3DS authentication response. Use the execute_threed option to initiate a proper 3DS flow.'
303
+ return false
304
+ end
305
+
306
+ super(response, options)
307
+ end
284
308
  end
285
309
  end
286
310
  end
@@ -95,7 +95,7 @@ module ActiveMerchant #:nodoc:
95
95
  end
96
96
 
97
97
  if !response.success? && options[:force_full_refund_if_unsettled] &&
98
- response.params['last_event'] == 'AUTHORISED'
98
+ response.params['last_event'] == 'AUTHORISED'
99
99
  void(authorization, options)
100
100
  else
101
101
  response
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = '1.107.3'
2
+ VERSION = '1.107.4'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.107.3
4
+ version: 1.107.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-08 00:00:00.000000000 Z
11
+ date: 2020-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport