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
« 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
4from ...typing import DateParseType
5from .. import BaseProvider
7localized = True
9CardType = TypeVar("CardType", "CreditCard", str)
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
28class Provider(BaseProvider):
29 """Implement default credit card provider for Faker.
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'``.
37 Sources:
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 """
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"]
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"]
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 }
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
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
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.
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)
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)
164 tpl = "{provider}\n" "{owner}\n" "{number} {expire_date}\n" "{security}: {security_nb}\n"
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 )
175 return self.generator.parse(tpl)
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)
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]
190 def _generate_number(self, prefix: str, length: int) -> str:
191 """Generate a credit card number.
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