CreditCardValidator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.validator.routines;

  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.List;

  22. import org.apache.commons.validator.GenericValidator;
  23. import org.apache.commons.validator.routines.checkdigit.CheckDigit;
  24. import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;

  25. /**
  26.  * Perform credit card validations.
  27.  *
  28.  * <p>
  29.  * By default, AMEX + VISA + MASTERCARD + DISCOVER card types are allowed.  You can specify which
  30.  * cards should pass validation by configuring the validation options. For
  31.  * example,
  32.  * </p>
  33.  *
  34.  * <pre>
  35.  * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
  36.  * </pre>
  37.  *
  38.  * <p>
  39.  * configures the validator to only pass American Express and Visa cards.
  40.  * If a card type is not directly supported by this class, you can create an
  41.  * instance of the {@link CodeValidator} class and pass it to a {@link CreditCardValidator}
  42.  * constructor along with any existing validators. For example:
  43.  * </p>
  44.  *
  45.  * <pre>
  46.  * <code>CreditCardValidator ccv = new CreditCardValidator(
  47.  *     new CodeValidator[] {
  48.  *         CreditCardValidator.AMEX_VALIDATOR,
  49.  *         CreditCardValidator.VISA_VALIDATOR,
  50.  *         new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR) // add VPAY
  51.  * };</code>
  52.  * </pre>
  53.  *
  54.  * <p>
  55.  * Alternatively you can define a validator using the {@link CreditCardRange} class.
  56.  * For example:
  57.  * </p>
  58.  *
  59.  * <pre>
  60.  * <code>CreditCardValidator ccv = new CreditCardValidator(
  61.  *    new CreditCardRange[]{
  62.  *        new CreditCardRange("300", "305", 14, 14), // Diners
  63.  *        new CreditCardRange("3095", null, 14, 14), // Diners
  64.  *        new CreditCardRange("36",   null, 14, 14), // Diners
  65.  *        new CreditCardRange("38",   "39", 14, 14), // Diners
  66.  *        new CreditCardRange("4",    null, new int[] {13, 16}), // VISA
  67.  *    }
  68.  * );
  69.  * </code>
  70.  * </pre>
  71.  * <p>
  72.  * This can be combined with a list of {@code CodeValidator}s
  73.  * </p>
  74.  * <p>
  75.  * More information can be found in Michael Gilleland's essay
  76.  * <a href="http://web.archive.org/web/20120614072656/http://www.merriampark.com/anatomycc.htm">Anatomy of Credit Card Numbers</a>.
  77.  * </p>
  78.  *
  79.  * @since 1.4
  80.  */
  81. public class CreditCardValidator implements Serializable {

  82.     /**
  83.      * Class that represents a credit card range.
  84.      * @since 1.6
  85.      */
  86.     public static class CreditCardRange {
  87.         final String low; // e.g. 34 or 644
  88.         final String high; // e.g. 34 or 65
  89.         final int minLen; // e.g. 16 or -1
  90.         final int maxLen; // e.g. 19 or -1
  91.         final int lengths[]; // e.g. 16,18,19

  92.         /**
  93.          * Create a credit card range specifier for use in validation
  94.          * of the number syntax including the IIN range.
  95.          * <p>
  96.          * The low and high parameters may be shorter than the length
  97.          * of an IIN (currently 6 digits) in which case subsequent digits
  98.          * are ignored and may range from 0-9.
  99.          * </p>
  100.          * <p>
  101.          * The low and high parameters may be different lengths.
  102.          * e.g. Discover "644" and "65".
  103.          * </p>
  104.          * @param low the low digits of the IIN range
  105.          * @param high the high digits of the IIN range
  106.          * @param minLen the minimum length of the entire number
  107.          * @param maxLen the maximum length of the entire number
  108.          */
  109.         public CreditCardRange(final String low, final String high, final int minLen, final int maxLen) {
  110.             this.low = low;
  111.             this.high = high;
  112.             this.minLen = minLen;
  113.             this.maxLen = maxLen;
  114.             this.lengths = null;
  115.         }

  116.         /**
  117.          * Create a credit card range specifier for use in validation
  118.          * of the number syntax including the IIN range.
  119.          * <p>
  120.          * The low and high parameters may be shorter than the length
  121.          * of an IIN (currently 6 digits) in which case subsequent digits
  122.          * are ignored and may range from 0-9.
  123.          * </p>
  124.          * <p>
  125.          * The low and high parameters may be different lengths.
  126.          * e.g. Discover "644" and "65".
  127.          * </p>
  128.          * @param low the low digits of the IIN range
  129.          * @param high the high digits of the IIN range
  130.          * @param lengths array of valid lengths
  131.          */
  132.         public CreditCardRange(final String low, final String high, final int [] lengths) {
  133.             this.low = low;
  134.             this.high = high;
  135.             this.minLen = -1;
  136.             this.maxLen = -1;
  137.             this.lengths = lengths.clone();
  138.         }
  139.     }

  140.     private static final long serialVersionUID = 5955978921148959496L;

  141.     private static final int MIN_CC_LENGTH = 12; // minimum allowed length

  142.     private static final int MAX_CC_LENGTH = 19; // maximum allowed length

  143.     /**
  144.      * Option specifying that no cards are allowed.  This is useful if
  145.      * you want only custom card types to validate so you turn off the
  146.      * default cards with this option.
  147.      *
  148.      * <pre>
  149.      * <code>
  150.      * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
  151.      * v.addAllowedCardType(customType);
  152.      * v.isValid(aCardNumber);
  153.      * </code>
  154.      * </pre>
  155.      */
  156.     public static final long NONE = 0;

  157.     /**
  158.      * Option specifying that American Express cards are allowed.
  159.      */
  160.     public static final long AMEX = 1 << 0;

  161.     /**
  162.      * Option specifying that Visa cards are allowed.
  163.      */
  164.     public static final long VISA = 1 << 1;

  165.     /**
  166.      * Option specifying that Mastercard cards are allowed.
  167.      */
  168.     public static final long MASTERCARD = 1 << 2;

  169.     /**
  170.      * Option specifying that Discover cards are allowed.
  171.      */
  172.     public static final long DISCOVER = 1 << 3; // CHECKSTYLE IGNORE MagicNumber

  173.     /**
  174.      * Option specifying that Diners cards are allowed.
  175.      */
  176.     public static final long DINERS = 1 << 4; // CHECKSTYLE IGNORE MagicNumber

  177.     /**
  178.      * Option specifying that VPay (Visa) cards are allowed.
  179.      * @since 1.5.0
  180.      */
  181.     public static final long VPAY = 1 << 5; // CHECKSTYLE IGNORE MagicNumber

  182.     /**
  183.      * Option specifying that Mastercard cards (pre Oct 2016 only) are allowed.
  184.      * @deprecated for use until Oct 2016 only
  185.      */
  186.     @Deprecated
  187.     public static final long MASTERCARD_PRE_OCT2016 = 1 << 6; // CHECKSTYLE IGNORE MagicNumber

  188.     /**
  189.      * Luhn checkdigit validator for the card numbers.
  190.      */
  191.     private static final CheckDigit LUHN_VALIDATOR = LuhnCheckDigit.LUHN_CHECK_DIGIT;

  192.     /**
  193.      * American Express (Amex) Card Validator
  194.      * <ul>
  195.      * <li>34xxxx (15)</li>
  196.      * <li>37xxxx (15)</li>
  197.      * </ul>
  198.      */
  199.     public static final CodeValidator AMEX_VALIDATOR = new CodeValidator("^(3[47]\\d{13})$", LUHN_VALIDATOR);

  200.     /**
  201.      * Diners Card Validator
  202.      * <ul>
  203.      * <li>300xxx - 305xxx (14)</li>
  204.      * <li>3095xx (14)</li>
  205.      * <li>36xxxx (14)</li>
  206.      * <li>38xxxx (14)</li>
  207.      * <li>39xxxx (14)</li>
  208.      * </ul>
  209.      */
  210.     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);

  211.     /**
  212.      * Discover Card regular expressions
  213.      * <ul>
  214.      * <li>6011xx (16)</li>
  215.      * <li>644xxx - 65xxxx (16)</li>
  216.      * </ul>
  217.      */
  218.     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})$");

  219.     /** Discover Card Validator */
  220.     public static final CodeValidator DISCOVER_VALIDATOR = new CodeValidator(DISCOVER_REGEX, LUHN_VALIDATOR);

  221.     /**
  222.      * Mastercard regular expressions
  223.      * <ul>
  224.      * <li>2221xx - 2720xx (16)</li>
  225.      * <li>51xxx - 55xxx (16)</li>
  226.      * </ul>
  227.      */
  228.     private static final RegexValidator MASTERCARD_REGEX = new RegexValidator(
  229.             "^(5[1-5]\\d{14})$",   // 51 - 55 (pre Oct 2016)
  230.             // valid from October 2016
  231.             "^(2221\\d{12})$",     // 222100 - 222199
  232.             "^(222[2-9]\\d{12})$", // 222200 - 222999
  233.             "^(22[3-9]\\d{13})$",  // 223000 - 229999
  234.             "^(2[3-6]\\d{14})$",   // 230000 - 269999
  235.             "^(27[01]\\d{13})$",   // 270000 - 271999
  236.             "^(2720\\d{12})$"      // 272000 - 272099
  237.         );

  238.     /** Mastercard Card Validator */
  239.     public static final CodeValidator MASTERCARD_VALIDATOR = new CodeValidator(MASTERCARD_REGEX, LUHN_VALIDATOR);

  240.     /**
  241.      * Mastercard Card Validator (pre Oct 2016)
  242.      * @deprecated for use until Oct 2016 only
  243.      */
  244.     @Deprecated
  245.     public static final CodeValidator MASTERCARD_VALIDATOR_PRE_OCT2016 = new CodeValidator("^(5[1-5]\\d{14})$", LUHN_VALIDATOR);

  246.     /**
  247.      * Visa Card Validator
  248.      * <p>
  249.      * 4xxxxx (13 or 16)
  250.      * </p>
  251.      */
  252.     public static final CodeValidator VISA_VALIDATOR = new CodeValidator("^(4)(\\d{12}|\\d{15})$", LUHN_VALIDATOR);

  253.     /**
  254.      * VPay (Visa) Card Validator
  255.      * <p>
  256.      * 4xxxxx (13-19)
  257.      * </p>
  258.      * @since 1.5.0
  259.      */
  260.     public static final CodeValidator VPAY_VALIDATOR = new CodeValidator("^(4)(\\d{12,18})$", LUHN_VALIDATOR);

  261.     // package protected for unit test access
  262.     static CodeValidator createRangeValidator(final CreditCardRange[] creditCardRanges, final CheckDigit digitCheck) {
  263.         return new CodeValidator(
  264.                 // must be numeric (rest of validation is done later)
  265.                 new RegexValidator("(\\d+)") {
  266.                     private static final long serialVersionUID = 1L;
  267.                     private final transient CreditCardRange[] ccr = creditCardRanges.clone();

  268.                     @Override
  269.                     public boolean isValid(final String value) {
  270.                         return validate(value) != null;
  271.                     }

  272.                     @Override
  273.                     public String[] match(final String value) {
  274.                         return new String[] { validate(value) };
  275.                     }

  276.                     @Override
  277.                     // must return full string
  278.                     public String validate(final String value) {
  279.                         if (super.match(value) != null) {
  280.                             final int length = value.length();
  281.                             for (final CreditCardRange range : ccr) {
  282.                                 if (validLength(length, range)) {
  283.                                     if (range.high == null) { // single prefix only
  284.                                         if (value.startsWith(range.low)) {
  285.                                             return value;
  286.                                         }
  287.                                     } else if (range.low.compareTo(value) <= 0 // no need to trim value here
  288.                                             &&
  289.                                     // here we have to ignore digits beyond the prefix
  290.                                             range.high.compareTo(value.substring(0, range.high.length())) >= 0) {
  291.                                         return value;
  292.                                     }
  293.                                 }
  294.                             }
  295.                         }
  296.                         return null;
  297.                     }
  298.                 }, digitCheck);
  299.     }

  300.     /**
  301.      * Creates a new generic CreditCardValidator which validates the syntax and check digit only.
  302.      * Does not check the Issuer Identification Number (IIN)
  303.      *
  304.      * @return the validator
  305.      * @since 1.6
  306.      */
  307.     public static CreditCardValidator genericCreditCardValidator() {
  308.         return genericCreditCardValidator(MIN_CC_LENGTH, MAX_CC_LENGTH);
  309.     }

  310.     /**
  311.      * Creates a new generic CreditCardValidator which validates the syntax and check digit only.
  312.      * Does not check the Issuer Identification Number (IIN)
  313.      *
  314.      * @param length exact length
  315.      * @return the validator
  316.      * @since 1.6
  317.      */
  318.     public static CreditCardValidator genericCreditCardValidator(final int length) {
  319.         return genericCreditCardValidator(length, length);
  320.     }

  321.     /**
  322.      * Creates a new generic CreditCardValidator which validates the syntax and check digit only.
  323.      * Does not check the Issuer Identification Number (IIN)
  324.      *
  325.      * @param minLen minimum allowed length
  326.      * @param maxLen maximum allowed length
  327.      * @return the validator
  328.      * @since 1.6
  329.      */
  330.     public static CreditCardValidator genericCreditCardValidator(final int minLen, final int maxLen) {
  331.         return new CreditCardValidator(new CodeValidator[] {new CodeValidator("(\\d+)", minLen, maxLen, LUHN_VALIDATOR)});
  332.     }

  333.     // package protected for unit test access
  334.     static boolean validLength(final int valueLength, final CreditCardRange range) {
  335.         if (range.lengths != null) {
  336.             for (final int length : range.lengths) {
  337.                 if (valueLength == length) {
  338.                     return true;
  339.                 }
  340.             }
  341.             return false;
  342.         }
  343.         return valueLength >= range.minLen && valueLength <= range.maxLen;
  344.     }

  345.     /**
  346.      * The CreditCardTypes that are allowed to pass validation.
  347.      */
  348.     private final List<CodeValidator> cardTypes = new ArrayList<>();

  349.     /**
  350.      * Constructs a new CreditCardValidator with default options.
  351.      * The default options are:
  352.      * AMEX, VISA, MASTERCARD and DISCOVER
  353.      */
  354.     public CreditCardValidator() {
  355.         this(AMEX + VISA + MASTERCARD + DISCOVER);
  356.     }

  357.     /**
  358.      * Constructs a new CreditCardValidator with the specified {@link CodeValidator}s.
  359.      * @param creditCardValidators Set of valid code validators
  360.      */
  361.     public CreditCardValidator(final CodeValidator[] creditCardValidators) {
  362.         if (creditCardValidators == null) {
  363.             throw new IllegalArgumentException("Card validators are missing");
  364.         }
  365.         Collections.addAll(cardTypes, creditCardValidators);
  366.     }

  367.     /**
  368.      * Constructs a new CreditCardValidator with the specified {@link CodeValidator}s
  369.      * and {@link CreditCardRange}s.
  370.      * <p>
  371.      * This can be used to combine predefined validators such as {@link #MASTERCARD_VALIDATOR}
  372.      * with additional validators using the simpler {@link CreditCardRange}s.
  373.      * @param creditCardValidators Set of valid code validators
  374.      * @param creditCardRanges Set of valid code validators
  375.      * @since 1.6
  376.      */
  377.     public CreditCardValidator(final CodeValidator[] creditCardValidators, final CreditCardRange[] creditCardRanges) {
  378.         if (creditCardValidators == null) {
  379.             throw new IllegalArgumentException("Card validators are missing");
  380.         }
  381.         if (creditCardRanges == null) {
  382.             throw new IllegalArgumentException("Card ranges are missing");
  383.         }
  384.         Collections.addAll(cardTypes, creditCardValidators);
  385.         Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
  386.     }

  387.     /**
  388.      * Constructs a new CreditCardValidator with the specified {@link CreditCardRange}s.
  389.      * @param creditCardRanges Set of valid code validators
  390.      * @since 1.6
  391.      */
  392.     public CreditCardValidator(final CreditCardRange[] creditCardRanges) {
  393.         if (creditCardRanges == null) {
  394.             throw new IllegalArgumentException("Card ranges are missing");
  395.         }
  396.         Collections.addAll(cardTypes, createRangeValidator(creditCardRanges, LUHN_VALIDATOR));
  397.     }

  398.     /**
  399.      * Constructs a new CreditCardValidator with the specified options.
  400.      * @param options Pass in
  401.      * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
  402.      * those are the only valid card types.
  403.      */
  404.     public CreditCardValidator(final long options) {
  405.         if (isOn(options, VISA)) {
  406.             this.cardTypes.add(VISA_VALIDATOR);
  407.         }

  408.         if (isOn(options, VPAY)) {
  409.             this.cardTypes.add(VPAY_VALIDATOR);
  410.         }

  411.         if (isOn(options, AMEX)) {
  412.             this.cardTypes.add(AMEX_VALIDATOR);
  413.         }

  414.         if (isOn(options, MASTERCARD)) {
  415.             this.cardTypes.add(MASTERCARD_VALIDATOR);
  416.         }

  417.         if (isOn(options, MASTERCARD_PRE_OCT2016)) {
  418.             this.cardTypes.add(MASTERCARD_VALIDATOR_PRE_OCT2016);
  419.         }

  420.         if (isOn(options, DISCOVER)) {
  421.             this.cardTypes.add(DISCOVER_VALIDATOR);
  422.         }

  423.         if (isOn(options, DINERS)) {
  424.             this.cardTypes.add(DINERS_VALIDATOR);
  425.         }
  426.     }

  427.     /**
  428.      * Tests whether the given flag is on.  If the flag is not a power of 2
  429.      * (ie. 3) this tests whether the combination of flags is on.
  430.      *
  431.      * @param options The options specified.
  432.      * @param flag Flag value to check.
  433.      *
  434.      * @return whether the specified flag value is on.
  435.      */
  436.     private boolean isOn(final long options, final long flag) {
  437.         return (options & flag) > 0;
  438.     }

  439.     /**
  440.      * Checks if the field is a valid credit card number.
  441.      * @param card The card number to validate.
  442.      * @return Whether the card number is valid.
  443.      */
  444.     public boolean isValid(final String card) {
  445.         if (GenericValidator.isBlankOrNull(card)) {
  446.             return false;
  447.         }
  448.         for (final CodeValidator cardType : cardTypes) {
  449.             if (cardType.isValid(card)) {
  450.                 return true;
  451.             }
  452.         }
  453.         return false;
  454.     }

  455.     /**
  456.      * Checks if the field is a valid credit card number.
  457.      * @param card The card number to validate.
  458.      * @return The card number if valid or {@code null}
  459.      * if invalid.
  460.      */
  461.     public Object validate(final String card) {
  462.         if (GenericValidator.isBlankOrNull(card)) {
  463.             return null;
  464.         }
  465.         Object result = null;
  466.         for (final CodeValidator cardType : cardTypes) {
  467.             result = cardType.validate(card);
  468.             if (result != null) {
  469.                 return result;
  470.             }
  471.         }
  472.         return null;

  473.     }

  474. }