Skip to content

feat(card): proof-of-address upload on stuck Rain applications#2446

Merged
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-poa-upload
Jul 17, 2026
Merged

feat(card): proof-of-address upload on stuck Rain applications#2446
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-poa-upload

Conversation

@jjramirezn

@jjramirezn jjramirezn commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Hotfix port of #2438 to main per Jota — same reviewed content, squashed onto main (every touched file is byte-identical between dev and main, verified). Full description and review trail on #2438.

Deploy order: peanut-api-ts hotfix/rain-poa-selfheal must be live first (fails closed otherwise — the CTA simply doesn't render).
Back-merge debt: standard main→dev back-merge after merge.

Summary by CodeRabbit

  • New Features

    • Added a self-serve proof-of-address upload flow for eligible rejected applications.
    • Added an “Upload proof of address” action with inline error messaging.
    • Application status updates to reviewing after submission and refreshes automatically.
  • Bug Fixes

    • Prevented duplicate proof-of-address submission attempts from rapid repeated clicks.
  • Tests

    • Added coverage for proof-of-address actions, eligibility states, and upload errors.

Hotfix port to main of peanut-ui#2438 (branch was dev-based; every
touched file is byte-identical between dev and main, so this is the
same reviewed content squashed onto main). Pairs with peanut-api-ts
hotfix/rain-poa-selfheal — deploy the API first.

When the rain rail carries the sumsub:proof_of_address next-action, the
card status screens (requires-info / rejected) render a primary
"Upload proof of address" CTA driving a dedicated lightweight flow:
resubmit action -> second SumsubKycWrapper -> inline initiation errors
-> optimistic "we received your document" banner (auto-reset when the
backend state takes over). Double-click guard; analytics tagged
source: poa-self-heal.

Full review trail on #2438.
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jul 17, 2026 8:30pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Rain proof-of-address self-heal flow with token-based Sumsub submission, upload controls and inline errors on supported status screens, optimistic reviewing messaging, and refreshed card data after completion.

Changes

Proof-of-address self-heal

Layer / File(s) Summary
Self-heal response contract
src/app/actions/sumsub.ts
The self-heal response supports RAIN_DOCUMENT, and Rain is accepted as a resubmission provider.
Status-screen upload controls
src/components/Card/ApplicationStatusScreen.tsx, src/components/Card/__tests__/ApplicationStatusScreen.test.tsx
Support variants render an optional “Upload proof of address” CTA and inline upload errors; tests cover supported, unsupported, absent-action, and error states.
Card-page PoA submission flow
src/app/(mobile-ui)/card/page.tsx
The card page mints Rain PoA tokens with a double-click guard, opens Sumsub, updates reviewing messaging, and refreshes overview and user data after completion.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CardUser
  participant ApplicationStatusScreen
  participant CardPage
  participant initiateSelfHealResubmission
  participant SumsubKycWrapper
  participant CardOverview
  CardUser->>ApplicationStatusScreen: Selects Upload proof of address
  ApplicationStatusScreen->>CardPage: Calls onUploadProofOfAddress
  CardPage->>initiateSelfHealResubmission: Requests RAIN token
  initiateSelfHealResubmission-->>CardPage: Returns PoA action token
  CardPage->>SumsubKycWrapper: Opens PoA submission with token
  SumsubKycWrapper-->>CardPage: Completes PoA submission
  CardPage->>CardOverview: Invalidates overview and refetches user
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: hugo0

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding proof-of-address upload support for stuck Rain card applications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/rain-poa-upload

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 6207.05 → 6208.86 (+1.81)
Findings: +1 net (+13 new, -12 resolved)

🆕 New findings (13)

  • critical complexity — src/app/(mobile-ui)/card/page.tsx — CC 125, MI 57.27, SLOC 475
  • high hotspot — src/app/(mobile-ui)/card/page.tsx — 44 commits, +1270/-494 lines since 6 months ago
  • medium high-mdd — src/app/(mobile-ui)/card/page.tsx:54 — CardPage: MDD 129.3 (uses across many lines from declarations)
  • medium high-dlt — src/app/(mobile-ui)/card/page.tsx:54 — CardPage: DLT 56 (calls 56 distinct functions — high context load)
  • medium complexity — src/app/actions/sumsub.ts — CC 25, MI 51.96, SLOC 95
  • medium hotspot — src/app/actions/sumsub.ts — 25 commits, +222/-70 lines since 6 months ago
  • medium method-complexity — src/app/(mobile-ui)/card/page.tsx:542 — CC 18 SLOC 51
  • medium complexity — src/components/Card/ApplicationStatusScreen.tsx — CC 11, MI 57.73, SLOC 24
  • medium react-effect-derives-state — src/app/(mobile-ui)/card/page.tsx:268 — small useEffect that only sets state from deps
  • low high-mdd — src/components/Card/ApplicationStatusScreen.tsx:57 — ApplicationStatusScreen: MDD 19.0 (uses across many lines from declarations)
  • low high-mdd — src/app/(mobile-ui)/card/page.tsx:424 — : MDD 15.8 (uses across many lines from declarations)
  • low high-mdd — src/app/(mobile-ui)/card/page.tsx:542 — renderState: MDD 16.1 (uses across many lines from declarations)
  • low high-mdd — src/app/(mobile-ui)/card/page.tsx:278 — : MDD 12.6 (uses across many lines from declarations)

✅ Resolved (12)

  • src/app/(mobile-ui)/card/page.tsx — CC 106, MI 56.91, SLOC 419
  • src/app/(mobile-ui)/card/page.tsx — 43 commits, +1181/-489 lines since 6 months ago
  • src/app/(mobile-ui)/card/page.tsx:53 — CardPage: MDD 110.2 (uses across many lines from declarations)
  • src/app/(mobile-ui)/card/page.tsx:53 — CardPage: DLT 49 (calls 49 distinct functions — high context load)
  • src/app/actions/sumsub.ts — CC 25, MI 51.94, SLOC 95
  • src/components/Card/share-asset/ShareAssetD3.tsx — 24 commits, +1525/-1137 lines since 6 months ago
  • src/app/(mobile-ui)/card/page.tsx:485 — CC 16 SLOC 51
  • src/components/Card/ApplicationStatusScreen.tsx — CC 8, MI 58.68, SLOC 23
  • src/components/Card/ApplicationStatusScreen.tsx:49 — ApplicationStatusScreen: MDD 19.0 (uses across many lines from declarations)
  • src/app/(mobile-ui)/card/page.tsx:367 — : MDD 15.8 (uses across many lines from declarations)
  • src/app/(mobile-ui)/card/page.tsx:485 — renderState: MDD 14.4 (uses across many lines from declarations)
  • src/app/(mobile-ui)/card/page.tsx:221 — : MDD 12.6 (uses across many lines from declarations)

📈 Painscore deltas (top movers)

File Before After Δ
src/app/(mobile-ui)/card/page.tsx 16.0 17.4 +1.4

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2000 ran, 0 failed, 0 skipped, 31.5s

📊 Coverage (unit)

metric %
statements 59.5%
branches 43.3%
functions 48.4%
lines 59.8%
⏱ 10 slowest test cases
time test
3.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.2s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.2s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › keeps stickers off the username pill (final pass respects the keep-out)
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/utils/__tests__/demo-balance.test.ts › starts at the full balance on a fresh install and stamps a timestamp
0.1s src/utils/__tests__/demo-balance.test.ts › debits and floors at zero
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@jjramirezn
jjramirezn marked this pull request as ready for review July 17, 2026 20:35
@jjramirezn
jjramirezn requested a review from Hugo0 July 17, 2026 20:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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/app/`(mobile-ui)/card/page.tsx:
- Around line 244-257: Move the posthog.capture call for CARD_SUMSUB_OPENED in
startPoaUpload to after initiateSelfHealResubmission returns a valid token and
setPoaToken is prepared. Do not record the event when the response contains an
error or no token.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d938d83c-af27-4c85-93f0-95e80171c044

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2eac8 and c8ee8c7.

📒 Files selected for processing (4)
  • src/app/(mobile-ui)/card/page.tsx
  • src/app/actions/sumsub.ts
  • src/components/Card/ApplicationStatusScreen.tsx
  • src/components/Card/__tests__/ApplicationStatusScreen.test.tsx

Comment on lines +244 to +257
const startPoaUpload = useCallback(async () => {
if (poaStartingRef.current) return
poaStartingRef.current = true
posthog.capture(ANALYTICS_EVENTS.CARD_SUMSUB_OPENED, { source: 'poa-self-heal' })
setPoaError(null)
try {
const response = await initiateSelfHealResubmission('RAIN')
if (response.error || !response.data?.token) {
// Surfaced inline on the status screen — a silent primary CTA on
// a stuck-application screen is worse than no CTA.
setPoaError(response.error ?? 'Could not start the upload. Please try again.')
return
}
setPoaToken(response.data.token)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Record CARD_SUMSUB_OPENED only after token creation succeeds.

Initiation failures currently count as opened sessions even though the SDK never launches, skewing the PoA funnel.

Proposed fix
         if (poaStartingRef.current) return
         poaStartingRef.current = true
-        posthog.capture(ANALYTICS_EVENTS.CARD_SUMSUB_OPENED, { source: 'poa-self-heal' })
         setPoaError(null)
         try {
             const response = await initiateSelfHealResubmission('RAIN')
             if (response.error || !response.data?.token) {
                 setPoaError(response.error ?? 'Could not start the upload. Please try again.')
                 return
             }
+            posthog.capture(ANALYTICS_EVENTS.CARD_SUMSUB_OPENED, { source: 'poa-self-heal' })
             setPoaToken(response.data.token)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const startPoaUpload = useCallback(async () => {
if (poaStartingRef.current) return
poaStartingRef.current = true
posthog.capture(ANALYTICS_EVENTS.CARD_SUMSUB_OPENED, { source: 'poa-self-heal' })
setPoaError(null)
try {
const response = await initiateSelfHealResubmission('RAIN')
if (response.error || !response.data?.token) {
// Surfaced inline on the status screen — a silent primary CTA on
// a stuck-application screen is worse than no CTA.
setPoaError(response.error ?? 'Could not start the upload. Please try again.')
return
}
setPoaToken(response.data.token)
const startPoaUpload = useCallback(async () => {
if (poaStartingRef.current) return
poaStartingRef.current = true
setPoaError(null)
try {
const response = await initiateSelfHealResubmission('RAIN')
if (response.error || !response.data?.token) {
// Surfaced inline on the status screen — a silent primary CTA on
// a stuck-application screen is worse than no CTA.
setPoaError(response.error ?? 'Could not start the upload. Please try again.')
return
}
posthog.capture(ANALYTICS_EVENTS.CARD_SUMSUB_OPENED, { source: 'poa-self-heal' })
setPoaToken(response.data.token)
🤖 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/app/`(mobile-ui)/card/page.tsx around lines 244 - 257, Move the
posthog.capture call for CARD_SUMSUB_OPENED in startPoaUpload to after
initiateSelfHealResubmission returns a valid token and setPoaToken is prepared.
Do not record the event when the response contains an error or no token.

@Hugo0
Hugo0 merged commit f9ab02d into main Jul 17, 2026
24 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants