Skip to content

Instantly share code, notes, and snippets.

@taka-tactical
Last active October 2, 2017 07:33
Show Gist options
  • Select an option

  • Save taka-tactical/965ebac0c8e14b75bad16fd1e503ffce to your computer and use it in GitHub Desktop.

Select an option

Save taka-tactical/965ebac0c8e14b75bad16fd1e503ffce to your computer and use it in GitHub Desktop.
[PHP: Credit card utility class] カード会社の判別やチェックディジットの検証を行うユーティリティクラス - ver. 2014/10/17 #php
<?php
/**
* クレジットカード ユーティリティクラス
*
* カード会社の判別やチェックディジットの検証を行うユーティリティクラス
*
* @see http://en.wikipedia.org/wiki/Bank_card_number
* @see http://www.ee.unb.ca/tervo/ee4253/luhn.shtml
*/
class CreditCardUtility {
/**
* カード会社 定数
*/
const AMEX = 'Amex';
const DINERS = 'Diners';
const DISCOVER = 'Discover';
const JCB = 'JCB';
const LASER = 'Laser';
const MAESTRO = 'Maestro';
const MASTERCARD = 'Master';
const SOLO = 'Solo';
const VISA = 'Visa';
/**
* カード会社検索用のカード番号インデックス
*
* @access private
* @var array
*/
private $_cardIndex = [
3 => [
self::AMEX, self::DINERS, self::JCB
],
4 => [
self::VISA
],
5 => [
self::MASTERCARD, self::MAESTRO
],
6 =>[
self::DISCOVER, self::LASER, self::MAESTRO, self::SOLO
],
];
/**
* カード番号のサイズ一覧
*
* @access private
* @var array
*/
private $_cardLength = [
self::AMEX => [15],
self::DINERS => [14],
self::DISCOVER => [16],
self::JCB => [16],
self::LASER => [16, 17, 18, 19],
self::MAESTRO => [12, 13, 14, 15, 16, 17, 18, 19],
self::MASTERCARD => [16],
self::SOLO => [16, 18, 19],
self::VISA => [16],
];
/**
* 先頭番号毎のカードタイプ一覧
*
* @access private
* @var array
*/
private $_cardType = [
self::AMEX => ['34', '37'],
self::DINERS => ['300', '301', '302', '303', '304', '305', '36'],
self::DISCOVER => [
'6011', '622126', '622127', '622128', '622129', '62213', '62214',
'62215', '62216', '62217', '62218', '62219', '6222', '6223',
'6224', '6225', '6226', '6227', '6228', '62290', '62291',
'622920', '622921', '622922', '622923', '622924', '622925', '644',
'645', '646', '647', '648', '649', '65',
],
self::JCB => ['3528', '3529', '353', '354', '355', '356', '357', '358'],
self::LASER => ['6304', '6706', '6771', '6709'],
//self::MAESTRO => ['5018', '5020', '5038', '6304', '6759', '6761', '6763'], // 6304 はLaserっぽい
self::MAESTRO => ['5018', '5020', '5038', '6759', '6761', '6763'],
self::MASTERCARD => ['51', '52', '53', '54', '55'],
self::SOLO => ['6334', '6767'],
self::VISA => ['4'],
];
/**
* カード種別の判別
*
* カード番号文字列からカード種別(クレジットカード会社)を判別する。
*
* @access public
* @param string $str カード番号文字列
* @return string カード種別を意味する文字列定数。判別不要な場合は FALSE を返す。
*/
public function detectCardType($str) {
// 入力チェック
if (!is_string($str) || $str == '') {
return false;
}
// 不要文字除去してインデックスを確認
$str = $this->stripCardNumber($str);
$pos = intval($str[0]);
$indexes =& $this->_cardIndex;
if (!isset($indexes[$pos])) {
return false;
}
// ディジットチェック
if (!$this->checkDigits($str)) {
return false;
}
// カード種別の検出
$cards =& $indexes[$pos];
$clens =& $this->_cardLength;
$ctype =& $this->_cardType;
$len = strlen($str);
foreach ($cards as $card) {
if (!in_array($len, $clens[$card])) {
continue;
}
foreach ($ctype[$card] as $type) {
if (strncmp($type, $str, strlen($type)) == 0) return $card;
}
}
return false;
}
/**
* カード番号の妥当性を検証
*
* カード番号文字列からカード番号の妥当性を検証する。
*
* @access public
* @param string $str カード番号文字列
* @param string $type カード種別(カード会社を意味する文字列定数)。未指定の場合は全種別から検証する
* @return boolean カード番号が妥当なものであれば TRUE を、そうでない場合は FALSE を返す。
*/
public function isValidCardNumber($str, $type = false) {
// 入力チェック
if (!is_string($str) || $str == '') {
return false;
}
// 不要文字除去してインデックスを確認
$str = $this->stripCardNumber($str);
$pos = intval($str[0]);
$indexes =& $this->_cardIndex;
if (!isset($indexes[$pos])) {
return false;
}
// ディジットチェック
if (!$this->checkDigits($str)) {
return false;
}
$ctype =& $this->_cardType;
$clens =& $this->_cardLength;
// カード種別の設定
if (isset($ctype[$type])) {
// この先頭文字をとる可能性がある種別と指定の種別が食い違う場合は不一致
if (!in_array($type, $indexes[$pos])) {
return false;
}
$types = [$type];
}
else {
$types = array_keys($ctype);
}
// カード番号の検証
$flgT = false;
$flgL = false;
$len = strlen($str);
foreach($types as $type) {
foreach($ctype[$type] as $card) {
if (strncmp($card, $str, strlen($card)) == 0) {
$flgT = true;
if (in_array($len, $clens[$type])) {
$flgL = true;
break 2;
}
}
}
}
return ($flgT && $flgL) ? true : false;
}
/**
* カード番号文字列のチェックディジット検算
*
* カード番号文字列からチェックディジットを算出してカード番号の妥当性を検証する。
*
* @access public
* @param string $str カード番号文字列
* @return boolean チェックディジットが一致すれば TRUE を、そうでない場合は FALSE を返す。
*/
public function checkDigits($str) {
// 入力チェック
if (!is_string($str) || $str == '') {
return false;
}
// 不要文字除去してフォーマットチェック
$str = $this->stripCardNumber($str);
if (!ctype_digit($str)) {
return false;
}
// チェックディジットを計算
$len = strlen($str);
$sum = 0;
$weight = 2;
for ($i = $len - 2; $i >= 0; $i--) {
$digit = $weight * $str[$i];
$sum += floor($digit / 10) + $digit % 10;
$weight = $weight % 2 + 1;
}
// 比較結果を返す
return ((10 - $sum % 10) % 10 == $str[$len - 1]) ? true : false;
}
/**
* カード番号文字列から不要な文字を除去
*
* カード番号文字列からハイフンと空白を除去する。
*
* @access public
* @param string $str カード番号文字列
* @return string 不要な文字を除去したカード番号文字列
*/
public function stripCardNumber($str) {
// ハイフン綴りまたは空白区切りを想定してこれらを除去
return ($str != '') ? str_replace(['-', ' '], '', $str) : '';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment