fix(cross-region): pass targetCountry from country-aware KYC initiate sites#2199
Conversation
… sites Pairs with peanut-api-ts#995. Five call sites of `sumsubFlow.handleInitiateKyc` knew the user's chosen country (selectedCountry.id / currentCountry.id / URL param) but dropped it on the floor — the 4th `targetCountry` arg was just omitted. That cascaded on the BE: no `pendingMantecaGeo` on metadata → no `-XX` suffix on the Sumsub action externalId → no PENDING Manteca rail pre-stamped → when the action went GREEN, the webhook handler couldn't determine which exchange to submit to and bailed silently (companion PR adds an applicant- country fallback + Sentry capture for that case). Sites fixed: - components/AddWithdraw/AddWithdrawCountriesList.tsx (2 call sites) - components/Claim/Link/views/BankFlowManager.view.tsx (2 call sites) - components/Claim/Link/MantecaFlowManager.tsx - app/(mobile-ui)/add-money/[country]/bank/page.tsx - app/(mobile-ui)/withdraw/[country]/bank/page.tsx UnlockedRegions.view.tsx intentionally NOT patched: at LATAM-macro click the country isn't known (no per-country picker at that surface). The companion BE fallback handles that case from the applicant's extracted country.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughPropagates selected/current country ID into all sumsubFlow.handleInitiateKyc call sites and hardens useSumsubKycFlow: it normalizes raw target country for Manteca, passes targetCountry into native flows, and clears the user-initiated guard on terminal failures. Tests added for gating and guard behavior. ChangesKYC Country ID Propagation & Flow Hardening
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 5832.1 → 5833.62 (+1.52) 🆕 New findings (33)
…and 13 more. ✅ Resolved (33)
…and 13 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
…uard on every terminal error Three hook-level holes around the new targetCountry plumbing: 1. Choke-point gating: call sites pass the raw destination country for every latam-region country, but the BE only ever consumes targetCountry as a Manteca geo — an unsupported value (MX, CL, …) stamps a poisoned pendingMantecaGeo (first-write-wins) that bails every later geo resolution. Drop non-AR/BR values in the hook so no call site can regress this. 2. Native token-refresh parity: the Capacitor refresh callback omitted targetCountry (web refreshToken and the 5s poller pass it), so a native token expiry mid-action minted a token for a different, suffix-less applicant action than the one the user was inside. 3. Generalize the 4527d38 race fix: it cleared userInitiatedRef only on the unsupported-region branch, but every other terminal-error exit (response.error — e.g. region_not_supported 400 —, no-token, native failure, throw; same in handleRestartIdentity/handleSelfHealResubmit) left the guard set while restoring prevStatusRef, so a late websocket APPROVED replay fired onKycSuccess on top of the rendered error — the exact "You're all set" loop again. Tests use a PENDING→APPROVED two-event sequence so the userInitiatedRef guard is isolated from the prevStatusRef guard.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/useSumsubKycFlow.ts (1)
199-205:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRestore
prevStatusRefon every terminal cross-region return, not justresponse.error/catch.Lines 166-168 preseed
prevStatusRef.currentto'APPROVED'. These three exits clearuserInitiatedRef, but they return without restoring that seed, so a later SDK flow that opens withstatus === 'APPROVED'can miss the finalAPPROVEDtransition and never callonKycSuccess.Proposed fix
if (response.data?.actionType === 'unsupported-region') { userInitiatedRef.current = false + if (crossRegion) prevStatusRef.current = savedPrevStatus setError( "Bank deposits aren't available in your region yet. We'll let you know as soon as they go live." ) return } @@ if (!SNSMobileSDK) { userInitiatedRef.current = false + if (crossRegion) prevStatusRef.current = savedPrevStatus setError('KYC SDK not available. Please update the app.') return } @@ } else { userInitiatedRef.current = false + if (crossRegion) prevStatusRef.current = savedPrevStatus setError('Could not initiate verification. Please try again.') }Also applies to: 247-250, 292-295
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useSumsubKycFlow.ts` around lines 199 - 205, The branch that handles cross-region terminal exits (e.g., the response.data?.actionType === 'unsupported-region' path) clears userInitiatedRef.current and returns without restoring prevStatusRef.current to its seeded value ('APPROVED'), which can break later SDK flows; update those terminal-return paths (including the unsupported-region branch and the similar exits around the other noted locations) to set prevStatusRef.current = 'APPROVED' before returning (while still clearing userInitiatedRef and calling setError), so prevStatusRef is restored for subsequent KYC flows and onKycSuccess can fire as expected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/hooks/useSumsubKycFlow.ts`:
- Around line 154-155: The branch that sets targetCountry currently uses
isMantecaSupportedCountryCode(rawTargetCountry) but does not normalize case, so
update the assignment in useSumsubKycFlow to uppercase the validated value
before storing/sending (i.e., if rawTargetCountry passes
isMantecaSupportedCountryCode, set targetCountry to
rawTargetCountry.toUpperCase()). Ensure any downstream usage in the same hook
(initiation/refresh flows) uses this normalized targetCountry value instead of
the raw input.
---
Outside diff comments:
In `@src/hooks/useSumsubKycFlow.ts`:
- Around line 199-205: The branch that handles cross-region terminal exits
(e.g., the response.data?.actionType === 'unsupported-region' path) clears
userInitiatedRef.current and returns without restoring prevStatusRef.current to
its seeded value ('APPROVED'), which can break later SDK flows; update those
terminal-return paths (including the unsupported-region branch and the similar
exits around the other noted locations) to set prevStatusRef.current =
'APPROVED' before returning (while still clearing userInitiatedRef and calling
setError), so prevStatusRef is restored for subsequent KYC flows and
onKycSuccess can fire as expected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1901a1ed-657f-4b03-8299-7b65cdc714ab
📒 Files selected for processing (2)
src/hooks/__tests__/useSumsubKycFlow.test.tssrc/hooks/useSumsubKycFlow.ts
isMantecaSupportedCountryCode uppercases internally, so a lowercase route-param country passed the predicate but was forwarded raw; the BE normalizes anyway, but the hook's contract is normalized-or-dropped.
Summary
Pairs with peanut-api-ts#995. Targets `main` — real prod incident, real user (2026-06-08).
Five `sumsubFlow.handleInitiateKyc` call sites knew the user's country (`selectedCountry.id` / `currentCountry.id` / URL param) but omitted the 4th `targetCountry` argument. The BE then had no way to scope the Manteca geo, the Sumsub action externalId was built without a `-XX` suffix, no PENDING Manteca rail was pre-stamped, and when the action went GREEN the webhook handler silently bailed.
Sites patched
Intentionally NOT patched
`components/Profile/views/UnlockedRegions.view.tsx` LATAM macro click — there's no per-country picker at that surface, so `targetCountry` genuinely isn't known until the user uploads ID inside the Sumsub WebSDK. The companion BE PR adds a fallback that pulls the geo from the applicant's extracted country in that case.
Risk
Very low. Adding a 4th arg whose value the BE only consumes on LATAM/Manteca paths; non-Manteca intents (`EU`, `NA`, `ROW`) ignore it. No FE behavior change for those flows.
Test plan
Hardening (audit pass, 2026-06-10 — commit
1f92fa07c)A deep review of the first cut added three hook-level fixes in
useSumsubKycFlow:region: 'latam'in the FE data, so the patched call sites were feedingtargetCountry: 'MX'/'CL'/… to the BE, which stamps it aspendingMantecaGeo(first-write-wins → poisons every later geo resolution). The hook now drops any non-AR/BR value, so no call site can regress this.targetCountry(webrefreshTokenand the 5s poller pass it), so a native token expiry mid-action minted a token for a different, suffix-less applicant action than the one the user was inside.4527d3833cleareduserInitiatedRefonly on theunsupported-regionbranch, but every other terminal-error exit (response.error— e.g. theregion_not_supported400 —, no-token, native failure, throw, and the same paths inhandleRestartIdentity/handleSelfHealResubmit) left the guard set while restoringprevStatusRef: a late websocket APPROVED replay firedonKycSuccesson top of the rendered error — the "You're all set" loop again. All terminal exits now clear the guard.5 new tests: targetCountry forwarded (AR) / dropped (MX); error + throw paths survive a late
PENDING→APPROVEDtwo-event sequence (isolates theuserInitiatedRefguard from theprevStatusRefguard, which a single APPROVED event can't do); control test that a successful SDK open keeps the guard armed.Known remaining gap (deliberate, follow-up):
MantecaFlowManager's claim-link entry (SendLinkActionList→ "Pix"/"Mercado Pago") still sendstargetCountry: undefined—regionalMethodTypeknows the geo but resets to its default on the?step=regional-claimauth-redirect remount, so a naive fallback could stamp AR for a PIX claim. Proper fix is carrying the method in the URL param. The BE applicant-country fallback (#995) covers this surface meanwhile.