LCOV - code coverage report
Current view: top level - lib/util - credit_card_type.dart (source / functions) Hit Total Coverage
Test: Folly Fields Lines: 40 42 95.2 %
Date: 2023-05-18 02:05:34 Functions: 0 0 -

          Line data    Source code
       1             : // ignore_for_file: always_specify_types
       2             : 
       3             : ///
       4             : ///
       5             : /// https://github.com/braintree/credit-card-type/blob/main/src/lib/card-types.ts
       6             : ///
       7             : enum CreditCardType {
       8             :   /// Visa
       9             :   visa(
      10             :     brand: 'Visa',
      11             :     mask: '#### #### #### #### ###',
      12             :     lengths: [16, 18, 19],
      13             :     code: CreditCardCode('CVV', [3]),
      14             :     patterns: {
      15             :       Range(4),
      16             :     },
      17             :   ),
      18             : 
      19             :   /// Mastercard
      20             :   mastercard(
      21             :     brand: 'Master',
      22             :     mask: '#### #### #### ####',
      23             :     lengths: [16],
      24             :     code: CreditCardCode('CVC', [3]),
      25             :     patterns: {
      26             :       Range(51, 55),
      27             :       Range(2221, 2229),
      28             :       Range(223, 229),
      29             :       Range(23, 26),
      30             :       Range(270, 271),
      31             :       Range(2720),
      32             :     },
      33             :     extraBrands: ['mastercard'],
      34             :   ),
      35             : 
      36             :   /// American Express
      37             :   amex(
      38             :     brand: 'Amex',
      39             :     mask: '#### ###### #####',
      40             :     lengths: [15],
      41             :     code: CreditCardCode('CID', [4]),
      42             :     patterns: {
      43             :       Range(34),
      44             :       Range(37),
      45             :     },
      46             :     extraBrands: ['americanexpress'],
      47             :   ),
      48             : 
      49             :   /// Diners Club
      50             :   dinersclub(
      51             :     brand: 'Diners',
      52             :     mask: '#### ###### #########',
      53             :     lengths: [14, 16, 19],
      54             :     code: CreditCardCode('CVV', [3]),
      55             :     patterns: {
      56             :       Range(300, 305),
      57             :       Range(36),
      58             :       Range(38),
      59             :       Range(39),
      60             :     },
      61             :     extraBrands: ['dinersclub'],
      62             :   ),
      63             : 
      64             :   /// Discover
      65             :   discover(
      66             :     brand: 'Discover',
      67             :     mask: '#### #### #### #### ###',
      68             :     lengths: [16, 19],
      69             :     code: CreditCardCode('CID', [3]),
      70             :     patterns: {
      71             :       Range(6011),
      72             :       Range(644, 649),
      73             :       Range(65),
      74             :     },
      75             :   ),
      76             : 
      77             :   /// JCB
      78             :   jcb(
      79             :     brand: 'JCB',
      80             :     mask: '#### #### #### #### ###',
      81             :     lengths: [16, 17, 18, 19],
      82             :     code: CreditCardCode('CVV', [3]),
      83             :     patterns: {
      84             :       Range(2131),
      85             :       Range(1800),
      86             :       Range(3528, 3589),
      87             :     },
      88             :   ),
      89             : 
      90             :   /// UnionPay
      91             :   unionpay(
      92             :     brand: 'UnionPay',
      93             :     mask: '#### #### #### #### ###',
      94             :     lengths: [14, 15, 16, 17, 18, 19],
      95             :     code: CreditCardCode('CVN', [3]),
      96             :     patterns: {
      97             :       Range(620),
      98             :       Range(62100, 62182),
      99             :       Range(62184, 62187),
     100             :       Range(62185, 62197),
     101             :       Range(62200, 62205),
     102             :       Range(622010, 622999),
     103             :       Range(622018),
     104             :       Range(62207, 62209),
     105             :       Range(623, 626),
     106             :       Range(6270),
     107             :       Range(6272),
     108             :       Range(6276),
     109             :       Range(627700, 627779),
     110             :       Range(627781, 627799),
     111             :       Range(6282, 6289),
     112             :       Range(6291),
     113             :       Range(6292),
     114             :       Range(810),
     115             :       Range(8110, 8131),
     116             :       Range(8132, 8151),
     117             :       Range(8152, 8163),
     118             :       Range(8164, 8171),
     119             :     },
     120             :     checkLuhn: false,
     121             :   ),
     122             : 
     123             :   /// Maestro
     124             :   maestro(
     125             :     brand: 'Maestro',
     126             :     mask: '#### #### #### #### ###',
     127             :     lengths: [12, 13, 14, 15, 16, 17, 18, 19],
     128             :     code: CreditCardCode('CVC', [3]),
     129             :     patterns: {
     130             :       Range(493698),
     131             :       Range(500000, 504174),
     132             :       Range(504176, 506698),
     133             :       Range(506779, 508999),
     134             :       Range(56, 59),
     135             :       Range(63),
     136             :       Range(67),
     137             :       Range(6),
     138             :     },
     139             :   ),
     140             : 
     141             :   /// Elo
     142             :   elo(
     143             :     brand: 'Elo',
     144             :     mask: '#### #### #### ####',
     145             :     lengths: [16],
     146             :     code: CreditCardCode('CVE', [3]),
     147             :     patterns: {
     148             :       Range(401178),
     149             :       Range(401179),
     150             :       Range(438935),
     151             :       Range(457631),
     152             :       Range(457632),
     153             :       Range(431274),
     154             :       Range(451416),
     155             :       Range(457393),
     156             :       Range(504175),
     157             :       Range(506699, 506778),
     158             :       Range(509000, 509999),
     159             :       Range(627780),
     160             :       Range(636297),
     161             :       Range(636368),
     162             :       Range(650031, 650033),
     163             :       Range(650035, 650051),
     164             :       Range(650405, 650439),
     165             :       Range(650485, 650538),
     166             :       Range(650541, 650598),
     167             :       Range(650700, 650718),
     168             :       Range(650720, 650727),
     169             :       Range(650901, 650978),
     170             :       Range(651652, 651679),
     171             :       Range(655000, 655019),
     172             :       Range(655021, 655058),
     173             :     },
     174             :   ),
     175             : 
     176             :   /// Mir
     177             :   mir(
     178             :     brand: 'Mir',
     179             :     mask: '#### #### #### #### ###',
     180             :     lengths: [16, 17, 18, 19],
     181             :     code: CreditCardCode('CVP2', [3]),
     182             :     patterns: {
     183             :       Range(2200, 2204),
     184             :     },
     185             :   ),
     186             : 
     187             :   /// Hiper
     188             :   hiper(
     189             :     brand: 'Hiper',
     190             :     mask: '#### #### #### ####',
     191             :     lengths: [16],
     192             :     code: CreditCardCode('CVC', [3]),
     193             :     patterns: {
     194             :       Range(637095),
     195             :       Range(63737423),
     196             :       Range(63743358),
     197             :       Range(637568),
     198             :       Range(637599),
     199             :       Range(637609),
     200             :       Range(637612),
     201             :     },
     202             :   ),
     203             : 
     204             :   /// Hipercard
     205             :   hipercard(
     206             :     brand: 'Hipercard',
     207             :     mask: '#### #### #### ####',
     208             :     lengths: [16],
     209             :     code: CreditCardCode('CVC', [3]),
     210             :     patterns: {
     211             :       Range(606282),
     212             :     },
     213             :   ),
     214             : 
     215             :   /// Unknown
     216             :   unknown(
     217             :     brand: 'Unknown',
     218             :     mask: '#### #### #### #### ###',
     219             :     lengths: [12, 13, 14, 15, 16, 17, 18, 19],
     220             :     code: CreditCardCode('CVV', [3, 4]),
     221             :     patterns: {},
     222             :   );
     223             : 
     224             :   ///
     225             :   ///
     226             :   ///
     227             :   final String brand;
     228             :   final String mask;
     229             :   final List<int> lengths;
     230             :   final CreditCardCode code;
     231             :   final Set<Range> patterns;
     232             :   final bool checkLuhn;
     233             :   final List<String> extraBrands;
     234             : 
     235             :   ///
     236             :   ///
     237             :   ///
     238             :   const CreditCardType({
     239             :     required this.brand,
     240             :     required this.mask,
     241             :     required this.lengths,
     242             :     required this.code,
     243             :     required this.patterns,
     244             :     this.checkLuhn = true,
     245             :     this.extraBrands = const <String>[],
     246             :   });
     247             : 
     248             :   ///
     249             :   ///
     250             :   ///
     251           0 :   bool validLength(String ccNum) => lengths.contains(clearNum(ccNum).length);
     252             : 
     253             :   ///
     254             :   ///
     255             :   ///
     256           1 :   bool validNumber(String ccNum) {
     257           1 :     if (!checkLuhn) {
     258             :       return true;
     259             :     }
     260             : 
     261           2 :     if (ccNum.length < 2) {
     262             :       return false;
     263             :     }
     264             : 
     265           2 :     ccNum = ccNum.replaceAll(RegExp(r'\D'), '');
     266             : 
     267           2 :     int mod = ccNum.length % 2;
     268             :     int sum = 0;
     269             : 
     270             :     try {
     271           4 :       for (int pos = ccNum.length - 1; pos >= 0; pos--) {
     272           2 :         int digit = int.parse(ccNum[pos]);
     273             : 
     274           2 :         if (pos % 2 == mod) {
     275           1 :           digit *= 2;
     276           1 :           if (digit > 9) {
     277           1 :             digit -= 9;
     278             :           }
     279             :         }
     280             : 
     281           1 :         sum += digit;
     282             :       }
     283             : 
     284           2 :       return sum % 10 == 0;
     285           0 :     } on Exception {
     286             :       return false;
     287             :     }
     288             :   }
     289             : 
     290             :   ///
     291             :   ///
     292             :   ///
     293           1 :   bool cvvCheck(String cvv) =>
     294           4 :       code.size.contains(cvv.length) &&
     295           5 :       code.size.contains(clearNum(cvv).length);
     296             : 
     297             :   ///
     298             :   ///
     299             :   ///
     300           3 :   static String clearNum(String ccNum) => ccNum.replaceAll(RegExp(r'\D'), '');
     301             : 
     302             :   ///
     303             :   ///
     304             :   ///
     305           1 :   static CreditCardType detectType(String ccNum) {
     306           1 :     ccNum = clearNum(ccNum);
     307             : 
     308           2 :     for (final CreditCardType type in CreditCardType.values) {
     309           2 :       for (final Range range in type.patterns) {
     310           1 :         if (range.isValid(ccNum)) {
     311             :           return type;
     312             :         }
     313             :       }
     314             :     }
     315             : 
     316             :     return CreditCardType.unknown;
     317             :   }
     318             : 
     319             :   ///
     320             :   ///
     321             :   ///
     322           1 :   static CreditCardType parse(String? value) =>
     323             :       // ignore: prefer-enums-by-name
     324           1 :       CreditCardType.values.firstWhere(
     325           1 :         (CreditCardType type) {
     326           3 :           String? newValue = value?.replaceAll(RegExp(r'\s'), '').toLowerCase();
     327             : 
     328           3 :           return type.brand.toLowerCase() == newValue ||
     329           2 :               type.extraBrands.fold<bool>(
     330             :                 false,
     331           1 :                 (bool previous, String element) =>
     332           2 :                     previous || element.toLowerCase() == newValue,
     333             :               );
     334             :         },
     335           1 :         orElse: () => CreditCardType.unknown,
     336             :       );
     337             : }
     338             : 
     339             : ///
     340             : ///
     341             : ///
     342             : class CreditCardCode {
     343             :   final String name;
     344             :   final List<int> size;
     345             : 
     346             :   ///
     347             :   ///
     348             :   ///
     349           2 :   const CreditCardCode(this.name, this.size);
     350             : }
     351             : 
     352             : ///
     353             : ///
     354             : ///
     355             : class Range {
     356             :   final int initialValue;
     357             :   final int? finalValue;
     358             : 
     359           2 :   const Range(this.initialValue, [this.finalValue]);
     360             : 
     361             :   ///
     362             :   ///
     363             :   ///
     364           1 :   bool isValid(String ccNum) {
     365           3 :     int qtd = initialValue.toString().length;
     366             : 
     367           2 :     if (ccNum.length < qtd) {
     368             :       return false;
     369             :     }
     370             : 
     371           2 :     int? ccInt = int.tryParse(ccNum.substring(0, qtd));
     372             : 
     373             :     if (ccInt == null) {
     374             :       return false;
     375             :     }
     376             : 
     377           1 :     return finalValue == null
     378           2 :         ? ccInt == initialValue
     379           4 :         : ccInt >= initialValue && ccInt <= finalValue!;
     380             :   }
     381             : }

Generated by: LCOV version 1.14