fix(kyc): launch Sumsub SDK when the modal portal mounts the container late#2440
Conversation
…r late Card creation has been at zero since the #2407 release went live: the Sumsub WebSDK opens but nobody can finish. card_sumsub_opened is normal (137/day) while card_sumsub_completed went 49-85/day -> 0, kyc_submitted 27-76 -> 0 and kyc_approved 16-42 -> 0. Users sit on a spinner and give up (51 closes, zero completions today). Downstream that is a full card outage — no new verified users, and the 334 approved-but-cardless users are routed back through the same SDK for Rain's extra docs, so card_apply terms-required collapsed from 27-42% of outcomes to 1.4% and 0 cards were created in 15h+ (baseline 24-43). Root cause: #2407 replaced the StartVerificationView click gate with auto-init on `visible`. The init effect bails on `!sdkContainerRef.current`, but Modal is a headlessui <Transition>/<Dialog> that renders through a Portal — the portal target is created in the portal's own effect, so the container mounts a commit AFTER `visible` flips true. A ref is not reactive and was not in the dep array, so the effect read null on its only run and never re-ran: the SDK was never launched. The old click gate hid this by guaranteeing the container was mounted long before init. It only reproduces when the wrapper is already mounted (the real SumsubKycModals case) so `sdkLoaded` has settled before `visible` flips — mounting straight to visible lets the sdkLoaded flip re-run the effect. Fix: hold the container in state via a callback ref so attachment re-runs the init effect. Keeps #2407's intended no-interstitial UX rather than reverting. Test reproduces the portal's late mount and fails on the current prod code (launch called 0 times), passes with the fix.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code-analysis diffPainscore total: 6206.95 → 6206.95 (0) |
🧪 UI test report — 🔴 1 failingSuites
🔴 Failing tests
📊 Coverage (unit)
⏱ 10 slowest test cases
|
Review pass on my own test: replace a tautological waitFor that did not actually guarantee sdkLoaded had settled (the premise the repro depends on), reset the mock's mounted flag so a close/re-open replays the late mount like the real portal, name the mock component so rules-of-hooks recognises it (it was adding 2 eslint errors), and stop leaking window.snsWebSdk.
…llback-ref fix(kyc): actually apply the callback-ref fix (#2440 shipped the test only)
Summary
P0 — card creation has been at zero for 15h+. The Sumsub WebSDK opens but nobody can complete verification, so no one gets a card.
The numbers (PostHog, prod):
card_sumsub_openedcard_sumsub_completedkyc_submittedkyc_approvedcard_sumsub_closedUsers sit on a spinner and walk away. Downstream this is a full card outage: no new verified users, and the 334 already-approved-but-cardless users get routed back through the same SDK for Rain's extra docs.
card_applyterms-requiredcollapsed from 27–42% of outcomes to 1.4%, and 0 cards were created in 15h+ (baseline 24–43/day). Last card:2026-07-16 21:16.Corroborated by a user report: "When I try to set up the new card, it just spins, nothing appears on the display."
Root cause
#2407 replaced the
StartVerificationViewclick gate with auto-init onvisible. The init effect bails on!sdkContainerRef.current— butModalis a headlessui<Transition>/<Dialog>that renders through a Portal, and the portal target is created in the portal's own effect. So the container mounts a commit aftervisibleflips true.A ref is not reactive and was not in the dep array, so the effect read
nullon its only run and never re-ran — the SDK was never launched. The old click gate hid this by guaranteeing the container was mounted long before init.It only reproduces when the wrapper is already mounted (the real
SumsubKycModalscase) sosdkLoadedhas settled beforevisibleflips. Mounting straight tovisiblelets thesdkLoadedflip re-run the effect and masks the bug — which is why it escaped review.Fix
Hold the container in state via a callback ref, so attachment re-runs the init effect. Keeps #2407's intended no-interstitial UX rather than reverting it.
Risks / breaking changes
SumsubKycModalssurface benefits (card apply, Manteca add-money, claim/bank flows, profile regions) — all were equally broken.main(prod hotfix) → back-merge debt main→dev.QA
SumsubKycWrapper.test.tsxreproduces the portal's late mount. Fails on current prod code (launchcalled 0 times), passes with the fix. Also asserts it does not launch while hidden or without a token.origin/main(content-submodule/worktree env), none from this change.Design notes / accepted trade-offs