feat(card): self-serve identity re-upload CTA via Rain card portal (TASK-21687) - #2904
Conversation
…ASK-21687) A card application rejected on an identity document showed only the contact-support dead end. The backend now classifies these as fixable and emits a `rain-hosted` next-action; render it as an "Upload identity documents" CTA that opens Rain's card-member portal — the same hosted handoff as bridge-hosted (Rain runs and re-adjudicates the re-upload). - Generalize the hosted-verification hook + server action to any hosted provider key: useBridgeHostedVerification → useHostedVerification(key), startBridgeHostedVerification → startHostedVerification(key). - card page: surface the rail's rain-hosted action as onUploadIdentity. - ApplicationStatusScreen: identity upload CTA on support variants. - add `rain-hosted` to NextActionKind; card.uploadIdentityDocuments copy (en/es-419/pt-BR).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Essentials Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Code-analysis diffPainscore total: 7151.3 → 7152.14 (+0.84) 🆕 New findings (20)
✅ Resolved (18)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
The Rain-hosted action is correctly gated and routed. One non-blocking launch re-entry race remains on the card CTA.
Findings
- MINOR · src/app/(mobile-ui)/card/page.tsx:283 · Disable the Rain CTA while its launch is pending
The card path ignores the hook's isStarting state. On Capacitor, a fast second tap while /users/kyc/start-action is pending invokes start() twice; because start() has no synchronous re-entry guard, both responses can call Browser.open and produce duplicate portal opens. Thread isStarting through as a disabled/loading prop, or guard start() with a ref before opening.
Checked clean
- Pinned head SHA, exact base SHA, and merge base all matched the supplied values.
- Capability lookup only exposes the CTA for the Rain rail's blocking rain-hosted action, matching the paired API contract.
- Hosted URL launch covers popup reservation, same-tab fallback, native browser handoff, friendly errors, reverse-tabnabbing protection, and return refetch behavior.
- Exact-head CI reported successful format, typecheck, eslint, unit, analyze, deploy-preview, and aggregate ci-success checks.
- Local targeted Jest execution was unavailable because the detached worktree has no node_modules; the exact-head unit check passed in CI.
Second opinion skipped: openrouter-unparseable-reply.
Exact head: af228731444b · Context: repo, paired-api
Chip: a fast second tap re-enters start() before isStarting (React state, set a tick later) disables anything, reserving/opening a second portal tab. Guard start() with a ref set in the same tick and reset in finally — covers every caller, matching the poaStartingRef pattern on the PoA CTA.
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
The Rain identity re-upload CTA and hosted handoff match the paired API contract. The prior duplicate-launch race is fixed by a synchronous re-entry guard.
Checked clean
- Pinned head SHA, exact base SHA, and merge base all matched the supplied values.
- The previous fast-double-tap finding is fixed: startingRef is set synchronously before tab reservation or native launch and reset on every exit path.
- The Rain rail exposes the CTA only when its blocking action resolves to rain-hosted, and the shared action posts the exact authorized key expected by paired API PR 1482.
- Hosted launch behavior covers popup reservation, same-tab fallback, native in-app browser handoff, friendly failures, reverse-tabnabbing protection, and return refetches.
- The Bridge hosted-verification caller was migrated without changing its action key or loading/error behavior, and no old hook/action references remain.
- All three locale JSON files parse and contain the new card.uploadIdentityDocuments string.
- Exact-head CI reported successful unit, typecheck, eslint, format, analyze, deploy-preview, and aggregate ci-success checks; ds-shots was still in progress. Local tests were unavailable because the detached worktree has no node_modules.
Second opinion skipped: openrouter-timeout.
Exact head: b6140b10fc06 · Context: repo, paired-api
Problem
A card application Rain rejected on an identity document (
BAD_PROOF_OF_IDENTITY/DOCUMENT_MISSING) showed only the contact-support dead end — no way for the user to fix it themselves.Change
Pairs with peanut-api-ts #1482, which reclassifies these rails as fixable and emits a new
rain-hostednext-action. This PR renders that action as an "Upload identity documents" CTA that opens Rain's card-member portal — where the user re-uploads and Rain re-adjudicates (verified live: the portal mints a fresh Sumsub token for a rejected applicant).useBridgeHostedVerification()→useHostedVerification(actionKey)(file renamed)startBridgeHostedVerification()→startHostedVerification(key)rain-hostedaction asonUploadIdentity, opening the portal via the hook.rain-hostedtoNextActionKind;card.uploadIdentityDocumentscopy in en / es-419 / pt-BR.Notes
selectBridgeTasks(home carousel) matches onlyaccept-tos/bridge-hosted, sorain-hostedis not swept into the Bridge flow — it surfaces on the card status screen where the user already is.src/types/capabilities.ts(the FE source of truth for the capabilities contract), so the generatedapi.openapi.json/api.generated.tsare untouched andcheck:apistays green; the generated spec syncs from the API repo separately.Test
ApplicationStatusScreen: new cases for the identity CTA (renders on support/rejected variants, omitted otherwise and on non-support variants).AdditionalVerificationView: updated for the renamed action; still green.tscclean.