Show Rain SMS requirement before card KYC - #3185
innolope-dev wants to merge 5 commits into
Conversation
|
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: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Code-analysis diffPainscore total: 8252.92 → 8254.82 (+1.9) 🆕 New findings (15)
✅ Resolved (14)
📈 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 checklist and incomplete-response gate are coherent, but the main-KYC response still bypasses the new SMS disclosure.
Findings
-
MAJOR · src/features/card/useCardFlow.ts:54 · Gate the main-KYC response through the Rain prep drawer
A card-access user with no approved base KYC receivesmain-kyc-required;advanceFromApplyResponsestill puts that token straight intosumsubToken, so the WebSDK opens without settingpendingSumsubToken. The backend mints this token for the same Rain verification level whose steps include phone verification, making this a reachable cohort that encounters the SMS challenge without the new warning. Stagemain-kyc-requiredthrough the prep token as well, and cover that response in the hook test. -
MAJOR · src/features/card/CardPage.tsx:254 · [claude-opus] Card prep uses
standardcopy, understating the Rain level's steps and its duration
CardPagerenders the prep withpath="standard", so the card user now sees the copy written for the 3-stepgenerallevel: intro "Get these ready now, so you are not hunting for them halfway through", items ID + selfie + SMS, andkyc.prep.howLong.standard= "Under 2 minutes for most people" (src/i18n/app/messages/en.json:2980).
Product truth disagrees on both counts, and the code is the wrong side:
- Steps: peanut-api-ts/src/routes/rain/apply.ts (~line 658) documents
VERIFICATION_LEVELS.rainas "document collection, selfie, applicant data (incl. editable phone + TIN), phone verification, email verification, and therain-card-extraInformationquestionnaire". /home/chip/mono/product/feedback/problems/card-onboarding-friction.md (2026-09-09 PostHog, nita creator cohort) states the same thing user-side: "the card Sumsub level has 6 steps (ID, selfie, personal data, SMS, email code, questionnaire) vs 3 on the standard level". product/card.md § KYC liststinamong the fields shared with Rain, and the friction doc's 2026-05-01 entry is a user being asked to resubmit his TIN during card KYC. The checklist exists precisely to stop users hunting mid-flow, yet the tax ID — the one item a user may have to go and find — is omitted, as is the email code. - Duration: the same PostHog entry measures "approved users take ~8 min" for this level. "Under 2 minutes for most people" is roughly 4x off for the path it is now shown on.
This PR is what puts that copy in front of card applicants for the first time (the modal is newly mounted in CardPage), so the disagreement ships with it.
Fix: give the Rain card path its own requirements and duration rather than borrowing standard — either add taxId (with copy that is not Manteca/LATAM-specific, since the existing items.taxId.body names CPF/CUIT only) and an email-code item to the provider === 'rain' add-on in KycPrepChecklist, and a howLong.rain string reflecting the measured ~8 minutes; or introduce a fourth path for the Rain level. If the 6-step figure is stale and the level has since been trimmed, the product doc is what needs correcting — but one of the two has to move.
Checked clean
- Confirmed the detached worktree HEAD, merge base, trusted author, base ref, and exact base/head SHAs match the supplied target.
- Traced incomplete, main-kyc-required, country-confirmation, terms, refresh, resume, close, and completion paths across the card hook and modal rendering.
- Checked the sibling API contract: both incomplete and main-kyc-required tokens target the Rain verification level, which includes phone verification.
- Reviewed locale keys and inheritance for English, Latin American Spanish, Argentine Spanish, and Brazilian Portuguese.
- Exact-head substantive CI is green, including unit, typecheck, ESLint, format, native-export, and analyze; advisory visual captures and preview deployment remain in progress.
- Focused local Jest execution was unavailable because the detached worktree has no node_modules; exact-head unit CI passed.
Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.
Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: a361fc7e997f · Context: repo, sibling-api, ci · Took 9m
|
Addressed both Chip findings at exact head 35f2eeb: (1) main-kyc-required now passes through the Rain prep drawer before Sumsub opens; (2) Rain prep now lists the full provider-specific set—ID, selfie, generic tax ID, SMS, email code, and regulatory questions—and uses the measured ~8-minute duration instead of standard KYC copy. All four app locales are covered, including the es-AR delta fallback. Validation: 10 focused tests, 246 i18n tests, targeted ESLint, Prettier, and pnpm typecheck all passed. |
🖼 Visual diff — 2 screens moved4 of 94 shots changed · 90 identical · baseline
job summary · before/after/diff images — artifact Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data. |
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
Both prior Rain KYC-prep defects are fixed; no new defects found.
Findings
- MAJOR · src/i18n/app/messages/en.json:2991 · [claude-opus] Rain card prep contradicts product truth: "no second pass, under 2 minutes"
This PR makes the app tell card applicants, before KYC, that the card check needs a tax ID, an SMS code, an email code and a questionnaire, and takes "About 8 minutes for most people" (en.jsonkyc.prep.howLong.rain, plus therainTaxId/sms/emailCodeitems, wired in at CardPage.tsx:249-256 withprovider="rain").
Product truth says the opposite, in three places:
- /home/chip/mono/product/kyc.md:31 — "The Peanut Card reuses the existing Sumsub verification (shared with Rain) — no card-specific verification flow." Also kyc.md:184 and product/card.md:191 ("users do not complete a second pass").
- /home/chip/mono/product/kyc.md:77 —
average_time: "Under 2 minutes for most users"; kyc.md:150 repeats it. - The generated customer-facing page already published from that truth: /home/chip/mono/content/help/peanut-card/en.md:74 — "Do I need to verify my identity again to get the card? No. If your Peanut account is already verified, the card uses the same verification and there is no second pass."
The drawer fires precisely on the case product denies: an incomplete response for an already-KYC'd user, i.e. a second, card-specific pass.
The code is the side that is right, not the docs. peanut-api-ts has a distinct rain-requirements level (src/kyc/level-registry.ts:28) with its own card questionnaire (rain-card-extraInformation, src/sumsub/consts.ts), and product/providers/rain.md lists tin, email, phone among the fields Rain requires — so a card-specific level with extra challenges genuinely exists. product/kyc.md and product/card.md are stale, and the help page generated from them will now tell users the opposite of what the app shows.
Fix: update product/kyc.md (kyc_scope.note, verification.average_time, the Peanut Card section) and product/card.md § KYC to describe the Rain card-application level and its duration, then regenerate content/help/peanut-card and content/help/verification. Separately, record where "about 8 minutes" comes from — nothing in product/ or either repo documents a duration for this level, so the new wait-time promise currently has no source of truth behind it.
- MINOR · src/features/card/useCardFlow.ts:194 · [claude-opus] Full Rain 8-minute prep is shown when only one doc step is missing
advanceFromApplyResponsenow stages everymain-kyc-requiredtoken behind the same Rain prep drawer (useCardFlow.ts:194-196). That branch has two very different causes in peanut-api-ts: no APPROVED Sumsub row at all (src/routes/rain/apply.ts:572, the whole level really is ahead of the user), and an approved user missing a single doc such as SELFIE (src/routes/rain/apply.ts:651). In the second case the SDK asks only for that one step — the code's own comment says so ("Sumsub itself still asks only for the missing step once the user continues"), as does apply.ts:585-590.
A user who needs a 30-second selfie re-take is therefore told to have a tax ID ready, that they will receive SMS and email codes, and that this will take "About 8 minutes". That is a wait-time overstatement on the most common re-entry path, and the kind of thing that makes people abandon the drawer instead of tapping through.
The response already carries the signal: the no-KYC branch sends the full RAIN_REQUIRED_DOC_TYPES, the doc-gap branch a subset. Gate the provider="rain" copy (or at least the duration line) on the full-level case and fall back to the base standard prep when missingDocTypes is a narrow subset. The P1 gating stays intact either way — this is about which copy the drawer shows, not whether it appears.
Checked clean
- All incomplete and main-kyc-required card-apply responses now stage the Sumsub token behind the Rain prep drawer; refresh and reload-resume stay inside an already-started SDK session.
- The Rain drawer resolves ID, selfie, tax-ID, SMS, email-code, and questionnaire requirements plus the longer Rain duration in all four app locales; non-Rain callers retain their existing path copy.
- The exact-head unit, typecheck, format, ESLint, native-export, screen, design-system lint, and backend-baseline checks passed. The report job failed only in its GitHub-comment step with an unexpected JSON parse error, so ci-success is red even though this PR changes no workflow files.
- The Product Lexicon, Rain compliance reference, card product sources, and paired API response/control-flow contract were checked.
Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.
Third opinion by claude-opus: 2 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: 35f2eeb4363d · Context: repo, product · Took 14m
|
Addressed both new Chip findings. At exact UI head 0422097, an already-verified user sent back for one missing main-KYC document now gets the short standard prep; users who still need the full Rain level keep the complete Rain checklist and ~8-minute estimate. The live backend has RAIN_REQUIRED_DOC_TYPES = [SELFIE], so missingDocTypes cannot distinguish the two cohorts; the UI uses the provider-neutral identityVerification.status signal that mirrors the backend approval gate. Added regression coverage. Validation: 11 focused tests, targeted ESLint and Prettier, and pnpm typecheck passed. The stale product and help claims are corrected at exact mono head 7aac3ad8c8d963e1f5f584069b10f9ef6ecd087d in peanutprotocol/mono#187. That PR documents the Rain-specific level, records the ~8-minute PostHog source, and refreshes every existing Peanut Card and verification help locale. Markdown diff checks pass. |
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
One supplied major finding remains: the Rain prep still presents an eight-minute full verification to already verified users, contradicting the documented shared-KYC flow. P1, P3, and P4 are fixed.
Findings
-
MAJOR · src/i18n/app/messages/en.json:2991 · Rain prep still promises a second full verification
Everyincompleteresponse is reached only after the API has selected an APPROVED Sumsub identity and confirmed its required ID/selfie documents, yet this path setsprovider=rain, lists those documents again, and shows this eight-minute estimate. The canonical card contract says the existing Sumsub KYC is shared with Rain and users do not complete a second pass. This makes a verified user expect substantially more work than the SDK should require. Keep retained ID/selfie out of the Rain add-on and estimate only the additional phone, email, tax-ID, and questionnaire steps, or update the authoritative product contract first if a full second pass is now intended. -
MAJOR · src/i18n/app/messages/en.json:2991 · [claude-opus] Product truth still promises "no second pass" for the card
This PR makes the app tell card applicants they need a phone SMS code, an email code, a tax ID and a questionnaire, and that it takes about 8 minutes. That is correct against the backend:/rain/applystep 5 moves every applicant — including ones with an APPROVED Sumsub row and all docs collected — toVERIFICATION_LEVELS.rain, and returnsstatus:'incomplete'with a token until that level is GREEN (peanut-api-ts/src/routes/rain/apply.ts:659-746; the comment there lists the level's steps as "document collection, selfie, applicant data (incl. editable phone + TIN), phone verification, email verification, and the rain-card-extraInformation questionnaire").
Product truth says the opposite in four places, so the docs are the wrong side and must be corrected:
- /home/chip/mono/product/card.md:191 — "users do not complete a second pass"
- /home/chip/mono/product/kyc.md:184 and :164 — "already-verified users do not complete a second pass"
- /home/chip/mono/product/providers/rain.md — "We do not collect KYC twice"
- and it has already reached customers: /home/chip/mono/content/help/peanut-card/en.md:74 — "If your Peanut account is already verified, the card uses the same verification and there is no second pass."
The published help answer will be flatly contradicted by the drawer this PR ships (6 items, ~8 minutes) for exactly the users it addresses. Support will be answering "the help page said no second verification" tickets on day one.
Fix: no code change here. Update card.md / kyc.md / providers/rain.md to say token sharing avoids re-uploading documents but the card still requires a Rain requirements pass (phone OTP, email OTP, TIN, questionnaire), regenerate the help page through the update-content path, and record where the "about 8 minutes" figure comes from so the docs and the string share one source. Rain's 14-day marketing-review window on public card copy (providers/rain.md) means this should start now, not after merge.
- MINOR · src/features/card/useCardFlow.ts:199 · [claude-opus] Verified user re-entering for a missing doc is promised "under 2 minutes" but lands in the 8-minute Rain level
Onmain-kyc-requiredthe hook now shows the short standard prep (ID + selfie, "Under 2 minutes for most people") wheneveruser.identityVerification.status === 'verified'. But the token behind that drawer is minted atVERIFICATION_LEVELS.rain, not at the user's old level: peanut-api-ts/src/routes/rain/apply.ts mintsgenerateAccessToken(userId, mainLevel)withmainLevel = VERIFICATION_LEVELS.rainin the missing-docs branch, and in the dominant case for that branch — the "Imported from Manteca" applicant with no SELFIE step at all — it first callsmoveToLevel(applicantId, rain), which re-applies the level's current step config and therefore adds the phone-verification, email-verification, TIN and questionnaire steps to that applicant. The WebSDK then presents every incomplete step, so the user who was just told "ID and selfie, under 2 minutes" is asked for an SMS code, an email code, a tax ID and a questionnaire in the same session.
The code comment added at useCardFlow.ts:193-197 states the problem and then discards it: "both tokens open the Rain verification level, which can ask for phone verification. An already-verified user is only completing the missing document" — the second sentence does not follow from the first.
This is the over-correction of the earlier P3 note: the answer is not to swap the Rain prep for the standard one. Either keep provider='rain' on main-kyc-required (accurate, at worst over-prepares), or drive the drawer off what is actually outstanding — the response already carries missingDocTypes, and the backend knows the applicant's remaining rain steps — rather than off the base verification status, which does not predict which Rain steps that applicant has completed.
Checked clean
- Exact head, supplied base, merge base, trusted author, PR title, and description matched.
- Token staging, drawer start/close transitions, refresh and reload paths, and CARD_SUMSUB_OPENED timing were traced.
- The sibling API's main-kyc-required and incomplete branches and canonical Sumsub identity selection were checked.
- The live Lexicon has no Rain-card prep or duration definition; product/card.md and the Rain compliance reference document the shared-KYC contract.
- The Sumsub access token remains in component state and is not logged or persisted by this change.
- Exact-head ci-success, unit, typecheck, eslint, format, native-export, and screen-tests checks are green; advisory visual captures were still running.
- Focused local Jest was unavailable because the detached worktree has no node_modules; exact-head unit CI is green.
- All four changed locale JSON files parse successfully and git diff --check is clean.
Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.
Third opinion by claude-opus: 2 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: 042209772123 · Context: repo, other-repo, product, notion-lexicon, ci · Took 16m
|
Exact-head follow-up at
Validation: focused Jest 13/13, targeted ESLint, full typecheck, Prettier check, locale JSON parse, and |
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
One major product-contract mismatch remains: the implemented Rain requirements pass and its 8-minute copy contradict the canonical no-second-pass, under-2-minute card verification model.
Findings
- MAJOR · src/i18n/app/messages/en.json:2991 · Align the Rain prep with the card KYC contract
At this head the drawer tells card applicants to expect an 8-minute Rain requirements pass. The canonical card and KYC sources still say the card reuses the existing Sumsub verification, has no card-specific second pass, and takes under 2 minutes for most users. A previously verified applicant therefore sees behavior and timing the product contract explicitly denies. Reconcile the product model before shipping: either remove or shorten the card-specific pass, or update the canonical product sources through product review and align this copy to the approved model.
Checked clean
- Confirmed the detached worktree head, supplied base SHA, merge base, trusted author, PR number, and dev base ref exactly match the request.
- Checked every supplied prior finding: main-KYC responses are now staged behind the prep drawer and CARD_SUMSUB_OPENED fires only after the user continues.
- Checked the Rain checklist composition, all four locale bundles, and focused hook/component regressions; normal approved users no longer see retained ID and selfie requirements.
- Checked the sibling API contract: both main-kyc-required and incomplete responses open the full Rain requirements level, including applicant data, phone and email verification, tax ID, and questionnaire steps; this refutes the claimed selfie-only timing defect but confirms the broader product-contract drift.
- Fetched the live Notion Lexicon; it defines Sumsub/provider verification milestones but no card timing. The canonical product card, KYC, and Rain sources still promise token-sharing with no second pass and under-2-minute verification.
- Checked exact-head CI: unit, typecheck, eslint, format, native export, screen tests, preview deployment, authorship, and review checks passed; locale visual captures and ds-shots were still running. git diff --check passed.
- Checked security and privacy boundaries: the diff stages an existing Sumsub token in client state and adds no new auth, authorization, secret, money, or outbound-data surface.
Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.
Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: 5602e32dc124 · Context: repo, product, sibling-api · Took 16m
There was a problem hiding this comment.
Chip review — no blocking findings — this is not an approval
The Rain prep flow now matches the backend contract, but the canonical card and KYC product sources still contradict the new card-specific verification stage.
Findings
-
MAJOR · src/components/Kyc/KycPrepChecklist.tsx:50 · Land the product-truth correction before this flow
The new Rain branch tells an already-verified card applicant to complete provider-specific tax-ID, SMS, email, and questionnaire steps, while the current canonical card and KYC sources still promise that sharing the Sumsub dossier means there is no second card-verification pass. If this UI head lands first, help and product copy will send the same applicant into a flow they were explicitly told would not exist. Merge the companion canonical/content correction before this PR (or otherwise make that dependency enforceable) so the shipped flow and product truth change together. -
MAJOR · src/i18n/app/messages/en.json:2962 · [claude-opus] Product truth still promises "no second pass" for the card
This PR makes the card's extra verification explicit in the UI — it lists a tax ID, an SMS code, an email code and a questionnaire that the user must complete after their base KYC. peanut-api-ts confirms that is the reality: every token returned by/rain/apply(bothincompleteandmain-kyc-required) is minted forVERIFICATION_LEVELS.rain=rain-requirements, described at src/routes/rain/apply.ts:648-660 as "document collection, selfie, applicant data (incl. editable phone + TIN), phone verification, email verification, and therain-card-extraInformationquestionnaire".
Product truth says the opposite, in four places: product/card.md:23 (kyc_shared_with: rain # via Sumsub token sharing — no second KYC pass), product/card.md:113 ("Token-sharing avoids a second KYC pass"), product/card.md:191 ("users do not complete a second pass"), product/kyc.md:30-31 ("no second pass … no card-specific verification flow"), product/kyc.md:85 and product/quick-ref.md:140 ("shared with Rain via Sumsub token — no second pass"). The kyc.md:85 line is customer-facing privacy copy, and support answers derive from it.
The code is right and the docs are wrong. Token sharing removes the document re-upload, not the Rain level itself. Fix in product/: change the claim to something like "documents are reused via token sharing; the card adds a short Rain step — tax ID, phone and email codes, and a short questionnaire", and update card.md:23/113/191, kyc.md:30-31/85 and quick-ref.md:140 together, since support currently tells users there is no second step at all.
- MINOR · src/i18n/app/messages/en.json:2990 · [claude-opus] Rain PR also relaxes the documented non-Rain KYC wait times
Beyond addinghowLong.rain, this PR rewrites two strings that have nothing to do with Rain, in all four locales:howLong.standard"Under 2 minutes for most people" → "A few minutes for most people" (en.json:2990), andhowLong.hosted"About 5 minutes with your documents in hand" → "A few minutes with your documents in hand" (en.json:2992).
The standard path is the plain photo-ID + selfie flow, and product truth states its duration precisely: product/kyc.md:77 average_time: "Under 2 minutes for most users", repeated at kyc.md:150, and published on the live site (content/help-articles/help-center-index.md:24 "ID Check in Under 2 Min", plus content/use-cases//en.md, content/deposit//en.md — all "takes under 2 minutes"). After this merge the app tells a user one thing and peanut.me tells them another for the identical flow.
Secondarily, the new howLong.rain string is now character-for-character identical to howLong.standard, so the separate key conveys nothing: a flow with four extra steps (TIN entry, SMS code, email code, questionnaire) reads exactly as fast as ID + selfie alone.
The app copy is the wrong side to change here. Restore standard and hosted to the documented figures and let rain carry the vaguer wording — or, if the 2-minute figure is genuinely stale, update product/kyc.md:77 and the content pages in the same change rather than leaving the app as the only surface with the new number.
Checked clean
- Verified the detached worktree head, trusted author, base ref, base SHA, and merge base against the supplied values.
- Checked every apply-response path into Sumsub: incomplete and main-kyc-required responses stage the token behind the Rain prep drawer, while refresh and reload only resume a session the user already started.
- Checked the sibling API contract and Rain readiness implementation: approved applicants reuse retained identity documents, missing main-level selfie is represented explicitly, and the Rain level requires tax ID, phone/SMS, email verification, and questionnaire data.
- Fetched the live Notion Lexicon and checked the current canonical card, KYC, and Rain product sources; the provider-specific flow is still inconsistent with their no-second-pass promise.
- Parsed all four changed locale files and checked key coverage, including es-AR inheritance; standard, Rain, and hosted automated durations now consistently use non-numeric few-minutes wording.
- Checked exact-head CI: substantive unit, typecheck, lint, format, native export, preview deploy, and aggregate ci-success checks passed; advisory visual captures were still running. git diff --check also passed.
Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.
Third opinion by claude-opus: 2 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: 2e3b411d84d1 · Context: repo, product · Took 16m
|
Exact-head review follow-up:
|
Summary
Product dependency
Validation