View Javadoc
1   /* ---------------------------------------------------------------------------------------
2    * Class:  com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
3    * Date:   2026/11/07
4    * ---------------------------------------------------------------------------------------
5    * Copyright (c) 2026 Carda Technologies, LLC.
6    *
7    * SPDX-License-Identifier: Apache-2.0
8    * ---------------------------------------------------------------------------------------
9    *
10   * Licensed under the Apache License, Version 2.0 (the "License");
11   * you may not use this file except in compliance with the License.
12   * You may obtain a copy of the License at:
13   *
14   *     https://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing, software
17   * distributed under the License is distributed on an "AS IS" BASIS,
18   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   * See the License for the specific language governing permissions and
20   * limitations under the License.
21   * ---------------------------------------------------------------------------------------
22   */
23  package com.cardatechnologies.utils.validators.abaroutevalidator;
24  
25  //~--- non-JDK imports --------------------------------------------------------
26  
27  import com.cardatechnologies.utils.validators.abaroutevalidator.exceptions.AbaRouteValidationException;
28  
29  /**
30   * <b>Description:</b><br>
31   *     This class is used to validate a ABA Routing Transit Number.
32   *
33   * @author     Daniel Carda
34   * <br>
35   * <br><b>Maintenance History:</b>
36   * <br>
37  <pre>
38      yyyy mm dd  Who                       Description
39      ----------  ------------------------  ----------------------------------------------------
40      2015/02/11  Daniel Carda              Initial Module Creation...
41      2020/12/23  Daniel Carda              Started using and ENum for error content.
42      2020/12/24  Daniel Carda              Optimized algorithms.
43      2021/08/01  Daniel Carda              Improved Class Javadocs
44      2026/09/17  Daniel Carda              Updated code.  New Class Header.
45  </pre>
46   * <hr>
47   */
48  public final class AbaRouteValidator {
49  
50      private static final int FED_NUMBER_GOV_0        =  0;
51      private static final int FED_NUMBER_GOV_12       = 12;
52      private static final int FED_NUMBER_THRIFT_21    = 21;
53      private static final int FED_NUMBER_THRIFT_32    = 32;
54      private static final int FED_NUMBER_CLEARING_61  = 61;
55      private static final int FED_NUMBER_CLEARNING_72 = 72;
56      private static final int FED_NUMBER_TRAVELERS_80 = 80;
57  
58      private static final int ABA_LENGTH = 9;
59      private static final int[] CHECKSUM_WEIGHTS = { 3, 7, 1 };
60  
61      private AbaRouteValidator() {
62          // Utility class.
63      }
64  
65      /**
66       * Method: validate
67       * <p>
68       * <b>Description:</b><br>
69       * Validates an ABA Routing Transit Number.
70       *
71       * @param routingNumber
72       *        The ABA Routing Transit Number to validate.
73       *
74       * @return true if the routing number passes all validation checks;
75       *         false if the checksum is invalid.
76       *
77       * @throws AbaRouteValidationException
78       *         If the routing number is null, blank, has an invalid length,
79       *         contains non-ASCII digits, or has an invalid Federal Reserve prefix.
80       */
81  
82      public static boolean validate(final String routingNumber)
83              throws AbaRouteValidationException {
84  
85          if (routingNumber == null) {
86              throw new AbaRouteValidationException(
87                      ErrorCodes.ABA_1000);
88          }
89  
90          if (routingNumber.isBlank()) {
91              throw new AbaRouteValidationException(ErrorCodes.ABA_1001);
92          }
93  
94          if (routingNumber.length() != ABA_LENGTH) {
95              if (routingNumber.length() < ABA_LENGTH) {
96                  throw new AbaRouteValidationException(ErrorCodes.ABA_1002);
97              }
98  
99              throw new AbaRouteValidationException(ErrorCodes.ABA_1003);
100         }
101 
102         if (!isAsciiDigits(routingNumber)) {
103             throw new AbaRouteValidationException(ErrorCodes.ABA_1004);
104         }
105 
106         validateFedNumber(routingNumber);
107 
108         return validateAbaNumberChecksum(routingNumber);
109     }
110 
111     /**
112      * Method: validateAbaNumberChecksum
113      * <p>
114      * <b>Description:</b><br>
115      * Validates the ABA routing number checksum.
116      *
117      * @param routingNumber
118      *        The nine-digit ABA Routing Transit Number to check.
119      *
120      * @return true if the checksum is valid; false otherwise.
121      */
122     private static boolean validateAbaNumberChecksum(
123             final String routingNumber) {
124         int checksum = 0;
125         for (int i = 0; i < ABA_LENGTH; i++) {
126             checksum +=
127                     (routingNumber.charAt(i) - '0')
128                             * CHECKSUM_WEIGHTS[i % CHECKSUM_WEIGHTS.length];
129         }
130 
131         return checksum % 10 == 0;
132     }
133 
134     /**
135      * Method: isAsciiDigits
136      * <p>
137      * <b>Description:</b><br>
138      * Determines whether every character in the supplied string
139      * is an ASCII digit.
140      *
141      * @param value
142      *        The string to examine.
143      *
144      * @return true if every character is an ASCII digit; false otherwise.
145      */
146     private static boolean isAsciiDigits(final String value) {
147 
148         for (int i = 0; i < value.length(); i++) {
149             final char c = value.charAt(i);
150 
151             if (c < '0' || c > '9') {
152                 return false;
153             }
154         }
155 
156         return true;
157     }
158 
159     /**
160      * Method: validateFedNumber
161      * <p>
162      * <b>Description:</b><br>
163      * Validates the Federal Reserve routing prefix represented
164      * by the first two digits of the routing number.
165      *
166      * @param routingNumber
167      *        The nine-digit ABA Routing Transit Number.
168      *
169      * @throws AbaRouteValidationException
170      *         If the Federal Reserve prefix is invalid.
171      */
172     private static void validateFedNumber(final String routingNumber)
173             throws AbaRouteValidationException {
174 
175         final int fedNumber =
176                 (routingNumber.charAt(0) - '0') * 10
177                         + (routingNumber.charAt(1) - '0');
178 
179         // https://en.wikipedia.org/wiki/Routing_transit_number
180         // The first two digits of the nine digit RTN must be in the ranges
181         // 00 through 12,  00=Gobermint, and 12 Federal Reserve Banks
182         // 21 through 32,  Thrift Institutions
183         // 61 through 72,  Payment Processors and Clearing Houses
184         // 80.             Traveler's checks
185         if (!((fedNumber >= FED_NUMBER_GOV_0 && fedNumber <= FED_NUMBER_GOV_12)
186                 || (fedNumber >= FED_NUMBER_THRIFT_21 && fedNumber <= FED_NUMBER_THRIFT_32)
187                 || (fedNumber >= FED_NUMBER_CLEARING_61 && fedNumber <= FED_NUMBER_CLEARNING_72)
188                 || fedNumber == FED_NUMBER_TRAVELERS_80)) {
189             throw new AbaRouteValidationException(ErrorCodes.ABA_1005);
190         }
191     }
192 }
193 /* ---------------------------------------------------------------------------------------
194  * Class:  com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
195  * Date:   2026/11/07
196  * --------------------------------------------------------------------------------------- */