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 * --------------------------------------------------------------------------------------- */