diff --git a/.changeset/strong-pillows-flow.md b/.changeset/strong-pillows-flow.md new file mode 100644 index 00000000..3db14335 --- /dev/null +++ b/.changeset/strong-pillows-flow.md @@ -0,0 +1,5 @@ +--- +"@razorpay/i18nify-js": patch +--- + +Add getBankCode api in geo module diff --git a/packages/i18nify-js/README.md b/packages/i18nify-js/README.md index 8298083a..cc6e1f78 100644 --- a/packages/i18nify-js/README.md +++ b/packages/i18nify-js/README.md @@ -1268,3 +1268,19 @@ async function fetchBanks() { fetchBanks(); ``` + +#### getBankCode(\_countryCode, bankName) + +🏦🔍 Need a bank's code in a specific country? This function has your back! Just pass the country code and the exact bank name, and it'll fetch the bank code for you from the central i18nify data source. If the bank or country isn't supported, it'll give you a heads-up with a helpful error. No more guessing or manual lookups—get the right bank code in seconds! + +##### Examples + +```javascript +getBankCode('IN', 'Abhyudaya Co-operative Bank') + .then((code) => console.log(code)) // Outputs the bank code for SBI in India (e.g., "ABHY") + .catch((err) => console.error(err.message)); + +getBankCode('US', 'Bank of America CORPORATION') + .then((code) => console.log(code)) // Outputs the bank code for Bank of America (e.g., "MLCO") + .catch((err) => console.error(err.message)); +``` diff --git a/packages/i18nify-js/src/modules/banking/__tests__/getBankCode.test.ts b/packages/i18nify-js/src/modules/banking/__tests__/getBankCode.test.ts new file mode 100644 index 00000000..56d2100e --- /dev/null +++ b/packages/i18nify-js/src/modules/banking/__tests__/getBankCode.test.ts @@ -0,0 +1,111 @@ +import getBankCode from '../getBankCode'; + +// A mock object to simulate the data returned by the fetch call +// You can customize this as needed for your tests +const mockBankData = { + details: [ + { + name: 'Abhyudaya Co-operative Bank', + short_code: 'ABHY', + }, + { + name: 'SBI Bank', + short_code: 'SBI', + }, + ], +}; + +// Mock global.fetch by default to return `mockBankData` +global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve(mockBankData), + } as Response), +); + +describe('getBankCode', () => { + beforeEach(() => { + // Reset fetch mock before each test case + jest.clearAllMocks(); + }); + + it('should reject if the bank is not found in the details array', async () => { + const bankName = 'Non Existent Bank'; + + await expect(getBankCode('IN', bankName)).rejects.toThrow( + `Unable to find bank code for bank "${bankName}" in IN. Please ensure the bank name is correct.`, + ); + }); + + it('should return bank code for a valid country code and bank name', async () => { + const countryCode = 'IN'; + const bankName = 'Abhyudaya Co-operative Bank'; + + const result = await getBankCode(countryCode, bankName); + + expect(result).toBe('ABHY'); + }); + + it('should reject with an error message when country code is invalid', async () => { + const invalidCountryCode = 'XYZ'; + const bankName = 'Abhyudaya Co-operative Bank'; + + await expect( + getBankCode(invalidCountryCode as any, bankName), + ).rejects.toThrow( + `Data not available for country code: XYZ. Data only available for country codes mentioned here: https://github.com/razorpay/i18nify/blob/master/packages/i18nify-js/src/modules/geo/constants.ts#L8`, + ); + }); + + it('should reject when bankName is not provided', async () => { + // @ts-expect-error Testing scenario with missing bank name + await expect(getBankCode('IN')).rejects.toThrow( + 'Bank name is required to fetch the bank code. Please provide a valid bank name.', + ); + }); + + it('should reject if the data is not in the expected format (missing details array)', async () => { + global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({}), // details is missing + } as Response), + ); + + await expect(getBankCode('IN', 'SBI Bank')).rejects.toThrow( + 'Bank data is not in the expected format for IN.', + ); + }); + + it('should reject if the bank is found but bank code is missing', async () => { + // Adjust mock data so the found bank does not have bank code + global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => + Promise.resolve({ + details: [{ name: 'Bank Without Shortcode' }], + }), + } as Response), + ); + + const bankName = 'Bank Without Shortcode'; + + await expect(getBankCode('IN', bankName)).rejects.toThrow( + `Unable to find bank code for bank "${bankName}" in IN. Please ensure the bank name is correct.`, + ); + }); + + it('should handle network or fetch errors gracefully', async () => { + global.fetch = jest.fn(() => Promise.reject('API Error')); + + await expect( + getBankCode('IN', 'Abhyudaya Co-operative Bank'), + ).rejects.toThrow( + 'An error occurred while fetching bank bank code. The error details are: undefined.', + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/banking/getBankCode.ts b/packages/i18nify-js/src/modules/banking/getBankCode.ts new file mode 100644 index 00000000..0edd8347 --- /dev/null +++ b/packages/i18nify-js/src/modules/banking/getBankCode.ts @@ -0,0 +1,76 @@ +import { withErrorBoundary } from '../../common/errorBoundary'; +import { + I18NIFY_DATA_SOURCE, + I18NIFY_DATA_SUPPORTED_COUNTRIES, +} from '../shared'; +import { I18nifyCountryCodeType } from '../geo/types'; + +/** + * Retrieves the bank code for the specified bank in a given country + * + * This function makes a network request to the central i18nify-data source and + * returns a promise for the bank code associated with the bank name passed. + * If the bank or country is invalid, it rejects with an appropriate error. + * + * @param {I18nifyCountryCodeType} _countryCode - The ISO country code (e.g., 'IN') + * @param {string} bankName - The exact name of the bank (as it appears in the data) + * @returns {Promise} Promise that resolves to the bank code of the bank + */ +const getBankCode = ( + _countryCode: I18nifyCountryCodeType, + bankName: string, +): Promise => { + const countryCode = _countryCode.toUpperCase(); + + if (!I18NIFY_DATA_SUPPORTED_COUNTRIES.includes(countryCode)) { + return Promise.reject( + new Error( + `Data not available for country code: ${countryCode}. Data only available for country codes mentioned here: https://github.com/razorpay/i18nify/blob/master/packages/i18nify-js/src/modules/geo/constants.ts#L8`, + ), + ); + } + + if (!bankName) { + return Promise.reject( + new Error( + `Bank name is required to fetch the bank code. Please provide a valid bank name.`, + ), + ); + } + + return fetch(`${I18NIFY_DATA_SOURCE}/bankcodes/${countryCode}.json`) + .then((res) => res.json()) + .then((res) => { + const { details } = res; + if (!details || !Array.isArray(details)) { + return Promise.reject( + new Error( + `Bank data is not in the expected format for ${countryCode}.`, + ), + ); + } + + // Find the bank entry matching the passed bankName + const bank = details.find( + (bankItem: { name: string }) => + bankItem.name.toLowerCase() === bankName.toLowerCase(), + ); + + if (!bank || !bank.short_code) { + return Promise.reject( + new Error( + `Unable to find bank code for bank "${bankName}" in ${countryCode}. Please ensure the bank name is correct.`, + ), + ); + } + + return bank.short_code; + }) + .catch((err) => { + throw new Error( + `An error occurred while fetching bank bank code. The error details are: ${err.message}.`, + ); + }); +}; + +export default withErrorBoundary(getBankCode); diff --git a/packages/i18nify-js/src/modules/banking/index.ts b/packages/i18nify-js/src/modules/banking/index.ts index e52cbe25..a351761a 100644 --- a/packages/i18nify-js/src/modules/banking/index.ts +++ b/packages/i18nify-js/src/modules/banking/index.ts @@ -1 +1,2 @@ +export { default as getBankCode } from './getBankCode'; export { default as getListOfBanks } from './getListOfBanks';