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;

  18. import java.util.ArrayList;
  19. import java.util.Collection;

  20. import org.apache.commons.validator.util.Flags;

  21. /**
  22.  * Perform credit card validations.
  23.  *
  24.  * <p>
  25.  * By default, all supported card types are allowed.  You can specify which
  26.  * cards should pass validation by configuring the validation options. For
  27.  * example,
  28.  * </p>
  29.  *
  30.  * <pre>
  31.  * <code>CreditCardValidator ccv = new CreditCardValidator(CreditCardValidator.AMEX + CreditCardValidator.VISA);</code>
  32.  * </pre>
  33.  *
  34.  * <p>
  35.  * configures the validator to only pass American Express and Visa cards.
  36.  * If a card type is not directly supported by this class, you can implement
  37.  * the CreditCardType interface and pass an instance into the
  38.  * <code>addAllowedCardType</code> method.
  39.  * </p>
  40.  *
  41.  * <p>
  42.  * For a similar implementation in Perl, reference Sean M. Burke's
  43.  * <a href="http://www.speech.cs.cmu.edu/~sburke/pub/luhn_lib.html">script</a>.
  44.  * More information is also available
  45.  * <a href="http://www.merriampark.com/anatomycc.htm">here</a>.
  46.  * </p>
  47.  *
  48.  * @since 1.1
  49.  * @deprecated Use the new CreditCardValidator in the routines package. This class
  50.  * will be removed in a future release.
  51.  */
  52. // CHECKSTYLE:OFF (deprecated code)
  53. @Deprecated
  54. public class CreditCardValidator {

  55.     private static class Amex implements CreditCardType {
  56.         static final Amex INSTANCE = new Amex();
  57.         private static final String PREFIX = "34,37,";
  58.         @Override
  59.         public boolean matches(final String card) {
  60.             final String prefix2 = card.substring(0, 2) + ",";
  61.             return PREFIX.contains(prefix2) && card.length() == 15;
  62.         }
  63.     }

  64.     /**
  65.      * CreditCardType implementations define how validation is performed
  66.      * for one type/brand of credit card.
  67.      * @since 1.1.2
  68.      */
  69.     public interface CreditCardType {

  70.         /**
  71.          * Returns true if the card number matches this type of credit
  72.          * card.  Note that this method is <strong>not</strong> responsible
  73.          * for analyzing the general form of the card number because
  74.          * <code>CreditCardValidator</code> performs those checks before
  75.          * calling this method.  It is generally only required to valid the
  76.          * length and prefix of the number to determine if it's the correct
  77.          * type.
  78.          * @param card The card number, never null.
  79.          * @return true if the number matches.
  80.          */
  81.         boolean matches(String card);

  82.     }

  83.     private static class Discover implements CreditCardType {
  84.         static final Discover INSTANCE = new Discover();
  85.         private static final String PREFIX = "6011";
  86.         @Override
  87.         public boolean matches(final String card) {
  88.             return card.substring(0, 4).equals(PREFIX) && card.length() == 16;
  89.         }
  90.     }

  91.     private static class Mastercard implements CreditCardType {
  92.         static final Mastercard INSTANCE = new Mastercard();
  93.         private static final String PREFIX = "51,52,53,54,55,";
  94.         @Override
  95.         public boolean matches(final String card) {
  96.             final String prefix2 = card.substring(0, 2) + ",";
  97.             return PREFIX.contains(prefix2) && card.length() == 16;
  98.         }
  99.     }

  100.     /**
  101.      *  Change to support Visa Carte Blue used in France
  102.      *  has been removed - see Bug 35926
  103.      */
  104.     private static class Visa implements CreditCardType {
  105.         static final Visa INSTANCE = new Visa();
  106.         private static final String PREFIX = "4";

  107.         @Override
  108.         public boolean matches(final String card) {
  109.             return card.substring(0, 1).equals(PREFIX) && (card.length() == 13 || card.length() == 16);
  110.         }
  111.     }

  112.     /**
  113.      * Option specifying that no cards are allowed.  This is useful if
  114.      * you want only custom card types to validate so you turn off the
  115.      * default cards with this option.
  116.      * <pre>
  117.      * <code>
  118.      * CreditCardValidator v = new CreditCardValidator(CreditCardValidator.NONE);
  119.      * v.addAllowedCardType(customType);
  120.      * v.isValid(aCardNumber);
  121.      * </code>
  122.      * </pre>
  123.      * @since 1.1.2
  124.      */
  125.     public static final int NONE = 0;

  126.     /**
  127.      * Option specifying that American Express cards are allowed.
  128.      */
  129.     public static final int AMEX = 1 << 0;

  130.     /**
  131.      * Option specifying that Visa cards are allowed.
  132.      */
  133.     public static final int VISA = 1 << 1;

  134.     /**
  135.      * Option specifying that Mastercard cards are allowed.
  136.      */
  137.     public static final int MASTERCARD = 1 << 2;

  138.     /**
  139.      * Option specifying that Discover cards are allowed.
  140.      */
  141.     public static final int DISCOVER = 1 << 3;

  142.     /**
  143.      * The CreditCardTypes that are allowed to pass validation.
  144.      */
  145.     private final Collection<CreditCardType> cardTypes = new ArrayList<>();

  146.     /**
  147.      * Create a new CreditCardValidator with default options.
  148.      */
  149.     public CreditCardValidator() {
  150.         this(AMEX + VISA + MASTERCARD + DISCOVER);
  151.     }

  152.     /**
  153.      * Creates a new CreditCardValidator with the specified options.
  154.      * @param options Pass in
  155.      * CreditCardValidator.VISA + CreditCardValidator.AMEX to specify that
  156.      * those are the only valid card types.
  157.      */
  158.     public CreditCardValidator(final int options) {
  159.         final Flags f = new Flags(options);
  160.         if (f.isOn(VISA)) {
  161.             this.cardTypes.add(Visa.INSTANCE);
  162.         }

  163.         if (f.isOn(AMEX)) {
  164.             this.cardTypes.add(Amex.INSTANCE);
  165.         }

  166.         if (f.isOn(MASTERCARD)) {
  167.             this.cardTypes.add(Mastercard.INSTANCE);
  168.         }

  169.         if (f.isOn(DISCOVER)) {
  170.             this.cardTypes.add(Discover.INSTANCE);
  171.         }
  172.     }

  173.     /**
  174.      * Adds an allowed CreditCardType that participates in the card
  175.      * validation algorithm.
  176.      * @param type The type that is now allowed to pass validation.
  177.      * @since 1.1.2
  178.      */
  179.     public void addAllowedCardType(final CreditCardType type){
  180.         this.cardTypes.add(type);
  181.     }

  182.     /**
  183.      * Checks if the field is a valid credit card number.
  184.      * @param card The card number to validate.
  185.      * @return Whether the card number is valid.
  186.      */
  187.     public boolean isValid(final String card) {
  188.         if (card == null || card.length() < 13 || card.length() > 19) {
  189.             return false;
  190.         }
  191.         if (!this.luhnCheck(card)) {
  192.             return false;
  193.         }
  194.         for (final Object cardType : this.cardTypes) {
  195.             final CreditCardType type = (CreditCardType) cardType;
  196.             if (type.matches(card)) {
  197.                 return true;
  198.             }
  199.         }
  200.         return false;
  201.     }

  202.     /**
  203.      * Checks for a valid credit card number.
  204.      * @param cardNumber Credit Card Number.
  205.      * @return Whether the card number passes the luhnCheck.
  206.      */
  207.     protected boolean luhnCheck(final String cardNumber) {
  208.         // number must be validated as 0..9 numeric first!!
  209.         final int digits = cardNumber.length();
  210.         final int oddOrEven = digits & 1;
  211.         long sum = 0;
  212.         for (int count = 0; count < digits; count++) {
  213.             int digit = 0;
  214.             try {
  215.                 digit = Integer.parseInt(cardNumber.charAt(count) + "");
  216.             } catch (final NumberFormatException e) {
  217.                 return false;
  218.             }
  219.             if ((count & 1 ^ oddOrEven) == 0) { // not
  220.                 digit *= 2;
  221.                 if (digit > 9) {
  222.                     digit -= 9;
  223.                 }
  224.             }
  225.             sum += digit;
  226.         }
  227.         return sum != 0 && sum % 10 == 0;
  228.     }

  229. }