Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ If you are using Laravel, add an alias in `config/app.php`
### Validate a card number knowing the type:

```php
$card = CreditCard::validCreditCard('5500005555555559', 'mastercard');
$card = CreditCard::validCreditCard('5500005555555559', CreditCard::TYPE_MASTERCARD);
print_r($card);
```

Expand All @@ -43,12 +43,47 @@ Output:
```
Array
(
[valid] => 1
[valid] => true
[number] => 5500005555555559
[type] => mastercard
)
```

### Validate a card number using a range of allowed types:

```php
$card = CreditCard::validCreditCard('5500005555555559', array(CreditCard::TYPE_VISA, CreditCard::TYPE_MASTERCARD));
print_r($card);
```

Output:

```
Array
(
[valid] => true
[number] => 5500005555555559
[type] => mastercard
)
```


```php
$card = CreditCard::validCreditCard('371449635398431', array(CreditCard::TYPE_VISA, CreditCard::TYPE_MASTERCARD));
print_r($card);
```

Output:

```
Array
(
[valid] => false
[number] =>
[type] =>
)
```

### Validate a card number and return the type:

```php
Expand All @@ -61,7 +96,7 @@ Output:
```
Array
(
[valid] => 1
[valid] => true
[number] => 371449635398431
[type] => amex
)
Expand All @@ -70,7 +105,7 @@ Array
### Validate the CVC

```php
$validCvc = CreditCard::validCvc('234', 'visa');
$validCvc = CreditCard::validCvc('234', CreditCard::TYPE_VISA);
var_dump($validCvc);
```

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lib-pcre": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "4.7.*"
"phpunit/phpunit": "^4.8"
},
"autoload": {
"psr-4": {
Expand Down
138 changes: 112 additions & 26 deletions src/CreditCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,93 +10,140 @@

namespace Inacho;

use Inacho\Exception\CreditCardException;
use Inacho\Exception\CreditCardLengthException;
use Inacho\Exception\CreditCardLuhnException;
use Inacho\Exception\CreditCardPatternException;
use Inacho\Exception\CreditCardTypeException;

class CreditCard
{
const TYPE_AMEX = 'amex';
const TYPE_DANKORT = 'dankort';
const TYPE_DINERS_CLUB = 'dinersclub';
const TYPE_DISCOVER = 'discover';
const TYPE_FORBRUGSFORENINGEN = 'forbrugsforeningen';
const TYPE_JCB = 'jcb';
const TYPE_MAESTRO = 'maestro';
const TYPE_MASTERCARD = 'mastercard';
const TYPE_MIR = 'mir';
const TYPE_UNION_PAY = 'unionpay';
const TYPE_UZCARD = 'uzcard';
const TYPE_HUMO = 'humo';
const TYPE_VISA = 'visa';
const TYPE_VISA_ELECTRON = 'visaelectron';

protected static $cards = array(
// Debit cards must come first, since they have more specific patterns than their credit-card equivalents.

'visaelectron' => array(
'type' => 'visaelectron',
self::TYPE_VISA_ELECTRON => array(
'type' => self::TYPE_VISA_ELECTRON,
'pattern' => '/^4(026|17500|405|508|844|91[37])/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
'maestro' => array(
'type' => 'maestro',
'pattern' => '/^(5(018|0[23]|[68])|6(39|7))/',
self::TYPE_MAESTRO => array(
'type' => self::TYPE_MAESTRO,
'pattern' => '/^(5(018|0[23]|[68])|6(05|39|7))/',
'length' => array(12, 13, 14, 15, 16, 17, 18, 19),
'cvcLength' => array(3),
'luhn' => true,
),
'forbrugsforeningen' => array(
'type' => 'forbrugsforeningen',
self::TYPE_FORBRUGSFORENINGEN => array(
'type' => self::TYPE_FORBRUGSFORENINGEN,
'pattern' => '/^600/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
'dankort' => array(
'type' => 'dankort',
self::TYPE_DANKORT => array(
'type' => self::TYPE_DANKORT,
'pattern' => '/^5019/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
// Credit cards
'visa' => array(
'type' => 'visa',
self::TYPE_VISA => array(
'type' => self::TYPE_VISA,
'pattern' => '/^4/',
'length' => array(13, 16),
'cvcLength' => array(3),
'luhn' => true,
),
'mastercard' => array(
'type' => 'mastercard',
self::TYPE_MIR => array(
'type' => self::TYPE_MIR,
'pattern' => '/^220[0-4]/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
self::TYPE_MASTERCARD => array(
'type' => self::TYPE_MASTERCARD,
'pattern' => '/^(5[0-5]|2[2-7])/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
'amex' => array(
'type' => 'amex',
self::TYPE_AMEX => array(
'type' => self::TYPE_AMEX,
'pattern' => '/^3[47]/',
'format' => '/(\d{1,4})(\d{1,6})?(\d{1,5})?/',
'length' => array(15),
'cvcLength' => array(3, 4),
'luhn' => true,
),
'dinersclub' => array(
'type' => 'dinersclub',
self::TYPE_DINERS_CLUB => array(
'type' => self::TYPE_DINERS_CLUB,
'pattern' => '/^3[0689]/',
'length' => array(14),
'cvcLength' => array(3),
'luhn' => true,
),
'discover' => array(
'type' => 'discover',
self::TYPE_DISCOVER => array(
'type' => self::TYPE_DISCOVER,
'pattern' => '/^6([045]|22)/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
'unionpay' => array(
'type' => 'unionpay',
self::TYPE_UNION_PAY => array(
'type' => self::TYPE_UNION_PAY,
'pattern' => '/^(62|88)/',
'length' => array(16, 17, 18, 19),
'cvcLength' => array(3),
'luhn' => false,
),
'jcb' => array(
'type' => 'jcb',
self::TYPE_UZCARD => array(
'type' => self::TYPE_UZCARD,
'pattern' => '/^8600/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
self::TYPE_HUMO => array(
'type' => self::TYPE_HUMO,
'pattern' => '/^9860/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
self::TYPE_JCB => array(
'type' => self::TYPE_JCB,
'pattern' => '/^35/',
'length' => array(16),
'cvcLength' => array(3),
'luhn' => true,
),
);

public static function validCreditCard($number, $type = null)
/**
* @param string $number
* @param string|string[]|null $allowTypes By default, all card types are allowed
* @return array
*/
public static function validCreditCard($number, $allowTypes = null)
{
$ret = array(
'valid' => false,
Expand All @@ -105,12 +152,18 @@ public static function validCreditCard($number, $type = null)
);

// Strip non-numeric characters
$number = preg_replace('/[^0-9]/', '', $number);
$number = preg_replace('/\D/', '', $number);

if (empty($type)) {
if (is_string($allowTypes)) {
$type = $allowTypes;
} else {
$type = self::creditCardType($number);
}

if (empty($type) || is_array($allowTypes) && !in_array($type, $allowTypes)) {
return $ret;
}

if (array_key_exists($type, self::$cards) && self::validCard($number, $type)) {
return array(
'valid' => true,
Expand All @@ -122,6 +175,39 @@ public static function validCreditCard($number, $type = null)
return $ret;
}

/**
* @param string $number
* @param string|string[]|null $allowTypes By default, all card types are allowed
* @throws CreditCardException
*/
public static function checkCreditCard($number, $allowTypes = null)
{
// Strip non-numeric characters
$number = preg_replace('/\D/', '', $number);

if (is_string($allowTypes)) {
$type = $allowTypes;
} else {
$type = self::creditCardType($number);
}

if (empty($type) || (is_array($allowTypes) && !in_array($type, $allowTypes))) {
throw new CreditCardTypeException(sprintf('Type "%s" card is not allowed', $type));
}

if (!self::validPattern($number, $type)) {
throw new CreditCardPatternException(sprintf('Wrong "%s" card pattern', $number));
}

if (!self::validLength($number, $type)) {
throw new CreditCardLengthException(sprintf('Incorrect "%s" card length', $number));
}

if (!self::validLuhn($number, $type)) {
throw new CreditCardLuhnException(sprintf('Invalid card number: "%s". Checksum is wrong', $number));
}
}

public static function validCvc($cvc, $type)
{
return (ctype_digit($cvc) && array_key_exists($type, self::$cards) && self::validCvcLength($cvc, $type));
Expand Down
6 changes: 6 additions & 0 deletions src/Exception/CreditCardException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Inacho\Exception;

class CreditCardException extends \Exception
{
}
6 changes: 6 additions & 0 deletions src/Exception/CreditCardLengthException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Inacho\Exception;

class CreditCardLengthException extends CreditCardException
{
}
6 changes: 6 additions & 0 deletions src/Exception/CreditCardLuhnException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Inacho\Exception;

class CreditCardLuhnException extends CreditCardException
{
}
6 changes: 6 additions & 0 deletions src/Exception/CreditCardPatternException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Inacho\Exception;

class CreditCardPatternException extends CreditCardException
{
}
6 changes: 6 additions & 0 deletions src/Exception/CreditCardTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Inacho\Exception;

class CreditCardTypeException extends CreditCardException
{
}
Loading