ach_client 1.1.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fadb80869dd7fcd712cce4344a08ddfa1da32e70b7cee237a39c9abcb96ac03a
4
- data.tar.gz: 440ea73e8b1cbaafad65ecc94fa4710a9f8df1daa53a5ad656b7f5c2a4cced72
3
+ metadata.gz: bdcfddb63a2f873af45df29f8fcf6765ec99aff800eb04e8ec2b7500f0827e3c
4
+ data.tar.gz: 3bfa32675ec76f4e31b830b3616cc913b13f865c2c789394d49cc07758e93b89
5
5
  SHA512:
6
- metadata.gz: 19e7c19755a287febc77466ccfda9abe3cc567897462ed3b32d9831e00e754283d1b168d069d4b08223e0e0e6ee56b5b7635fb712d23aa8d2780670b93ff792d
7
- data.tar.gz: 0b5139c86630be28a2432fcf709772cb4bccdf725a724a6718b11b5057e69bcff71e2e0594075126781480e4fe9b494d3537aea816270dbec3afb1e97e77b9b6
6
+ metadata.gz: 25670e16c050eb8248d80f22ffeea4a649cef6f624828118e32c866b7b7e0b0e19f0735150b7b636e5cb88dafc7d57fe7a2ac3225b8c507100b42a3a9fba668e
7
+ data.tar.gz: ed197b466da4182a732f463b7c51f4a0bd24fdfbfa58755bd058d2f830790719e5f6d6c600abf480e045430e186c962f331ee0a7e17534546d93f7ed44cc8819
@@ -0,0 +1,28 @@
1
+ ### 2.0.0
2
+
3
+ * Add new AchClient::ICheckGateway::InstantRejectionError to handle API errors raised by ICheckGateway in situations
4
+ where other providers would accept the transaction and issue a return when polling in the future. This is a breaking
5
+ change as previous versions would raise a RuntimeError with the message 'ICheckGateway ACH Transaction Failure' and
6
+ including the API response.
7
+
8
+ * Include "internal corrections" (return codes starting with 'XZ') in the AchClient::ReturnCode#correction? predicate
9
+
10
+ ### 1.1.0
11
+
12
+ * Add AchClient::Fake provider to facilitate testing
13
+
14
+ ### 1.0.3
15
+
16
+ * Add presumed description for X09 return code
17
+
18
+ ### 1.0.2
19
+
20
+ * Add previously undocumented X01 internal return code
21
+
22
+ ### 1.0.1
23
+
24
+ * Remove newline characters from fields before generating NACHA files
25
+
26
+ ### 1.0.0
27
+
28
+ * Prior to 1.0.0, ach_client did not have a stable API.
data/README.md CHANGED
@@ -115,6 +115,16 @@ returned by the `#send` method. So you should:
115
115
 
116
116
  4) When you eventually poll the provider for the status of your transactions, you can use the `external_ach_id` you stored to reconcile the returned data against your records
117
117
 
118
+ ### ICheckGateway instant rejection caveat
119
+
120
+ ICheckGateway sometimes returns an API error when a valid ACH transaction is sent in a handful of
121
+ rejection scenarios. This is unusual because most providers will accept the transaction, return an
122
+ `external_ach_id`, and then supply the rejection info when you poll for responses (see section on response polling
123
+ below) at a later date. This idiosyncrasy is handled by raising an exception `InstantRejectionError` which contains
124
+ information about the premature ACH return. In this case, no `external_ach_id` is returned by the `#send` method
125
+ because ICheck does not return one, nor do they maintain a record of the transaction in their system. See the
126
+ `InstantRejectionError` class for more details.
127
+
118
128
  ## Batched ACH transactions
119
129
 
120
130
  A group of ACH transactions can also be sent in a single batched transaction to
@@ -9,6 +9,9 @@ module AchClient
9
9
  # The first character in an internal return code
10
10
  INTERNAL_START_CHARACTER = 'X'
11
11
 
12
+ # Returns that are both internal and corrections start with this string
13
+ INTERNAL_CORRECTION_STRING = 'XZ'
14
+
12
15
  attr_accessor :code,
13
16
  :description,
14
17
  :reason
@@ -25,7 +28,7 @@ module AchClient
25
28
 
26
29
  # @return Whether or not this return is a correction/notice of change
27
30
  def correction?
28
- @code.start_with?(CORRECTION_START_CHARACTER)
31
+ @code.start_with?(CORRECTION_START_CHARACTER) || @code.start_with?(INTERNAL_CORRECTION_STRING)
29
32
  end
30
33
 
31
34
  # @return Whether or not the return is internal
@@ -3,11 +3,23 @@ module AchClient
3
3
  # ICheckGateway implementation for AchTransaction
4
4
  class AchTransaction < Abstract::AchTransaction
5
5
 
6
+ # When ICheck API gives us an error response containing a correction, the response field looks like this:
7
+ # DECLINED - Notice of Change (XXX - Change Data: YYYYYY)
8
+ # Where X is a three character string representing the return code (example: C01)
9
+ # Where Y is any number of digits representing the updated information for the correction - the
10
+ # ACH attribute that should be updated (example: 123456789)
11
+ # The (\w{3}) capture group matches the return code, while the (\d+) capture group matches the correction data
12
+ # for later use.
13
+ NOC_RESPONSE_MATCHER = /DECLINED - Notice of Change \((\w{3}) - Change Data: (\d+)\)/
14
+
6
15
  # Sends this transaction to ICheckGateway
7
16
  # If successful, returns a string from the response that seems to be
8
17
  # a unique identifier for the transaction from ICheckGateway
9
18
  # Raises an exception with as much info as possible if something goes
10
- # wrong
19
+ # wrong.
20
+ # ICheck sometimes returns an API error for certain rejection scenarios. In this case we raise a
21
+ # InstantRejectionError which can be caught to handle any business logic appropriate for this edge case.
22
+ # The exception contains a method ach_response that returns the information about the return.
11
23
  # @return [String] a string returned by ICheckGateway - external_ach_id
12
24
  def do_send
13
25
  # The response comes back as a | separated list of field values with
@@ -21,6 +33,19 @@ module AchClient
21
33
  if response[0] == 'APPROVED'
22
34
  # Return the confirmation number
23
35
  response[7]
36
+ elsif response[0].include?('DECLINED - Notice of Change')
37
+ return_code, addendum = NOC_RESPONSE_MATCHER.match(response[0]).captures
38
+ # The API error message incorrectly uses the normal correction return codes when the internal correction
39
+ # return codes should be used instead (since the transaction is never forwarded through to the NACHA system)
40
+ # Correcting this involves replacing `C0` with `XZ` (ie C01 becomes XZ1)
41
+ corrected_return_code = "XZ#{return_code.last}"
42
+ raise ICheckGateway::InstantRejectionError.new(
43
+ nacha_return_code: corrected_return_code,
44
+ addendum: addendum,
45
+ transaction: self
46
+ ), response[0]
47
+ elsif response[0].include?('DECLINED - Invalid Routing Number')
48
+ raise ICheckGateway::InstantRejectionError.new(nacha_return_code: 'X13', transaction: self), response[0]
24
49
  else
25
50
  # Don't have a reliable way of getting the error message, so we will
26
51
  # just raise the whole response.
@@ -0,0 +1,33 @@
1
+ module AchClient
2
+ class ICheckGateway
3
+ # ICheckGateway sometimes returns an API error when a valid ACH transaction is sent in a handful of
4
+ # rejection scenarios. This is unusual because most providers will accept the transaction, return an
5
+ # external_ach_id, and then supply the rejection info when you poll for responses at a later date.
6
+ # So far we have observed this happening in the following scenarios:
7
+ # - When an invalid routing number is supplied (X13 - Invalid ACH Routing Number - Entry contains a Receiving DFI
8
+ # Identification or Gateway Identification that is not a valid ACH routing number.)
9
+ # - When there is a Notice of Change for the account number (C01 - ACH Change Code. Incorrect Account Number)
10
+ # - When there is a Notice of Change for the routing number (C02 - ACH Change Code. Incorrect Transit Route)
11
+ # This exception can be caught to handle the API error in the appropriate manner.
12
+ # The NACHA return code inferred from the error message is retrievable from the exception instance as well as any
13
+ # addendum information provided by the API error (ie the correct new account/routing number)
14
+ class InstantRejectionError < RuntimeError
15
+ attr_reader :ach_response
16
+
17
+ def initialize(message = nil, nacha_return_code:, addendum: nil, transaction:)
18
+ super(message)
19
+ return_code = ReturnCodes.find_by(code: nacha_return_code)
20
+ response_args = {
21
+ amount: transaction.amount,
22
+ date: transaction.effective_entry_date,
23
+ return_code: return_code,
24
+ }
25
+ @ach_response = if return_code.correction?
26
+ CorrectedAchResponse.new(**response_args, corrections: addendum)
27
+ else
28
+ ReturnedAchResponse.new(**response_args)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,4 +1,4 @@
1
1
  module AchClient
2
2
  # Increment this when changes are published
3
- VERSION = '1.1.0'
3
+ VERSION = '2.0.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ach_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Cotter
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-03 00:00:00.000000000 Z
11
+ date: 2020-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ach
@@ -262,6 +262,7 @@ files:
262
262
  - ".tool-versions"
263
263
  - ".tool-versions-e"
264
264
  - ".travis.yml"
265
+ - CHANGELOG.md
265
266
  - Gemfile
266
267
  - README.md
267
268
  - Rakefile
@@ -321,6 +322,7 @@ files:
321
322
  - lib/ach_client/providers/soap/i_check_gateway/ach_transaction.rb
322
323
  - lib/ach_client/providers/soap/i_check_gateway/company_info.rb
323
324
  - lib/ach_client/providers/soap/i_check_gateway/i_check_gateway.rb
325
+ - lib/ach_client/providers/soap/i_check_gateway/instant_rejection_error.rb
324
326
  - lib/ach_client/providers/soap/i_check_gateway/response_record_processor.rb
325
327
  - lib/ach_client/providers/soap/i_check_gateway/transaction_type_transformer.rb
326
328
  - lib/ach_client/providers/soap/soap_provider.rb