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