1use crate::{
2 calculator::{iban::checksum, luhn},
3 helpers::{
4 base::{numerify, random_digit, random_element, random_index, random_key, random_letter},
5 miscellaneous::boolean,
6 },
7};
8use chrono::{Datelike, TimeZone, Utc};
9use rand::Rng;
10use std::collections::HashMap;
11
12pub trait PaymentTrait<'a> {
13 fn card_vendors() -> Vec<&'a str> {
14 vec![
15 "Visa",
16 "MasterCard",
17 "American Express",
18 "Discover Card",
19 "Visa Retired",
20 "JCB",
21 ]
22 }
23
24 fn card_params() -> HashMap<&'a str, Vec<&'a str>> {
26 let mut card_params = HashMap::new();
27
28 card_params.insert(
29 "Visa",
30 vec![
31 "4539###########",
32 "4556###########",
33 "4916###########",
34 "4532###########",
35 "4929###########",
36 "40240071#######",
37 "4485###########",
38 "4716###########",
39 "4##############",
40 ],
41 );
42
43 card_params.insert(
44 "Visa Retired",
45 vec![
46 "4539########",
47 "4556########",
48 "4916########",
49 "4532########",
50 "4929########",
51 "40240071####",
52 "4485########",
53 "4716########",
54 "4###########",
55 ],
56 );
57
58 card_params.insert(
59 "MasterCard",
60 vec![
61 "2221###########",
62 "23#############",
63 "24#############",
64 "25#############",
65 "26#############",
66 "2720###########",
67 "51#############",
68 "52#############",
69 "53#############",
70 "54#############",
71 "55#############",
72 ],
73 );
74
75 card_params.insert("America Express", vec!["34############", "37############"]);
76
77 card_params.insert("Discover Card", vec!["6011###########"]);
78
79 card_params.insert("JCB", vec!["3528###########", "3589###########"]);
80
81 card_params
82 }
83
84 fn iban_formats() -> HashMap<&'a str, Vec<(char, u8)>> {
85 let mut iban_formats = HashMap::new();
86
87 iban_formats.insert("AD", vec![('n', 4), ('n', 4), ('c', 12)]);
88 iban_formats.insert("AE", vec![('n', 3), ('n', 16)]);
89 iban_formats.insert("AL", vec![('n', 8), ('c', 16)]);
90 iban_formats.insert("AT", vec![('n', 5), ('n', 11)]);
91 iban_formats.insert("AZ", vec![('a', 4), ('c', 20)]);
92 iban_formats.insert("BA", vec![('n', 3), ('n', 3), ('n', 8), ('n', 2)]);
93 iban_formats.insert("BE", vec![('n', 3), ('n', 7), ('n', 2)]);
94 iban_formats.insert("BG", vec![('a', 4), ('n', 4), ('n', 2), ('c', 8)]);
95 iban_formats.insert("BH", vec![('a', 4), ('c', 14)]);
96 iban_formats.insert(
97 "BR",
98 vec![('n', 8), ('n', 5), ('n', 10), ('a', 1), ('c', 1)],
99 );
100 iban_formats.insert("CH", vec![('n', 5), ('c', 12)]);
101 iban_formats.insert("CR", vec![('n', 4), ('n', 14)]);
102 iban_formats.insert("CY", vec![('n', 3), ('n', 5), ('c', 16)]);
103 iban_formats.insert("CZ", vec![('n', 4), ('n', 6), ('n', 10)]);
104 iban_formats.insert("DE", vec![('n', 8), ('n', 10)]);
105 iban_formats.insert("DK", vec![('n', 4), ('n', 9), ('n', 1)]);
106 iban_formats.insert("DO", vec![('c', 4), ('n', 20)]);
107 iban_formats.insert("EE", vec![('n', 2), ('n', 2), ('n', 11), ('n', 1)]);
108 iban_formats.insert("EG", vec![('n', 4), ('n', 4), ('n', 17)]);
109 iban_formats.insert(
110 "ES",
111 vec![('n', 4), ('n', 4), ('n', 1), ('n', 1), ('n', 10)],
112 );
113 iban_formats.insert("FI", vec![('n', 6), ('n', 7), ('n', 1)]);
114 iban_formats.insert("FR", vec![('n', 5), ('n', 5), ('c', 11), ('n', 2)]);
115 iban_formats.insert("GB", vec![('a', 4), ('n', 6), ('n', 8)]);
116 iban_formats.insert("GE", vec![('a', 2), ('n', 16)]);
117 iban_formats.insert("GI", vec![('a', 4), ('c', 15)]);
118 iban_formats.insert("GR", vec![('n', 3), ('n', 4), ('c', 16)]);
119 iban_formats.insert("GT", vec![('c', 4), ('c', 20)]);
120 iban_formats.insert("HR", vec![('n', 7), ('n', 10)]);
121 iban_formats.insert(
122 "HU",
123 vec![('n', 3), ('n', 4), ('n', 1), ('n', 15), ('n', 1)],
124 );
125 iban_formats.insert("IE", vec![('a', 4), ('n', 6), ('n', 8)]);
126 iban_formats.insert("IL", vec![('n', 3), ('n', 3), ('n', 13)]);
127 iban_formats.insert("IS", vec![('n', 4), ('n', 2), ('n', 6), ('n', 10)]);
128 iban_formats.insert("IT", vec![('a', 1), ('n', 5), ('n', 5), ('c', 12)]);
129 iban_formats.insert("KW", vec![('a', 4), ('n', 22)]);
130 iban_formats.insert("KZ", vec![('n', 3), ('c', 13)]);
131 iban_formats.insert("LB", vec![('n', 4), ('c', 20)]);
132 iban_formats.insert("LI", vec![('n', 5), ('c', 12)]);
133 iban_formats.insert("LT", vec![('n', 5), ('n', 11)]);
134 iban_formats.insert("LU", vec![('n', 3), ('c', 13)]);
135 iban_formats.insert("LV", vec![('a', 4), ('c', 13)]);
136 iban_formats.insert("MC", vec![('n', 5), ('n', 5), ('c', 11), ('n', 2)]);
137 iban_formats.insert("MD", vec![('c', 2), ('c', 18)]);
138 iban_formats.insert("ME", vec![('n', 3), ('n', 13), ('n', 2)]);
139 iban_formats.insert("MK", vec![('n', 3), ('c', 10), ('n', 2)]);
140 iban_formats.insert("MR", vec![('n', 5), ('n', 5), ('n', 11), ('n', 2)]);
141 iban_formats.insert("MT", vec![('a', 4), ('n', 5), ('c', 18)]);
142 iban_formats.insert(
143 "MU",
144 vec![('a', 4), ('n', 2), ('n', 2), ('n', 12), ('n', 3), ('a', 3)],
145 );
146 iban_formats.insert("NL", vec![('a', 4), ('n', 10)]);
147 iban_formats.insert("NO", vec![('n', 4), ('n', 6), ('n', 1)]);
148 iban_formats.insert("PK", vec![('a', 4), ('c', 16)]);
149 iban_formats.insert("PL", vec![('n', 8), ('n', 16)]);
150 iban_formats.insert("PS", vec![('a', 4), ('c', 21)]);
151 iban_formats.insert("PT", vec![('n', 4), ('n', 4), ('n', 11), ('n', 2)]);
152 iban_formats.insert("RO", vec![('a', 4), ('c', 16)]);
153 iban_formats.insert("RS", vec![('n', 3), ('n', 13), ('n', 2)]);
154 iban_formats.insert("SA", vec![('n', 2), ('c', 18)]);
155 iban_formats.insert("SE", vec![('n', 3), ('n', 16), ('n', 1)]);
156 iban_formats.insert("SI", vec![('n', 5), ('n', 8), ('n', 2)]);
157 iban_formats.insert("SK", vec![('n', 4), ('n', 6), ('n', 10)]);
158 iban_formats.insert("SM", vec![('a', 1), ('n', 5), ('n', 5), ('c', 12)]);
159 iban_formats.insert("TN", vec![('n', 2), ('n', 3), ('n', 13), ('n', 2)]);
160 iban_formats.insert("TR", vec![('n', 5), ('n', 1), ('c', 16)]);
161 iban_formats.insert("VG", vec![('a', 4), ('n', 16)]);
162
163 iban_formats
164 }
165
166 fn credit_card_type() -> String {
168 random_element(&Self::card_vendors()).to_string()
169 }
170
171 fn credit_card_number(
173 credit_card_type: Option<&str>,
174 formatted: Option<bool>,
175 separator: Option<char>,
176 ) -> String {
177 let card_number;
178 let default_card_param = vec!["2221###########"];
179
180 let card_type = if let Some(card_type) = credit_card_type {
181 if !Self::card_vendors().contains(&card_type) {
182 panic!("{} is not a valid credit_card_type \n This is a list of the valid card types {:?}",
183 card_type, Self::card_vendors())
184 }
185 card_type
186 } else {
187 let card_vendors = Self::card_vendors();
188 let random_index = random_index(&card_vendors);
189 let card_type = card_vendors[random_index];
190 card_type
191 };
192
193 let card_params = Self::card_params();
194
195 let card_param = card_params.get(&card_type).unwrap_or(&default_card_param);
196
197 let mask = random_element(&card_param);
198
199 let mut number = numerify(Some(mask));
200 number = format!("{}{}", number, luhn::compute_check_digit(&number));
201
202 card_number = if let Some(true) = formatted {
203 let separator = separator.unwrap_or('-');
204 let p1 = number.chars().take(4).collect::<String>();
205 let p2 = number.chars().skip(4).take(4).collect::<String>();
206 let p3 = number.chars().skip(8).take(4).collect::<String>();
207 let p4 = number.chars().skip(12).collect::<String>();
208 format!(
209 "{}{}{}{}{}{}{}",
210 p1, separator, p2, separator, p3, separator, p4
211 )
212 } else {
213 number
214 };
215
216 card_number
217 }
218
219 fn credit_card_expiration_date(valid: Option<bool>) -> String {
222 let valid = valid.unwrap_or(true);
223
224 let random_day: u32 = rand::thread_rng().gen_range(1..28);
225 let random_month: u32 = rand::thread_rng().gen_range(1..12);
226
227 if valid {
228 let now = Utc::now().year();
229 let end = now + 3;
230 let random_year: i32 = rand::thread_rng().gen_range(now..end);
231
232 let date = Utc
233 .ymd(random_year, random_month, random_day)
234 .format("%m/%y");
235
236 return format!("{}", date);
237 }
238
239 let now = Utc::now().year();
240 let end = now - 3;
241 let random_year: i32 = rand::thread_rng().gen_range(end..now);
242
243 let date = Utc
244 .ymd(random_year, random_month, random_day)
245 .format("%m/%y");
246
247 format!("{}", date)
248 }
249
250 fn credit_card_details(valid: Option<bool>) -> HashMap<&'a str, String> {
259 let credit_card_type = Self::credit_card_type();
260 let credit_card_number = Self::credit_card_number(Some(&credit_card_type), None, None);
261 let name = "Hello";
262 let expiration_date = Self::credit_card_expiration_date(valid);
263
264 let mut details = HashMap::new();
265 details.insert("credit_card_type", credit_card_type);
266 details.insert("credit_card_number", credit_card_number);
267 details.insert("holder_name", name.to_owned());
268 details.insert("expiration_date", expiration_date);
269 details
270 }
271
272 fn iban(country_code: Option<&str>, prefix: Option<&str>, mut length: Option<u8>) -> String {
274 let country_code = country_code
275 .unwrap_or(&random_key(&Self::iban_formats()).to_string())
276 .to_uppercase();
277
278 let iban_formats = Self::iban_formats();
279
280 let mut format = if iban_formats.contains_key(country_code.as_str()) {
281 iban_formats.get(country_code.as_str())
282 } else {
283 None
284 };
285
286 if let None = length {
287 length = if let None = format {
288 Some(24)
289 } else {
290 let mut length = 0;
291
292 for part in format.unwrap() {
293 let (_class, group_count) = part;
294
295 length = length + group_count;
296 }
297
298 Some(length)
299 };
300 }
301
302 let default_format = &vec![('n', length.unwrap())];
303
304 if format == None {
305 format = Some(default_format);
306 }
307
308 let mut expanded_format = "".to_string();
309
310 for item in format.unwrap() {
311 let (class, length) = item;
312 let format_part = class.to_string().as_str().repeat(*length as usize);
313 expanded_format.push_str(&format_part);
314 }
315
316 let mut result = prefix.unwrap_or("").to_string();
317 expanded_format = (&expanded_format[result.len()..]).to_string();
318
319 for class in expanded_format.chars() {
320 match class {
321 'c' => {
322 if boolean(None) {
323 result.push(random_digit() as char)
324 } else {
325 result.push_str(&random_letter().to_string().as_str().to_uppercase())
326 };
327 }
328 'a' => result.push_str(&random_letter().to_string().as_str().to_uppercase()),
329 'n' => result.push(random_digit() as char),
330 _ => result.push_str(&random_letter().to_string().as_str().to_uppercase()),
331 }
332 }
333
334 let checksum = checksum(
335 format!(
336 "{country_code}00{result}",
337 country_code = country_code,
338 result = result
339 )
340 .as_str(),
341 );
342
343 format!("{}{}{}", country_code, checksum, result)
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350 struct TestPay {}
351 impl PaymentTrait<'_> for TestPay {}
352
353 #[test]
354 fn test_credit_card_type() {
355 let card_type = TestPay::credit_card_type();
356 assert!(TestPay::card_vendors().contains(&card_type.as_str()));
357 }
358
359 #[test]
360 fn test_credit_card_number() {
361 let card_number = TestPay::credit_card_number(None, None, None);
362 assert_eq!(card_number.len(), 16);
363 }
364
365 #[test]
366 fn test_credit_card_number_with_type() {
367 let card_number = TestPay::credit_card_number(Some("MasterCard"), None, None);
368 assert_eq!(card_number.len(), 16);
369 }
370
371 #[test]
373 fn test_credit_card_number_with_separator() {
374 let card_number = TestPay::credit_card_number(None, Some(true), Some('-'));
375 println!("{}", card_number);
376 assert_eq!(card_number.len(), 19);
377 }
378
379 #[test]
380 fn test_card_expiration_date_with_valid_set_to_true() {
381 let date = TestPay::credit_card_expiration_date(Some(true));
382 assert_eq!(date.len(), 5);
383 }
384
385 #[test]
386 fn test_card_expiration_date_with_valid_set_to_false() {
387 let date = TestPay::credit_card_expiration_date(Some(false));
388 assert_eq!(date.len(), 5);
389 }
390
391 #[test]
392 fn test_card_details() {
393 let details = TestPay::credit_card_details(Some(true));
394 println!("{:?}", details);
395 assert_eq!(details.len(), 4);
396 }
397
398 #[test]
399 fn iban() {
400 let iban = TestPay::iban(None, None, None);
401 println!("{}", iban);
402 assert!(iban.len() > 0);
403 }
404}