1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator.routines;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23
24 import org.apache.commons.validator.GenericValidator;
25 import org.apache.commons.validator.routines.checkdigit.CheckDigit;
26 import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class CreditCardValidator implements Serializable {
85
86
87
88
89
90 public static class CreditCardRange {
91 final String low;
92 final String high;
93 final int minLen;
94 final int maxLen;
95 final int lengths[];
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public CreditCardRange(final String low, final String high, final int minLen, final int maxLen) {
115 this.low = low;
116 this.high = high;
117 this.minLen = minLen;
118 this.maxLen = maxLen;
119 this.lengths = null;
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public CreditCardRange(final String low, final String high, final int [] lengths) {
139 this.low = low;
140 this.high = high;
141 this.minLen = -1;
142 this.maxLen = -1;
143 this.lengths = lengths.clone();
144 }
145 }
146
147 private static final long serialVersionUID = 5955978921148959496L;
148
149 private static final int MIN_CC_LENGTH = 12;
150
151 private static final int MAX_CC_LENGTH = 19;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 public static final long NONE = 0;
167
168
169
170
171 public static final long AMEX = 1 << 0;
172
173
174
175
176 public static final long VISA = 1 << 1;
177
178
179
180
181 public static final long MASTERCARD = 1 << 2;
182
183
184
185
186 public static final long DISCOVER = 1 << 3;
187
188
189
190
191 public static final long DINERS = 1 << 4;
192
193
194
195
196
197 public static final long VPAY = 1 << 5;
198
199
200
201
202
203 @Deprecated
204 public static final long MASTERCARD_PRE_OCT2016 = 1 << 6;
205
206
207
208
209 private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT;
210
211
212
213
214
215
216
217
218 public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);
219
220
221
222
223
224
225
226
227
228
229
230 public static final CodeValidator DINERS_VALIDATOR = new CodeValidator("^(30[0-5]\\d{11}|3095\\d{10}|36\\d{12}|3[8-9]\\d{12})$", LUHN_VALIDATOR);
231
232
233
234
235
236
237
238
239 private static final RegexValidator DISCOVER_REGEX = new RegexValidator("^(6011\\d{12,13})$", "^(64[4-9]\\d{13})$", "^(65\\d{14})$", "^(62[2-8]\\d{13})$");
240
241
242 public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);
243
244
245
246
247
248
249
250
251 private static final RegexValidator MASTERCARD_REGEX = new RegexValidator(
252 "^(5[1-5]\\d{14})$",
253
254 "^(2221\\d{12})$",
255 "^(222[2-9]\\d{12})$",
256 "^(22[3-9]\\d{13})$",
257 "^(2[3-6]\\d{14})$",
258 "^(27[01]\\d{13})$",
259 "^(2720\\d{12})$"
260 );
261
262
263 public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator(MASTERCARD_REGEX, LUHN_VALIDATOR);
264
265
266
267
268
269 @Deprecated
270 public static final CodeValidator MASTERCARD_VALIDATOR_PRE_OCT2016 = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);
271
272
273
274
275
276
277
278 public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);
279
280
281
282
283
284
285
286
287 public static final CodeValidator VPAY_VALIDATOR = new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR);
288
289
290 static CodeValidator createRangeValidator(final CreditCardRange[] creditCardRanges, final CheckDigit digitCheck) {
291 return new CodeValidator(
292
293 new RegexValidator("(\\d+)") {
294 private static final long serialVersionUID = 1L;
295 private final transient CreditCardRange[] ccr = creditCardRanges.clone();
296
297 @Override
298 public boolean isValid(final String value) {
299 return validate(value) != null;
300 }
301
302 @Override
303 public String[] match(final String value) {
304 return new String[] { validate(value) };
305 }
306
307 @Override
308
309 public String validate(final String value) {
310 if (super.match(value) != null) {
311 final int length = value.length();
312 for (final CreditCardRange range : ccr) {
313 if (validLength(length, range)) {
314 if (range.high == null) {
315 if (value.startsWith(range.low)) {
316 return value;
317 }
318 } else if (range.low.compareTo(value) <= 0
319 &&
320
321 range.high.compareTo(value.substring(0, range.high.length())) >= 0) {
322 return value;
323 }
324 }
325 }
326 }
327 return null;
328 }
329 }, digitCheck);
330 }
331
332
333
334
335
336
337
338
339 public static CreditCardValidator genericCreditCardValidator() {
340 return genericCreditCardValidator(MIN_CC_LENGTH, MAX_CC_LENGTH);
341 }
342
343
344
345
346
347
348
349
350
351 public static CreditCardValidator genericCreditCardValidator(final int length) {
352 return genericCreditCardValidator(length, length);
353 }
354
355
356
357
358
359
360
361
362
363
364 public static CreditCardValidator genericCreditCardValidator(final int minLen, final int maxLen) {
365 return new CreditCardValidator(new CodeValidator[] {new CodeValidator("(\\d+)", minLen, maxLen, LUHN_VALIDATOR)});
366 }
367
368
369 static boolean validLength(final int valueLength, final CreditCardRange range) {
370 if (range.lengths != null) {
371 for (final int length : range.lengths) {
372 if (valueLength == length) {
373 return true;
374 }
375 }
376 return false;
377 }
378 return valueLength >= range.minLen && valueLength <= range.maxLen;
379 }
380
381
382
383
384 private final List<CodeValidator> cardTypes = new ArrayList<>();
385
386
387
388
389
390
391 public CreditCardValidator() {
392 this(AMEX + VISA + MASTERCARD + DISCOVER);
393 }
394
395
396
397
398
399 public CreditCardValidator(final CodeValidator[] creditCardValidators) {
400 if (creditCardValidators == null) {
401 throw new IllegalArgumentException("Card validators are missing");
402 }
403 Collections.addAll(cardTypes, creditCardValidators);
404 }
405
406
407
408
409
410
411
412
413
414
415
416 public CreditCardValidator(final CodeValidator[] creditCardValidators, final CreditCardRange[] creditCardRanges) {
417 if (creditCardValidators == null) {
418 throw new IllegalArgumentException("Card validators are missing");
419 }
420 if (creditCardRanges == null) {
421 throw new IllegalArgumentException("Card ranges are missing");
422 }
423 Collections.addAll(cardTypes, creditCardValidators);
424 Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
425 }
426
427
428
429
430
431
432 public CreditCardValidator(final CreditCardRange[] creditCardRanges) {
433 if (creditCardRanges == null) {
434 throw new IllegalArgumentException("Card ranges are missing");
435 }
436 Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
437 }
438
439
440
441
442
443
444
445 public CreditCardValidator(final long options) {
446 if (isOn(options, VISA)) {
447 this.cardTypes.add(VISA_VALIDATOR);
448 }
449
450 if (isOn(options, VPAY)) {
451 this.cardTypes.add(VPAY_VALIDATOR);
452 }
453
454 if (isOn(options, AMEX)) {
455 this.cardTypes.add(AMEX_VALIDATOR);
456 }
457
458 if (isOn(options, MASTERCARD)) {
459 this.cardTypes.add(MASTERCARD_VALIDATOR);
460 }
461
462 if (isOn(options, MASTERCARD_PRE_OCT2016)) {
463 this.cardTypes.add(MASTERCARD_VALIDATOR_PRE_OCT2016);
464 }
465
466 if (isOn(options, DISCOVER)) {
467 this.cardTypes.add(DISCOVER_VALIDATOR);
468 }
469
470 if (isOn(options, DINERS)) {
471 this.cardTypes.add(DINERS_VALIDATOR);
472 }
473 }
474
475
476
477
478
479
480
481
482
483
484 private boolean isOn(final long options, final long flag) {
485 return (options & flag) > 0;
486 }
487
488
489
490
491
492
493 public boolean isValid(final String card) {
494 if (GenericValidator.isBlankOrNull(card)) {
495 return false;
496 }
497 for (final CodeValidator cardType : cardTypes) {
498 if (cardType.isValid(card)) {
499 return true;
500 }
501 }
502 return false;
503 }
504
505
506
507
508
509
510
511 public Object validate(final String card) {
512 if (GenericValidator.isBlankOrNull(card)) {
513 return null;
514 }
515 Object result = null;
516 for (final CodeValidator cardType : cardTypes) {
517 result = cardType.validate(card);
518 if (result != null) {
519 return result;
520 }
521 }
522 return null;
523
524 }
525
526 }