โปรแกรมตรวจสอบความถูกต้องของหมายเลขบัตรเครดิตโดยใช้อัลกอริทึม Luhn Algorithm (Mod 10) มาตรฐานสากลสำหรับ validate identification numbers
กฎการตรวจสอบพื้นฐาน
- ความยาว: 13-16 หลัก
- Prefix (Major Industry Identifier):
- Visa: เริ่มต้นด้วย
4 - Mastercard: เริ่มต้นด้วย
5(หรือ 2221-2720 สำหรับรุ่นใหม่) - American Express: เริ่มต้นด้วย
37 - Discover: เริ่มต้นด้วย
6
- Visa: เริ่มต้นด้วย
- Luhn Checksum: ผ่านการคำนวณ Mod 10
ขั้นตอน Luhn Algorithm
- เริ่มจากขวาไปซ้าย คูณ 2 ทุกหลักที่ 2 (even positions from right)
- ถ้าผลคูณ ≥ 10 ให้บวกหลักสองตัวเข้าด้วยกัน (เช่น 14 → 1+4=5) หรือใช้สูตร:
n - 9 - บวกผลลัพธ์จากข้อ 1 กับหลักที่ไม่ได้คูณ 2 (odd positions from right)
- ถ้าผลรวมหาร 10 ลงตัว (mod 10 = 0) → Valid
ตัวอย่างการคำนวณ: 4440967484181607
Digits: 4 4 4 0 9 6 7 4 8 4 1 8 1 6 0 7 Position: 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 (จากขวา) Double: ×2 ×2 ×2 ×2 ×2 ×2 ×2 ×2 Result: 8 4 8 0 18 6 14 4 16 4 2 8 2 12 0 7 Sum digits: 8+4+8+0+(1+8)+6+(1+4)+4+(1+6)+4+2+8+2+(1+2)+0+7 = 70 70 % 10 = 0 → VALID
Implementation แบบสมบูรณ์
public class CreditCardValidator {
public static void main(String[] args) {
String[] testCards = {
"4440967484181607", // Visa - Valid
"379354508162306", // Amex - Valid
"5555555555554444", // Mastercard - Valid
"6011111111111117", // Discover - Valid
"1234567890123456", // Invalid Luhn
"4440967484181608", // Invalid Luhn (last digit wrong)
};
for (String card : testCards) {
System.out.printf("%-20s : %s (%s)%n",
card,
isValid(card) ? "VALID" : "INVALID",
getCardType(card)
);
}
}
public static boolean isValid(String cardNumber) {
if (cardNumber == null || cardNumber.trim().isEmpty()) {
return false;
}
// Remove spaces and dashes
String cleaned = cardNumber.replaceAll("[\\s-]", "");
// Check length
if (cleaned.length() < 13 || cleaned.length() > 19) {
return false;
}
// Check all digits
if (!cleaned.matches("\\d+")) {
return false;
}
// Check prefix
if (!isValidPrefix(cleaned)) {
return false;
}
// Luhn check
return luhnCheck(cleaned);
}
private static boolean isValidPrefix(String card) {
// Visa
if (card.startsWith("4")) return true;
// Mastercard (51-55 or 2221-2720)
if (card.startsWith("5")) {
int prefix2 = Integer.parseInt(card.substring(0, 2));
if (prefix2 >= 51 && prefix2 <= 55) return true;
if (card.length() >= 4) {
int prefix4 = Integer.parseInt(card.substring(0, 4));
if (prefix4 >= 2221 && prefix4 <= 2720) return true;
}
}
// Amex
if (card.startsWith("34") || card.startsWith("37")) return true;
// Discover
if (card.startsWith("6")) return true;
return false;
}
private static boolean luhnCheck(String card) {
int sum = 0;
boolean alternate = false;
// Process from right to left
for (int i = card.length() - 1; i >= 0; i--) {
int digit = card.charAt(i) - '0';
if (alternate) {
digit *= 2;
if (digit > 9) {
digit = (digit / 10) + (digit % 10); // หรือ digit - 9
}
}
sum += digit;
alternate = !alternate;
}
return (sum % 10 == 0);
}
private static String getCardType(String card) {
String cleaned = card.replaceAll("[\\s-]", "");
if (cleaned.startsWith("4")) return "Visa";
if (cleaned.startsWith("5")) return "Mastercard";
if (cleaned.startsWith("34") || cleaned.startsWith("37")) return "American Express";
if (cleaned.startsWith("6")) return "Discover";
return "Unknown";
}
}
Output
4440967484181607 : VALID (Visa) 379354508162306 : VALID (American Express) 5555555555554444 : VALID (Mastercard) 6011111111111117 : VALID (Discover) 1234567890123456 : INVALID (Unknown) 4440967484181608 : INVALID (Visa)
เวอร์ชัน Short (ใช้ long) - สำหรับเลขที่ fit ใน long
public class ShortValidator {
public static boolean isValid(long cardNumber) {
String num = String.valueOf(cardNumber);
if (num.length() < 13 || num.length() > 16) return false;
// Prefix check
if (!(num.startsWith("4") || num.startsWith("5") ||
num.startsWith("37") || num.startsWith("6"))) {
return false;
}
return luhnCheck(num);
}
private static boolean luhnCheck(String num) {
int sum = 0;
for (int i = num.length() - 1, alt = 0; i >= 0; i--, alt++) {
int digit = num.charAt(i) - '0';
if (alt % 2 == 1) {
digit = digit * 2 > 9 ? digit * 2 - 9 : digit * 2;
}
sum += digit;
}
return sum % 10 == 0;
}
}
การใช้งานจริง: Input Validation ใน Web Form
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CreditCardValidator implements ConstraintValidator {
@Override
public boolean isValid(String cardNumber, ConstraintValidatorContext context) {
if (cardNumber == null) return false;
String cleaned = cardNumber.replaceAll("[\\s-]", "");
return cleaned.matches("\\d{13,19}") &&
isValidPrefix(cleaned) &&
luhnCheck(cleaned);
}
// ... luhnCheck and isValidPrefix methods
}
Test Cases สำคัญ
| Test Case | Expected | Reason |
|---|---|---|
| Valid Visa: 4111111111111111 | VALID | Luhn pass, prefix 4 |
| Valid Amex: 378282246310005 | VALID | Luhn pass, prefix 37, 15 digits |
| Invalid Luhn: 4111111111111112 | INVALID | Last digit wrong |
| Wrong length: 4111 | INVALID | Too short |
| Non-digits: 4111-1111-1111-111a | INVALID | Contains letter |
| With spaces: 4111 1111 1111 1111 | VALID | Should strip spaces |
หมายเหตุ
- Luhn algorithm ตรวจสอบได้แค่ "รูปแบบถูกต้อง" ไม่ได้ยืนยันว่าบัตร active จริง
- Production ควรใช้ Payment Gateway API สำหรับการ authorize จริง
- PCI DSS ต้องการไม่เก็บ CVV และ mask PAN ใน logs