LCOV - code coverage report
Current view: top level - lib/util - credit_card_type.dart (source / functions) Hit Total Coverage
Test: Folly Fields Lines: 40 44 90.9 %
Date: 2023-05-11 23:59:26 Functions: 0 0 -

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

Generated by: LCOV version 1.14