activemerchant 1.118.0 → 1.123.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +185 -0
  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 +4 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +44 -17
  8. data/lib/active_merchant/billing/gateways/adyen.rb +36 -19
  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/cashnet.rb +7 -2
  15. data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
  16. data/lib/active_merchant/billing/gateways/credorax.rb +15 -9
  17. data/lib/active_merchant/billing/gateways/cyber_source.rb +53 -6
  18. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/decidir.rb +15 -3
  20. data/lib/active_merchant/billing/gateways/elavon.rb +76 -25
  21. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  22. data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
  23. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  24. data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
  25. data/lib/active_merchant/billing/gateways/hps.rb +64 -1
  26. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  27. data/lib/active_merchant/billing/gateways/litle.rb +4 -4
  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/moka.rb +277 -0
  31. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  32. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  33. data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
  34. data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
  35. data/lib/active_merchant/billing/gateways/orbital.rb +127 -64
  36. data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
  37. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  38. data/lib/active_merchant/billing/gateways/payeezy.rb +34 -6
  39. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  40. data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
  41. data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
  42. data/lib/active_merchant/billing/gateways/paymentez.rb +5 -0
  43. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  44. data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
  45. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  46. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  47. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  48. data/lib/active_merchant/billing/gateways/redsys.rb +44 -25
  49. data/lib/active_merchant/billing/gateways/safe_charge.rb +20 -11
  50. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  51. data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
  52. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +79 -25
  53. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  54. data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
  55. data/lib/active_merchant/billing/gateways/worldpay.rb +64 -19
  56. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  57. data/lib/active_merchant/billing.rb +1 -0
  58. data/lib/active_merchant/version.rb +1 -1
  59. data/lib/certs/cacert.pem +1582 -2431
  60. metadata +10 -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,7 @@ 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)
62
63
  commit('authorise', post, options)
63
64
  end
64
65
 
@@ -67,21 +68,34 @@ module ActiveMerchant #:nodoc:
67
68
  add_invoice_for_modification(post, money, options)
68
69
  add_reference(post, authorization, options)
69
70
  add_splits(post, options)
71
+ add_network_transaction_reference(post, options)
70
72
  commit('capture', post, options)
71
73
  end
72
74
 
73
75
  def refund(money, authorization, options = {})
74
76
  post = init_post(options)
75
77
  add_invoice_for_modification(post, money, options)
76
- add_original_reference(post, authorization, options)
78
+ add_reference(post, authorization, options)
77
79
  add_splits(post, options)
80
+ add_network_transaction_reference(post, options)
78
81
  commit('refund', post, options)
79
82
  end
80
83
 
84
+ def credit(money, payment, options = {})
85
+ post = init_post(options)
86
+ add_invoice(post, money, options)
87
+ add_payment(post, payment, options)
88
+ add_shopper_reference(post, options)
89
+ add_network_transaction_reference(post, options)
90
+ commit('refundWithData', post, options)
91
+ end
92
+
81
93
  def void(authorization, options = {})
82
94
  post = init_post(options)
95
+ endpoint = options[:cancel_or_refund] ? 'cancelOrRefund' : 'cancel'
83
96
  add_reference(post, authorization, options)
84
- commit('cancel', post, options)
97
+ add_network_transaction_reference(post, options)
98
+ commit(endpoint, post, options)
85
99
  end
86
100
 
87
101
  def adjust(money, authorization, options = {})
@@ -374,7 +388,7 @@ module ActiveMerchant #:nodoc:
374
388
  }
375
389
 
376
390
  card.delete_if { |_k, v| v.blank? }
377
- card[:holderName] ||= 'Not Provided' if credit_card.is_a?(NetworkTokenizationCreditCard)
391
+ card[:holderName] ||= 'Not Provided'
378
392
  requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
379
393
  post[:card] = card
380
394
  end
@@ -385,14 +399,14 @@ module ActiveMerchant #:nodoc:
385
399
  options
386
400
  end
387
401
 
388
- def add_reference(post, authorization, options = {})
389
- _, psp_reference, = authorization.split('#')
390
- post[:originalReference] = single_reference(authorization) || psp_reference
402
+ def add_network_transaction_reference(post, options)
403
+ post[:additionalData] = {} unless post[:additionalData]
404
+ post[:additionalData][:networkTxReference] = options[:network_transaction_id] if options[:network_transaction_id]
391
405
  end
392
406
 
393
- def add_original_reference(post, authorization, options = {})
394
- original_psp_reference, = authorization.split('#')
395
- post[:originalReference] = single_reference(authorization) || original_psp_reference
407
+ def add_reference(post, authorization, options = {})
408
+ original_reference = authorization.split('#').reject(&:empty?).first
409
+ post[:originalReference] = original_reference
396
410
  end
397
411
 
398
412
  def add_mpi_data_for_network_tokenization_card(post, payment)
@@ -403,10 +417,6 @@ module ActiveMerchant #:nodoc:
403
417
  post[:mpiData][:eci] = payment.eci || '07'
404
418
  end
405
419
 
406
- def single_reference(authorization)
407
- authorization if !authorization.include?('#')
408
- end
409
-
410
420
  def add_recurring_contract(post, options = {})
411
421
  return unless options[:recurring_contract_type]
412
422
 
@@ -498,6 +508,7 @@ module ActiveMerchant #:nodoc:
498
508
  raw_response = e.response.body
499
509
  response = parse(raw_response)
500
510
  end
511
+
501
512
  success = success_from(action, response, options)
502
513
  Response.new(
503
514
  success,
@@ -506,6 +517,7 @@ module ActiveMerchant #:nodoc:
506
517
  authorization: authorization_from(action, parameters, response),
507
518
  test: test?,
508
519
  error_code: success ? nil : error_code_from(response),
520
+ network_transaction_id: network_transaction_id_from(response),
509
521
  avs_result: AVSResult.new(code: avs_code_from(response)),
510
522
  cvv_result: CVVResult.new(cvv_result_from(response))
511
523
  )
@@ -552,11 +564,10 @@ module ActiveMerchant #:nodoc:
552
564
  response['refusalReason'] = 'Received unexpected 3DS authentication response. Use the execute_threed and/or threed_dynamic options to initiate a proper 3DS flow.'
553
565
  return false
554
566
  end
555
-
556
567
  case action.to_s
557
568
  when 'authorise', 'authorise3d'
558
569
  %w[Authorised Received RedirectShopper].include?(response['resultCode'])
559
- when 'capture', 'refund', 'cancel'
570
+ when 'capture', 'refund', 'cancel', 'cancelOrRefund'
560
571
  response['response'] == "[#{action}-received]"
561
572
  when 'adjustAuthorisation'
562
573
  response['response'] == 'Authorised' || response['response'] == '[adjustAuthorisation-received]'
@@ -564,6 +575,8 @@ module ActiveMerchant #:nodoc:
564
575
  response['result'] == 'Success'
565
576
  when 'disable'
566
577
  response['response'] == '[detail-successfully-disabled]'
578
+ when 'refundWithData'
579
+ response['resultCode'] == 'Received'
567
580
  else
568
581
  false
569
582
  end
@@ -572,7 +585,7 @@ module ActiveMerchant #:nodoc:
572
585
  def message_from(action, response)
573
586
  return authorize_message_from(response) if %w(authorise authorise3d authorise3ds2).include?(action.to_s)
574
587
 
575
- response['response'] || response['message'] || response['result']
588
+ response['response'] || response['message'] || response['result'] || response['resultCode']
576
589
  end
577
590
 
578
591
  def authorize_message_from(response)
@@ -595,7 +608,7 @@ module ActiveMerchant #:nodoc:
595
608
  def init_post(options = {})
596
609
  post = {}
597
610
  add_merchant_account(post, options)
598
- post[:reference] = options[:order_id] if options[:order_id]
611
+ post[:reference] = options[:order_id][0..79] if options[:order_id]
599
612
  post
600
613
  end
601
614
 
@@ -607,6 +620,10 @@ module ActiveMerchant #:nodoc:
607
620
  STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
608
621
  end
609
622
 
623
+ def network_transaction_id_from(response)
624
+ response.dig('additionalData', 'networkTxReference')
625
+ end
626
+
610
627
  def add_browser_info(browser_info, post)
611
628
  return unless browser_info
612
629
 
@@ -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'),
@@ -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',