Skip to content

Commit

Permalink
v0.15.0 feature to get banks for specific countries
Browse files Browse the repository at this point in the history
  • Loading branch information
linda-rian committed Jan 8, 2019
1 parent ba191f1 commit a73ed8e
Show file tree
Hide file tree
Showing 9 changed files with 691 additions and 879 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ See also best practices for billing forms:

## API

There are two options to use BanksDB depends of whether you need specific countries or not.

#### If you need banks for all countries
Library exports `banksDB` function. It accepts bankcard number and returns
bank object.

Expand Down Expand Up @@ -118,6 +121,28 @@ for ( let bank of banksDB.data ) {
}
```

#### If you need only banks for specific countries

Instead of `banks-db` use `banks-db/core`:
```js
var banksDBCore = require('banks-db/core');
```
Then require desired countries from `banks-db/banks` by two letters code:
```js
var banksOfRussia = require('banks-db/banks/ru');
var banksOfChina = require('banks-db/banks/cn');
```
All that left is to call `banksDBCore` with your countries data to initialize. `banksDBCore` is a function that accepts one argument with banks data for countries that you've specified, and returns an instance of BanksDB object with `findBank` method and `data` property.
```js
var BanksDB = banksDBCore([banksOfRussia, banksOfChina]);
// var BanksDB = banksDBCore(banksOfRussia); no need for an array if there's only one country
```
That's it! Ready to use:
```js
var bank = BanksDB.findBank('5275 9400 0000 0000');
var data = BanksDB.data;
```

### Bank Object

* `type`: bankcard type. For example, `'visa'` or `'mastercard'`.
Expand Down
22 changes: 12 additions & 10 deletions build-indexes.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
const fs = require('fs-promise');
const path = require('path');
const helper = require('./helper');
const { writeFile, readdir, lstatSync } = require('fs-promise');
const { join } = require('path');
const { logError, logSuccess } = require('./helper');

const path = name => join(__dirname, name ? `banks/${name}` : 'banks');

const generateRequiresFile = (filePath, requires) => {
const content = `module.exports = [${requires}\n];\n`;

fs.writeFile(path.join(__dirname, `banks/${filePath}`), content).then(() => {
writeFile(path(filePath), content).then(() => {
const dirPath = new RegExp(`${__dirname}/`);
helper.success(filePath.replace(dirPath, ''));
}).catch((err) => { helper.error(`Can't write ${filePath} \n${err}`); });
logSuccess(filePath.replace(dirPath, ''));
}).catch((err) => { logError(`Can't write ${filePath} \n${err}`); });
};

const createRequires = (files) => {
const countries = files.filter(file => fs.lstatSync(path.join(__dirname, `banks/${file}`)).isDirectory());
const countries = files.filter(file => lstatSync(path(file)).isDirectory());
const indexFilesRequires = countries.map(country => `\n require('./${country}/index')`);

countries.forEach((country) => {
fs.readdir(path.join(__dirname, `banks/${country}`))
readdir(path(country))
.then((items) => {
const banksRequires = items
.filter(file => /\.json$/.test(file))
.map(bank => `\n require('./${bank.replace(/\.json$/, '')}')`);
generateRequiresFile(`${country}/index.js`, banksRequires);
}).catch((err) => { helper.error(`Can't read ${country} directory \n${err}`); });
}).catch((err) => { logError(`Can't read ${country} directory \n${err}`); });
});

generateRequiresFile('index.js', indexFilesRequires);
};

fs.readdir(path.join(__dirname, 'banks')).then(createRequires).catch(helper.error);
readdir(path()).then(createRequires).catch(logError);
49 changes: 49 additions & 0 deletions core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
var type = require('./type');

function BanksDB(data) {
var banksData = [];
var prefixes = {};

if (Array.isArray(data)) {
banksData = data;
} else if (data) {
banksData.push(data);
} else {
console.warn('Empty or invalid arguments of Banks DB Core call. Check Banks DB docs for details.');
}

this.data = banksData.reduce(function (acc, current) {
return acc.concat(current);
}, []);

this.data.forEach(function (bank) {
bank.code = bank.country + '-' + bank.name;
bank.prefixes.forEach(function (prefix) {
prefixes[prefix] = bank;
});
});

this.findBank = function (cardNumber) {
cardNumber = cardNumber || '';

var card = cardNumber.toString().replace(/[^\d]/g, '');
var first5 = card.substr(0, 5);
var first6 = card.substr(0, 6);
var bank = prefixes[first6] || prefixes[first5];
var result = {
type: type(card)
};

if (bank) {
for (var el in bank) {
result[el] = bank[el];
}
}

return result;
};
}

module.exports = function banksDBCore(data) {
return new BanksDB(data);
};
6 changes: 3 additions & 3 deletions helper.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const chalk = require('chalk');

module.exports = {
error: (text) => {
logError: (text) => {
console.error(chalk.red(`✘ ${text}`));
},
success: (text) => {
logSuccess: (text) => {
console.log(`${chalk.green('✓ ')}${chalk.white(text)}`);
},
warn: (text) => {
logWarning: (text) => {
console.error(chalk.yellow(`⚠ ${text}`));
}
};
37 changes: 4 additions & 33 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,8 @@
var type = require('./type');
var data = require('./banks/index');
var banksDBCore = require('./core');

var banks = [];
var prefixes = {};
var BanksDB = banksDBCore(data);

data.forEach(function (item) {
banks = banks.concat(item);
});
module.exports = BanksDB.findBank.bind(BanksDB);

banks.forEach(function (bank) {
bank.code = bank.country + '-' + bank.name;
bank.prefixes.forEach(function (prefix) {
prefixes[prefix] = bank;
});
});

module.exports = function findBank(cardNumber) {
cardNumber = cardNumber || '';
var card = cardNumber.toString().replace(/[^\d]/g, '');
var first5 = card.substr(0, 5);
var first6 = card.substr(0, 6);
var bank = prefixes[first6] || prefixes[first5];
var result = {
type: type(card)
};

if (bank) {
for (var el in bank) {
result[el] = bank[el];
}
}

return result;
};

module.exports.data = banks;
module.exports.data = BanksDB.data;
11 changes: 5 additions & 6 deletions lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require('fs-promise');
const path = require('path');
const { JSV } = require('JSV');
const jsonfile = require('jsonfile-promised');
const helper = require('./helper');
const { logError, logWarning } = require('./helper');

const linter = JSV.createEnvironment();

Expand All @@ -23,15 +23,14 @@ const lintBank = (bank, bankPath, bankName, country, schema) => new Promise((res
bank.prefixes.sort();
if (/[A-F]\w*/.test(bank.color)) {
bank.color = bank.color.toLowerCase();
helper.warn(`${bankPath}: bank color was changed to lowercase`);
logWarning(`${bankPath}: bank color was changed to lowercase`);
}
resolve(bank);
}
});

const lint = (files, schema) => new Promise((resolve, reject) => {
const countries = files.filter(file => fs.lstatSync(path.join(__dirname, `banks/${file}`)).isDirectory());

countries.reduce((countryPromise, country) => {
const banks = fs.readdirSync(
path.join(__dirname, `banks/${country}`)
Expand All @@ -44,7 +43,7 @@ const lint = (files, schema) => new Promise((resolve, reject) => {
.then(bank => lintBank(bank, bankPath, bankName, country, schema))
.then(bank => jsonfile.writeFile(fullPath, bank, { spaces: 2 }))
.then(() => {
helper.success(`banks/${country}/${bankName}`);
// logSuccess(`banks/${country}/${bankName}`);
})
.catch(reject));
}, Promise.resolve()));
Expand All @@ -57,7 +56,7 @@ const lint = (files, schema) => new Promise((resolve, reject) => {

jsonfile.readFile(path.join(__dirname, 'schema.json')).then((schema) => {
fs.readdir(path.join(__dirname, 'banks')).then(files => lint(files, schema)).catch((err) => {
helper.error(err);
logError(err);
process.exit(1);
});
}).catch(helper.error);
}).catch(logError);
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "banks-db",
"version": "0.14.0",
"version": "0.15.0",
"description": "Bankcards BIN database",
"author": "Linda Rian <[email protected]>",
"repository": "ramoona/banks-db",
Expand All @@ -22,8 +22,8 @@
"eslint-plugin-jsx-a11y": "6.x.x",
"eslint-plugin-react": "7.x.x",
"fs-promise": "2.x.x",
"jsonfile-promised": "0.x.x",
"jest": "23.x.x"
"jest": "23.x.x",
"jsonfile-promised": "0.x.x"
},
"scripts": {
"test": "jest && node lint.js && eslint *.js",
Expand Down
34 changes: 21 additions & 13 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
const banksDB = require('.');
const findBank = require('.');
const banksDBCore = require('./core');
const ruBanksData = require('./banks/ru');
const type = require('./type');

const BanksDB = banksDBCore(ruBanksData);

/* eslint-disable no-undef */

test('returns card type', () => expect(banksDB('5211784563802833').type).toBe('mastercard'));
test('returns card type', () => expect(findBank('5211784563802833').type).toBe('mastercard'));

test('finds bank by first 6 symbols', () => expect(banksDB('5211784563802833').name).toBe('alfabank'));
test('finds bank by first 6 symbols', () => expect(findBank('5211784563802833').name).toBe('alfabank'));

test('finds bank by first 5 symbols', () => expect(banksDB('4622384563802833').name).toBe('vtb24'));
test('finds bank by first 5 symbols', () => expect(findBank('4622384563802833').name).toBe('vtb24'));

test('returns false on unknown bank', () => expect(banksDB('4111111111111111').name).toBeUndefined());
test('returns false on unknown bank', () => expect(findBank('4111111111111111').name).toBeUndefined());

test('returns card type on unknown bank', () => expect(banksDB('4111111111111111').type).toBe('visa'));
test('returns card type on unknown bank', () => expect(findBank('4111111111111111').type).toBe('visa'));

test('finds bank by converted to string card number', () => expect(banksDB(4377734563802833).name).toBe('tinkoff'));
test('finds bank by converted to string card number', () => expect(findBank(4377734563802833).name).toBe('tinkoff'));

test('ignores non-digits symbols in card number', () => expect(banksDB('43-77-73-45-63-80-28-33').name).toBe('tinkoff'));
test('ignores non-digits symbols in card number', () => expect(findBank('43-77-73-45-63-80-28-33').name).toBe('tinkoff'));

test('ignores whitespaces', () => expect(banksDB('4627 3045 6380 2833').name).toBe('raiffeisen'));
test('ignores whitespaces', () => expect(findBank('4627 3045 6380 2833').name).toBe('raiffeisen'));

test('accepts undefined', () => expect(banksDB(undefined).name).toBeUndefined());
test('accepts undefined', () => expect(findBank(undefined).name).toBeUndefined());

test('returns card type', () => expect(type(4111111111111111)).toBe('visa'));

test('returns undefined on unknown card type', () => expect(type(123456)).toBeUndefined());

test('returns all banks data', () => expect(Array.isArray(banksDB.data)).toBeTruthy());
test('returns country + bankname', () => expect(findBank('5211784563802833').code).toBe('ru-alfabank'));

test('returns code from findBank.data', () => expect(typeof findBank.data[0].code).toBe('string'));

test('returns all banks data', () => expect(Array.isArray(findBank.data)).toBeTruthy());

test('returns country + bankname', () => expect(banksDB('5211784563802833').code).toBe('ru-alfabank'));
test('returns only specified banks', () => expect(BanksDB.data.length).toEqual(ruBanksData.length));

test('returns code from banksDB.data', () => expect(typeof banksDB.data[0].code).toBe('string'));
test('finds bank in specified banks', () => expect(BanksDB.findBank('4622384563802833').name).toBe('vtb24'));

/* eslint-enable no-undef */
Loading

0 comments on commit a73ed8e

Please sign in to comment.