-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCreditCardMasker.java
More file actions
152 lines (126 loc) · 5.42 KB
/
Copy pathCreditCardMasker.java
File metadata and controls
152 lines (126 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
public class CreditCardMasker {
/**
* Validates a credit card number using the Luhn algorithm.
*
* Accepted formats: plain digits, space-separated groups, or dash-separated groups.
* e.g. "4111111111111111", "4111 1111 1111 1111", "4111-1111-1111-1111"
*
* Validation steps:
* 1. Strip all spaces and dashes.
* 2. Reject if not 13-19 digits, or contains non-digit characters.
* 3. Apply Luhn check:
* - From the second-to-last digit, moving left, double every other digit.
* - If doubling produces a value > 9, subtract 9.
* - Sum all digits. Valid if the total is divisible by 10.
*
* @param cardNumber the raw card number string
* @return true if the card number is valid, false otherwise
*/
public static boolean isValid(String cardNumber) {
// get card number without spaces or dashes
// if the length is not 13-19 return false
// define total count
// for loop starting on length-2 and decrementing by 2
// double number, if greater than 9 subtract 9, add to total
// if total is divisible by 10 return true otherwise false
if (cardNumber == null){
return false;
}
String strippedCardNumber = cardNumber.replaceAll("[ -]", "");
if (!strippedCardNumber.matches("\\d+")) {
return false;
}
if (strippedCardNumber.length() < 13 || strippedCardNumber.length() > 19) {
return false;
}
int total = 0;
for (int i=1; i <= strippedCardNumber.length(); i++) {
int index = strippedCardNumber.length() - i;
int digit = strippedCardNumber.charAt(index) - '0';
if (i % 2 == 0) {
digit = digit * 2;
if (digit > 9) {
digit = digit - 9;
}
}
total += digit;
}
if (total % 10 == 0) {
return true;
}
return false;
}
/**
* Returns a masked version of the card number, preserving original formatting.
*
* If the card is invalid, returns "INVALID".
* Otherwise, replaces every digit except the last 4 with '*',
* leaving any spaces or dashes in their original positions.
*
* e.g. "4111 1111 1111 1111" -> "**** **** **** 1111"
* "4111-1111-1111-1111" -> "****-****-****-1111"
* "4111111111111111" -> "************1111"
*
* @param cardNumber the raw card number string
* @return the masked card string, or "INVALID"
*/
public static String mask(String cardNumber) {
// check isValid, if false return "INVALID"
// get last 4 digits, and first other digits as substrings
// replace all numeric values in other with *
// concat strings back together and return
if (cardNumber == null || !isValid(cardNumber)) {
return "INVALID";
}
String lastFour = cardNumber.substring(cardNumber.length()-4);
String rest = cardNumber.substring(0, cardNumber.length()-4);
return rest.replaceAll("[0-9]", "*") + lastFour;
}
// -------------------------------------------------------------------------
// Test runner
// -------------------------------------------------------------------------
public static void main(String[] args) {
int passed = 0, failed = 0;
Object[][] tests = {
// { input, expectedValid, expectedMask }
// valid - space-separated
{ "4111 1111 1111 1111", true, "**** **** **** 1111" },
// valid - dash-separated
{ "4111-1111-1111-1111", true, "****-****-****-1111" },
// valid - no separators
{ "4111111111111111", true, "************1111" },
// valid - different real card number
{ "3782 822463 10005", true, "**** ****** *0005" },
// invalid - fails Luhn
{ "1234 5678 9012 3456", false, "INVALID" },
// invalid - too short
{ "1234", false, "INVALID" },
// invalid - too long (20 digits)
{ "12345678901234567890", false, "INVALID" },
// invalid - non-numeric character
{ "4111 1111 1111 111A", false, "INVALID" },
// invalid - empty string
{ "", false, "INVALID" },
// invalid - null
{ null, false, "INVALID" },
};
for (Object[] test : tests) {
String input = (String) test[0];
boolean expectValid = (boolean) test[1];
String expectMask = (String) test[2];
boolean actualValid = isValid(input);
String actualMask = mask(input);
boolean validOk = actualValid == expectValid;
boolean maskOk = expectMask.equals(actualMask);
boolean ok = validOk && maskOk;
String display = input == null ? "null" : "\"" + input + "\"";
System.out.printf(" %-25s -> valid=%-5s mask=%-22s %s%n",
display,
actualValid,
"\"" + actualMask + "\"",
ok ? "PASS" : "FAIL (expected valid=" + expectValid + ", mask=\"" + expectMask + "\")");
if (ok) passed++; else failed++;
}
System.out.printf("%nResults: %d passed, %d failed%n", passed, failed);
}
}