Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/faker/providers/credit_card/__init__.py: 46%

67 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from collections import OrderedDict 

2from typing import Dict, List, Optional, TypeVar 

3 

4from ...typing import DateParseType 

5from .. import BaseProvider 

6 

7localized = True 

8 

9CardType = TypeVar("CardType", "CreditCard", str) 

10 

11 

12class CreditCard: 

13 def __init__( 

14 self, 

15 name: str, 

16 prefixes: List[str], 

17 length: int = 16, 

18 security_code: str = "CVC", 

19 security_code_length: int = 3, 

20 ) -> None: 

21 self.name = name 

22 self.prefixes = prefixes 

23 self.length = length 

24 self.security_code = security_code 

25 self.security_code_length = security_code_length 

26 

27 

28class Provider(BaseProvider): 

29 """Implement default credit card provider for Faker. 

30 

31 For all methods that take ``card_type`` as an argument, a random card type 

32 will be used if the supplied value is ``None``. The list of valid card types 

33 includes ``'amex'``, ``'diners'``, ``'discover'``, ``'jcb'``, ``'jcb15'``, 

34 ``'jcb16'``, ``'maestro'``, ``'mastercard'``, ``'visa'``, ``'visa13'``, 

35 ``'visa16'``, and ``'visa19'``. 

36 

37 Sources: 

38 

39 - https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_.28IIN.29 

40 - https://www.regular-expressions.info/creditcard.html 

41 - https://creditcardjs.com/credit-card-type-detection 

42 """ 

43 

44 prefix_maestro: List[str] = [ 

45 "5018", 

46 "5020", 

47 "5038", 

48 "56##", 

49 "57##", 

50 "58##", 

51 "6304", 

52 "6759", 

53 "6761", 

54 "6762", 

55 "6763", 

56 "0604", 

57 "6390", 

58 ] 

59 prefix_mastercard: List[str] = [ 

60 "51", 

61 "52", 

62 "53", 

63 "54", 

64 "55", 

65 "222%", 

66 "223", 

67 "224", 

68 "225", 

69 "226", 

70 "227", 

71 "228", 

72 "229", 

73 "23", 

74 "24", 

75 "25", 

76 "26", 

77 "270", 

78 "271", 

79 "2720", 

80 ] 

81 prefix_visa: List[str] = ["4"] 

82 prefix_amex: List[str] = ["34", "37"] 

83 prefix_discover: List[str] = ["6011", "65"] 

84 prefix_diners: List[str] = ["300", "301", "302", "303", "304", "305", "36", "38"] 

85 prefix_jcb16: List[str] = ["35"] 

86 prefix_jcb15: List[str] = ["2131", "1800"] 

87 

88 credit_card_types: Dict[str, CreditCard] = OrderedDict( 

89 ( 

90 ("maestro", CreditCard("Maestro", prefix_maestro, 12, security_code="CVV")), 

91 ( 

92 "mastercard", 

93 CreditCard("Mastercard", prefix_mastercard, 16, security_code="CVV"), 

94 ), 

95 ("visa16", CreditCard("VISA 16 digit", prefix_visa)), 

96 ("visa13", CreditCard("VISA 13 digit", prefix_visa, 13)), 

97 ("visa19", CreditCard("VISA 19 digit", prefix_visa, 19)), 

98 ( 

99 "amex", 

100 CreditCard( 

101 "American Express", 

102 prefix_amex, 

103 15, 

104 security_code="CID", 

105 security_code_length=4, 

106 ), 

107 ), 

108 ("discover", CreditCard("Discover", prefix_discover)), 

109 ("diners", CreditCard("Diners Club / Carte Blanche", prefix_diners, 14)), 

110 ("jcb15", CreditCard("JCB 15 digit", prefix_jcb15, 15)), 

111 ("jcb16", CreditCard("JCB 16 digit", prefix_jcb16)), 

112 ) 

113 ) 

114 credit_card_types["visa"] = credit_card_types["visa16"] 

115 credit_card_types["jcb"] = credit_card_types["jcb16"] 

116 

117 luhn_lookup = { 

118 "0": 0, 

119 "1": 2, 

120 "2": 4, 

121 "3": 6, 

122 "4": 8, 

123 "5": 1, 

124 "6": 3, 

125 "7": 5, 

126 "8": 7, 

127 "9": 9, 

128 } 

129 

130 def credit_card_provider(self, card_type: Optional[CardType] = None) -> str: 

131 """Generate a credit card provider name.""" 

132 if card_type is None: 

133 card_type = self.random_element(self.credit_card_types.keys()) # type: ignore[assignment] 

134 return self._credit_card_type(card_type).name 

135 

136 def credit_card_number(self, card_type: Optional[CardType] = None) -> str: 

137 """Generate a valid credit card number.""" 

138 card = self._credit_card_type(card_type) 

139 prefix: str = self.random_element(card.prefixes) 

140 number = self._generate_number(self.numerify(prefix), card.length) 

141 return number 

142 

143 def credit_card_expire( 

144 self, 

145 start: DateParseType = "now", 

146 end: DateParseType = "+10y", 

147 date_format: str = "%m/%y", 

148 ) -> str: 

149 """Generate a credit card expiry date. 

150 

151 This method uses |date_time_between| under the hood to generate the 

152 expiry date, so the ``start`` and ``end`` arguments work in the same way 

153 here as it would in that method. For the actual formatting of the expiry 

154 date, |strftime| is used and ``date_format`` is simply passed 

155 to that method. 

156 """ 

157 expire_date = self.generator.date_time_between(start, end) 

158 return expire_date.strftime(date_format) 

159 

160 def credit_card_full(self, card_type: Optional[CardType] = None) -> str: 

161 """Generate a set of credit card details.""" 

162 card = self._credit_card_type(card_type) 

163 

164 tpl = "{provider}\n" "{owner}\n" "{number} {expire_date}\n" "{security}: {security_nb}\n" 

165 

166 tpl = tpl.format( 

167 provider=card.name, 

168 owner=self.generator.parse("{{first_name}} {{last_name}}"), 

169 number=self.credit_card_number(card), 

170 expire_date=self.credit_card_expire(), 

171 security=card.security_code, 

172 security_nb=self.credit_card_security_code(card), 

173 ) 

174 

175 return self.generator.parse(tpl) 

176 

177 def credit_card_security_code(self, card_type: Optional[CardType] = None) -> str: 

178 """Generate a credit card security code.""" 

179 sec_len = self._credit_card_type(card_type).security_code_length 

180 return self.numerify("#" * sec_len) 

181 

182 def _credit_card_type(self, card_type: Optional[CardType] = None) -> CreditCard: 

183 """Generate a random CreditCard instance of the specified card type.""" 

184 if card_type is None: 

185 card_type = self.random_element(self.credit_card_types.keys()) # type: ignore[assignment] 

186 elif isinstance(card_type, CreditCard): 

187 return card_type 

188 return self.credit_card_types[card_type] # type: ignore[index] 

189 

190 def _generate_number(self, prefix: str, length: int) -> str: 

191 """Generate a credit card number. 

192 

193 The ``prefix`` argument is the start of the CC number as a string which 

194 may contain any number of digits. The ``length`` argument is the length 

195 of the CC number to generate which is typically 13 or 16. 

196 """ 

197 number = prefix 

198 # Generate random char digits 

199 number += "#" * (length - len(prefix) - 1) 

200 number = self.numerify(number) 

201 reverse = number[::-1] 

202 # Calculate sum 

203 tot = 0 

204 pos = 0 

205 while pos < length - 1: 

206 tot += Provider.luhn_lookup[reverse[pos]] 

207 if pos != (length - 2): 

208 tot += int(reverse[pos + 1]) 

209 pos += 2 

210 # Calculate check digit 

211 check_digit = (10 - (tot % 10)) % 10 

212 number += str(check_digit) 

213 return number