tarjetas - transferencias bancarias php
¿Cuál es la mejor manera de validar una tarjeta de crédito en PHP? (9)
De 10 expresiones regulares que no puedes vivir sin en 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;
}
Muestra de entrada:
$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/>";
}
Esto nos da
4111 1111 1111 1111 - Visa
Dado un número de tarjeta de crédito y ninguna información adicional, ¿cuál es la mejor manera en PHP para determinar si es un número válido o no?
Ahora mismo necesito algo que funcione con American Express, Discover, MasterCard y Visa, pero podría ser útil si también funciona con otros tipos.
El algoritmo de Luhn es una suma de verificación que puede utilizarse para validar el formato de muchos formatos de tarjetas de crédito (y también números de seguro social canadiense ...)
El artículo de wikipedia también vincula a muchas implementaciones diferentes; aquí hay un PHP:
http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps
Esto es solo para asegurarse de que los números sean válidos usando algunos patrones básicos de RegEX.
Tenga en cuenta que esto no comprueba si los números están en uso por alguien.
http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html
Hay tres partes para la validación del número de tarjeta:
- PATRÓN : ¿coincide con un patrón de emisores (por ejemplo, VISA / Mastercard / etc.)?
- CHECKSUM - ¿es realmente suma de verificación (por ejemplo, no solo 13 números aleatorios después de "34" para convertirlo en un número de tarjeta AMEX)
- EXISTE REALMENTE : ¿tiene realmente una cuenta asociada (es poco probable que consigas esto sin una cuenta mercantil)?
Patrón
- Prefijo MASTERCARD = 51-55, longitud = 16 (suma de comprobación Mod10)
- Prefijo VISA = 4, longitud = 13 o 16 (Mod10)
- Prefijo AMEX = 34 o 37, longitud = 15 (Mod10)
- Diners Club / Carte Prefijo = 300-305, 36 o 38, longitud = 14 (Mod10)
- Discover Prefix = 6011,622126-622925,644-649,65, Length = 16, (Mod10)
- etc. ( lista detallada de prefijos )
Checksum
La mayoría de las tarjetas usan el algoritmo Luhn para sumas de comprobación:
Algoritmo de Luhn descrito en Wikipedia
Hay enlaces a muchas implementaciones en el enlace de Wikipedia, incluido 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;
}
?>
Hay un paquete PEAR que maneja la validación de muchos números financieros, también la validación de la tarjeta de crédito: http://pear.php.net/package/Validate_Finance_CreditCard
Por cierto, aquí hay algunos números de cuenta de la tarjeta de crédito de prueba de PayPal.
Podemos usar lo siguiente para validar la tarjeta de crédito. Funciona perfectamente para mí.
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);
}
Probablemente es mejor NO validar el código en su extremo. Envíe la información de la tarjeta directamente a su pasarela de pago y luego trate su respuesta. Les ayuda a detectar fraudes si no hace nada como comprobar primero a Luhn; permítales ver los intentos fallidos.
Solo agregue algunos fragmentos de código adicionales que otros pueden encontrar útiles (no el código PHP).
PYTHON (código de línea única, probablemente no tan eficiente)
Validar:
>>> 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
Para devolver el dígito de control requerido:
>>> (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
Funciones de MySQL
Funciones "ccc" y "ccd" (verificación de tarjeta de crédito y dígito de tarjeta de crédito)
Tenga en cuenta que la función "ccc" tiene una verificación adicional en la que si la suma calculada es 0, el resultado devuelto será siempre FALSO, por lo que un número CC totalmente cero nunca se validará como correcto (bajo el comportamiento normal, se validaría correctamente). Esta característica se puede agregar / eliminar según sea necesario; tal vez sea útil, dependiendo de los requisitos específicos.
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;
Las funciones se pueden usar directamente en consultas 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)
Código 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";
}
}
Uso
echo validateCC("1738292928284637", "Dinners");
Más información teórica se puede encontrar aquí: