結帳表單裡看起來最不需要動腦的欄位,往往是信用卡有效期限格式這一欄。只有四位數字,卻是最容易寫出「差一個月」錯誤的地方。問題不在格式本身,而在一個很容易被忽略的語意細節:印在卡上的那個月份,指的是有效期間的最後一個月,而不是失效的起點。把這件事弄錯,就會出現「卡片明明還在手上,系統卻說過期」這種最難解釋的客訴。這篇把卡面寫法、系統儲存方式與判斷邏輯一次講清楚。
卡面上的寫法
卡片正面的有效期限固定用「月在前、年在後」的形式呈現,月份與年份各兩位,中間用斜線分開。幾個實務細節值得留意:
- 月份一定要補零。 一月印成 01,不會寫成 1;這是為了讓欄位寬度固定。
- 年份只有兩位。 卡片空間有限,因此省略世紀,只留年份的後兩位。這帶來一個經典的模糊地帶:00 究竟該解讀成兩千年代還是兩千一百年代。實務上的通行作法是解讀為兩千年代,並且拒絕明顯過於久遠的年份。
- 順序固定是先月後年。 世界上有些地區的日期寫法正好相反,但卡面一律是月在前。
- 卡片上可能還有其他日期。 有些卡片在卡號下方或背面另印一組較小的數字,那不是有效期限;判斷時要看欄位標示。
卡片是有效到當月月底嗎?
是。這是整個主題最重要的一句話:印在卡上的月份代表有效期間的最後一個月,卡片在該月的最後一天仍然可以使用,從下個月的第一天起才失效。
把它寫成判斷條件時,最常見的兩種錯誤剛好相反。第一種寫成「月份小於等於當月、年份小於等於當年就有效」,結果把當月的卡片算成過期,使用者明明卡片還在有效期內卻被拒。第二種寫成「只要年份不小於當年就有效」,於是已經過期好幾個月的舊卡被放行。
比較穩妥的作法是先把那組月年轉成一個可比較的時點,取該月的最後一刻,再與現在時間比較。等價的寫法有很多種,重點是團隊裡只能有一種定義,而且前後端共用同一套規則。
顯示格式與儲存格式為什麼不一樣?
和卡號一樣,有效期限在不同層有不同的表示方式。
| 層次 | 常見表示 | 說明 |
|---|---|---|
| 卡片正面 | 月兩位加年兩位 | 印刷慣例,供人閱讀 |
| 輸入欄位 | 單一欄位或月、年兩個欄位 | 依引導方式而定 |
| 系統內部 | 完整的年月或該月月底 | 便於比較、排序與查詢 |
| 標準編碼 | 年在前的四位數字 | 磁條規範與卡面順序相反 |
最後一列值得特別注意:國際標準在磁條上編碼的順序是年在前、月在後,與卡面完全相反。這提醒我們,顯示格式與儲存格式本來就可以不同,硬要統一反而容易出錯。儲存時若有選擇權,建議用標準的日期型別或該月月底的時間點,而不是存成一串四位數字——字串無法排序,也無法做日期運算。
有效期限欄位該怎麼設計
這個欄位有兩種常見做法,各有代價。
單一欄位讓使用者連續輸入四位數字,程式自動插入斜線。優點是鍵盤操作流暢、欄位數少;缺點是貼上帶斜線、帶連字號或月份沒補零的內容時,都要能正確解析。
兩個獨立欄位把月份與年份分開。優點是自動跳格的行為直覺,行動裝置上較不容易誤觸;缺點是多一個焦點停駐點,使用者也可能在兩個欄位之間來回修正。
無論選哪一種,都要能處理這些輸入變體:帶斜線的、月份沒補零的、連寫四位的、用連字號分隔的、前後帶空白的,以及全形數字。行動裝置上還有一個常被忽略的情況:年份欄位若使用數字鍵盤,使用者可能直接輸入四位年份,欄位必須明確決定是截斷還是拒絕。
給開發者:邊界值與過期判斷
除了月底那條線,還有幾項在真實專案裡反覆出問題,建議直接寫進測試案例:
- 時區。 伺服器用協調世界時、而使用者在月初或月底操作時,「是否過期」的判斷可能相差一天。基準要用哪個時區必須寫進規格,不能靠默契。
- 月份欄位的非法值。 大於 12 或等於 0 的月份必須被拒絕,而且錯誤訊息要指出是哪一個欄位有問題。
- 世紀判定。 如果採用「年份後兩位小於某個門檻就視為下一個世紀」的滑動規則,跨年時行為會改變,一定要測。
- 前後端判斷不一致。 前端放行、後端拒絕,是使用者體驗最糟的一種失敗;兩邊的過期判斷必須來自同一套規則。
- 與驗證流程的互動。 有效期限是授權請求的一部分,格式錯誤有時要到送出後才回報,錯誤歸因要對得起使用者看到的那一步。
準備測試資料時,記得配一組到期月份剛好是當月、下個月與上個月的號碼,才能把上面三種邊界一次覆蓋。本站的虛擬信用卡工具在產生測試卡時會一併給出有效期限,可以直接拿來組這些案例;產出的號碼結構正確、但從未發行,無法完成任何真實付款。想更完整地理解這批資料的性質,讀測試信用卡號。
下一步
要理解安全碼欄位的處理原則,讀 CVV 是什麼;要把整條結帳流程納入回歸測試,讀支付表單測試清單。手上缺測試資料的話,開產生器一次產生幾組不同到期月份的卡號最快。