├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .php-cs-fixer.dist.php ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── db └── bank_db.php ├── parse.php ├── phpcs.xml ├── phpunit.xml ├── psalm.xml ├── rector.php ├── src ├── BankDb.php ├── BankDbException.php └── BankInfo.php └── tests └── BankDb ├── BankDbTest.php └── BankInfoTest.php /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gitsubmodule 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | reviewers: 9 | - chekalsky 10 | assignees: 11 | - chekalsky 12 | 13 | - package-ecosystem: composer 14 | directory: "/" 15 | schedule: 16 | interval: weekly 17 | open-pull-requests-limit: 10 18 | reviewers: 19 | - chekalsky 20 | assignees: 21 | - chekalsky 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: PHP Tests 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | php-lint: 8 | name: PHP Lint 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | php-version: [ '8.3', '8.4' ] 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup PHP with tools 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: ${{ matrix.php-version }} 20 | tools: php-cs-fixer, cs2pr 21 | 22 | - name: PHP CS Fixer 23 | #run: php-cs-fixer fix --using-cache=no --dry-run --format=checkstyle | cs2pr 24 | # tmp fix for PHP 8.4 25 | run: PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --using-cache=no --dry-run --format=checkstyle | cs2pr 26 | 27 | php-unit: 28 | name: PHP Unit Tests (php${{ matrix.php-versions }}) 29 | runs-on: ubuntu-latest 30 | continue-on-error: ${{ matrix.experimental }} 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | php-versions: [ '8.3', '8.4' ] 35 | experimental: [ false ] 36 | #include: 37 | # - php-versions: '8.1' 38 | # experimental: true 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Setup PHP with tools 43 | uses: shivammathur/setup-php@v2 44 | with: 45 | php-version: ${{ matrix.php-versions }} 46 | tools: composer 47 | 48 | - name: Get Composer Cache Directory 49 | id: composer-cache 50 | run: | 51 | echo "::set-output name=dir::$(composer config cache-files-dir)" 52 | 53 | - uses: actions/cache@v4 54 | with: 55 | path: ${{ steps.composer-cache.outputs.dir }} 56 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 57 | restore-keys: | 58 | ${{ runner.os }}-composer- 59 | 60 | - name: Setup problem matchers for PHPUnit 61 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 62 | 63 | - name: Install dependencies 64 | run: composer install --prefer-dist --no-progress 65 | 66 | - name: PHPUnit 67 | run: vendor/bin/phpunit 68 | 69 | psalm: 70 | name: PHP Static Analysis (php${{ matrix.php-versions }}) 71 | runs-on: ubuntu-latest 72 | continue-on-error: ${{ matrix.experimental }} 73 | strategy: 74 | fail-fast: false 75 | matrix: 76 | php-versions: [ '8.3', '8.4' ] 77 | experimental: [ false ] 78 | #include: 79 | # - php-versions: '8.1' 80 | # experimental: true 81 | steps: 82 | - uses: actions/checkout@v4 83 | 84 | - name: Setup PHP with tools 85 | uses: shivammathur/setup-php@v2 86 | with: 87 | php-version: ${{ matrix.php-versions }} 88 | tools: composer, psalm 89 | 90 | - name: Get Composer Cache Directory 91 | id: composer-cache 92 | run: | 93 | echo "::set-output name=dir::$(composer config cache-files-dir)" 94 | 95 | - uses: actions/cache@v4 96 | with: 97 | path: ${{ steps.composer-cache.outputs.dir }} 98 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 99 | restore-keys: | 100 | ${{ runner.os }}-composer- 101 | 102 | - name: Install dependencies 103 | run: composer install --prefer-dist --no-progress 104 | 105 | - name: Psalm 106 | run: psalm --output-format=github -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | composer.lock 4 | .phpunit.result.cache 5 | .php_cs.cache 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "banks-db"] 2 | path = banks-db 3 | url = https://github.com/ramoona/banks-db.git 4 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ->exclude('vendor') 6 | ->exclude('banks-db'); 7 | 8 | $config = new PhpCsFixer\Config(); 9 | 10 | return $config->setUsingCache(true) 11 | ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) 12 | ->setCacheFile(__DIR__ . '/.php_cs.cache') 13 | ->setRules([ 14 | '@PER-CS2.0' => true, 15 | 'align_multiline_comment' => true, 16 | 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'return', 'throw', 'try']], 17 | 'class_attributes_separation' => ['elements' => ['method' => 'one']], 18 | 'no_empty_statement' => true, 19 | 'no_leading_namespace_whitespace' => true, 20 | 'no_multiline_whitespace_around_double_arrow' => true, 21 | 'multiline_whitespace_before_semicolons' => false, 22 | 'no_unused_imports' => true, 23 | 'no_unneeded_import_alias' => true, 24 | 'no_whitespace_before_comma_in_array' => true, 25 | 'no_whitespace_in_blank_line' => true, 26 | 'single_import_per_statement' => true, 27 | 'ordered_imports' => [ 28 | 'sort_algorithm' => 'alpha', 29 | 'imports_order' => ['class', 'function', 'const'], 30 | ], 31 | 'phpdoc_add_missing_param_annotation' => true, 32 | 'phpdoc_align' => true, 33 | 'phpdoc_annotation_without_dot' => true, 34 | 'phpdoc_indent' => true, 35 | 'phpdoc_no_empty_return' => true, 36 | 'phpdoc_no_useless_inheritdoc' => true, 37 | 'phpdoc_order' => true, 38 | 'phpdoc_return_self_reference' => ['replacements' => ['this' => 'self']], 39 | 'phpdoc_scalar' => true, 40 | 'phpdoc_separation' => ['skip_unlisted_annotations' => true], 41 | 'phpdoc_single_line_var_spacing' => true, 42 | 'phpdoc_trim' => true, 43 | 'phpdoc_trim_consecutive_blank_line_separation' => true, 44 | 'phpdoc_types_order' => true, 45 | 'phpdoc_var_annotation_correct_order' => true, 46 | 'phpdoc_var_without_name' => true, 47 | 'single_quote' => true, 48 | 'short_scalar_cast' => true, 49 | 'standardize_not_equals' => true, 50 | 'ternary_operator_spaces' => true, 51 | 'ternary_to_null_coalescing' => true, 52 | 'trim_array_spaces' => true, 53 | 'visibility_required' => ['elements' => ['property', 'method', 'const']], 54 | 'yoda_style' => false, 55 | 'no_trailing_comma_in_singleline' => true, 56 | 'single_line_after_imports' => true, 57 | 'global_namespace_import' => [ 58 | 'import_classes' => true, 59 | 'import_constants' => true, 60 | 'import_functions' => true, 61 | ], 62 | 'clean_namespace' => true, 63 | ]) 64 | ->setFinder($finder); 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2 5 | - 7.3 6 | - 7.4 7 | 8 | install: 9 | - travis_retry composer self-update 10 | - travis_retry composer install --no-interaction --no-suggest --prefer-dist 11 | 12 | script: 13 | - composer test 14 | 15 | after_success: 16 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ilya Chekalskiy 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Banks DB 2 | 3 | [![PHP Tests](https://github.com/chekalsky/php-banks-db/actions/workflows/ci.yml/badge.svg)](https://github.com/chekalsky/php-banks-db/actions/workflows/ci.yml) 4 | 5 | > It is a PHP port of [ramoona's banks-db](https://github.com/ramoona/banks-db). 6 | 7 | Returns bank's name and brand color by bank card number's first digits (BIN, Issuer Identification Numbers, IIN). 8 | 9 | ### Installation 10 | 11 | ``` 12 | composer require chekalskiy/php-banks-db 13 | ``` 14 | 15 | ### Basic usage 16 | 17 | ```php 18 | $card_prefix = '5275 9400 0000 0000'; // we only need first 6 digits but it could be the whole card number 19 | 20 | try { 21 | $bank_db = new BankDb(); 22 | $bank_info = $bank_db->getBankInfo($card_prefix); 23 | 24 | $result = [ 25 | 'is_unknown' => $bank_info->isUnknown(), // is bank unknown 26 | 'name' => $bank_info->getTitle(true), 27 | 'color' => $bank_info->getColor(), 28 | 'type' => $bank_info->getCardType(), 29 | ]; 30 | 31 | return $result; 32 | } catch (BankDbException $e) { 33 | // todo handle exception 34 | } 35 | ``` 36 | 37 | ### About database 38 | 39 | We use simple PHP-file with an array inside (it's regenerates every time ramoona's repository is updated). It's very fast and simple way to work with data because of opcache enabled by default in PHP 7. But you can extend `BankDB` class to make it work with redis or something, but for most cases compiled php-file is OK. 40 | 41 | #### Database update 42 | Database updates from [original library](https://github.com/ramoona/banks-db) by me. To update php cache file after manual database change run `composer rebuild`. 43 | 44 | ### Contributions 45 | 46 | Feel free to open [an issue](https://github.com/chekalskiy/php-banks-db/issues) on every question you have. If you have new prefixes for database please commit them to [ramoona/banks-db repository](https://github.com/ramoona/banks-db) — I will update them once your PR will me merged there. 47 | 48 | --- 49 | 50 | > It's a community driven database, so it can potentially contains mistakes. 51 | 52 | For UI examples see the [original library](https://github.com/ramoona/banks-db). 53 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chekalskiy/php-banks-db", 3 | "description": "PHP bank cards IIN/BIN database. Get bank info by card number", 4 | "license": "MIT", 5 | "keywords": ["bin", "card", "creditcard", "iin", "issuer identification numbers"], 6 | "homepage": "https://github.com/chekalsky/php-banks-db", 7 | "authors": [ 8 | { 9 | "name": "Ilya Chekalsky", 10 | "email": "ilya@chekalsky.com", 11 | "homepage": "https://chekalsky.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=8.3" 16 | }, 17 | "require-dev": { 18 | "ext-json": "*", 19 | "phpunit/phpunit": "^12.0", 20 | "rector/rector": "^2.0", 21 | "symfony/var-exporter": "^7.2" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "BankDb\\": "src/" 26 | } 27 | }, 28 | "scripts": { 29 | "test": [ 30 | "vendor/bin/phpunit --coverage-clover=coverage.xml" 31 | ], 32 | "lint": [ 33 | "vendor/bin/php-cs-fixer fix --config=.php_cs", 34 | "vendor/bin/phpcbf -w" 35 | ], 36 | "rebuild": [ 37 | "php parse.php" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /parse.php: -------------------------------------------------------------------------------- 1 | [], 26 | 'banks' => [], 27 | ]; 28 | 29 | $bank_id = 0; 30 | $max_length = 6; 31 | 32 | $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($banks_db_path)); 33 | 34 | /** @var SplFileInfo $file */ 35 | foreach ($rii as $file) { 36 | if ($file->isDir()) { 37 | continue; 38 | } 39 | 40 | if ($file->getExtension() !== 'json') { 41 | continue; 42 | } 43 | 44 | $json_file = $file->openFile(); 45 | 46 | $contents = $json_file->fread($json_file->getSize()); 47 | 48 | $bank_data = json_decode($contents, true); 49 | 50 | if (!isset($bank_data['prefixes'])) { 51 | printError('Corrupted file: ' . $file->getPathname()); 52 | 53 | continue; 54 | } 55 | 56 | ++$bank_id; 57 | 58 | $bank_data_stored = $bank_data; 59 | unset($bank_data_stored['prefixes']); 60 | 61 | $database['banks'][$bank_id] = $bank_data_stored; 62 | 63 | foreach ($bank_data['prefixes'] as $prefix) { 64 | $len = strlen((string) $prefix); 65 | 66 | for ($i = $len; $i <= $len; $i++) { 67 | $len_diff = $max_length - $len; 68 | 69 | if ($len_diff > 0) { 70 | for ($l = 1; $l <= $len_diff; $l++) { 71 | $count_prefixes = 10 ** $l; 72 | 73 | for ($k = 0; $k < $count_prefixes; $k++) { 74 | $new_prefix = (int) sprintf('%d%d', $prefix, $k); 75 | 76 | addPrefix($new_prefix, $bank_id, $database); 77 | } 78 | } 79 | } else { 80 | addPrefix($prefix, $bank_id, $database); 81 | } 82 | } 83 | } 84 | } 85 | 86 | $database_export = sprintf(" %s)', $prefix, $bank_id, $database['prefixes'][$prefix])); 105 | 106 | return; 107 | } 108 | 109 | $database['prefixes'][$prefix] = $bank_id; 110 | } 111 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | parse.php 15 | src/ 16 | tests/ 17 | 18 | 19 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./src 6 | 7 | 8 | 9 | 10 | tests 11 | 12 | 13 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | withPaths([ 9 | __DIR__ . '/src', 10 | __DIR__ . '/tests', 11 | ]) 12 | ->withPhpSets() 13 | ->withTypeCoverageLevel(0) 14 | ->withDeadCodeLevel(0) 15 | ->withCodeQualityLevel(0); 16 | -------------------------------------------------------------------------------- /src/BankDb.php: -------------------------------------------------------------------------------- 1 | initializeDatabase($db_file_path); 29 | } 30 | 31 | /** 32 | * @param string $card_number 33 | * 34 | * @return BankInfo 35 | */ 36 | public function getBankInfo(string $card_number): BankInfo 37 | { 38 | $card_number = preg_replace('/\D/', '', $card_number); 39 | 40 | $prefix = str_pad(substr((string) $card_number, 0, static::PREFIX_LENGTH), static::PREFIX_LENGTH, '0'); 41 | 42 | $bank_id = $this->getBankIdByPrefix((int) $prefix); 43 | 44 | if ($bank_id > 0) { 45 | return new BankInfo($this->getBankInfoFromDatabase($bank_id), $prefix); 46 | } 47 | 48 | return new BankInfo([], $card_number); 49 | } 50 | 51 | /** 52 | * Database init 53 | * 54 | * @param null|string $file_path 55 | * 56 | * @throws BankDbException 57 | */ 58 | protected function initializeDatabase(string $file_path = null): void 59 | { 60 | if ($file_path === null) { 61 | $file_path = __DIR__ . '/../db/bank_db.php'; 62 | } 63 | 64 | if (!is_readable($file_path)) { 65 | throw new BankDbException('Cannot find DB file'); 66 | } 67 | 68 | $this->database = include $file_path; 69 | } 70 | 71 | /** 72 | * @param int $prefix 73 | * 74 | * @return int `0` if not found 75 | */ 76 | protected function getBankIdByPrefix(int $prefix): int 77 | { 78 | if (isset($this->database['prefixes'][$prefix])) { 79 | return (int) $this->database['prefixes'][$prefix]; 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | /** 86 | * @param int $id 87 | * 88 | * @return array 89 | */ 90 | protected function getBankInfoFromDatabase(int $id): array 91 | { 92 | return $this->database['banks'][$id]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/BankDbException.php: -------------------------------------------------------------------------------- 1 | '/^(4026|417500|4405|4508|4844|4913|4917)/', 34 | 'interpayment' => '/^636/', 35 | 'unionpay' => '/^(62|88)/', 36 | 'discover' => '/^6(?:011|4|5)/', 37 | 'maestro' => '/^(50|5[6-9]|6)/', 38 | 'visa' => '/^4/', 39 | 'mastercard' => '/^(5[1-5]|(?:222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720))/', // [2221-2720] 40 | 'amex' => '/^3[47]/', 41 | 'diners' => '/^3(?:0([0-5]|95)|[689])/', 42 | 'jcb' => '/^(?:2131|1800|(?:352[89]|35[3-8][0-9]))/', // 3528-3589 43 | 'mir' => '/^220[0-4]/', 44 | ]; 45 | 46 | public function __construct(protected array $data, string $prefix = '') 47 | { 48 | if (!isset($this->data['name'])) { 49 | $this->makeUnknown(); 50 | } 51 | 52 | $prefix = substr($prefix, 0, static::PREFIX_LENGTH); 53 | 54 | foreach (static::$card_prefixes as $card_type => $card_prefix) { 55 | if (preg_match($card_prefix, $prefix)) { 56 | $this->card_type = $card_type; 57 | break; 58 | } 59 | } 60 | } 61 | 62 | public function getTitle(bool $is_local = true): string 63 | { 64 | if ($is_local && isset($this->data['localTitle'])) { 65 | return $this->data['localTitle']; 66 | } 67 | 68 | return $this->data['engTitle'] ?? $this->data['name']; 69 | } 70 | 71 | public function getCountryCode(): string 72 | { 73 | return $this->data['country']; 74 | } 75 | 76 | public function getUrl(): string 77 | { 78 | return $this->data['url']; 79 | } 80 | 81 | /** 82 | * @return string in hex format `#0088cf` 83 | */ 84 | public function getColor(): string 85 | { 86 | return $this->data['color']; 87 | } 88 | 89 | /** 90 | * @return bool returns true for banks with revoked license 91 | */ 92 | public function isDefunct(): bool 93 | { 94 | return isset($this->data['defunct']) && $this->data['defunct']; 95 | } 96 | 97 | /** 98 | * @return bool 99 | */ 100 | public function isUnknown(): bool 101 | { 102 | return $this->is_unknown; 103 | } 104 | 105 | /** 106 | * Card type from prefix 107 | * 108 | * For possible types see `$card_prefixes` 109 | * 110 | * @return string 111 | */ 112 | public function getCardType(): string 113 | { 114 | return $this->card_type; 115 | } 116 | 117 | /** 118 | * Make this bank object unknown 119 | */ 120 | protected function makeUnknown(): void 121 | { 122 | $this->is_unknown = true; 123 | $this->data = [ 124 | 'name' => 'unknown', 125 | 'localTitle' => 'Unknown Bank', 126 | 'engTitle' => 'Unknown Bank', 127 | 'country' => 'us', 128 | 'url' => '', 129 | 'color' => '#ffffff', 130 | ]; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /tests/BankDb/BankDbTest.php: -------------------------------------------------------------------------------- 1 | bankDb = new BankDb(); 16 | } 17 | 18 | public function testCanBeConstructed(): void 19 | { 20 | $this->assertNotEmpty($this->bankDb); 21 | } 22 | 23 | public function testExceptionIfDatabaseNotFound(): void 24 | { 25 | $this->expectException(BankDbException::class); 26 | 27 | new BankDb('/unknown/path'); 28 | } 29 | 30 | public function testGetBankInfoIsWorking(): void 31 | { 32 | $bank_info = $this->bankDb->getBankInfo('400000'); 33 | 34 | $this->assertTrue($bank_info->isUnknown()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/BankDb/BankInfoTest.php: -------------------------------------------------------------------------------- 1 | bankDb = new BankDb(); 18 | } 19 | 20 | #[DataProvider('anyInputProvider')] 21 | public function testAnyInput(string $input, bool $expectedUnknown, ?string $compareWith = null): void 22 | { 23 | $bankInfo = $this->bankDb->getBankInfo($input); 24 | 25 | $this->assertSame($expectedUnknown, $bankInfo->isUnknown()); 26 | 27 | if ($compareWith !== null) { 28 | $this->assertSame( 29 | $this->bankDb->getBankInfo($compareWith)->getTitle(), 30 | $bankInfo->getTitle(), 31 | ); 32 | } 33 | } 34 | 35 | public static function anyInputProvider(): array 36 | { 37 | return [ 38 | 'spaced input' => ['5321 30', false, '532130'], 39 | 'clean input' => ['522319', false, '522319'], 40 | 'special chars' => ['5!3_2l1$3*0', false, '532130'], 41 | 'long number' => ['5321300000000000', false, '532130'], 42 | 'short valid number' => ['48153', false, '48153'], 43 | 'empty input' => ['', true, null], 44 | ]; 45 | } 46 | 47 | #[DataProvider('cardTypesProvider')] 48 | public function testCardTypes(string $prefix, string $expectedType): void 49 | { 50 | $this->assertSame($expectedType, $this->bankDb->getBankInfo($prefix)->getCardType()); 51 | } 52 | 53 | public static function cardTypesProvider(): array 54 | { 55 | return [ 56 | 'amex 1' => ['340000', 'amex'], 57 | 'amex 2' => ['370000', 'amex'], 58 | 'unionpay 1' => ['620000', 'unionpay'], 59 | 'unionpay 2' => ['880000', 'unionpay'], 60 | 'discover 1' => ['601100', 'discover'], 61 | 'discover 2' => ['640000', 'discover'], 62 | 'discover 3' => ['650000', 'discover'], 63 | 'electron 1' => ['402600', 'electron'], 64 | 'electron 2' => ['417500', 'electron'], 65 | 'interpayment' => ['636000', 'interpayment'], 66 | 'maestro 1' => ['501800', 'maestro'], 67 | 'maestro 2' => ['561200', 'maestro'], 68 | 'maestro 3' => ['589300', 'maestro'], 69 | 'maestro 4' => ['630400', 'maestro'], 70 | 'maestro 5' => ['639000', 'maestro'], 71 | 'visa 1' => ['4', 'visa'], 72 | 'visa 2' => ['411111', 'visa'], 73 | 'visa 3' => ['400000', 'visa'], 74 | 'mastercard 1' => ['51', 'mastercard'], 75 | 'mastercard 2' => ['510000', 'mastercard'], 76 | 'mastercard 3' => ['520000', 'mastercard'], 77 | 'mastercard 4' => ['530000', 'mastercard'], 78 | 'mastercard 5' => ['540000', 'mastercard'], 79 | 'mastercard 6' => ['550000', 'mastercard'], 80 | 'mastercard 7' => ['222100', 'mastercard'], 81 | 'mastercard 8' => ['259000', 'mastercard'], 82 | 'mastercard 9' => ['272000', 'mastercard'], 83 | 'diners 1' => ['360000', 'diners'], 84 | 'diners 2' => ['300000', 'diners'], 85 | 'diners 3' => ['305000', 'diners'], 86 | 'diners 4' => ['309500', 'diners'], 87 | 'diners 5' => ['380000', 'diners'], 88 | 'diners 6' => ['390000', 'diners'], 89 | 'jcb 1' => ['352800', 'jcb'], 90 | 'jcb 2' => ['355000', 'jcb'], 91 | 'jcb 3' => ['358900', 'jcb'], 92 | 'mir 1' => ['220000', 'mir'], 93 | 'mir 2' => ['220400', 'mir'], 94 | ]; 95 | } 96 | 97 | #[DataProvider('banksProvider')] 98 | public function testBanks(string $prefix, string $expectedTitle): void 99 | { 100 | $this->assertSame($expectedTitle, $this->bankDb->getBankInfo($prefix)->getTitle(false)); 101 | } 102 | 103 | public static function banksProvider(): array 104 | { 105 | return [ 106 | 'Bank Otkritie' => ['532130', 'Bank Otkritie'], 107 | 'Alfa-Bank' => ['428906', 'Alfa-Bank'], 108 | ]; 109 | } 110 | 111 | #[DataProvider('prefixProvider')] 112 | public function testColor(string $prefix): void 113 | { 114 | $this->assertMatchesRegularExpression( 115 | '/^#[a-f0-9]{6}$/', 116 | $this->bankDb->getBankInfo($prefix)->getColor(), 117 | ); 118 | } 119 | 120 | public static function prefixProvider(): array 121 | { 122 | return [ 123 | 'valid prefix' => ['428906'], 124 | 'empty prefix' => [''], 125 | ]; 126 | } 127 | 128 | public function testCountryCode(): void 129 | { 130 | $this->assertSame('ru', $this->bankDb->getBankInfo('428906')->getCountryCode()); 131 | } 132 | 133 | public function testUrl(): void 134 | { 135 | $this->assertMatchesRegularExpression( 136 | '/^https?:\/\//', 137 | $this->bankDb->getBankInfo('428906')->getUrl(), 138 | ); 139 | } 140 | 141 | public function testDefunct(): void 142 | { 143 | $this->assertFalse($this->bankDb->getBankInfo('428906')->isDefunct()); 144 | } 145 | } 146 | --------------------------------------------------------------------------------