Last active
December 28, 2022 18:20
-
-
Save ArrayIterator/342064b1355adec857df21924cf35290 to your computer and use it in GitHub Desktop.
Revisions
-
ArrayIterator revised this gist
Dec 28, 2022 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -364,8 +364,8 @@ public static function generateFake(int $type = null): ?array $card .= $random; } $range = range(0, 9); shuffle($range); do { $currentCard = $card . array_shift($range); } while (count($range) > 0 && false === self::calculateChecksum($currentCard)); -
ArrayIterator revised this gist
Dec 28, 2022 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -367,7 +367,7 @@ public static function generateFake(int $type = null): ?array do { shuffle($range); $currentCard = $card . array_shift($range); } while (count($range) > 0 && false === self::calculateChecksum($currentCard)); return [ 'id' => $type, -
ArrayIterator revised this gist
Dec 28, 2022 . 1 changed file with 0 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,11 +8,6 @@ */ class CreditCard { const AMERICAN_EXPRESS = 0; const UNIONPAY = 1; const DINERS_CLUB = 2; -
ArrayIterator created this gist
Dec 28, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,384 @@ <?php declare(strict_types=1); namespace ArrayIterator\Generator; /** * Credit card validator & generator */ class CreditCard { /** * Detected CCI list * * @var string */ const AMERICAN_EXPRESS = 0; const UNIONPAY = 1; const DINERS_CLUB = 2; const DINERS_CLUB_US = 3; const DISCOVER = 4; const JCB = 5; const LASER = 6; const MAESTRO = 7; const MASTERCARD = 8; const SOLO = 9; const VISA = 10; const MIR = 11; const CARD_NAME = [ self::AMERICAN_EXPRESS => 'American Express', self::UNIONPAY => 'Unionpay', self::DINERS_CLUB => 'Diners Club', self::DINERS_CLUB_US => 'Diners Club US', self::DISCOVER => 'Discover', self::JCB => 'JCB', self::LASER => 'Laser', self::MAESTRO => 'Maestro', self::MASTERCARD => 'Mastercard', self::SOLO => 'Solo', self::VISA => 'Visa', self::MIR => 'Mir', ]; const CARD_LENGTH = [ self::AMERICAN_EXPRESS => [15], self::DINERS_CLUB => [14], self::DINERS_CLUB_US => [16], self::DISCOVER => [16, 19], self::JCB => [15, 16], self::LASER => [16, 17, 18, 19], self::MAESTRO => [12, 13, 14, 15, 16, 17, 18, 19], self::MASTERCARD => [16], self::SOLO => [16, 18, 19], self::UNIONPAY => [16, 17, 18, 19], self::VISA => [13, 16, 19], self::MIR => [13, 16], ]; const CARD_PREFIX = [ self::AMERICAN_EXPRESS => ['34', '37'], self::DINERS_CLUB => ['300', '301', '302', '303', '304', '305', '36'], self::DINERS_CLUB_US => ['54', '55'], self::DISCOVER => [ '6011', '622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925', '644', '645', '646', '647', '648', '649', '65', ], self::JCB => ['1800', '2131', '3528', '3529', '353', '354', '355', '356', '357', '358'], self::LASER => ['6304', '6706', '6771', '6709'], self::MAESTRO => [ '5018', '5020', '5038', '6304', '6759', '6761', '6762', '6763', '6764', '6765', '6766', '6772', ], self::MASTERCARD => [ '2221', '2222', '2223', '2224', '2225', '2226', '2227', '2228', '2229', '223', '224', '225', '226', '227', '228', '229', '23', '24', '25', '26', '271', '2720', '51', '52', '53', '54', '55', ], self::SOLO => ['6334', '6767'], self::UNIONPAY => [ '622126', '622127', '622128', '622129', '62213', '62214', '62215', '62216', '62217', '62218', '62219', '6222', '6223', '6224', '6225', '6226', '6227', '6228', '62290', '62291', '622920', '622921', '622922', '622923', '622924', '622925', ], self::VISA => ['4'], self::MIR => ['2200', '2201', '2202', '2203', '2204'], ]; private static ?array $cardPrefixes = null; private static ?array $cardValidLength = null; private static array $cardPrefixesLength = []; /** * @return int[][] */ public static function getCardPrefixes() : array { if (self::$cardPrefixes !== null) { return self::$cardPrefixes; } self::$cardPrefixes = []; foreach (self::CARD_PREFIX as $cardType => $prefixes) { foreach ($prefixes as $prefix) { self::$cardPrefixes[$prefix][] = $cardType; } } ksort(self::$cardPrefixes); self::$cardPrefixes = array_reverse(self::$cardPrefixes, true); return self::$cardPrefixes; } /** * @param int $length * * @return bool */ public static function isValidLength(int $length): bool { return isset(self::getCardLengthList()[$length]); } /** * @return array<int[]> */ private static function getCardLengthList(): array { if (self::$cardValidLength === null) { self::$cardValidLength = []; foreach (self::CARD_LENGTH as $cardType => $lengths) { foreach ($lengths as $l) { self::$cardValidLength[$l][] = $cardType; } } } return self::$cardValidLength; } /** * @param int $length * * @return ?array<int[]> */ private static function getCardPrefixesLength(int $length): ?array { if (isset(self::$cardPrefixesLength[$length])) { return self::$cardPrefixesLength[$length]; } $identities = self::getCardLengthList()[$length]??null; if (!$identities) { return null; } self::$cardPrefixesLength[$length] = []; foreach ($identities as $identity) { if (!isset(self::CARD_PREFIX[$identity])) { continue; } array_map(static function ($prefix) use ($length, $identity) { // keep prefix as string self::$cardPrefixesLength[$length][":$prefix"][] = $identity; }, self::CARD_PREFIX[$identity]); } ksort(self::$cardPrefixesLength[$length]); self::$cardPrefixesLength[$length] = array_reverse(self::$cardPrefixesLength[$length], true); return self::$cardPrefixesLength[$length]; } /** * @param string|int $number * * @return ?string */ public static function checkSum(string|int $number): ?string { $number = is_string($number) ? str_replace(['-', '.', ' '], '', $number) : (string) $number; // 12 -> 19 if (!preg_match('~^[1-9][0-9]{11,18}$~', $number)) { return null; } return self::calculateChecksum($number) ? $number : null; } /** * @param string $number * * @return bool */ private static function calculateChecksum(string $number): bool { $length = strlen($number); $sum = 0; $weight = 2; for ($i = $length - 2; $i >= 0; $i--) { $digit = $weight * (int) $number[$i]; $sum += floor($digit / 10) + $digit % 10; $weight = $weight % 2 + 1; } return ((10 - $sum % 10) % 10 == $number[$length - 1]); } /** * @param string|int $number * * @return ?array returning null if credit card does not recognize, * and contains 2 array when card is co-branding */ public static function getType(string|int $number): ?array { $number = self::checkSum($number); if (!$number) { return null; } $length = strlen($number); $prefixes = self::isValidLength($length) ? self::getCardPrefixesLength($length) : null; if (!$prefixes) { return null; } $number = ":$number"; foreach ($prefixes as $prefix => $identities) { if (str_starts_with($number, $prefix)) { $result = []; foreach ($identities as $identity) { $result[$identity] = [ 'id' => $identity, 'name' => self::CARD_NAME[$identity], ]; } return $result; } } return null; } /** * @param string|int $number * * @return bool */ public static function isValid(string|int $number): bool { return self::getType($number) !== null; } /** * Returning null if not valid type * @param ?int $type * * @return ?array{id:integer,name:string,number:string,cvv:integer} */ public static function generateFake(int $type = null): ?array { if ($type === null) { $types = array_keys(self::CARD_NAME); shuffle($types); $type = reset($types); } $lengths = self::CARD_LENGTH[$type]??null; $prefixes = self::CARD_PREFIX[$type]??null; if (!$lengths || !$prefixes) { return null; } $min = min($lengths); $counted = 16; while ($counted > $min && !in_array($counted, $lengths, true)) { $counted--; } if ($counted <= $min) { $counted = reset($lengths); } shuffle($prefixes); $card = reset($prefixes); while (strlen($card) < ($counted-1)) { $random = rand(0, 9); $card .= $random; } $range = range(0, 9); do { shuffle($range); $currentCard = $card . array_shift($range); } while (false === self::calculateChecksum($currentCard)); return [ 'id' => $type, 'name' => self::CARD_NAME[$type], 'number' => implode('-', str_split($currentCard, 4)), 'cvv' => mt_rand(100, 999) ]; } }