Skip to content
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

feat [ROW-547]: Add getBankCode api in geo module #206

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/strong-pillows-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@razorpay/i18nify-js": patch
---

Add getBankCode api in geo module
16 changes: 16 additions & 0 deletions packages/i18nify-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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));
```
111 changes: 111 additions & 0 deletions packages/i18nify-js/src/modules/banking/__tests__/getBankCode.test.ts
Original file line number Diff line number Diff line change
@@ -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.',
);
});
});
76 changes: 76 additions & 0 deletions packages/i18nify-js/src/modules/banking/getBankCode.ts
Original file line number Diff line number Diff line change
@@ -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<string>} Promise that resolves to the bank code of the bank
*/
const getBankCode = (
_countryCode: I18nifyCountryCodeType,
bankName: string,
): Promise<string> => {
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<typeof getBankCode>(getBankCode);
1 change: 1 addition & 0 deletions packages/i18nify-js/src/modules/banking/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as getBankCode } from './getBankCode';
export { default as getListOfBanks } from './getListOfBanks';
Loading