本系列第五篇,進入 ValidateCard.cpp 實作核心驗證方法 ValidateCreditCard 及三大檢查演算法:Prefix(前綴)、Length(長度)、Luhn(模 10 校驗)。
STDMETHODIMP CValidateCard::ValidateCreditCard(BSTR bstrCCNo, BSTR bstrCCType, long* val)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // MFC 模組狀態切換
CString strType(bstrCCType);
strType.MakeUpper(); // 不區分大小寫
// 卡別代碼映射
if (strType == _T("VISA")) bstrCCType = SysAllocString(OLESTR("V"));
else if (strType == _T("MASTER")) bstrCCType = SysAllocString(OLESTR("M"));
else if (strType == _T("AMERICAN")) bstrCCType = SysAllocString(OLESTR("A"));
else {
*val = 0; return S_FALSE; // 不支援卡別
}
// 三大檢查全部通過才有效
if (PrefixCheck(bstrCCNo, bstrCCType) == S_OK &&
LengthCheck(bstrCCNo, bstrCCType) == S_OK &&
LuhnCheck(bstrCCNo, bstrCCType) == S_OK) {
*val = 1; return S_OK;
}
*val = 0; return S_FALSE;
}
| 卡別代碼 | 前綴規則 | 範例 |
|---|---|---|
| V (Visa) | 以 4 開頭 | 4111 1111 1111 1111 |
| M (MasterCard) | 51~55 或 2221~2720 | 5555 5555 5555 4444 |
| A (American Express) | 34 或 37 | 3782 822463 10005 |
HRESULT CValidateCard::PrefixCheck(BSTR bstrCCNo, BSTR bstrCCType)
{
CString strNo(bstrCCNo);
strNo.Remove(_T(' ')); strNo.Remove(_T('-')); // 移除分隔符
if (bstrCCType[0] == L'V') {
return (strNo[0] == _T('4')) ? S_OK : S_FALSE;
}
else if (bstrCCType[0] == L'M') {
int prefix2 = _ttoi(strNo.Left(2));
int prefix4 = _ttoi(strNo.Left(4));
return ((prefix2 >= 51 && prefix2 <= 55) || (prefix4 >= 2221 && prefix4 <= 2720)) ? S_OK : S_FALSE;
}
else if (bstrCCType[0] == L'A') {
return (strNo.Left(2) == _T("34") || strNo.Left(2) == _T("37")) ? S_OK : S_FALSE;
}
return S_FALSE;
}
| 卡別 | 長度 |
|---|---|
| Visa | 13, 16, 19 碼 |
| MasterCard | 16 碼 |
| American Express | 15 碼 |
HRESULT CValidateCard::LengthCheck(BSTR bstrCCNo, BSTR bstrCCType)
{
CString strNo(bstrCCNo);
strNo.Remove(_T(' ')); strNo.Remove(_T('-'));
int len = strNo.GetLength();
if (bstrCCType[0] == L'V') return (len == 13 || len == 16 || len == 19) ? S_OK : S_FALSE;
if (bstrCCType[0] == L'M') return (len == 16) ? S_OK : S_FALSE;
if (bstrCCType[0] == L'A') return (len == 15) ? S_OK : S_FALSE;
return S_FALSE;
}
Luhn 演算法步驟:
HRESULT CValidateCard::LuhnCheck(BSTR bstrCCNo, BSTR bstrCCType)
{
CString strNo(bstrCCNo);
strNo.Remove(_T(' ')); strNo.Remove(_T('-'));
int sum = 0;
bool doubleDigit = false;
for (int i = strNo.GetLength() - 1; i >= 0; --i) {
int digit = strNo[i] - _T('0');
if (digit < 0 || digit > 9) return S_FALSE; // 非數字
if (doubleDigit) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
doubleDigit = !doubleDigit;
}
return (sum % 10 == 0) ? S_OK : S_FALSE;
}
| 卡別 | 卡號 | 預期結果 | 說明 |
|---|---|---|---|
| VISA | 4111 1111 1111 1111 | Valid | 標準測試卡 |
| MASTER | 5555 5555 5555 4444 | Valid | 標準測試卡 |
| AMEX | 3782 822463 10005 | Valid | 標準測試卡 |
| VISA | 4111 1111 1111 1112 | Invalid | 檢查碼錯誤 |
| MASTER | 5105 1051 0510 5100 | Valid | MasterCard 2-series BIN |
enum 或結構體區分「前綴錯」、「長度錯」、「校驗錯」,方便前端提示。核心驗證邏輯完成,下一篇將示範如何建立安裝專案、註冊 COM 元件、處理版本升級與相容性。