【问题标题】:What is the best way to validate a credit card in PHP?在 PHP 中验证信用卡的最佳方法是什么?
【发布时间】:2010-09-15 12:58:51
【问题描述】:

给定一个信用卡号并且没有其他信息,在 PHP 中确定它是否是一个有效号码的最佳方法是什么?

现在我需要一些适用于 American Express、Discover、MasterCard 和 Visa 的东西,但如果它也适用于其他类型可能会有所帮助。

【问题讨论】:

    标签: php validation e-commerce numbers credit-card


    【解决方案1】:

    这只是为了确保数字在使用一些基本的 RegEX 模式时有效。

    注意,这不会检查号码是否被某人使用。

    http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html

    【讨论】:

      【解决方案2】:

      PHP 代码

      function validateCC($cc_num, $type) {
      
          if($type == "American") {
          $denum = "American Express";
          } elseif($type == "Dinners") {
          $denum = "Diner's Club";
          } elseif($type == "Discover") {
          $denum = "Discover";
          } elseif($type == "Master") {
          $denum = "Master Card";
          } elseif($type == "Visa") {
          $denum = "Visa";
          }
      
          if($type == "American") {
          $pattern = "/^([34|37]{2})([0-9]{13})$/";//American Express
          if (preg_match($pattern,$cc_num)) {
          $verified = true;
          } else {
          $verified = false;
          }
      
      
          } elseif($type == "Dinners") {
          $pattern = "/^([30|36|38]{2})([0-9]{12})$/";//Diner's Club
          if (preg_match($pattern,$cc_num)) {
          $verified = true;
          } else {
          $verified = false;
          }
      
      
          } elseif($type == "Discover") {
          $pattern = "/^([6011]{4})([0-9]{12})$/";//Discover Card
          if (preg_match($pattern,$cc_num)) {
          $verified = true;
          } else {
          $verified = false;
          }
      
      
          } elseif($type == "Master") {
          $pattern = "/^([51|52|53|54|55]{2})([0-9]{14})$/";//Mastercard
          if (preg_match($pattern,$cc_num)) {
          $verified = true;
          } else {
          $verified = false;
          }
      
      
          } elseif($type == "Visa") {
          $pattern = "/^([4]{1})([0-9]{12,15})$/";//Visa
          if (preg_match($pattern,$cc_num)) {
          $verified = true;
          } else {
          $verified = false;
          }
      
          }
      
          if($verified == false) {
          //Do something here in case the validation fails
          echo "Credit card invalid. Please make sure that you entered a valid <em>" . $denum . "</em> credit card ";
      
          } else { //if it will pass...do something
          echo "Your <em>" . $denum . "</em> credit card is valid";
          }
      
      
      }
      

      用法

      echo validateCC("1738292928284637", "Dinners");
      

      更多理论信息可以在这里找到:

      Credit Card Validation - Check Digits

      Checksum

      【讨论】:

      • 该算法可以大致识别发卡机构,它不会验证该数字是否有意义..例如它不使用 Luhn 算法检查校验和!
      【解决方案3】:

      卡号的验证分三部分:

      1. PATTERN - 是否匹配发行人模式(例如 VISA/Mastercard/等)
      2. CHECKSUM - 它是否真的校验和(例如,不仅仅是“34”之后的 13 个随机数使其成为美国运通卡号)
      3. 确实存在 - 它是否真的有一个关联的帐户(如果没有商家帐户,您不太可能得到这个)

      图案

      • MASTERCARD 前缀=51-55,长度=16(Mod10 校验和)
      • VISA 前缀 = 4,长度 = 13 或 16 (Mod10)
      • AMEX 前缀=34 或 37,长度=15 (Mod10)
      • Diners Club/Carte 前缀=300-305、36 或 38,长度=14 (Mod10)
      • 发现前缀=6011,622126-622925,644-649,65,长度=16,(Mod10)
      • 等。 (detailed list of prefixes)

      校验和

      大多数卡片使用 Luhn 算法进行校验和:

      Luhn Algorithm described on Wikipedia

      Wikipedia 链接上有很多实现的链接,包括 PHP:

      <?
      /* Luhn algorithm number checker - (c) 2005-2008 shaman - www.planzero.org *
       * This code has been released into the public domain, however please      *
       * give credit to the original author where possible.                      */
      
      function luhn_check($number) {
      
        // Strip any non-digits (useful for credit card numbers with spaces and hyphens)
        $number=preg_replace('/\D/', '', $number);
      
        // Set the string length and parity
        $number_length=strlen($number);
        $parity=$number_length % 2;
      
        // Loop through each digit and do the maths
        $total=0;
        for ($i=0; $i<$number_length; $i++) {
          $digit=$number[$i];
          // Multiply alternate digits by two
          if ($i % 2 == $parity) {
            $digit*=2;
            // If the sum is two digits, add them together (in effect)
            if ($digit > 9) {
              $digit-=9;
            }
          }
          // Total up the digits
          $total+=$digit;
        }
      
        // If the total mod 10 equals 0, the number is valid
        return ($total % 10 == 0) ? TRUE : FALSE;
      
      }
      ?>
      

      【讨论】:

      • 在模式下,你可以添加“发现”,前缀=“6”(可能是“60”),长度=16
      • 根据beachnet.com/~hstiles/cardtype.html,MasterCard 的前缀是 51-55 而不是 51 或 55
      • 该函数将字符串中的所有非数字去掉,所以卡号“FRED”是有效的。在调用此函数之前,请确保您验证了卡号只有数字!
      • 信用卡号由 0 组成,同样有效
      • @BijuPDais 如文中所述 - 要检查它是否真的存在,您可能需要成为商家并实际尝试向卡计费。许多行动(如酒店)收费,然后将少量退款到信用卡上。在所有验证方法中 - 这是唯一真正测试卡是否有效的方法!
      【解决方案4】:

      luhn algorithm 是一个校验和,可用于验证许多信用卡格式的格式(以及加拿大社会保险号码...)

      维基百科文章还链接到许多不同的实现;这是一个 PHP 的:

      http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps

      【讨论】:

        【解决方案5】:

        来自10 regular expressions you can't live without in PHP

        function check_cc($cc, $extra_check = false){
            $cards = array(
                "visa" => "(4\d{12}(?:\d{3})?)",
                "amex" => "(3[47]\d{13})",
                "jcb" => "(35[2-8][89]\d\d\d{10})",
                "maestro" => "((?:5020|5038|6304|6579|6761)\d{12}(?:\d\d)?)",
                "solo" => "((?:6334|6767)\d{12}(?:\d\d)?\d?)",
                "mastercard" => "(5[1-5]\d{14})",
                "switch" => "(?:(?:(?:4903|4905|4911|4936|6333|6759)\d{12})|(?:(?:564182|633110)\d{10})(\d\d)?\d?)",
            );
            $names = array("Visa", "American Express", "JCB", "Maestro", "Solo", "Mastercard", "Switch");
            $matches = array();
            $pattern = "#^(?:".implode("|", $cards).")$#";
            $result = preg_match($pattern, str_replace(" ", "", $cc), $matches);
            if($extra_check && $result > 0){
                $result = (validatecard($cc))?1:0;
            }
            return ($result>0)?$names[sizeof($matches)-2]:false;
        }
        

        示例输入:

        $cards = array(
            "4111 1111 1111 1111",
        );
        
        foreach($cards as $c){
            $check = check_cc($c, true);
            if($check!==false)
                echo $c." - ".$check;
            else
                echo "$c - Not a match";
            echo "<br/>";
        }
        

        这给了我们

        4111 1111 1111 1111 - 签证
        

        【讨论】:

        【解决方案6】:

        有一个 PEAR 包可以处理许多财务号码的验证,以及信用卡验证:http://pear.php.net/package/Validate_Finance_CreditCard

        顺便说一下,这里有一些Test Credit Card Account Numbers 来自 PayPal。

        【讨论】:

          【解决方案7】:

          最后最好不要在代码中进行验证。将卡信息直接发送到您的支付网关,然后处理他们的响应。如果您不先进行 Luhn 检查之类的操作,它可以帮助他们检测欺诈行为——让他们看到失败的尝试。

          【讨论】:

          • 唯一的问题是每笔交易都有成本。它可能很小,但它加起来,如果有人通过您的系统运行大量欺诈性卡号,费用可能会变得不堪重负。
          【解决方案8】:

          只需添加一些其他人可能会觉得有用的更多代码 sn-ps(不是 PHP 代码)。

          PYTHON(单行代码;可能效率不高)

          验证:

          >>> not(sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('1234567890123452'))))))%10)
          True
          >>> not(sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('1234567890123451'))))))%10)
          False
          

          返回所需的校验位:

          >>> (10-sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('123456789012345')), start=1)))))%10
          2
          >>> (10-sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('234567890123451')), start=1)))))%10
          1
          

          MySQL 函数

          函数“ccc”和“ccd”(信用卡支票和信用卡数字)

          请注意,“ccc”函数有一个额外的检查,如果计算的总和为 0,则返回的结果将始终为 FALSE,因此全零 CC 数字永远不会验证为正确(在正常行为下,它将验证正确)。可以根据需要添加/删除此功能;可能有用,具体取决于具体要求。

          DROP FUNCTION IF EXISTS ccc;
          DROP FUNCTION IF EXISTS ccd;
          
          DELIMITER //
          
          CREATE FUNCTION ccc (n TINYTEXT) RETURNS BOOL
          BEGIN
            DECLARE x TINYINT UNSIGNED;
            DECLARE l TINYINT UNSIGNED DEFAULT length(n);
            DECLARE i TINYINT UNSIGNED DEFAULT l;
            DECLARE s SMALLINT UNSIGNED DEFAULT 0;
            WHILE i > 0 DO
              SET x = mid(n,i,1);
              IF (l-i) mod 2 = 1 THEN
                SET x = x * 2;
              END IF;
              SET s = s + x div 10 + x mod 10;
              SET i = i - 1;
            END WHILE;
            RETURN s != 0 && s mod 10 = 0;
          END;
          
          CREATE FUNCTION ccd (n TINYTEXT) RETURNS TINYINT
          BEGIN
            DECLARE x TINYINT UNSIGNED;
            DECLARE l TINYINT UNSIGNED DEFAULT length(n);
            DECLARE i TINYINT UNSIGNED DEFAULT l;
            DECLARE s SMALLINT UNSIGNED DEFAULT 0;
            WHILE i > 0 DO
              SET x = mid(n,i,1);
              IF (l-i) mod 2 = 0 THEN
                SET x = x * 2;
              END IF;
              SET s = s + x div 10 + x mod 10;
              SET i = i - 1;
            END WHILE;
            RETURN ceil(s/10)*10-s;
          END;
          

          然后可以在 SQL 查询中直接使用函数:

          mysql> SELECT ccc(1234567890123452);
          +-----------------------+
          | ccc(1234567890123452) |
          +-----------------------+
          |                     1 |
          +-----------------------+
          1 row in set (0.00 sec)
          
          mysql> SELECT ccc(1234567890123451);
          +-----------------------+
          | ccc(1234567890123451) |
          +-----------------------+
          |                     0 |
          +-----------------------+
          1 row in set (0.00 sec)
          
          mysql> SELECT ccd(123456789012345);
          +----------------------+
          | ccd(123456789012345) |
          +----------------------+
          |                    2 |
          +----------------------+
          1 row in set (0.00 sec)
          
          mysql> SELECT ccd(234567890123451);
          +----------------------+
          | ccd(234567890123451) |
          +----------------------+
          |                    1 |
          +----------------------+
          1 row in set (0.00 sec)
          

          【讨论】:

            猜你喜欢
            • 2012-01-19
            • 2015-09-14
            • 2021-01-02
            • 2018-04-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多