Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

Java: ตรวจสอบความถูกต้องของหมายเลขบัตรเครดิต (Luhn Algorithm)

โปรแกรมตรวจสอบความถูกต้องของหมายเลขบัตรเครดิตโดยใช้อัลกอริทึม Luhn Algorithm (Mod 10) มาตรฐานสากลสำหรับ validate identification numbers

กฎการตรวจสอบพื้นฐาน

  1. ความยาว: 13-16 หลัก
  2. Prefix (Major Industry Identifier):
    • Visa: เริ่มต้นด้วย 4
    • Mastercard: เริ่มต้นด้วย 5 (หรือ 2221-2720 สำหรับรุ่นใหม่)
    • American Express: เริ่มต้นด้วย 37
    • Discover: เริ่มต้นด้วย 6
  3. Luhn Checksum: ผ่านการคำนวณ Mod 10

ขั้นตอน Luhn Algorithm

  1. เริ่มจากขวาไปซ้าย คูณ 2 ทุกหลักที่ 2 (even positions from right)
  2. ถ้าผลคูณ ≥ 10 ให้บวกหลักสองตัวเข้าด้วยกัน (เช่น 14 → 1+4=5) หรือใช้สูตร: n - 9
  3. บวกผลลัพธ์จากข้อ 1 กับหลักที่ไม่ได้คูณ 2 (odd positions from right)
  4. ถ้าผลรวมหาร 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