- ID
- 741771f4-b78c-4abf-a587-4a1ebfebca04
MMPY-70 API for routing confirmation
- source :: https://multimediallc.atlassian.net/browse/MMPY-70
- tags :: Falcon Nest Project Story Falcon Nest Project
- epic :: MMPY-225 ACH Account Creator Interface
- sprint :: MM Sprint 26
- status :: Status - Complete
Background
When collecting Direct Deposit fields, we want to ensure that there aren’t errors. Many services offer ACH routing number validation and we should incorporate that into our work.
Requirements
DONE See how far Claude can get us with a new app
- ID
- 6ee9737b-af86-7a57-37c9-003bad922e78
DONE Tweak data model from Claude and test interface
- ID
- ce6da97a-1a14-04d6-8290-df5ad096171e
DONE Add a proper LN client to implementation
- ID
- ed00c4a0-e152-8985-1f78-325b12cf96b5
Turns out we need to a user/pass auth flow to get a time-limited token to make
requests. So probably, we should have a proper client that exposes methods for
things like `authenticate` and `validate` that do the network-y stuff of
hitting the endpoints. The model can then just call the:
#+begin_src python
ln_client = LexisNexisClient(user, pass)
response = ln_client.validate("country_code", "routing_number")
#+end_src
And so forth.
DONE Add tests for new client and models
- ID
- 2aa355c0-9bfb-0696-643a-ca849bec13ab
DONE Restructure API calls to be all bank info through one
- ID
- 28b99b5f-ed01-c3c5-51e2-f2ad20ca4ed9
DONE Add tests for new client and models
- ID
- 74461159-c866-193f-3164-1df95f985c66
DONE Fix linting issues
- ID
- c971458e-5110-f9f8-bd87-9bbcf4f0ce5c
DONE Fix PR issues
- ID
- c5d28f99-be78-5913-8071-ab1ba17617b8
GIVEN
A routing number is submitted for check
WHEN
the routing number passes basic validation for 9-char length and digit-only characters
THEN
Look up the routing number at lexis nexis https://beta.validate.bankersalmanac.com/validateweb/result and return the bank name if a bank is found
Example
https://api.validate.bankersalmanac.com/swagger-ui/index.html#/
Use the validation endpoint and provide the following two data points
{
"countryCode": "<2char iso>",
"nationalId": "<routing>"
}
If the recommendedNatId returns something other than the nationalId passed, update the recommendedNatId in the interface.
If the status is not PASS return an error to the interface: “The routing number provided is not valid. Please check the number and enter it again”. This will show in the UI in MMPY-69
Display the Branch Details Name & Address in the bank information box in the UI. This will show in the UI in MMPY-69
Example response:
{
"countryCode": "US",
"nationalId": "036001808",
"accountNumber": "",
"status": "PASS",
"comment": "Account number not entered (08an)",
"recommendedNatId": "036001808",
"recommendedAcct": "",
"recommendedBIC": "NRTHUS33XXX",
"ref": "20251105172818586",
"group": "",
"branchDetails": [
{
"bankName": "TD Bank National Association",
"branch": "19th and Tilghman",
"street": "1828 W Tilghman Street",
"city": "Allentown",
"postZip": "18104",
"region": "Pennsylvania",
"country": "UNITED STATES",
"codeDetails": {
"codeName1": "BIC",
"codeValue1": "",
"codeName2": "CHIPS",
"codeValue2": "",
"codeName3": "BIC-4",
"codeValue3": "",
"codeName4": "ABA",
"codeValue4": "036001808"
},
"sepaDetails": {
"ctStatus": "NO",
"ddStatus": "NO",
"bbStatus": "NO"
},
"additionalData": {
"ssiAvailable": "Y",
"payServiceAvailable": "Y",
"contactsAvailable": "N",
"messageAvailable": "Y",
"holidayAvailable": "Y"
},
"bankToken": ""freeToken": "<token>",
"sanctionsDetails": {
"euSanctions": "N",
"hmtSanctions": "N",
"ofacSanctions": "N",
"unSanctions": "N"
}
}
],
"headOfficeDetails": {
"bankName": "TD Bank National Association",
"branch": "",
"street": "2035 Limestone Rd",
"city": "Wilmington",
"postZip": "19808",
"region": "Delaware",
"country": "UNITED STATES",
"codeDetails": {
"codeName1": "BIC",
"codeValue1": "NRTHUS33XXX",
"codeName2": "CHIPS",
"codeValue2": "",
"codeName3": "BIC-4",
"codeValue3": "NRTH",
"codeName4": "ABA",
"codeValue4": "031101266"
},
"additionalData": {
"ssiAvailable": "Y",
"payServiceAvailable": "Y",
"contactsAvailable": "N",
"messageAvailable": "Y",
"holidayAvailable": "Y"
},
"sanctionsDetails": {
"euSanctions": "N",
"hmtSanctions": "N",
"ofacSanctions": "N",
"unSanctions": "N"
}
},
"paymentBicDetails": {
"branchTypeLabel": "Payment BIC Branch",
"bankName": "TD Bank National Association",
"branch": "",
"street": "2035 Limestone Rd",
"city": "Wilmington",
"postZip": "19808",
"region": "Delaware",
"country": "UNITED STATES",
"codeDetails": {
"codeName1": "BIC",
"codeValue1": "NRTHUS33XXX",
"codeName2": "CHIPS",
"codeValue2": "",
"codeName3": "BIC-4",
"codeValue3": "NRTH",
"codeName4": "ABA",
"codeValue4": "031101266"
},
"additionalData": {
"ssiAvailable": "Y",
"payServiceAvailable": "Y",
"contactsAvailable": "N",
"messageAvailable": "Y",
"holidayAvailable": "Y"
},
"sanctionsDetails": {
"euSanctions": "N",
"hmtSanctions": "N",
"ofacSanctions": "N",
"unSanctions": "N"
}
},
"bic8": "N",
"dataStore": "N",
"noBranch": "N",
"payBranchType": "D",
"freeToken": "<token>"
}
Validation takes a username and password, then provides a short-lived (30 mins?) token.
The Lexis Nexis validation endpoint is a little expensive, so we should cache routing information for a year, as it does not change frequently.