activemerchant 1.119.0 → 1.124.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +216 -1
  3. data/README.md +4 -2
  4. data/lib/active_merchant/billing/check.rb +19 -12
  5. data/lib/active_merchant/billing/credit_card.rb +3 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +32 -14
  8. data/lib/active_merchant/billing/gateways/adyen.rb +94 -25
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +3 -0
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +52 -8
  14. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  15. data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
  16. data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
  17. data/lib/active_merchant/billing/gateways/credorax.rb +15 -9
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +53 -6
  19. data/lib/active_merchant/billing/gateways/d_local.rb +9 -2
  20. data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
  21. data/lib/active_merchant/billing/gateways/elavon.rb +70 -28
  22. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  23. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  24. data/lib/active_merchant/billing/gateways/global_collect.rb +24 -10
  25. data/lib/active_merchant/billing/gateways/hps.rb +55 -1
  26. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  27. data/lib/active_merchant/billing/gateways/litle.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
  29. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  30. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +290 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  34. data/lib/active_merchant/billing/gateways/netbanx.rb +26 -2
  35. data/lib/active_merchant/billing/gateways/nmi.rb +27 -9
  36. data/lib/active_merchant/billing/gateways/orbital.rb +99 -59
  37. data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
  38. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  39. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  40. data/lib/active_merchant/billing/gateways/payeezy.rb +34 -6
  41. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  42. data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
  43. data/lib/active_merchant/billing/gateways/payment_express.rb +5 -5
  44. data/lib/active_merchant/billing/gateways/paymentez.rb +5 -0
  45. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  46. data/lib/active_merchant/billing/gateways/paysafe.rb +376 -0
  47. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  48. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  49. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  50. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +42 -24
  52. data/lib/active_merchant/billing/gateways/safe_charge.rb +25 -13
  53. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  54. data/lib/active_merchant/billing/gateways/stripe.rb +18 -8
  55. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +126 -48
  56. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  57. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  58. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  59. data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
  60. data/lib/active_merchant/billing/gateways/worldpay.rb +78 -18
  61. data/lib/active_merchant/billing/response.rb +4 -0
  62. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  63. data/lib/active_merchant/billing.rb +1 -0
  64. data/lib/active_merchant/version.rb +1 -1
  65. data/lib/certs/cacert.pem +1582 -2431
  66. metadata +11 -3
@@ -16,8 +16,8 @@ module ActiveMerchant #:nodoc:
16
16
  self.homepage_url = 'https://www.adyen.com/'
17
17
  self.display_name = 'Adyen'
18
18
 
19
- PAYMENT_API_VERSION = 'v40'
20
- RECURRING_API_VERSION = 'v30'
19
+ PAYMENT_API_VERSION = 'v64'
20
+ RECURRING_API_VERSION = 'v49'
21
21
 
22
22
  STANDARD_ERROR_CODE_MAPPING = {
23
23
  '101' => STANDARD_ERROR_CODE[:incorrect_number],
@@ -59,6 +59,8 @@ module ActiveMerchant #:nodoc:
59
59
  add_3ds_authenticated_data(post, options)
60
60
  add_splits(post, options)
61
61
  add_recurring_contract(post, options)
62
+ add_network_transaction_reference(post, options)
63
+ add_application_info(post, options)
62
64
  commit('authorise', post, options)
63
65
  end
64
66
 
@@ -67,21 +69,35 @@ module ActiveMerchant #:nodoc:
67
69
  add_invoice_for_modification(post, money, options)
68
70
  add_reference(post, authorization, options)
69
71
  add_splits(post, options)
72
+ add_network_transaction_reference(post, options)
70
73
  commit('capture', post, options)
71
74
  end
72
75
 
73
76
  def refund(money, authorization, options = {})
74
77
  post = init_post(options)
75
78
  add_invoice_for_modification(post, money, options)
76
- add_original_reference(post, authorization, options)
79
+ add_reference(post, authorization, options)
77
80
  add_splits(post, options)
81
+ add_network_transaction_reference(post, options)
78
82
  commit('refund', post, options)
79
83
  end
80
84
 
85
+ def credit(money, payment, options = {})
86
+ action = 'refundWithData'
87
+ post = init_post(options)
88
+ add_invoice(post, money, options)
89
+ add_payment(post, payment, options, action)
90
+ add_shopper_reference(post, options)
91
+ add_network_transaction_reference(post, options)
92
+ commit(action, post, options)
93
+ end
94
+
81
95
  def void(authorization, options = {})
82
96
  post = init_post(options)
97
+ endpoint = options[:cancel_or_refund] ? 'cancelOrRefund' : 'cancel'
83
98
  add_reference(post, authorization, options)
84
- commit('cancel', post, options)
99
+ add_network_transaction_reference(post, options)
100
+ commit(endpoint, post, options)
85
101
  end
86
102
 
87
103
  def adjust(money, authorization, options = {})
@@ -138,12 +154,19 @@ module ActiveMerchant #:nodoc:
138
154
  true
139
155
  end
140
156
 
157
+ def supports_network_tokenization?
158
+ true
159
+ end
160
+
141
161
  def scrub(transcript)
142
162
  transcript.
143
163
  gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
144
- gsub(%r(("number\\?":\\?")[^"]*)i, '\1[FILTERED]').
145
- gsub(%r(("cvc\\?":\\?")[^"]*)i, '\1[FILTERED]').
146
- gsub(%r(("cavv\\?":\\?")[^"]*)i, '\1[FILTERED]')
164
+ gsub(%r(("number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
165
+ gsub(%r(("cvc\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
166
+ gsub(%r(("cavv\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
167
+ gsub(%r(("bankLocationId\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
168
+ gsub(%r(("iban\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
169
+ gsub(%r(("bankAccountNumber\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
147
170
  end
148
171
 
149
172
  private
@@ -195,7 +218,7 @@ module ActiveMerchant #:nodoc:
195
218
  }
196
219
 
197
220
  def add_extra_data(post, payment, options)
198
- post[:telephoneNumber] = options[:billing_address][:phone] if options.dig(:billing_address, :phone)
221
+ post[:telephoneNumber] = (options[:billing_address][:phone_number] if options.dig(:billing_address, :phone_number)) || (options[:billing_address][:phone] if options.dig(:billing_address, :phone)) || ''
199
222
  post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset]
200
223
  post[:selectedBrand] = options[:selected_brand] if options[:selected_brand]
201
224
  post[:selectedBrand] ||= NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
@@ -318,7 +341,7 @@ module ActiveMerchant #:nodoc:
318
341
  post[:deliveryAddress][:stateOrProvince] = get_state(address)
319
342
  post[:deliveryAddress][:country] = address[:country] if address[:country]
320
343
  end
321
- return unless post[:card]&.kind_of?(Hash)
344
+ return unless post[:bankAccount]&.kind_of?(Hash) || post[:card]&.kind_of?(Hash)
322
345
 
323
346
  if (address = options[:billing_address] || options[:address]) && address[:country]
324
347
  post[:billingAddress] = {}
@@ -341,6 +364,7 @@ module ActiveMerchant #:nodoc:
341
364
  value: localized_amount(money, currency),
342
365
  currency: currency
343
366
  }
367
+
344
368
  post[:amount] = amount
345
369
  end
346
370
 
@@ -353,17 +377,32 @@ module ActiveMerchant #:nodoc:
353
377
  post[:modificationAmount] = amount
354
378
  end
355
379
 
356
- def add_payment(post, payment, options)
380
+ def add_payment(post, payment, options, action = nil)
357
381
  if payment.is_a?(String)
358
382
  _, _, recurring_detail_reference = payment.split('#')
359
383
  post[:selectedRecurringDetailReference] = recurring_detail_reference
360
384
  options[:recurring_contract_type] ||= 'RECURRING'
385
+ elsif payment.is_a?(Check)
386
+ add_bank_account(post, payment, options, action)
361
387
  else
362
388
  add_mpi_data_for_network_tokenization_card(post, payment) if payment.is_a?(NetworkTokenizationCreditCard)
363
389
  add_card(post, payment)
364
390
  end
365
391
  end
366
392
 
393
+ def add_bank_account(post, bank_account, options, action)
394
+ bank = {
395
+ bankAccountNumber: bank_account.account_number,
396
+ ownerName: bank_account.name,
397
+ countryCode: options[:billing_address][:country]
398
+ }
399
+
400
+ action == 'refundWithData' ? bank[:iban] = bank_account.routing_number : bank[:bankLocationId] = bank_account.routing_number
401
+
402
+ requires!(bank, :bankAccountNumber, :ownerName, :countryCode)
403
+ post[:bankAccount] = bank
404
+ end
405
+
367
406
  def add_card(post, credit_card)
368
407
  card = {
369
408
  expiryMonth: credit_card.month,
@@ -374,7 +413,7 @@ module ActiveMerchant #:nodoc:
374
413
  }
375
414
 
376
415
  card.delete_if { |_k, v| v.blank? }
377
- card[:holderName] ||= 'Not Provided' if credit_card.is_a?(NetworkTokenizationCreditCard)
416
+ card[:holderName] ||= 'Not Provided'
378
417
  requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
379
418
  post[:card] = card
380
419
  end
@@ -385,14 +424,16 @@ module ActiveMerchant #:nodoc:
385
424
  options
386
425
  end
387
426
 
388
- def add_reference(post, authorization, options = {})
389
- _, psp_reference, = authorization.split('#')
390
- post[:originalReference] = single_reference(authorization) || psp_reference
427
+ def add_network_transaction_reference(post, options)
428
+ return unless ntid = options[:network_transaction_id] || options.dig(:stored_credential, :network_transaction_id)
429
+
430
+ post[:additionalData] = {} unless post[:additionalData]
431
+ post[:additionalData][:networkTxReference] = ntid
391
432
  end
392
433
 
393
- def add_original_reference(post, authorization, options = {})
394
- original_psp_reference, = authorization.split('#')
395
- post[:originalReference] = single_reference(authorization) || original_psp_reference
434
+ def add_reference(post, authorization, options = {})
435
+ original_reference = authorization.split('#').reject(&:empty?).first
436
+ post[:originalReference] = original_reference
396
437
  end
397
438
 
398
439
  def add_mpi_data_for_network_tokenization_card(post, payment)
@@ -403,10 +444,6 @@ module ActiveMerchant #:nodoc:
403
444
  post[:mpiData][:eci] = payment.eci || '07'
404
445
  end
405
446
 
406
- def single_reference(authorization)
407
- authorization if !authorization.include?('#')
408
- end
409
-
410
447
  def add_recurring_contract(post, options = {})
411
448
  return unless options[:recurring_contract_type]
412
449
 
@@ -417,6 +454,31 @@ module ActiveMerchant #:nodoc:
417
454
  post[:recurring] = recurring
418
455
  end
419
456
 
457
+ def add_application_info(post, options)
458
+ post[:applicationInfo] ||= {}
459
+ add_external_platform(post, options)
460
+ add_merchant_application(post, options)
461
+ end
462
+
463
+ def add_external_platform(post, options)
464
+ return unless options[:externalPlatform]
465
+
466
+ post[:applicationInfo][:externalPlatform] = {
467
+ name: options[:externalPlatform][:name],
468
+ version: options[:externalPlatform][:version],
469
+ integrator: options[:externalPlatform][:integrator]
470
+ }
471
+ end
472
+
473
+ def add_merchant_application(post, options)
474
+ return unless options[:merchantApplication]
475
+
476
+ post[:applicationInfo][:merchantApplication] = {
477
+ name: options[:merchantApplication][:name],
478
+ version: options[:merchantApplication][:version]
479
+ }
480
+ end
481
+
420
482
  def add_installments(post, options)
421
483
  post[:installments] = {
422
484
  value: options[:installments]
@@ -498,6 +560,7 @@ module ActiveMerchant #:nodoc:
498
560
  raw_response = e.response.body
499
561
  response = parse(raw_response)
500
562
  end
563
+
501
564
  success = success_from(action, response, options)
502
565
  Response.new(
503
566
  success,
@@ -506,6 +569,7 @@ module ActiveMerchant #:nodoc:
506
569
  authorization: authorization_from(action, parameters, response),
507
570
  test: test?,
508
571
  error_code: success ? nil : error_code_from(response),
572
+ network_transaction_id: network_transaction_id_from(response),
509
573
  avs_result: AVSResult.new(code: avs_code_from(response)),
510
574
  cvv_result: CVVResult.new(cvv_result_from(response))
511
575
  )
@@ -552,11 +616,10 @@ module ActiveMerchant #:nodoc:
552
616
  response['refusalReason'] = 'Received unexpected 3DS authentication response. Use the execute_threed and/or threed_dynamic options to initiate a proper 3DS flow.'
553
617
  return false
554
618
  end
555
-
556
619
  case action.to_s
557
620
  when 'authorise', 'authorise3d'
558
621
  %w[Authorised Received RedirectShopper].include?(response['resultCode'])
559
- when 'capture', 'refund', 'cancel'
622
+ when 'capture', 'refund', 'cancel', 'cancelOrRefund'
560
623
  response['response'] == "[#{action}-received]"
561
624
  when 'adjustAuthorisation'
562
625
  response['response'] == 'Authorised' || response['response'] == '[adjustAuthorisation-received]'
@@ -564,6 +627,8 @@ module ActiveMerchant #:nodoc:
564
627
  response['result'] == 'Success'
565
628
  when 'disable'
566
629
  response['response'] == '[detail-successfully-disabled]'
630
+ when 'refundWithData'
631
+ response['resultCode'] == 'Received'
567
632
  else
568
633
  false
569
634
  end
@@ -572,7 +637,7 @@ module ActiveMerchant #:nodoc:
572
637
  def message_from(action, response)
573
638
  return authorize_message_from(response) if %w(authorise authorise3d authorise3ds2).include?(action.to_s)
574
639
 
575
- response['response'] || response['message'] || response['result']
640
+ response['response'] || response['message'] || response['result'] || response['resultCode']
576
641
  end
577
642
 
578
643
  def authorize_message_from(response)
@@ -595,7 +660,7 @@ module ActiveMerchant #:nodoc:
595
660
  def init_post(options = {})
596
661
  post = {}
597
662
  add_merchant_account(post, options)
598
- post[:reference] = options[:order_id] if options[:order_id]
663
+ post[:reference] = options[:order_id][0..79] if options[:order_id]
599
664
  post
600
665
  end
601
666
 
@@ -607,6 +672,10 @@ module ActiveMerchant #:nodoc:
607
672
  STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
608
673
  end
609
674
 
675
+ def network_transaction_id_from(response)
676
+ response.dig('additionalData', 'networkTxReference')
677
+ end
678
+
610
679
  def add_browser_info(browser_info, post)
611
680
  return unless browser_info
612
681
 
@@ -567,14 +567,16 @@ module ActiveMerchant
567
567
 
568
568
  xml.customerIP(options[:ip]) unless empty?(options[:ip])
569
569
 
570
- xml.cardholderAuthentication do
571
- three_d_secure = options.fetch(:three_d_secure, {})
572
- xml.authenticationIndicator(
573
- options[:authentication_indicator] || three_d_secure[:eci]
574
- )
575
- xml.cardholderAuthenticationValue(
576
- options[:cardholder_authentication_value] || three_d_secure[:cavv]
577
- )
570
+ if !empty?(options.fetch(:three_d_secure, {})) || options[:authentication_indicator] || options[:cardholder_authentication_value]
571
+ xml.cardholderAuthentication do
572
+ three_d_secure = options.fetch(:three_d_secure, {})
573
+ xml.authenticationIndicator(
574
+ options[:authentication_indicator] || three_d_secure[:eci]
575
+ )
576
+ xml.cardholderAuthenticationValue(
577
+ options[:cardholder_authentication_value] || three_d_secure[:cavv]
578
+ )
579
+ end
578
580
  end
579
581
  end
580
582
 
@@ -708,9 +710,10 @@ module ActiveMerchant
708
710
  return unless options[:stored_credential]
709
711
 
710
712
  xml.processingOptions do
711
- if options[:stored_credential][:initial_transaction]
713
+ if options[:stored_credential][:initial_transaction] && options[:stored_credential][:reason_type] == 'recurring'
714
+ xml.isFirstRecurringPayment 'true'
715
+ elsif options[:stored_credential][:initial_transaction]
712
716
  xml.isFirstSubsequentAuth 'true'
713
- # xml.isFirstRecurringPayment 'true' if options[:stored_credential][:reason_type] == 'recurring'
714
717
  elsif options[:stored_credential][:initiator] == 'cardholder'
715
718
  xml.isStoredCredentials 'true'
716
719
  else
@@ -720,7 +723,7 @@ module ActiveMerchant
720
723
  end
721
724
 
722
725
  def add_subsequent_auth_information(xml, options)
723
- return unless options.dig(:stored_credential, :reason_type) == 'unscheduled'
726
+ return unless options.dig(:stored_credential, :initiator) == 'merchant'
724
727
 
725
728
  xml.subsequentAuthInformation do
726
729
  xml.reason options[:stored_credential_reason_type_override] if options[:stored_credential_reason_type_override]
@@ -934,6 +937,11 @@ module ActiveMerchant
934
937
  empty?(element.content) ? nil : element.content
935
938
  end
936
939
 
940
+ response[:network_trans_id] =
941
+ if element = doc.at_xpath('//networkTransId')
942
+ empty?(element.content) ? nil : element.content
943
+ end
944
+
937
945
  response
938
946
  end
939
947
 
@@ -563,6 +563,8 @@ module ActiveMerchant #:nodoc:
563
563
 
564
564
  def build_get_customer_profile_request(xml, options)
565
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]
566
568
  xml.target!
567
569
  end
568
570
 
@@ -574,6 +576,7 @@ module ActiveMerchant #:nodoc:
574
576
  xml.tag!('customerProfileId', options[:customer_profile_id])
575
577
  xml.tag!('customerPaymentProfileId', options[:customer_payment_profile_id])
576
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]
577
580
  xml.target!
578
581
  end
579
582
 
@@ -84,6 +84,7 @@ module ActiveMerchant #:nodoc:
84
84
  add_customer_data(post, options)
85
85
  add_rebill(post, options) if options[:rebill]
86
86
  add_duplicate_override(post, options)
87
+ add_stored_credential(post, options)
87
88
  post[:TRANS_TYPE] = 'AUTH'
88
89
  commit('AUTH_ONLY', money, post, options)
89
90
  end
@@ -107,6 +108,7 @@ module ActiveMerchant #:nodoc:
107
108
  add_customer_data(post, options)
108
109
  add_rebill(post, options) if options[:rebill]
109
110
  add_duplicate_override(post, options)
111
+ add_stored_credential(post, options)
110
112
  post[:TRANS_TYPE] = 'SALE'
111
113
  commit('AUTH_CAPTURE', money, post, options)
112
114
  end
@@ -461,6 +463,33 @@ module ActiveMerchant #:nodoc:
461
463
  post[:REB_CYCLES] = options[:rebill_cycles]
462
464
  end
463
465
 
466
+ def add_stored_credential(post, options)
467
+ post[:cof] = initiator(options)
468
+ post[:cofscheduled] = scheduled(options)
469
+ end
470
+
471
+ def initiator(options)
472
+ return unless initiator = options.dig(:stored_credential, :initiator)
473
+
474
+ case initiator
475
+ when 'merchant'
476
+ 'M'
477
+ when 'cardholder'
478
+ 'C'
479
+ end
480
+ end
481
+
482
+ def scheduled(options)
483
+ return unless reason_type = options.dig(:stored_credential, :reason_type)
484
+
485
+ case reason_type
486
+ when 'recurring' || 'installment'
487
+ 'Y'
488
+ when 'unscheduled'
489
+ 'N'
490
+ end
491
+ end
492
+
464
493
  def post_data(action, parameters = {})
465
494
  post = {}
466
495
  post[:version] = '1'
@@ -386,7 +386,7 @@ module ActiveMerchant
386
386
 
387
387
  def parse(response)
388
388
  return bad_authentication_response if response.code.to_i == 401
389
- return forbidden_response(response.body) if response.code.to_i == 403
389
+ return generic_error_response(response.body) if [403, 429].include?(response.code.to_i)
390
390
 
391
391
  parsed = {}
392
392
  doc = Nokogiri::XML(response.body)
@@ -564,7 +564,7 @@ module ActiveMerchant
564
564
  { 'description' => 'Unable to authenticate. Please check your credentials.' }
565
565
  end
566
566
 
567
- def forbidden_response(body)
567
+ def generic_error_response(body)
568
568
  { 'description' => body }
569
569
  end
570
570
  end
@@ -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'] if response.params['cvv_result']
139
+ response.avs_result['code'] = response.params['avs_result'][:code] if response.params.dig('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
@@ -558,7 +588,8 @@ module ActiveMerchant #:nodoc:
558
588
  'merchant_account_id' => transaction.merchant_account_id,
559
589
  'risk_data' => risk_data,
560
590
  'network_transaction_id' => transaction.network_transaction_id || nil,
561
- 'processor_response_code' => response_code_from_result(result)
591
+ 'processor_response_code' => response_code_from_result(result),
592
+ 'recurring' => transaction.recurring
562
593
  }
563
594
  end
564
595
 
@@ -592,6 +623,7 @@ module ActiveMerchant #:nodoc:
592
623
  add_addresses(parameters, options)
593
624
 
594
625
  add_descriptor(parameters, options)
626
+ add_risk_data(parameters, options)
595
627
  add_travel_data(parameters, options) if options[:travel_data]
596
628
  add_lodging_data(parameters, options) if options[:lodging_data]
597
629
  add_channel(parameters, options)
@@ -652,6 +684,15 @@ module ActiveMerchant #:nodoc:
652
684
  }
653
685
  end
654
686
 
687
+ def add_risk_data(parameters, options)
688
+ return unless options[:risk_data]
689
+
690
+ parameters[:risk_data] = {
691
+ customer_browser: options[:risk_data][:customer_browser],
692
+ customer_ip: options[:risk_data][:customer_ip]
693
+ }
694
+ end
695
+
655
696
  def add_level_2_data(parameters, options)
656
697
  parameters[:tax_amount] = options[:tax_amount] if options[:tax_amount]
657
698
  parameters[:tax_exempt] = options[:tax_exempt] if options[:tax_exempt]
@@ -730,6 +771,8 @@ module ActiveMerchant #:nodoc:
730
771
  else
731
772
  parameters[:transaction_source] = stored_credential[:reason_type]
732
773
  end
774
+ elsif %w(recurring_first moto).include?(stored_credential[:reason_type])
775
+ parameters[:transaction_source] = stored_credential[:reason_type]
733
776
  else
734
777
  parameters[:transaction_source] = ''
735
778
  end
@@ -761,7 +804,8 @@ module ActiveMerchant #:nodoc:
761
804
  eci_indicator: credit_card_or_vault_id.eci
762
805
  }
763
806
  elsif credit_card_or_vault_id.source == :android_pay || credit_card_or_vault_id.source == :google_pay
764
- parameters[:android_pay_card] = {
807
+ Braintree::Version::Major < 3 ? pay_card = :android_pay_card : pay_card = :google_pay_card
808
+ parameters[pay_card] = {
765
809
  number: credit_card_or_vault_id.number,
766
810
  cryptogram: credit_card_or_vault_id.payment_cryptogram,
767
811
  expiration_month: credit_card_or_vault_id.month.to_s.rjust(2, '0'),
@@ -150,23 +150,13 @@ module ActiveMerchant #:nodoc:
150
150
 
151
151
  def authorize(money, credit_card_or_reference, options = {})
152
152
  post = {}
153
- add_pair(post, :captureDelay, -1)
154
- add_amount(post, money, options)
155
- add_invoice(post, credit_card_or_reference, money, options)
156
- add_credit_card_or_reference(post, credit_card_or_reference)
157
- add_customer_data(post, options)
158
- add_remote_address(post, options)
153
+ add_auth_purchase(post, -1, money, credit_card_or_reference, options)
159
154
  commit('SALE', post)
160
155
  end
161
156
 
162
157
  def purchase(money, credit_card_or_reference, options = {})
163
158
  post = {}
164
- add_pair(post, :captureDelay, 0)
165
- add_amount(post, money, options)
166
- add_invoice(post, credit_card_or_reference, money, options)
167
- add_credit_card_or_reference(post, credit_card_or_reference)
168
- add_customer_data(post, options)
169
- add_remote_address(post, options)
159
+ add_auth_purchase(post, 0, money, credit_card_or_reference, options)
170
160
  commit('SALE', post)
171
161
  end
172
162
 
@@ -184,6 +174,7 @@ module ActiveMerchant #:nodoc:
184
174
  add_pair(post, :xref, authorization)
185
175
  add_amount(post, money, options)
186
176
  add_remote_address(post, options)
177
+ add_country_code(post, options)
187
178
  response = commit('REFUND_SALE', post)
188
179
 
189
180
  return response if response.success?
@@ -223,6 +214,16 @@ module ActiveMerchant #:nodoc:
223
214
 
224
215
  private
225
216
 
217
+ def add_auth_purchase(post, pair_value, money, credit_card_or_reference, options)
218
+ add_pair(post, :captureDelay, pair_value)
219
+ add_amount(post, money, options)
220
+ add_invoice(post, credit_card_or_reference, money, options)
221
+ add_credit_card_or_reference(post, credit_card_or_reference)
222
+ add_customer_data(post, options)
223
+ add_remote_address(post, options)
224
+ add_country_code(post, options)
225
+ end
226
+
226
227
  def add_amount(post, money, options)
227
228
  currency = options[:currency] || currency(money)
228
229
  add_pair(post, :amount, localized_amount(money, currency), required: true)
@@ -286,6 +287,10 @@ module ActiveMerchant #:nodoc:
286
287
  add_pair(post, :remoteAddress, options[:ip] || '1.1.1.1')
287
288
  end
288
289
 
290
+ def add_country_code(post, options)
291
+ post[:countryCode] = options[:country_code] || self.supported_countries[0]
292
+ end
293
+
289
294
  def normalize_line_endings(str)
290
295
  str.gsub(/%0D%0A|%0A%0D|%0D/, '%0A')
291
296
  end
@@ -309,7 +314,6 @@ module ActiveMerchant #:nodoc:
309
314
  end
310
315
 
311
316
  def commit(action, parameters)
312
- parameters.update(countryCode: self.supported_countries[0]) unless %w[CAPTURE CANCEL].include?(action)
313
317
  parameters.update(
314
318
  merchantID: @options[:login],
315
319
  action: action
@@ -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',