activemerchant 1.107.3 → 1.107.4

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.
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