신용카드를 쓰는 사람이 많기 때문에 신용카드 번호는 꽤 길다. American Express의 경우 15자리, MasterCard는 16자리, 그리고 VISA의 경우는 13자리 또는 16자리의 번호를 사용한다. 십진수를 사용하기 때문에 10^15 = 1,000,000,000,000,000개의 고유한 번호를 발급할 수 있다.
하지만 사실 신용카드 번호는 나름의 구조가 있기 때문에 그만큼 발급할 수 있는 것은 아니다. 신용카드의 앞 자릿수로 발행사를 알 수 있는데, American Express는 34 또는 37로 시작하고, MasterCard는 51, 52, 53, 54 또는 55로 시작하며, VISA는 4로 시작한다. 또한 많은 발행사들은 카드 번호의 유효성을 검사하기 위해 Luhn’s algorithm을 사용하고 있다.
IBM의 Hans Peter Luhn가 발명했으며, 신용카드 번호 등 식별용 번호가 유효한지 확인하기 위한 알고리즘이다. 암호화 해시 함수처럼 악의적인 공격을 막기 위한 것이 아니라, 번호 오기입 등 우연한 실수를 방지하기 위해 고안되었다. 현재는 퍼블릭 도메인이 되어 널리 쓰이고있다.
이 알고리즘을 따르고 있는 번호는 다음과 같은 방법을 통해 유효한 것인지 알 수 있다:
get_long
을 이용하자. 신용카드는 최대 16자리이기 때문에 int
타입의 크기를 초과한다./*
Pseudocode:
1.사용자에게 카드번호 입력을 받아 변수에 저장한다.
2.배열의 길이를 계산한다.
3.길이에 맞는 배열을 생성한다.
4.한 자리씩 각 요소에 저장한다.
5.checksum 연산
- 카드번호의 첫번째 숫자부터 검사한다.
- 배열의 짝수번째 요소이면 2를 곱해준다.
- 십의자리(한자리 숫자면 0), 일의자리를 각각 sum에 가산한다.
6.카드의 종류를 판단한다.
- sum % 10 != 0인 경우 INVALID
- sum % 10 == 0인 경우 카드사의 종류를 판단한다.
7.결과값 출력
*/
int main(void)
{
// 카드번호 프롬프트
long n = get_long("Number: ");
// 카드번호의 자리수 구하기
int count = 0;
long nTemp = n;
while (nTemp != 0)
{
nTemp = nTemp / 10;
count++;
}
// 카드번호 배열에 한자리씩 저장하기
int numbers[count];
int exponent = count - 1;
nTemp = n;
for (int i = 0; i < count; i++)
{
long digit = (long)pow(10.0, (double)exponent - i);
numbers[i] = (int)(nTemp / digit);
nTemp %= digit;
}
// 카드번호 자릿수의 홀짝 판단. 총 자릿수가 짝수 -> 배열[짝수]일때 곱하기 2
int parity = count % 2;
// checksum 연산
int sum = 0;
for (int i = 0; i < count; i++)
{
int digit = numbers[i];
if (i % 2 == parity)
digit *= 2;
sum += digit / 10;
sum += digit % 10;
}
// 카드번호가 유효한지 식별
string result;
if (sum % 10 != 0)
{
result = "INVALID";
}
else
{
// 유효한 경우 카드사 식별
int creditor = (numbers[0] * 10) + numbers[1];
if ((creditor == 34 || creditor == 37) && count == 15)
{
result = "AMEX";
}
else if (creditor >= 51 && creditor <= 55 && count == 16)
{
result = "MASTERCARD";
}
else if (creditor >= 40 && creditor <= 49 && (count == 13 || count == 16))
{
result = "VISA";
}
else
{
result = "INVALID";
}
}
// 결과 출력
printf("%s\n", result);
}