-
Notifications
You must be signed in to change notification settings - Fork 115
Feat: Add bank validations #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export function isEven(number: number): boolean { | ||
| return number % 2 === 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { BankValidator } from "./bankValidator"; | ||
|
|
||
| export class BancoBanrisulValidator extends BankValidator{ | ||
|
|
||
| protected isValidFormat(bankAccount: string): boolean { | ||
| return /^\d{8}\-(\d{1}|x)$/.test(bankAccount); | ||
| } | ||
|
|
||
| protected checkDV(bankAccount: string): boolean { | ||
| return true; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("001", '00210169-6')).toBe(true); | ||
| expect(isValidAccount("001", '1171032-2')).toBe(true); | ||
| expect(isValidAccount("001", '19932-X')).toBe(true); | ||
| expect(isValidAccount("001", '1050769-8')).toBe(true); | ||
| expect(isValidAccount("001", '87889-8')).toBe(true); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("001", '')).toBe(false); | ||
| expect(isValidAccount("001", '123456789-0')).toBe(false); | ||
| expect(isValidAccount("001", 'asdfasdf')).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("237", '0238069-2')).toBe(true); | ||
| expect(isValidAccount("237", '0301357-P')).toBe(true); | ||
| expect(isValidAccount("237", '0325620-0')).toBe(true); | ||
| expect(isValidAccount("237", '0284025-1')).toBe(true); | ||
| expect(isValidAccount("237", '0195470-9')).toBe(true); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("237", '')).toBe(false); | ||
| expect(isValidAccount("237", '123456789-0')).toBe(false); | ||
| expect(isValidAccount("237", 'asdfasdf')).toBe(false); | ||
| }) | ||
| }) | ||
| describe("invalid account",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("237", '0195470-1')).toBe(false); | ||
| expect(isValidAccount("237", '0195471-P')).toBe(false); | ||
| expect(isValidAccount("237", '0195491-0')).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("104", '00100000448-6', "2004")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("104", '00100000448-7', "0189")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("104", '', "")).toBe(false); | ||
| expect(isValidAccount("104", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("104", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("745", '0007500465-8', "0075")).toBe(true); | ||
| expect(isValidAccount("745", '1420412612-0', "0013")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("745", '1420412612-1', "0013")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("745", '', "")).toBe(false); | ||
| expect(isValidAccount("745", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("745", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("399", '853838-6', "0007")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("399", '853838-5', "0007")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("399", '', "")).toBe(false); | ||
| expect(isValidAccount("399", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("399", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("341", '02366-1', "2545")).toBe(true); | ||
| expect(isValidAccount("341", '98197-0', "0031")).toBe(true); | ||
| expect(isValidAccount("341", '11745-6', "4102")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("341", '11745-6', "0031")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("341", '', "")).toBe(false); | ||
| expect(isValidAccount("341", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("341", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("356", '5711460-9', "1835")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("356", '5711460-0', "1835")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("356", '', "")).toBe(false); | ||
| expect(isValidAccount("356", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("356", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { isValidAccount } from '..'; | ||
|
|
||
| describe("isValid", ()=>{ | ||
| describe("valid accounts",()=>{ | ||
| it("returns true", ()=>{ | ||
| expect(isValidAccount("033", '01017417-9', "0189")).toBe(true); | ||
| }) | ||
| }) | ||
| describe("invalid accounts",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("033", '01017417-1', "0189")).toBe(false); | ||
| }) | ||
| }) | ||
| describe("wrong format",()=>{ | ||
| it("returns false", ()=>{ | ||
| expect(isValidAccount("001", '', "")).toBe(false); | ||
| expect(isValidAccount("001", '123456789-0', "")).toBe(false); | ||
| expect(isValidAccount("001", 'asdfasdf', "")).toBe(false); | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { CitibankValidator } from './validators/citibank'; | ||
| import { HSBCValidator } from './validators/hsbc'; | ||
| import { RealValidator } from './validators/real'; | ||
| import { ItauValidator } from './validators/itau'; | ||
| import { BradescoValidator } from './validators/bradesco'; | ||
| import { CaixaEconomicaFederalValidator } from './validators/caixaEconomicaFederal'; | ||
| import { SantanderValidator } from './validators/santander'; | ||
| import { BancoDoBrasilValidator } from './validators/bancoDoBrasil'; | ||
|
|
||
| type BankCode = "001" | "033" | "104" | "237" | "341" | "356" | "399" | "745"; | ||
|
|
||
| const BANKS = { | ||
| "001": BancoDoBrasilValidator, | ||
| "033": SantanderValidator, | ||
| "104": CaixaEconomicaFederalValidator, | ||
| "237": BradescoValidator, | ||
| "341": ItauValidator, | ||
| "356": RealValidator, | ||
| "399": HSBCValidator, | ||
| "745": CitibankValidator | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param bankCode 001: Banco do Brasil, 033: Santander, 104: CEF, 237: Bradesco, 341: Itau, 356: Real, 399: HSBC, 745: Citibank | ||
| * @param accountNumber | ||
| * @param agencyNumber Banks 003, 104, 237, 341, 356, 399 and 745 requires agencyNumber | ||
| * @returns | ||
| */ | ||
| export function isValidAccount(bankCode:BankCode, accountNumber: string, agencyNumber?: string) { | ||
| const bank = new BANKS[bankCode](); | ||
| return bank.isValid(accountNumber, agencyNumber); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a lot of more banks in Brazil...It would be nice to provide something where we can add more banks to BANKS. addBankValidator(BankCode, Validator);There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont know if we need that... I think if some bank appears and we don't have support for it, we just can integrate and publish another library version, probably if someone needs that and we don't have it, they can open an issue telling that to us and we just add it. What do you think about it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the only case where I'm a pessimist...It's more likely that people will implement on their end and don't make a PR. 😞 So, it'd be nice to have that, but it's not mandatory. |
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,31 @@ | ||||||
| import { BankValidator } from "./bankValidator"; | ||||||
|
|
||||||
| export class BancoDoBrasilValidator extends BankValidator{ | ||||||
|
|
||||||
| protected isValidFormat(bankAccount: string): boolean { | ||||||
| return /^\d{1,8}(\-(\d{1}|X))?$/.test(bankAccount); | ||||||
| } | ||||||
|
|
||||||
| protected checkDV(bankAccount: string): boolean { | ||||||
| const bankAccountSplited = bankAccount.split("-"); | ||||||
| const account = bankAccountSplited[0].length < 8 ? this.formatLeftZeros(8 - bankAccountSplited[0].length, bankAccountSplited[0]) : bankAccountSplited[0]; | ||||||
| const verificationDigit = bankAccountSplited[1] || 'X' | ||||||
| return this.getAccountVerificationDigit(account) === verificationDigit; | ||||||
| } | ||||||
|
|
||||||
| private formatLeftZeros(count:number, initialValue:string):string{ | ||||||
| if(count) { | ||||||
| return this.formatLeftZeros(count-1, `0${initialValue}`) | ||||||
| } | ||||||
| return initialValue; | ||||||
| } | ||||||
|
|
||||||
| private getAccountVerificationDigit(accountNumber:string){ | ||||||
| const digitSum = accountNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (9-index),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const dv = (11 - digitSum%11); | ||||||
| if(dv === 10) return "X"; | ||||||
| if(dv === 11) return "0"; | ||||||
| return dv.toString() | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| export abstract class BankValidator { | ||
| public isValid(bankAccount: string, bankAgency?: string): boolean { | ||
| return this.isValidFormat(bankAccount,bankAgency) && this.checkDV(bankAccount, bankAgency); | ||
| } | ||
|
|
||
| protected abstract isValidFormat(bankAccount: string, bankAgency?: string): boolean; | ||
|
|
||
| protected abstract checkDV(bankAccount: string, bankAgency?: string): boolean; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,22 @@ | ||||||
| import { BankValidator } from "./bankValidator"; | ||||||
|
|
||||||
| export class BradescoValidator extends BankValidator{ | ||||||
| protected isValidFormat(bankAccount: string): boolean { | ||||||
| return /^\d{7}\-(\d{1}|P)?$/.test(bankAccount); | ||||||
| } | ||||||
|
|
||||||
| protected checkDV(bankAccount: string): boolean { | ||||||
| const bankAccountSplited = bankAccount.split("-"); | ||||||
| const account = bankAccountSplited[0]; | ||||||
| const verificationDigit = bankAccountSplited[1]; | ||||||
| return this.getAccountVerificationDigit(account) === verificationDigit; | ||||||
| } | ||||||
|
|
||||||
| private getAccountVerificationDigit(accountNumber:string){ | ||||||
| const digitSum = accountNumber.split("").reverse().map(Number).reduce((acc,cur,index)=> acc + cur * ((index%7+2)),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const rest = digitSum%11; | ||||||
| if(rest === 1) return "P"; | ||||||
| if(rest === 0) return "0"; | ||||||
| return (11 - rest).toString() | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||
| import { BankValidator } from "./bankValidator"; | ||||||
|
|
||||||
| export class CaixaEconomicaFederalValidator extends BankValidator{ | ||||||
| protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { | ||||||
| return /^\d{11}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); | ||||||
| } | ||||||
|
|
||||||
| protected checkDV(bankAccount: string, agencyNumber:string): boolean { | ||||||
| const account = bankAccount.split("-")[0]; | ||||||
| const accountType = account.slice(0,3); | ||||||
| const accountNumber = account.slice(-8) | ||||||
| const verificationDigit = bankAccount.split("-")[1]; | ||||||
| return this.getAccountVerificationDigit(accountNumber, agencyNumber, accountType) === verificationDigit; | ||||||
| } | ||||||
|
|
||||||
| private getAccountVerificationDigit(accountNumber:string, agencyNumber:string, accountType: string){ | ||||||
| const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (8 - index),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const accountTypeSum = accountType.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (4 - index),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const accountNumberSum = accountNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (9 - index),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const sum = (agencySum + accountNumberSum + accountTypeSum)* 10 | ||||||
| const mod = Math.trunc(sum/11); | ||||||
| const rest = Math.abs(mod * 11 - sum) | ||||||
| return (rest === 10 ? 0: rest).toString(); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||
| import { BankValidator } from "./bankValidator"; | ||||||
|
|
||||||
| export class CitibankValidator extends BankValidator{ | ||||||
| protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { | ||||||
| return /^\d{7}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); | ||||||
| } | ||||||
|
|
||||||
| protected checkDV(bankAccount: string, bankAgency:string): boolean { | ||||||
| const accountNumber = bankAccount.split("-")[0]; | ||||||
| const verificationDigit = bankAccount.split("-")[1]; | ||||||
| return this.getAccountVerificationDigit(accountNumber, bankAgency) === verificationDigit; | ||||||
| } | ||||||
|
|
||||||
| private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ | ||||||
| const sum = `${agencyNumber}${accountNumber}`.split("").map(Number).reduce((acc,cur,index) => acc + cur * (11-index),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const rest = (sum - Math.trunc(sum / 11) * 11); | ||||||
| if (rest > 1) return (11 - rest).toString() | ||||||
| return "0" | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,23 @@ | ||||||
| import { BankValidator } from "./bankValidator"; | ||||||
|
|
||||||
| export class HSBCValidator extends BankValidator{ | ||||||
| private agencyMultiplicationWeights = [8,9,2,3]; | ||||||
|
|
||||||
| protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { | ||||||
| return /^\d{6}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); | ||||||
| } | ||||||
|
|
||||||
| protected checkDV(bankAccount: string, bankAgency:string): boolean { | ||||||
| const accountNumber = bankAccount.split("-")[0]; | ||||||
| const verificationDigit = bankAccount.split("-")[1]; | ||||||
| return this.getAccountVerificationDigit(accountNumber, bankAgency) === verificationDigit; | ||||||
| } | ||||||
|
|
||||||
| private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ | ||||||
| const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur,index) => acc + cur * this.agencyMultiplicationWeights[index],0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const accountSum = accountNumber.split("").map(Number).reduce((acc,cur,index) => acc + cur * (index + 4),0); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| const rest = (agencySum+accountSum)%11; | ||||||
| if (rest === 10) return "0" | ||||||
| return rest.toString(); | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an implementation, not a test.