You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$175] Wallet - Going back from the international bank account success page lets the same account be submitted again #102213
Select a non-US country (e.g. United Kingdom) and press Next.
Fill in every step with valid details.
On the confirmation page, tick the terms and press Confirm.
On the success page, press the browser Back button (web) or the device Back button (Android).
Press Confirm again.
Expected Result:
The flow closes, or the success page stays. The same bank account cannot be submitted twice.
Actual Result:
The confirmation page for the account just added opens with every detail filled in and the terms ticked. Pressing Confirm submits the same account again.
The "IFSC Code is invalid" string is not an App string — it comes from Corpay via the backend and the App renders it verbatim.
When you pick a bank country, the App calls GetCorpayBankAccountFields and stores the response in the corpayFields Onyx key. Each field carries validationRules[].errorMessage, already rendered in English by the provider. Client-side validation just passes it straight through:
Two things make it impossible for this string to be Spanish today:
The App never sends the user's locale on that request — GetCorpayBankAccountFieldsParams.ts only carries countryISO, currency, isWithdrawal, isBusinessBankAccount. preferredLocale is sent on sign-in commands only.
There is no IFSC translation key anywhere in src/languages/ — grep -i ifsc src/ returns nothing.
So this is a systemic gap, not a one-off. The same Corpay strings are rendered raw in several other places, which is why the form already looks half-translated: the field label is English with a Spanish (opcional) suffix at subPages/BankAccountDetails.tsx:102, and dropdown option text and region names are raw too.
What changes do you think we should make in order to solve the problem?
The complete fix is backend-side and outside this repo: send the user's preferredLocale on GetCorpayBankAccountFields and have the backend return localized label, valueSet[].text, and validationRules[].errorMessage. That needs Web-Expensify/Auth changes (and confirmation that Corpay's upstream API accepts a locale), so it should be escalated to an internal engineer rather than solved here.
An App-only mitigation is available following the precedent this team already accepted in #101772, if a full fix isn't wanted now:
Add an addPersonalBankAccount.ifscFormatError key to all src/languages/*.ts files describing the real IFSC format (11 characters: 4 letters, then 0, then 6 alphanumerics).
Generalize the hard-coded SWIFT branch in getValidationErrors into a small lookup keyed by bank country plus field name, so the IFSC rule for IN resolves to that key and SWIFT keeps its current behavior. values.bankCountry is already available inside that function, and scoping by country is required because routingCode is reused for UK sort codes, AU BSB, and others.
Be aware this mitigation only fixes the strings we enumerate. The English field label sitting right above the error stays English either way, so the screen still reads as partly untranslated.
What alternative solutions did you explore? (Optional)
Map Corpay's English message text to translation keys. Rejected: keying on provider prose breaks the moment Corpay rewords anything, and there are ~100+ country/currency combinations.
Translate everything client-side with a full Corpay string table. Rejected: unbounded maintenance for labels, options, and region names across every supported country.
Verification details and an unverified assumption
Verified from code (App at 05d0f31ea045bcdf2f91b774033bfce870d31ba3):
corpayFields.formFields[] is written only by the server's onyxData; the client never populates it. Both entry points are BankAccounts.ts:1479 (fetchCorpayFields, used by this Wallet flow) and BankAccounts.ts:825.
Nothing localizes the string downstream: addErrorMessage concatenates it as-is and FormProvider renders it as errorText.
The IFSC field lands in the ACCOUNT_DETAILS bucket via utils.ts:34-37, which matches the reported screen.
Not verified: I did not reproduce against the live Corpay response, so I don't know India's exact field id and regex. routingCode is the most likely id — India's IFSC is the routing code, and ReimbursementAccountForm.ts:104-106 notes it pairs with the SWIFT/BIC input. Whoever implements this should confirm the id and regex from an actual GetCorpayBankAccountFields response for countryISO=IN before wiring up the lookup.
Alternative explanation considered and ruled out: that the message is the top-level field.errorMessage or a backend error surfaced on the Confirmation step. Neither fits — field.errorMessage is declared in the type but never read by any App code, and the reported repro shows the error inline on blur, which is the client-side getValidationErrors path.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/102213) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Please re-state the problem that we are trying to solve in this issue.
When the user's language is set to Spanish and they navigate to Account > Wallet > Add International account for India, entering an invalid IFSC code (e.g. 2–4 characters) and blurring the field displays the error message "IFSC Code is invalid" in English instead of Spanish.
What is the root cause of that problem?
Dynamic form fields and validation rules for international bank accounts are returned by Corpay via the GetCorpayBankAccountFields API command.
In src/pages/settings/Wallet/InternationalDepositAccount/utils.ts (lines 144–154), getValidationErrors() checks input values against each rule's regular expression (rule.regEx):
for(construleoffield.validationRules){constregExpCheck=newRegExp(rule.regEx);if(!regExpCheck.test(values[fieldName])){// Corpay's strict SWIFT rule also requires a six-letter prefix, which its message may omit.consterrorMessage=fieldName===INPUT_IDS.ADDITIONAL_DATA.CORPAY.SWIFT_BIC_CODE&&rule.regEx===CONST.CORPAY_FIELDS.STRICT_SWIFT_BIC_REGEX
? translate('addPersonalBankAccount.swiftBicFormatError')
: rule.errorMessage;addErrorMessage(errors,fieldName,errorMessage);}}
For India (bankCountry === 'IN'), the IFSC field uses the routingCode field name. When the regex check fails, it falls through to rule.errorMessage. Because rule.errorMessage is supplied directly by Corpay in English ("IFSC Code is invalid"), the app renders the unlocalized English string regardless of the user's selected language.
While PR #101772 solved this exact problem for swiftBicCode by introducing translate('addPersonalBankAccount.swiftBicFormatError'), no localized translation key or mapping was added for the Indian IFSC routing code validation.
What changes do you think we should make in order to solve the problem?
Add localized IFSC error messages to src/languages/en.ts and src/languages/es.ts:
In src/languages/en.ts:
addPersonalBankAccount: {swiftBicFormatError: 'SWIFT/BIC must be 8 or 11 characters long, with 6 letters followed by 2 or 5 letters or numbers.',ifscFormatError: 'IFSC Code is invalid.',
In src/languages/es.ts:
addPersonalBankAccount: {swiftBicFormatError: 'El SWIFT/BIC debe tener 8 u 11 caracteres, con 6 letras seguidas de 2 o 5 letras o números.',ifscFormatError: 'El código IFSC no es válido.',
In src/pages/settings/Wallet/InternationalDepositAccount/utils.ts:
Update getValidationErrors to translate the error when validating the Indian IFSC routing code:
diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts b/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts--- a/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts+++ b/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts@@ -145,10 +145,13 @@ function getValidationErrors(values: FormOnyxValues<typeof ONYXKEYS.FORMS.INTER
for (const rule of field.validationRules) {
const regExpCheck = new RegExp(rule.regEx);
if (!regExpCheck.test(values[fieldName])) {
+ const isIndianIFSC = values.bankCountry === 'IN' && (fieldName === INPUT_IDS.ROUTING_CODE || fieldName === 'routingCode');
// Corpay's strict SWIFT rule also requires a six-letter prefix, which its message may omit.
const errorMessage =
fieldName === INPUT_IDS.ADDITIONAL_DATA.CORPAY.SWIFT_BIC_CODE && rule.regEx === CONST.CORPAY_FIELDS.STRICT_SWIFT_BIC_REGEX
? translate('addPersonalBankAccount.swiftBicFormatError')
+ : isIndianIFSC+ ? translate('addPersonalBankAccount.ifscFormatError')
: rule.errorMessage;
addErrorMessage(errors, fieldName, errorMessage);
}
Update tests/unit/InternationalDepositAccountUtilsTest.ts:
Add unit test cases verifying that when bankCountry === 'IN' and the routingCode fails validation, translateLocal('addPersonalBankAccount.ifscFormatError') is returned.
What alternative solutions did you explore? (Optional)
Translating Corpay error messages on the backend: While passing preferredLocale upstream to Corpay or translating on the Web-Expensify backend would be ideal long-term, Corpay's upstream API is third-party and may not support localized validation errors. Handling known provider validation format errors client-side follows the exact architectural precedent established in PR Fix misleading SWIFT validation guidance for international bank accounts #101772 for swiftBicCode.
Dynamic form fields and validation rules for international bank accounts are returned from Corpay via the GetCorpayBankAccountFields API.
In src/pages/settings/Wallet/InternationalDepositAccount/utils.ts (lines 144–154), getValidationErrors() iterates over each field's validationRules and evaluates input values against rule.regEx:
When input validation fails, it defaults to Corpay's raw provider message: rule.errorMessage.
For Indian bank accounts (bankCountry === 'IN'), the IFSC field uses the routingCode field, and Corpay provides the error message in English as "IFSC Code is invalid.".
Because rule.errorMessage is passed directly to addErrorMessage(errors, fieldName, errorMessage) without translation, the error message remains in English even when the app language is set to Spanish (or any non-English locale).
Recently merged PR #101772 solved this exact problem for swiftBicCode by introducing a localized translation key (addPersonalBankAccount.swiftBicFormatError) and mapping Corpay's strict SWIFT validation failure in getValidationErrors(). However, no corresponding localized translation or mapping was added for the Indian IFSC routing code validation rule.
What changes do you think we should make in order to solve the problem?
Following the exact architectural pattern established in PR #101772:
Add localized IFSC error messages across all language files in src/languages/:
Add ifscFormatError under addPersonalBankAccount in src/languages/en.ts, src/languages/es.ts, and the remaining supported locale files (de.ts, fr.ts, it.ts, ja.ts, nl.ts, pl.ts, pt-BR.ts, zh-hans.ts, el.ts):
English (en.ts): 'IFSC Code is invalid.'
Spanish (es.ts): 'El código IFSC no es válido.'
In src/pages/settings/Wallet/InternationalDepositAccount/utils.ts:
In getValidationErrors(), check when the Indian routing code IFSC rule fails (e.g. fieldName === INPUT_IDS.ROUTING_CODE || fieldName === 'routingCode' for Indian bank accounts or matching the IFSC rule identifier) and map the message to translate('addPersonalBankAccount.ifscFormatError') instead of falling back to the raw English rule.errorMessage.
Add unit test coverage in tests/unit/InternationalDepositAccountUtilsTest.ts:
Add test cases verifying that when an Indian international deposit account routing code fails validation, translateLocal('addPersonalBankAccount.ifscFormatError') is returned in the errors object.
What alternative solutions did you explore? (Optional)
Translating provider error strings on the Web-Expensify backend: Third-party provider APIs like Corpay return static English validation strings. Handling known provider field format errors on the client aligns directly with the established pattern in PR Fix misleading SWIFT validation guidance for international bank accounts #101772 for swiftBicCode and avoids backend dependency.
Updated the issue title and description to track the duplicate-submission bug, including prerequisites, reproduction steps, expected/actual results, and the video.
Investigation complete. The single-route approach has a precedent in the adjacent deposit-account flow, and the code indicates success can remain visible after its draft is cleared. No production logs are needed to establish the repeat client submission; whether the server creates a duplicate account remains unverified.
The proposal will explicitly note that browser Back during an unfinished flow would exit it, while in-app and Android step navigation would remain available. No code changes or runtime tests were performed.
Proposal
What is the root cause of that problem?
Successful submission does not make the Corpay bank-account flow terminal:
Success is pushed onto navigation history, leaving confirmation accessible.
The form draft—including accepted terms—is retained until Continue.
The success flag is immediately cleared, so returning to confirmation enables another submission.
Android Back explicitly moves to the preceding step, even from success.
This explains a second API submission; it does not establish whether the server creates a duplicate account.
Code evidence
Confirmation.tsx:63–76: submits the form, advances on success, then clears the success flag.
What changes do you think we should make in order to solve the problem?
Use the existing single-route wizard pattern for this Corpay flow:
Enable shouldReplaceRoute for its step navigation, including explicitly replacing the route when returning from an edit to confirmation. Completed form pages will no longer accumulate in history.
Handle Android Back on success as finish/exit, never as a previous-step action.
After confirmed API success, navigate to success and clear the bank-account draft and Corpay fields. Keep the existing finish cleanup as well.
Preserve the draft on failure so users can correct details or retry.
Navigation tradeoff: browser Back during an unfinished flow would exit rather than visit earlier steps. In-app step navigation would remain available. This matches the adjacent deposit-account flow but should be approved as part of this change.
Existing precedent and regression coverage
The adjacent flow already uses replacement navigation, explicit edit-return replacement, and terminal success Back handling: CollectDepositAccount/index.tsx:66–114. It also clears its draft after advancing on success: Confirmation.tsx:46–53.
Add regression coverage for:
Browser Back and Android Back after success: no completed confirmation and no second creation request.
Edit/return and unfinished-step navigation.
Failure followed by retry: values retained, retry allowed.
Reopening after success without pressing Continue: no old details or accepted terms.
Existing Continue destinations.
Verification: code-traced only; no runtime reproduction or fix verification performed.
What alternative solutions did you explore? (Optional)
Replace only confirmation with success: earlier wizard pages remain reachable, and Android still explicitly navigates backward.
Only clear terms or disable Confirm while loading: neither prevents resubmission after completion.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/102213) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Going back from the international bank account success page reopens the confirmation page of the account just added, with every detail filled in and the terms ticked. Pressing Confirm sends the same account again. This fix leaves the flow instead, following the pattern the US personal bank account flow already uses.
Root cause
Each sub page is pushed as its own screen (useSubPage), so the confirmation screen stays under the success screen.
Android Back on the success page runs prevPage() (InternationalDepositAccountContent). Browser Back and the iOS swipe pop the success screen.
The success flag is cleared as soon as the success page opens (Confirmation), so the confirmation page stays put when the user lands back on it.
Back on the success page runs handleFinishStep, like Continue.
The confirmation screen remembers it moved the flow on to success. If it comes back into focus after that, the user went back from success, so it leaves the flow.
The sibling CollectDepositAccount flow also exits on Back from success, but it replaces routes, which is meant for dynamic routes. This flow is a static route that pushes, like the US flow.
diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx
index 2b4485eb215..f6aa46a0ec6 100644
--- a/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx+++ b/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx@@ -29,8 +29,8 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {OnyxEntry} from 'react-native-onyx';
-import {useRoute} from '@react-navigation/native';-import React from 'react';+import {useIsFocused, useRoute} from '@react-navigation/native';+import React, {useEffect, useEffectEvent, useRef} from 'react';
import type CustomSubPageProps from './types';
@@ -153,20 +153,53 @@ function InternationalDepositAccountContent({
return true;
}
+ // The account already exists by the success page, so going back to resubmit it makes no sense+ if (pageIndex === CONST.CORPAY_FIELDS.INDEXES.MAPPING.SUCCESS) {+ handleFinishStep();+ return true;+ }+
prevPage();
return true;
};
useAndroidBackButtonHandler(handleBackButtonPress);
+ // Whether this page moved the flow on to the success page, and was out of focus since, like under the success page+ const hasMovedToSuccess = useRef(false);+ const wasUnfocusedAfterSuccess = useRef(false);+ const isFocused = useIsFocused();+ const leaveFlow = useEffectEvent(() => handleFinishStep());+
const handleNextScreen = () => {
if (isEditing) {
goBackToConfirmStep();
return;
}
+ if (pageIndex === CONST.CORPAY_FIELDS.INDEXES.MAPPING.CONFIRMATION) {+ hasMovedToSuccess.current = true;+ }
nextPage();
};
+ // Focus coming back after that means the user went back from the success page.+ // The account is already added, so that leaves the flow, like Back on the success page.+ useEffect(() => {+ if (!hasMovedToSuccess.current) {+ return;+ }+ if (!isFocused) {+ wasUnfocusedAfterSuccess.current = true;+ return;+ }+ if (!wasUnfocusedAfterSuccess.current) {+ return;+ }+ wasUnfocusedAfterSuccess.current = false;+ hasMovedToSuccess.current = false;+ leaveFlow();+ }, [isFocused]);+
return (
<ScreenWrapper
shouldEnableMaxHeight
What changes in production
Only the paths that remove the success screen and leave the confirmation screen under it:
Browser Back, Android Back and the iOS swipe on the success page now leave the flow.
Continue and Escape normally close the whole RHP in one action, so they are unchanged. The one exception is below.
Editing a row from the confirmation page is unchanged. The marker is only set when moving on to success.
One existing bug is touched. When the flow opens without backTo from a tab other than Settings or Inbox (e.g. pay invoice as individual, then Add bank account), Continue runs a bare Navigation.goBack() (goBack), which pops only the success screen. On main that lands on an empty confirmation page. With this fix it lands one step earlier. The flow doesn't close in either case. The US flow closes the RHP for those tabs.
Tests
Wise reimbursements beta off. Go to Account > Wallet > Add bank account.
Select a non-US country and fill in every step.
On the confirmation page, tick the terms and press Confirm.
On the success page, press the browser Back button (web) or the device Back button (Android).
Expected: the flow closes and the Wallet page shows the new account. Actual on main: the confirmation page of the account just added opens with the terms ticked, and Confirm sends it again.
Browser verification was blocked by a blank app page; no bank-account actions were possible.
Step
Status
Open Account > Wallet > Add bank accountBoth snapshots returned zero nodes.
❌
Verify submission, Back, draft reset, and unfinished navigationNot tested because the app rendered no accessible content.
❌
Implementation blocked; no PR created. Local changes are uncommitted. Five of six new regression tests passed; the retry test still expected two requests but recorded one after three debugging attempts. Required checks remain incomplete.
changed the title [-]Wallet - Going back from the international bank account success page lets the same account be submitted again[/-][+][$175] Wallet - Going back from the international bank account success page lets the same account be submitted again[/+]on Nov 13, 2026
Activity
applause-bot commentedon Sep 24, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/922f8851f57ce4d2c9ce8cd61b187498bdd773ae0071eda7e7135f12c604739e
MelvinBot commentedon Sep 24, 2026
Proposal
What is the root cause of that problem?
The "IFSC Code is invalid" string is not an App string — it comes from Corpay via the backend and the App renders it verbatim.
When you pick a bank country, the App calls
GetCorpayBankAccountFieldsand stores the response in thecorpayFieldsOnyx key. Each field carriesvalidationRules[].errorMessage, already rendered in English by the provider. Client-side validation just passes it straight through:src/pages/settings/Wallet/InternationalDepositAccount/utils.ts:144-154— onlycommon.error.fieldRequiredand the one SWIFT/BIC case added by Fix misleading SWIFT validation guidance for international bank accounts go throughtranslate(). Every other rule, IFSC included, falls through torule.errorMessage.Two things make it impossible for this string to be Spanish today:
GetCorpayBankAccountFieldsParams.tsonly carriescountryISO,currency,isWithdrawal,isBusinessBankAccount.preferredLocaleis sent on sign-in commands only.src/languages/—grep -i ifsc src/returns nothing.So this is a systemic gap, not a one-off. The same Corpay strings are rendered raw in several other places, which is why the form already looks half-translated: the field label is English with a Spanish
(opcional)suffix atsubPages/BankAccountDetails.tsx:102, and dropdown option text and region names are raw too.What changes do you think we should make in order to solve the problem?
The complete fix is backend-side and outside this repo: send the user's
preferredLocaleonGetCorpayBankAccountFieldsand have the backend return localizedlabel,valueSet[].text, andvalidationRules[].errorMessage. That needs Web-Expensify/Auth changes (and confirmation that Corpay's upstream API accepts a locale), so it should be escalated to an internal engineer rather than solved here.An App-only mitigation is available following the precedent this team already accepted in #101772, if a full fix isn't wanted now:
addPersonalBankAccount.ifscFormatErrorkey to allsrc/languages/*.tsfiles describing the real IFSC format (11 characters: 4 letters, then0, then 6 alphanumerics).getValidationErrorsinto a small lookup keyed by bank country plus field name, so the IFSC rule forINresolves to that key and SWIFT keeps its current behavior.values.bankCountryis already available inside that function, and scoping by country is required becauseroutingCodeis reused for UK sort codes, AU BSB, and others.rule.errorMessageraw with no special cases:NonUSD/BankInfo/subSteps/BankAccountDetails.tsx:73andAccountHolderDetails.tsx:92. Best done by having both call the sharedgetValidationErrorsinstead of duplicating it.tests/unit/InternationalDepositAccountUtilsTest.ts:47-62, which currently asserts that non-SWIFT fields preserve the provider message.Be aware this mitigation only fixes the strings we enumerate. The English field label sitting right above the error stays English either way, so the screen still reads as partly untranslated.
What alternative solutions did you explore? (Optional)
Verification details and an unverified assumption
Verified from code (App at
05d0f31ea045bcdf2f91b774033bfce870d31ba3):corpayFields.formFields[]is written only by the server'sonyxData; the client never populates it. Both entry points areBankAccounts.ts:1479(fetchCorpayFields, used by this Wallet flow) andBankAccounts.ts:825.addErrorMessageconcatenates it as-is andFormProviderrenders it aserrorText.ACCOUNT_DETAILSbucket viautils.ts:34-37, which matches the reported screen.Not verified: I did not reproduce against the live Corpay response, so I don't know India's exact field id and regex.
routingCodeis the most likely id — India's IFSC is the routing code, andReimbursementAccountForm.ts:104-106notes it pairs with the SWIFT/BIC input. Whoever implements this should confirm the id and regex from an actualGetCorpayBankAccountFieldsresponse forcountryISO=INbefore wiring up the lookup.Alternative explanation considered and ruled out: that the message is the top-level
field.errorMessageor a backend error surfaced on the Confirmation step. Neither fits —field.errorMessageis declared in the type but never read by any App code, and the reported repro shows the error inline on blur, which is the client-sidegetValidationErrorspath.Related: Wallet- SWIFT code field validation error persist after enter valid code for International BA is the same underlying gap for the SWIFT field, fixed narrowly by #101772. No open duplicate of this IFSC report exists.
Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/102213)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
melvin-bot commentedon Sep 24, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @situchan (
External)melvin-bot commentedon Sep 24, 2026
Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.
ashusatyarthi-wq commentedon Sep 27, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When the user's language is set to Spanish and they navigate to Account > Wallet > Add International account for India, entering an invalid IFSC code (e.g. 2–4 characters) and blurring the field displays the error message
"IFSC Code is invalid"in English instead of Spanish.What is the root cause of that problem?
Dynamic form fields and validation rules for international bank accounts are returned by Corpay via the
GetCorpayBankAccountFieldsAPI command.In
src/pages/settings/Wallet/InternationalDepositAccount/utils.ts(lines 144–154),getValidationErrors()checks input values against each rule's regular expression (rule.regEx):For India (
bankCountry === 'IN'), the IFSC field uses theroutingCodefield name. When the regex check fails, it falls through torule.errorMessage. Becauserule.errorMessageis supplied directly by Corpay in English ("IFSC Code is invalid"), the app renders the unlocalized English string regardless of the user's selected language.While PR #101772 solved this exact problem for
swiftBicCodeby introducingtranslate('addPersonalBankAccount.swiftBicFormatError'), no localized translation key or mapping was added for the Indian IFSC routing code validation.What changes do you think we should make in order to solve the problem?
src/languages/en.tsandsrc/languages/es.ts:In
src/languages/en.ts:In
src/languages/es.ts:src/pages/settings/Wallet/InternationalDepositAccount/utils.ts:Update
getValidationErrorsto translate the error when validating the Indian IFSC routing code:tests/unit/InternationalDepositAccountUtilsTest.ts:Add unit test cases verifying that when
bankCountry === 'IN'and theroutingCodefails validation,translateLocal('addPersonalBankAccount.ifscFormatError')is returned.What alternative solutions did you explore? (Optional)
preferredLocaleupstream to Corpay or translating on the Web-Expensify backend would be ideal long-term, Corpay's upstream API is third-party and may not support localized validation errors. Handling known provider validation format errors client-side follows the exact architectural precedent established in PR Fix misleading SWIFT validation guidance for international bank accounts #101772 forswiftBicCode.ashusatyarthi-wq commentedon Sep 27, 2026
Compare branch: https://github.com/ashusatyarthi-wq/App/compare/fix/ifsc-spanish-error-102213
fix: localize IFSC error message for Indian bank accounts
meanusarcanus commentedon Sep 27, 2026
Proposal
What is the root cause of that problem?
Dynamic form fields and validation rules for international bank accounts are returned from Corpay via the
GetCorpayBankAccountFieldsAPI.In
src/pages/settings/Wallet/InternationalDepositAccount/utils.ts(lines 144–154),getValidationErrors()iterates over each field'svalidationRulesand evaluates input values againstrule.regEx:rule.errorMessage.bankCountry === 'IN'), the IFSC field uses theroutingCodefield, and Corpay provides the error message in English as"IFSC Code is invalid.".rule.errorMessageis passed directly toaddErrorMessage(errors, fieldName, errorMessage)without translation, the error message remains in English even when the app language is set to Spanish (or any non-English locale).Recently merged PR #101772 solved this exact problem for
swiftBicCodeby introducing a localized translation key (addPersonalBankAccount.swiftBicFormatError) and mapping Corpay's strict SWIFT validation failure ingetValidationErrors(). However, no corresponding localized translation or mapping was added for the Indian IFSC routing code validation rule.What changes do you think we should make in order to solve the problem?
Following the exact architectural pattern established in PR #101772:
Add localized IFSC error messages across all language files in
src/languages/:Add
ifscFormatErrorunderaddPersonalBankAccountinsrc/languages/en.ts,src/languages/es.ts, and the remaining supported locale files (de.ts,fr.ts,it.ts,ja.ts,nl.ts,pl.ts,pt-BR.ts,zh-hans.ts,el.ts):en.ts):'IFSC Code is invalid.'es.ts):'El código IFSC no es válido.'In
src/pages/settings/Wallet/InternationalDepositAccount/utils.ts:In
getValidationErrors(), check when the Indian routing code IFSC rule fails (e.g.fieldName === INPUT_IDS.ROUTING_CODE || fieldName === 'routingCode'for Indian bank accounts or matching the IFSC rule identifier) and map the message totranslate('addPersonalBankAccount.ifscFormatError')instead of falling back to the raw Englishrule.errorMessage.Add unit test coverage in
tests/unit/InternationalDepositAccountUtilsTest.ts:Add test cases verifying that when an Indian international deposit account routing code fails validation,
translateLocal('addPersonalBankAccount.ifscFormatError')is returned in the errors object.What alternative solutions did you explore? (Optional)
swiftBicCodeand avoids backend dependency.Contributor details
Your Expensify account email: nadrested@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~01f9f6e0f44dbc3e49
melvin-bot commentedon Sep 27, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
32 remaining items
MelvinBot commentedon Nov 12, 2026
Updated the issue title and description to track the duplicate-submission bug, including prerequisites, reproduction steps, expected/actual results, and the video.
view run
MelvinBot commentedon Nov 12, 2026
Run details
situchan commentedon Nov 12, 2026
@MelvinBot submit new proposal
MelvinBot commentedon Nov 12, 2026
Investigation complete. The single-route approach has a precedent in the adjacent deposit-account flow, and the code indicates success can remain visible after its draft is cleared. No production logs are needed to establish the repeat client submission; whether the server creates a duplicate account remains unverified.
The proposal will explicitly note that browser Back during an unfinished flow would exit it, while in-app and Android step navigation would remain available. No code changes or runtime tests were performed.
Proposal
What is the root cause of that problem?
Successful submission does not make the Corpay bank-account flow terminal:
This explains a second API submission; it does not establish whether the server creates a duplicate account.
Code evidence
prevPage()from success.What changes do you think we should make in order to solve the problem?
Use the existing single-route wizard pattern for this Corpay flow:
shouldReplaceRoutefor its step navigation, including explicitly replacing the route when returning from an edit to confirmation. Completed form pages will no longer accumulate in history.Navigation tradeoff: browser Back during an unfinished flow would exit rather than visit earlier steps. In-app step navigation would remain available. This matches the adjacent deposit-account flow but should be approved as part of this change.
Existing precedent and regression coverage
The adjacent flow already uses replacement navigation, explicit edit-return replacement, and terminal success Back handling: CollectDepositAccount/index.tsx:66–114. It also clears its draft after advancing on success: Confirmation.tsx:46–53.
Add regression coverage for:
Verification: code-traced only; no runtime reproduction or fix verification performed.
What alternative solutions did you explore? (Optional)
Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/102213)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
situchan commentedon Nov 12, 2026
@MelvinBot implement your proposal
Alternative solution
Summary
Going back from the international bank account success page reopens the confirmation page of the account just added, with every detail filled in and the terms ticked. Pressing Confirm sends the same account again. This fix leaves the flow instead, following the pattern the US personal bank account flow already uses.
Root cause
Each sub page is pushed as its own screen (useSubPage), so the confirmation screen stays under the success screen.
prevPage()(InternationalDepositAccountContent). Browser Back and the iOS swipe pop the success screen.Fix
Same as the US personal bank account flow (AddPersonalBankAccountPage, from #102708):
handleFinishStep, like Continue.The sibling CollectDepositAccount flow also exits on Back from success, but it replaces routes, which is meant for dynamic routes. This flow is a static route that pushes, like the US flow.
What changes in production
Only the paths that remove the success screen and leave the confirmation screen under it:
One existing bug is touched. When the flow opens without
backTofrom a tab other than Settings or Inbox (e.g. pay invoice as individual, then Add bank account), Continue runs a bareNavigation.goBack()(goBack), which pops only the success screen. On main that lands on an empty confirmation page. With this fix it lands one step earlier. The flow doesn't close in either case. The US flow closes the RHP for those tabs.Tests
Expected: the flow closes and the Wallet page shows the new account.
Actual on main: the confirmation page of the account just added opens with the terms ticked, and Confirm sends it again.
MelvinBot commentedon Nov 12, 2026
🤖 Web test steps verification results: ❌ fail
Browser verification was blocked by a blank app page; no bank-account actions were possible.
Open Account > Wallet > Add bank account
Both snapshots returned zero nodes.Verify submission, Back, draft reset, and unfinished navigation
Not tested because the app rendered no accessible content.Implementation blocked; no PR created. Local changes are uncommitted. Five of six new regression tests passed; the retry test still expected two requests but recorded one after three debugging attempts. Required checks remain incomplete.
view run · no recording available
situchan commentedon Nov 12, 2026
@MelvinBot even if you're not able to test, do not block the implementation. Still create PR. I'll test and verify myself.
MelvinBot commentedon Nov 12, 2026
Created draft PR: Prevent resubmitting an international bank account after success, assigned to you.
46 targeted tests and TypeScript passed. Incomplete checks are documented in the PR; manual verification is left to you.
view run
[-]Wallet - Going back from the international bank account success page lets the same account be submitted again[/-][+][$175] Wallet - Going back from the international bank account success page lets the same account be submitted again[/+]melvin-bot commentedon Nov 13, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022121112976100450700
melvin-bot commentedon Nov 13, 2026
Triggered auto assignment to @Julesssss, see https://stackoverflow.com/c/expensify/questions/7972 for more details.