it-swarm.com.de

Was ist der beste Weg, eine Kreditkarte in PHP zu validieren?

Wie kann man in PHP anhand einer Kreditkartennummer und ohne zusätzliche Informationen feststellen, ob es sich um eine gültige Nummer handelt oder nicht?

Im Moment brauche ich etwas, das mit American Express, Discover, MasterCard und Visa zusammenarbeitet. Es kann jedoch hilfreich sein, wenn es auch mit anderen Typen funktioniert.

66
Joe Lencioni

Die Validierung der Kartennummer besteht aus drei Teilen:

  1. PATTERN - Entspricht das Muster eines Ausstellers (z. B. VISA/Mastercard/etc.)
  2. CHECKSUMME - prüft die Summe tatsächlich (z. B. nicht nur 13 Zufallszahlen nach "34", um eine AMEX-Kartennummer zu erhalten)
  3. WIRKLICH EXISTE - Hat das Konto tatsächlich ein zugehöriges Konto (Sie werden dies wahrscheinlich nicht ohne ein Händlerkonto erhalten)

Muster

  • MASTERCARD-Präfix = 51-55, Länge = 16 (Mod10-Prüfsumme)
  • VISA-Präfix = 4, Länge = 13 oder 16 (Mod10)
  • AMEX-Präfix = 34 oder 37, Länge = 15 (Mod10)
  • Diners Club/Karten-Präfix = 300-305, 36 oder 38, Länge = 14 (Mod10)
  • Discover Prefix = 6011,622126-622925,644-649,65, Länge = 16, (Mod10)
  • usw. ( detaillierte Liste der Präfixe )

Prüfsumme

Die meisten Karten verwenden den Luhn-Algorithmus für Prüfsummen:

Luhn-Algorithmus auf Wikipedia beschrieben

Es gibt Links zu vielen Implementierungen auf dem Wikipedia-Link, einschließlich 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;

}
?>
143
Ray Hayes

Von 10 regulären Ausdrücken können Sie ohne PHP nicht leben: /:

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;
}

Beispieleingabe:

$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/>";
}

Das gibt uns

4111 1111 1111 1111 - Visa 
26
ConroyP

Es ist wahrscheinlich besser, KEINEN Code an Ihrem Ende zu validieren. Senden Sie die Karteninformationen direkt an Ihr Zahlungs-Gateway und bearbeiten Sie die Antwort. Es hilft ihnen, Betrug zu erkennen, wenn Sie nichts wie Luhn zuerst prüfen - lassen Sie die fehlgeschlagenen Versuche erkennen.

12
PartialOrder

PHP Code

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";
    }


}

Verwendungszweck

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

Weitere theoretische Informationen finden Sie hier:

Kreditkartenvalidierung - Prüfziffern

Checksum

6

Wir können das Folgende verwenden, um die Kreditkarte zu bestätigen. Es funktioniert perfekt für mich.

protected function luhn($number)
{
    // Force the value to be a string as this method uses string functions.
    // Converting to an integer may pass PHP_INT_MAX and result in an error!
    $number = (string)$number;

    if (!ctype_digit($number)) {
        // Luhn can only be used on numbers!
        return FALSE;
    }

    // Check number length
    $length = strlen($number);

    // Checksum of the card number
    $checksum = 0;

    for ($i = $length - 1; $i >= 0; $i -= 2) {
        // Add up every 2nd digit, starting from the right
        $checksum += substr($number, $i, 1);
    }

    for ($i = $length - 2; $i >= 0; $i -= 2) {
        // Add up every 2nd digit doubled, starting from the right
        $double = substr($number, $i, 1) * 2;

        // Subtract 9 from the double where value is greater than 10
        $checksum += ($double >= 10) ? ($double - 9) : $double;
    }

    // If the checksum is a multiple of 10, the number is valid
    return ($checksum % 10 === 0);
}

protected function ValidCreditcard($number)
{
    $card_array = array(
        'default' => array(
            'length' => '13,14,15,16,17,18,19',
            'prefix' => '',
            'luhn' => TRUE,
        ),
        'american express' => array(
            'length' => '15',
            'prefix' => '3[47]',
            'luhn' => TRUE,
        ),
        'diners club' => array(
            'length' => '14,16',
            'prefix' => '36|55|30[0-5]',
            'luhn' => TRUE,
        ),
        'discover' => array(
            'length' => '16',
            'prefix' => '6(?:5|011)',
            'luhn' => TRUE,
        ),
        'jcb' => array(
            'length' => '15,16',
            'prefix' => '3|1800|2131',
            'luhn' => TRUE,
        ),
        'maestro' => array(
            'length' => '16,18',
            'prefix' => '50(?:20|38)|6(?:304|759)',
            'luhn' => TRUE,
        ),
        'mastercard' => array(
            'length' => '16',
            'prefix' => '5[1-5]',
            'luhn' => TRUE,
        ),
        'visa' => array(
            'length' => '13,16',
            'prefix' => '4',
            'luhn' => TRUE,
        ),
    );

    // Remove all non-digit characters from the number
    if (($number = preg_replace('/\D+/', '', $number)) === '')
        return FALSE;

    // Use the default type
    $type = 'default';

    $cards = $card_array;

    // Check card type
    $type = strtolower($type);

    if (!isset($cards[$type]))
        return FALSE;

    // Check card number length
    $length = strlen($number);

    // Validate the card length by the card type
    if (!in_array($length, preg_split('/\D+/', $cards[$type]['length'])))
        return FALSE;

    // Check card number prefix
    if (!preg_match('/^' . $cards[$type]['prefix'] . '/', $number))
        return FALSE;

    // No Luhn check required
    if ($cards[$type]['luhn'] == FALSE)
        return TRUE;

    return $this->luhn($number);

}
5
jeeva

Der luhn-Algorithmus ist eine Prüfsumme, mit der das Format vieler Kreditkartenformate (und auch der kanadischen Sozialversicherungsnummern ...) überprüft werden kann

Der Wikipedia-Artikel enthält auch Links zu vielen verschiedenen Implementierungen. Hier ist eine PHP Eins:

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

3
Dana

Es gibt ein PEAR -Paket, das die Validierung zahlreicher Finanznummern übernimmt, auch Kreditkarten-Validierung: http://pear.php.net/package/Validate_Finance_CreditCard

Übrigens, hier sind einige Test Credit Card Kontonummern von Paypal.

2
powtac

Geben Sie einfach weitere Codeausschnitte ein, die für andere nützlich sein könnten (nicht PHP - Code).

PYTHON(einzeiliger Code; wahrscheinlich nicht so effizient)

Bestätigen:

>>> 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

Rückgabe der erforderlichen Prüfziffer:

>>> (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-Funktionen

Funktionen "ccc" und "ccd" (Kreditkartenprüfung und Kreditkartenziffer)

Beachten Sie, dass die Funktion "ccc" zusätzlich geprüft wird. Wenn die berechnete Summe 0 ist, ist das zurückgegebene Ergebnis immer FALSE, sodass eine CC-Zahl mit Nullen niemals als korrekt gilt (bei normalem Verhalten würde dies korrekt erfolgen). Diese Funktion kann nach Bedarf hinzugefügt/entfernt werden. Je nach spezifischen Anforderungen möglicherweise nützlich.

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;

Funktionen können dann direkt in SQL-Abfragen verwendet werden:

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)
0
parkamark