├── .gitignore ├── phpunit.xml ├── src └── Plansky │ └── CreditCard │ ├── Validator.php │ ├── LuhnCalculator.php │ └── Generator.php ├── composer.json ├── bin ├── ccgenerator └── ccvalidator ├── LICENSE ├── test ├── Plansky │ └── CreditCard │ │ ├── GeneratorTest.php │ │ ├── ValidatorTest.php │ │ └── TestCase.php └── bin │ ├── CcValidatorTest.php │ └── CcGeneratorTest.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./test 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Plansky/CreditCard/Validator.php: -------------------------------------------------------------------------------- 1 | verificationDigit($number) == $lastDigit; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rplansky/credit-card", 3 | "description": "Simple package to generate and validate credit card numbers in PHP", 4 | "keywords": ["credit-card", "helper", "validator"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Ricardo Plansky", 9 | "homepage": "http://www.rplansky.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.4.0" 14 | }, 15 | "bin": [ 16 | "bin/ccgenerator", 17 | "bin/ccvalidator" 18 | ], 19 | "autoload": { 20 | "psr-0": { 21 | "Plansky\\CreditCard": "src/" 22 | }, 23 | "classmap": [ 24 | "test/Plansky/CreditCard/TestCase.php" 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bin/ccgenerator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | lot( 14 | isset($parameters['amount']) ? $parameters['amount'] : 1, 15 | isset($parameters['prefix']) ? $parameters['prefix'] : null, 16 | isset($parameters['length']) ? $parameters['length'] : 16 17 | ), 18 | isset($parameters['separator']) ? $parameters['separator'] : PHP_EOL 19 | ); 20 | } catch (\InvalidArgumentException $error) { 21 | echo 'ERROR: '.$error->getMessage(); 22 | } 23 | -------------------------------------------------------------------------------- /bin/ccvalidator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | $number, 16 | 'valid' => $validator->isValid($number) ? 'valid' : 'invalid', 17 | ]; 18 | 19 | if (strlen($number) > $length) { 20 | $length = strlen($number); 21 | } 22 | } 23 | 24 | foreach ($result as $line) { 25 | echo sprintf( 26 | '%s %s| %s', 27 | $line['number'], 28 | str_repeat(' ', $length - strlen($line['number'])), 29 | $line['valid'] 30 | ).PHP_EOL; 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ricardo Plansky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/Plansky/CreditCard/LuhnCalculator.php: -------------------------------------------------------------------------------- 1 | multiplyNumber($digit) : $digit; 26 | } 27 | 28 | return $sum; 29 | } 30 | 31 | /** 32 | * Retrives the corresponding verfication digit of the given credit card 33 | * number. If the verification digit is ten, returns zero 34 | * 35 | * @param string|integer $number 36 | * 37 | * @return integer 38 | */ 39 | public function verificationDigit($number) 40 | { 41 | return 10 - ($this->sum($number) % 10 ?: 10); 42 | } 43 | 44 | /** 45 | * Multiplies number by two and decrease 9 if the number is greater than 10 46 | * 47 | * @param integer $number 48 | * 49 | * @return integer 50 | */ 51 | private function multiplyNumber($number) 52 | { 53 | $result = $number * 2; 54 | 55 | return ($result >= 10) ? $result - 9 : $result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Plansky/CreditCard/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | generator->single(); 12 | 13 | $this->assertLength(16, $number); 14 | $this->assertTrue($this->validator->isValid($number)); 15 | } 16 | 17 | public function testSingleShouldGenerateAValidNumberWithGivenPrefix() 18 | { 19 | $this->assertPrefix('123', $this->generator->single(123)); 20 | } 21 | 22 | public function testSingleShouldGenerateAValidNumberWithCustomLength() 23 | { 24 | $this->assertLength(10, $this->generator->single(null, 10)); 25 | } 26 | 27 | public function testLotShouldGenerateAnArrayOfValidNumbers() 28 | { 29 | $numbers = $this->generator->lot(10); 30 | 31 | foreach ($numbers as $number) { 32 | $this->assertLength(16, $number); 33 | $this->assertTrue($this->validator->isValid($number)); 34 | } 35 | } 36 | 37 | public function testLotShouldGenerateAnArrayOfNumbersWithGivenPrefix() 38 | { 39 | $numbers = $this->generator->lot(10, 123); 40 | 41 | foreach ($numbers as $number) { 42 | $this->assertPrefix('123', $number); 43 | } 44 | } 45 | 46 | public function testLotShouldGenerateAnArrayOfValidNumbersWithCustomLength() 47 | { 48 | $numbers = $this->generator->lot(10, null, 10); 49 | 50 | foreach ($numbers as $number) { 51 | $this->assertLength(10, $number); 52 | } 53 | } 54 | 55 | /** 56 | * @expectedException \InvalidArgumentException 57 | * @expectedExceptionMessage The 'length' parameter should be greater than 'prefix' string length 58 | */ 59 | public function testSingleWithInsuficientLengthShouldThrowsException() 60 | { 61 | $this->generator->single('1234', 4); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/bin/CcValidatorTest.php: -------------------------------------------------------------------------------- 1 | commandPath = __DIR__.'/../../bin/ccvalidator'; 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function tearDown() 27 | { 28 | parent::tearDown(); 29 | unset($this->commandPath); 30 | } 31 | 32 | public function testCommandShouldValidateUniqueValidCreditCard() 33 | { 34 | $this->assertEquals( 35 | ['4072592516178960 | valid'], 36 | $this->exec(['4072592516178960']) 37 | ); 38 | } 39 | 40 | public function testCommandShouldValidateUniqueInvalidCreditCard() 41 | { 42 | $this->assertEquals( 43 | ['4072592516178965 | invalid'], 44 | $this->exec(['4072592516178965']) 45 | ); 46 | } 47 | 48 | public function testCommandShouldValidateMultipleNumbersAtOnce() 49 | { 50 | $numbers = [ 51 | '7042360861483694', 52 | '6668152906742750', 53 | '9464790787926399', 54 | ]; 55 | 56 | $expectedOutput = [ 57 | '7042360861483694 | valid', 58 | '6668152906742750 | invalid', 59 | '9464790787926399 | valid', 60 | ]; 61 | 62 | $this->assertEquals($expectedOutput, $this->exec($numbers)); 63 | } 64 | 65 | /** 66 | * Executes the ccvalidator command with the given numbers 67 | * 68 | * @param integer[] 69 | * 70 | * @return string[] Command output 71 | */ 72 | private function exec(array $numbers) 73 | { 74 | exec( 75 | sprintf('%s %s', $this->commandPath, implode($numbers, ' ')), 76 | $output 77 | ); 78 | 79 | return $output; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/Plansky/CreditCard/ValidatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $this->validator->isValid($number)); 15 | } 16 | 17 | /** 18 | * Retrives some credit card numbers 19 | * 20 | * @return array 21 | */ 22 | public function getNumbers() 23 | { 24 | return [ 25 | [4189343786029529, true], 26 | [3314371235421856, true], 27 | [6374319798301353, true], 28 | [4795233356809085, true], 29 | [5622154023963004, true], 30 | [8478091326016016, true], 31 | [5677882395681093, true], 32 | [1939738871582291, true], 33 | [4851540879338546, true], 34 | [7630614502651988, true], 35 | [5079130680956042, true], 36 | [5082644595250395, true], 37 | [1808712880876570, true], 38 | [1189343703328455, true], 39 | [8548419608785345, true], 40 | [3452386767190945, true], 41 | [5790632602471008, true], 42 | [5674392856342973, false], 43 | [5478659746532453, false], 44 | [2543294548735347, false], 45 | [3653654645643632, false], 46 | [1111111111111111, false], 47 | [5674302389652843, false], 48 | [9374326542364373, false], 49 | [0173543743843642, false], 50 | [1538435485473433, false], 51 | [5272937454172925, false], 52 | [7032532823732326, false], 53 | [4358713621331562, false], 54 | [6381517653213173, false], 55 | [2463174863424434, false], 56 | [4397482639449327, false], 57 | [0493747823649832, false], 58 | [4315487538423465, false], 59 | [3271849637432431, false], 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Plansky/CreditCard/Generator.php: -------------------------------------------------------------------------------- 1 | getRand($length - strlen($prefix)); 30 | 31 | return $number . (new LuhnCalculator())->verificationDigit($number); 32 | } 33 | 34 | /** 35 | * Generates the given amount of credit card numbers 36 | * 37 | * @param integer $amount 38 | * @param integer $prefix 39 | * @param integer $length 40 | * 41 | * @return integer[] 42 | */ 43 | public function lot($amount, $prefix = null, $length = 16) 44 | { 45 | $numbers = []; 46 | for ($index = 1; $index <= $amount; $index++) { 47 | $numbers[] = $this->single($prefix, $length); 48 | } 49 | 50 | return $numbers; 51 | } 52 | 53 | /** 54 | * Retrieves a random number to put in the middle of card number 55 | * 56 | * Example: 57 | * length = 5: Generates a number between 00000 and 99999 58 | * 59 | * @param integer $length 60 | * 61 | * @return integer 62 | */ 63 | private function getRand($length) 64 | { 65 | $rand = ''; 66 | for ($index = 1; $index < $length; $index++) { 67 | $rand .= rand(0, 9); 68 | } 69 | 70 | return $rand; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/Plansky/CreditCard/TestCase.php: -------------------------------------------------------------------------------- 1 | generator = new Generator(); 26 | $this->validator = new Validator(); 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function tearDown() 33 | { 34 | parent::tearDown(); 35 | unset($this->generator); 36 | unset($this->validator); 37 | } 38 | 39 | /** 40 | * Checks length of the given string 41 | * 42 | * @param integer $length 43 | * @param string $number 44 | */ 45 | protected function assertLength($length, $number) 46 | { 47 | $this->assertEquals($length, strlen($number)); 48 | } 49 | 50 | /** 51 | * Checks prefix of the given credit card number 52 | * 53 | * @param integer $prefix 54 | * @param string $number 55 | */ 56 | protected function assertPrefix($prefix, $number) 57 | { 58 | $this->assertStringStartsWith($prefix, $number); 59 | } 60 | 61 | /** 62 | * Checks if the given number is valid using Luhn Algorithm 63 | * 64 | * @param integer $number 65 | */ 66 | protected function assertCreditCard($number) 67 | { 68 | $lastDigit = substr($number, -1); 69 | $numberArray = array_reverse(str_split($number)); 70 | 71 | $sum = 0; 72 | for ($index = 0; $index < count($numberArray); $index++) { 73 | $digit = (int)$numberArray[$index]; 74 | $result = $digit * 2; 75 | $result = ($result >= 10) ? $result - 9 : $result; 76 | $sum += ($index % 2 == 0) ? $result : $digit; 77 | } 78 | 79 | return $lastDigit == (10 - ($sum % 10 ?: 10)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Credit Card helpers in PHP 2 | 3 | This is a simple package to create and validate credit card numbers. 4 | 5 | ## Installation via Composer 6 | 7 | ### In your project 8 | 9 | To use this package in your project, add it to your `composer.json` 10 | 11 | ```json 12 | "require": { 13 | "rplansky/credit-card": "dev-master" 14 | } 15 | ``` 16 | 17 | ### Global Installation 18 | 19 | This kind of installation is useful to use `ccgenerator` and `ccvalidator` 20 | commands globally on command line 21 | 22 | ```shell 23 | $ composer global require "rplansky/credit-card=*@dev" 24 | ``` 25 | 26 | ## Usage 27 | 28 | ### In code 29 | 30 | #### Validate Numbers 31 | 32 | This validator uses the [Luhn Algorithm](http://en.wikipedia.org/wiki/Luhn_algorithm) 33 | 34 | ```php 35 | $validator = new Plansky\CreditCard\Validator(); 36 | $validator->isValid('57234651187928922'); 37 | // true 38 | ``` 39 | 40 | #### Generate Random Numbers 41 | 42 | ##### Totally random 43 | 44 | ```php 45 | $generator = new Plansky\CreditCard\Generator(); 46 | $generator->single(); 47 | // 99119662018492824 48 | ``` 49 | 50 | ##### Prefix 51 | 52 | You can generate a random number using a brand prefix. 53 | 54 | ```php 55 | $generator = new Plansky\CreditCard\Generator(); 56 | $generator->single(301); // Generates a DINERS number 57 | // 30192056094873699 58 | ``` 59 | 60 | ##### Length 61 | 62 | Some brands have different number length. 63 | 64 | ```php 65 | $generator = new Plansky\CreditCard\Generator(); 66 | $generator->single(347, 15); // Generates an AMEX number 67 | // 3479966030620031 68 | ``` 69 | 70 | #### Generating Lots 71 | 72 | Using same parameters of `generate` method, except for the first parameter that 73 | is the amount of numbers that you want to generate, you can generate a lot of 74 | numbers at once 75 | 76 | ```php 77 | $generator = new Plansky\CreditCard\Generator(); 78 | $generator->lot(10, 347, 15); // Generates 10 AMEX numbers 79 | // array( 80 | // 0 => '3479132843454361', 81 | // 1 => '3479587605801416', 82 | // 2 => '3479861510504500', 83 | // 3 => '3479130090772634', 84 | // 4 => '3479427298826638', 85 | // 5 => '3479894458677616', 86 | // 6 => '3479713475691154', 87 | // 7 => '3479468840371814', 88 | // 8 => '3479219690811825', 89 | // 9 => '3479326005723791' 90 | // ) 91 | ``` 92 | 93 | ### In command line 94 | 95 | #### ccgenerator 96 | 97 | You can use same parameters of `Generator::lot()` method, plus, 98 | `separator` parameter to render your output 99 | 100 | ```shell 101 | $ ccgenerator --amount=3 --prefix=347 --length=15 --separator=" " 102 | # 347544073859505 347615533406853 347845409364916 103 | ``` 104 | 105 | #### ccvalidator 106 | 107 | You can pass numbers separated by spaces to validate more than one number at 108 | once. 109 | 110 | ```shell 111 | $ ccvalidator 347544073859505 347615533406852 347845409364916 112 | # 347544073859505 | valid 113 | # 347615533406852 | invalid 114 | # 347845409364916 | valid 115 | ``` 116 | -------------------------------------------------------------------------------- /test/bin/CcGeneratorTest.php: -------------------------------------------------------------------------------- 1 | commandPath = __DIR__.'/../../bin/ccgenerator'; 28 | $this->validator = new Validator(); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function tearDown() 35 | { 36 | parent::tearDown(); 37 | unset($this->commandPath); 38 | unset($this->validator); 39 | } 40 | 41 | public function testCommandShouldGenerateARandomValidNumber() 42 | { 43 | $this->assertTrue($this->validator->isValid($this->exec()[0])); 44 | } 45 | 46 | public function testCommandShouldGenerateARandomValidNumberWithPrefix() 47 | { 48 | $number = $this->exec(null, '34')[0]; 49 | 50 | $this->assertTrue($this->validator->isValid($number)); 51 | $this->assertStringStartsWith('34', $number); 52 | } 53 | 54 | public function testCommandShouldGenerateARandomValidNumberWithCustomLength() 55 | { 56 | $number = $this->exec(null, null, 10)[0]; 57 | 58 | $this->assertTrue($this->validator->isValid($number)); 59 | $this->assertEquals(10, strlen($number)); 60 | } 61 | 62 | public function testCommandShouldGenerateALotOfValidNumbers() 63 | { 64 | $numbers = $this->exec(10); 65 | 66 | foreach ($numbers as $number) { 67 | $this->assertTrue($this->validator->isValid($number)); 68 | } 69 | 70 | $this->assertEquals(10, count($numbers)); 71 | } 72 | 73 | public function testCommandShouldGenerateALotOfValidNumbersWithCustomSeparator() 74 | { 75 | $numbers = $this->exec(3, null, null, ','); 76 | $this->assertEquals(1, count($numbers)); 77 | 78 | $numbers = explode(',', $numbers[0]); 79 | $this->assertEquals(3, count($numbers)); 80 | 81 | foreach ($numbers as $number) { 82 | $this->assertTrue($this->validator->isValid($number)); 83 | } 84 | } 85 | 86 | public function testCommandWithInvalidLengthShouldReturnsAnError() 87 | { 88 | $this->assertEquals( 89 | 'ERROR: The \'length\' parameter should be greater than '. 90 | '\'prefix\' string length', 91 | $this->exec(null, '12345', 5)[0] 92 | ); 93 | } 94 | 95 | /** 96 | * Executes the ccgenerator command with the given parameters 97 | * 98 | * @param integer $amount 99 | * @param integer $prefix 100 | * @param integer $length 101 | * @param string $separator 102 | * 103 | * @return string[] Command output 104 | */ 105 | private function exec( 106 | $amount = null, 107 | $prefix = null, 108 | $length = null, 109 | $separator = null 110 | ) { 111 | $command = $this->commandPath; 112 | 113 | if (false === is_null($amount)) { 114 | $command .= ' --amount='.$amount; 115 | } 116 | 117 | if (false === is_null($prefix)) { 118 | $command .= ' --prefix='.$prefix; 119 | } 120 | 121 | if (false === is_null($length)) { 122 | $command .= ' --length='.$length; 123 | } 124 | 125 | if (false === is_null($separator)) { 126 | $command .= ' --separator='.$separator; 127 | } 128 | 129 | exec($command, $output); 130 | 131 | return $output; 132 | } 133 | } 134 | --------------------------------------------------------------------------------