決済画面などで入力されたクレジットカード番号が正しい物であるか検証するコードを書いてみます。
npmでモジュールを探すとcard-validatorなどいくつか見つかるので、実際にはこれらを使うのが楽ちんですね。メンテナンスされていればカード番号の仕様が変わったときもアップデートされることが期待できますし。
ここでは興味本位でスクラッチで書いてみたいと思いますw
クレジットカード番号の仕様
開始番号と桁数
カード番号の冒頭数桁でカード会社を判定できるようになっています。例えば「4」でカード番号が始まっていればVISAであることがわかります。またカード会社毎に桁数は異なりますが13〜16桁の整数である部分は共通です。
国際ブランド | 開始番号 | 桁数 | セキュリティコード |
---|---|---|---|
VISA | 4 | 13, 16 | CVV(3桁) |
MASTER Card | 510000〜559999, 222100〜272099 | 16 | CVC(3桁) |
JCB | 3528〜3589 | 16 | CVV(3桁) |
American Express | 34, 37 | 15 | CID(4桁) |
Diners Club | 300〜303574, 3095, 36, 38〜39 | 14 | CVV(3桁) |
- この情報は主にWikipediaを参考にしています。誤りや古くなっている箇所などあればご指摘ください。
チェックサム
クレジットカード番号の大半はLuhnアルゴリズムが用いられており、完璧ではありませんがある程度の入力ミスを検出することができます。
具体的なロジックは以下の通り。
- カード番号を「逆順」にする
4111111111111111
→1111111111111114
- 偶数番目の桁を2倍にする
1111111111111114
→1212121212121218
- 2倍にした結果2桁になった場合は1桁目と2桁目を足す。例えば
8 * 2 = 16
となった場合は1 + 6 = 7
とする
- 2倍にした結果2桁になった場合は1桁目と2桁目を足す。例えば
- すべての桁を合算する
1+2+1+2+1+2+1+2+1+2+1+2+1+2+1+8 = 30
- 合算した値が 10 で割り切れればカード番号は正しい
30 % 10 = 0
ちなみに2倍にした結果2桁になった場合は各桁を足すのが本来の仕様でありますが、9を引いても同じ結果になる法則があります。コードを書くときはこちらのほうが楽ですね。
元の数 | 2倍 | 1桁目+2桁目 | 2倍-9 |
---|---|---|---|
0 | 0*2=0 | ||
1 | 1*2=2 | ||
2 | 2*2=4 | ||
3 | 3*2=6 | ||
4 | 4*2=8 | ||
5 | 5*2=10 | 1+0=1 | 10-9=1 |
6 | 6*2=12 | 1+2=3 | 12-9=3 |
7 | 7*2=14 | 1+4=5 | 14-9=5 |
8 | 8*2=16 | 1+6=7 | 16-9=7 |
9 | 9*2=18 | 1+8=9 | 18-9=9 |
JavaScriptで検証する
チェックサムで入力ミスを検出する
まずは単純に入力ミスをチェックサムで検証します。先ほどの仕様を素直にコードにしたものです。
// true or false const result = checksum('4111111111111111') console.log(result) /** * カード番号の入力ミスを検出する * * @param {string} number カード番号 * @return {boolean} */ function checksum(number){ const temp = (typeof number === 'string')? number:String(number) const n = temp .split('') // 1文字ずつ分割し配列に .map( a => Number(a) ) // 配列の各要素を文字列型→数値型に変換 .reverse() // 配列を逆順にする let total = 0 for(let i=0; i<n.length; i++){ if( ((i + 1) % 2) === 0 ){ const value = n[i] * 2 total += (value > 9)? value - 9 : value } else{ total += n[i] } } return( total % 10 === 0 ) }
カード番号の桁数をチェック
クレジットカード番号の仕様が全世界共通なら良かったのですが、カード会社毎に違うのでまずはどの会社か判定した上で文字列長のチェックを行います。
// 正しければブランド名の文字列、誤りならfalse const result = checkLength('4111111111111111') // 'visa' console.log(result) /** * カード番号の桁数をチェックする * * @param {string} number カード番号 * @return {mixed} 正=[visa|mastercard|jcb|americanexpress|dinersclub], 誤=false */ function checkLength(number){ const n = (typeof number === 'string')? number:String(number) const between = (value, min, max) => ((min <= Number(value)) && (Number(value) <= max)) // 13〜16桁の整数で無いならfalseを返して終了 if( ! n.match(/^[0-9]{13,16}$/) ){ return(false) } //-------------------------------------- // カード会社を判定しつつチェック //-------------------------------------- // VISA // (13桁はすでに存在しない説があるため後ろに持ってくる) if( (n.substr(0,1) === '4') && ((n.length === 16) || (n.length === 13)) ){ return('visa') } // MASTER Card else if( (n.match(/^5[1-5]/) || between(n.substr(0,6), 222100, 272099)) && n.length === 16){ return('mastercard') } // JCB else if( between(n.substr(0,4), 3528, 3589) && n.length === 16 ){ return('jcb') } // American Express else if( n.match(/^3[47]/) && n.length === 15 ){ return('americanexpress') } // Diners Club else if( (between(n.substr(0,6), 300000, 303574) || n.substr(0,4) === '3095' || n.match(/^3[689]/)) && (n.length === 14) ){ return('dinersclub') } return(false) }
あわせ技
というわけで上記2つの関数を組み合わせることでチェックサムと文字列長の簡単なチェックができますね。
const number = '4111111111111111' if( checksum(number) && checkLength(number) !== false ){ console.log('valid') } else{ console.log('invalid') }
モジュールを利用する
このページえ書いたコードを利用したモジュールをnpmで公開しています。GitHubで中身も公開しているので気になる方は以下からどうぞ。 github.com
インストール
適当なディレクトリを作ったら、package.jsonを生成します。
$ mkdir foo; cd foo $ npm init
npmコマンドでインストールします。
$ npm install creditcard-checkerjs
利用方法
チェックサムの検証は以下の通りです。
const creditcard = require('creditcard-checkerjs') const number = '4111111111111111' if( creditcard.verify(number) ){ console.log('valid') } else{ console.log('invalid') }
カードブランドの検出はこちら。
const creditcard = require('creditcard-checkerjs') const number = '4111111111111111' const type = creditcard.cardtype(number) switch( type ){ case creditcard.type.VISA: console.log('Visa'); break; case creditcard.type.MASTER: console.log('Mastercard'); break; case creditcard.type.JCB: console.log('JCB'); break; case creditcard.type.AMEX: console.log('American Express'); break; case creditcard.type.DINERS: console.log('Diners'); break; case creditcard.type.UNKNOWN: default: console.log('Unknown'); break; }
よろしければご利用ください!プルリクもお待ちしております。