diff --git a/lib/card_bins.js b/lib/card_bins.js new file mode 100644 index 0000000..e9c11f4 --- /dev/null +++ b/lib/card_bins.js @@ -0,0 +1,112 @@ +const CARD_BINS = { + '400443': '40036', + '400820': '40131', + '402766': '40127', + '403707': '40002', + '404360': '40036', + '405306': '40002', + '409851': '40012', + '415231': '40012', + '416916': '40137', + '418914': '40072', + '418928': '40072', + '419821': '40127', + '421003': '40030', + '421316': '40021', + '426808': '40132', + '430967': '40021', + '433454': '40072', + '434798': '40058', + '455504': '40012', + '455509': '40012', + '455510': '40012', + '455511': '40012', + '457249': '40130', + '457476': '40136', + '460766': '40128', + '462974': '40036', + '465828': '40036', + '469693': '40133', + '473701': '40036', + '474174': '40058', + '474176': '40021', + '476588': '40072', + '476684': '40002', + '476687': '40002', + '477210': '40012', + '477211': '40012', + '477213': '40012', + '477214': '40012', + '477291': '40012', + '477292': '40012', + '481279': '37019', + '481515': '40012', + '481516': '40012', + '483030': '40021', + '483104': '40127', + '483112': '40127', + '491089': '40021', + '491282': '40021', + '491344': '37019', + '491566': '40072', + '491580': '40072', + '511114': '40072', + '512280': '40030', + '517439': '40012', + '517712': '40002', + '517721': '40002', + '518004': '40002', + '518853': '40002', + '518899': '40002', + '520021': '40002', + '520116': '40002', + '520416': '40002', + '520694': '40002', + '520698': '40002', + '522130': '40002', + '524711': '40002', + '525424': '40002', + '525678': '40002', + '526192': '37166', + '526354': '40127', + '526424': '40072', + '526476': '40042', + '526498': '40036', + '527333': '40036', + '528843': '40002', + '528851': '40002', + '529091': '40002', + '530056': '40002', + '534381': '40127', + '535875': '40012', + '535943': '40062', + '539975': '40036', + '539978': '40060', + '542015': '40012', + '542073': '40012', + '543924': '40138', + '545631': '40002', + '548234': '40002', + '548236': '40002', + '549138': '40002', + '549639': '40002', + '549949': '40002', + '551238': '40127', + '551507': '37166', + '553467': '40127', + '553800': '40113', + '554629': '40012', + '557604': '40127', + '557907': '40014', + '557908': '40014', + '557909': '40014', + '557910': '40014', + '557920': '40044', + '557922': '40044', + '558426': '40002', + '585678': '40002', + '854829': '40002', + '882290': '40002', +}; + +module.exports = { CARD_BINS }; diff --git a/lib/index.d.ts b/lib/index.d.ts index 468497b..518e90e 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,9 +1,10 @@ export const BANKS: Record; export const BANK_NAMES: Record; +export const CARD_BINS: Record; export function validateClabe(clabe: string): boolean; export function computeControlDigit(clabe: string): string; -export function getBankName(clabe: string | null | undefined): string | null; -export function getBankNameOrThrow(clabe: string): string; +export function getBankName(account: string | null | undefined): string | null; +export function getBankNameOrThrow(account: string): string; export function generateNewClabes( numberOfClabes: number, prefix: string, diff --git a/lib/index.js b/lib/index.js index 9225239..ab2404a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,5 @@ const { BANKS, BANK_NAMES } = require('./banks.js'); +const { CARD_BINS } = require('./card_bins.js'); const CLABE_LENGTH = 18; const CLABE_WEIGHTS = [3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7]; @@ -35,33 +36,60 @@ function validateClabe(clabe) { clabe.substring(CLABE_LENGTH - 1) === computeControlDigit(clabe); } -function getBankName(clabe) { +function getBankName(account) { /* - Devuelve el nombre del banco basado en los primeros 3 dígitos. - Devuelve null si la entrada es nula/vacía o si el código no está registrado. - Para mantener el comportamiento previo (lanzar excepción), usar getBankNameOrThrow. + Devuelve el nombre del banco basado en el tipo de cuenta: + - 18 dígitos: CLABE → busca por código ABM (primeros 3 dígitos) + - 15-16 dígitos: Tarjeta → busca por BIN (primeros 6 dígitos) + Devuelve null si la entrada es inválida o si el código no está registrado. + Para mantener el comportamiento estricto (lanzar excepción), usar getBankNameOrThrow. */ - if (typeof clabe !== 'string' || clabe.length < 3) return null; - const code = clabe.substring(0, 3); - const bankName = BANK_NAMES[BANKS[code]]; - return bankName === undefined ? null : bankName; + if (typeof account !== 'string' || account.length < 3) return null; + + if (account.length === CLABE_LENGTH) { + const code = account.substring(0, 3); + const bankName = BANK_NAMES[BANKS[code]]; + return bankName === undefined ? null : bankName; + } + + if (account.length >= 15 && account.length <= 16) { + const bin = account.substring(0, 6); + const bankName = BANK_NAMES[CARD_BINS[bin]]; + return bankName === undefined ? null : bankName; + } + + return null; } -function getBankNameOrThrow(clabe) { +function getBankNameOrThrow(account) { /* - Versión estricta de getBankName. Lanza Error si la CLABE es inválida o si el - código no está registrado. Equivalente al comportamiento de getBankName antes - de la versión 1.3.0. + Versión estricta de getBankName. Lanza Error si la cuenta es inválida o si el + código no está registrado. + Soporta CLABEs (18 dígitos) y tarjetas (15-16 dígitos). */ - if (typeof clabe !== 'string' || clabe.length < 3) { - throw new Error('CLABE inválida: debe ser un string de al menos 3 caracteres'); + if (typeof account !== 'string' || account.length < 3) { + throw new Error('Cuenta inválida: debe ser un string de al menos 3 caracteres'); } - const code = clabe.substring(0, 3); - const bankName = BANK_NAMES[BANKS[code]]; - if (bankName === undefined) { - throw new Error('Ningún banco tiene este código ' + code); + + if (account.length === CLABE_LENGTH) { + const code = account.substring(0, 3); + const bankName = BANK_NAMES[BANKS[code]]; + if (bankName === undefined) { + throw new Error('Ningún banco tiene este código ' + code); + } + return bankName; + } + + if (account.length >= 15 && account.length <= 16) { + const bin = account.substring(0, 6); + const bankName = BANK_NAMES[CARD_BINS[bin]]; + if (bankName === undefined) { + throw new Error('Ningún banco tiene este BIN ' + bin); + } + return bankName; } - return bankName; + + throw new Error('Cuenta inválida: debe ser CLABE (18 dígitos) o tarjeta (15-16 dígitos)'); } function generateNewClabes(numberOfClabes, prefix) { @@ -148,6 +176,7 @@ function removeBank(bankCodeBanxico) { module.exports = { BANKS, BANK_NAMES, + CARD_BINS, validateClabe, computeControlDigit, getBankName, diff --git a/package-lock.json b/package-lock.json index cb6fee0..d89754c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,7 +280,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -727,7 +726,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", diff --git a/package.json b/package.json index babb045..403556c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clabe-js", - "version": "1.3.0", + "version": "1.4.0", "description": "Validate, generate and resolve bank names for Mexican CLABE numbers.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/test/test.js b/test/test.js index 89d1005..e7b3f77 100644 --- a/test/test.js +++ b/test/test.js @@ -16,6 +16,8 @@ const { expect } = require('chai'); const VALID_CLABE = '002000000000000008'; const INVALID_CLABE_CONTROL_DIGIT = '002000000000000007'; const INVALID_CLABE_BANK_CODE = '000000000000000000'; // Control digit es valido +const VALID_CARD_BBVA = '4152310000000000'; // BIN 415231 -> 40012 -> BBVA Mexico +const VALID_CARD_BANAMEX = '5177120000000000'; // BIN 517712 -> 40002 -> Banamex describe('computeControlDigit', function () { @@ -67,19 +69,47 @@ describe('getBankName', function () { }); }); +describe('getBankName con tarjetas', function () { + it('resuelve BBVA Mexico para tarjeta con BIN 415231', function () { + expect(getBankName(VALID_CARD_BBVA)).to.equal('BBVA Mexico'); + }); + it('resuelve Banamex para tarjeta con BIN 517712', function () { + expect(getBankName(VALID_CARD_BANAMEX)).to.equal('Banamex'); + }); + it('retorna null para tarjeta con BIN desconocido', function () { + expect(getBankName('0000000000000000')).to.equal(null); + }); + it('soporta tarjetas de 15 dígitos (Amex)', function () { + expect(getBankName('415231000000000')).to.equal('BBVA Mexico'); + }); + it('retorna null para longitudes no soportadas (10 dígitos)', function () { + expect(getBankName('4152310000')).to.equal(null); + }); +}); + + describe('getBankNameOrThrow', function () { - it('returns bank name if code exists', function () { + it('returns bank name if code exists (CLABE)', function () { expect(getBankNameOrThrow(VALID_CLABE)).to.equal('Banamex'); }); + it('returns bank name for card number', function () { + expect(getBankNameOrThrow(VALID_CARD_BBVA)).to.equal('BBVA Mexico'); + }); it('throws when bank code is unknown', function () { expect(() => getBankNameOrThrow(INVALID_CLABE_BANK_CODE)).to.throw(Error); }); - it('throws when CLABE is null', function () { + it('throws when card BIN is unknown', function () { + expect(() => getBankNameOrThrow('0000000000000000')).to.throw(Error); + }); + it('throws when input is null', function () { expect(() => getBankNameOrThrow(null)).to.throw(Error); }); - it('throws when CLABE is too short', function () { + it('throws when input is too short', function () { expect(() => getBankNameOrThrow('00')).to.throw(Error); }); + it('throws when length is not CLABE nor card', function () { + expect(() => getBankNameOrThrow('4152310000')).to.throw(Error); + }); }); describe('addBank / removeBank', function () {