Skip to content

feat: kyc 2.0 frontend#1679

Merged
kushagrasarathe merged 49 commits intopeanut-wallet-devfrom
feat/kyc2.0
Feb 24, 2026
Merged

feat: kyc 2.0 frontend#1679
kushagrasarathe merged 49 commits intopeanut-wallet-devfrom
feat/kyc2.0

Conversation

@kushagrasarathe
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Feb 16, 2026

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Feb 24, 2026 2:23pm

Request Review

@kushagrasarathe kushagrasarathe changed the base branch from peanut-wallet to peanut-wallet-dev February 16, 2026 17:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Sumsub KYC end-to-end: server action and types, WebSDK typings, new hook, WebSocket event handling, SDK wrapper and UI flows; centralizes KYC status predicates; updates hooks/interfaces to include Sumsub; removes legacy identity-verification pages/components and simplifies verification layout; minor text and small-export removals.

Changes

Cohort / File(s) Summary
Sumsub server action & types
src/app/actions/sumsub.ts, src/app/actions/types/sumsub.types.ts
Adds initiateSumsubKyc server action and Sumsub types (InitiateSumsubKycResponse, SumsubKycStatus, KYCRegionIntent).
WebSDK typings
src/types/sumsub-websdk.d.ts
Adds global TypeScript declarations for the Sumsub WebSDK (builder/instance) on window.
Sumsub UI components & KycFlow
src/components/Kyc/SumsubKycFlow.tsx, src/components/Kyc/SumsubKycWrapper.tsx, src/components/Kyc/KycFlow.tsx
New Sumsub components (flow button, wrapper with SDK lifecycle, modals) and KycFlow refactor to delegate to Sumsub flow.
KYC orchestration hook & websocket
src/hooks/useSumsubKycFlow.ts, src/hooks/useWebSocket.ts
New useSumsubKycFlow hook managing tokens, wrapper, modal state, WS status updates; useWebSocket gains onSumsubKycStatusUpdate callback and event wiring.
Unified KYC status & predicates
src/constants/kyc.consts.ts, src/hooks/useUnifiedKycStatus.ts, src/hooks/useKycStatus.tsx
Introduces centralized KYC status constants and helpers; adds useUnifiedKycStatus and remaps useKycStatus to expose Sumsub-aware flags.
KYC UI updates
src/components/Kyc/KycStatusDrawer.tsx, src/components/Kyc/KycStatusItem.tsx, src/components/Home/KycCompletedModal/index.tsx
Integrates Sumsub into status drawer/item and completed modal; replaces direct status checks with predicate helpers.
Profile/regions UI changes
src/components/Profile/views/RegionsVerification.view.tsx, src/features/limits/views/LimitsPageView.tsx
Region-level Sumsub flow wiring (region intent, modals) and adjusted navigation target from region-specific to generic identity-verification route.
Legacy identity verification removed
src/components/Profile/views/IdentityVerification.view.tsx, src/components/Profile/views/RegionsPage.view.tsx, src/components/Profile/components/IdentityVerificationCountryList.tsx, src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx, src/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx
Deletes legacy identity verification view, regions page, country list, and two dynamic mobile route pages.
Layout simplified
src/app/(mobile-ui)/profile/identity-verification/layout.tsx
Simplifies layout: removes modal/UI side effects and routing logic; now only renders PageContainer with children.
Interfaces updated
src/interfaces/interfaces.ts
Extends IUserKycVerification to include SUMSUB, Sumsub-aware status typing, optional sumsubApplicantId, and rejectLabels.
Start verification text & modal props
src/components/Global/IframeWrapper/StartVerificationView.tsx, src/components/IdentityVerification/StartVerificationModal.tsx
Text updates generalizing provider references; StartVerificationModal props changed from country-based to region-based (selectedRegion, isLoading) and content simplified.
Removed/changed small exports
src/utils/general.utils, src/hooks/useGetDeviceType
Removes public exports getRedirectUrl and DeviceType respectively.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a descriptive pull request description that explains the purpose, scope, and key changes of this KYC 2.0 implementation (Sumsub integration, route restructuring, etc.).
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: kyc 2.0 frontend' clearly and concisely summarizes the main change: implementing KYC 2.0 functionality on the frontend, which aligns with the extensive changes across KYC-related components, hooks, and server actions throughout the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/kyc2.0

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@src/app/actions/sumsub.ts`:
- Line 15: Check the jwt token returned by getJWTCookie() before using it: after
const jwtToken = (await getJWTCookie())?.value, verify jwtToken is a non-empty
string and if not throw or return a clear error (e.g., "Missing JWT for Sumsub
action") instead of allowing "Bearer undefined" in the Authorization header;
apply the same validation where the token is read later (the block around lines
30-33) so all usages of jwtToken in this module are protected.

In `@src/components/Global/IframeWrapper/StartVerificationView.tsx`:
- Around line 35-39: The privacy line "only shares a yes/no with Peanut" is
inaccurate given Sumsub returns multiple states (NOT_STARTED, PENDING,
IN_REVIEW, APPROVED, REJECTED, ACTION_REQUIRED) and rejectLabels are persisted
on the user's KYC verification record; update the copy in StartVerificationView
(the <p> elements currently stating verification details) to accurately reflect
what is shared—for example, change to wording such as "shares your verification
status and any relevant feedback with Peanut" or otherwise soften the claim so
it does not assert a binary-only payload; ensure the new copy aligns with the
data stored (status and rejectLabels) and appears where the three <p> elements
are rendered.

In `@src/components/Kyc/KycFlow.tsx`:
- Around line 5-12: KycFlow's props currently omit the optional callbacks
accepted by SumsubKycFlow; update the KycFlowProps interface to include optional
onKycSuccess and onManualClose properties with the same signatures/types used by
SumsubKycFlow (or import the prop types from SumsubKycFlow to keep them in
sync), then forward those props in the component JSX (i.e., pass
onKycSuccess={onKycSuccess} and onManualClose={onManualClose} into
<SumsubKycFlow ... />) so callers can subscribe to KYC completion and manual
close events.

In `@src/components/Profile/views/RegionsVerification.view.tsx`:
- Around line 8-52: regionIntent is lost because it's derived from
selectedRegion which is cleared when starting KYC; persist the intent in state
for the active session instead: add a new state (e.g., sessionRegionIntent) and
set it inside handleStartKyc (or when user confirms start) using the current
selectedRegion.path, pass sessionRegionIntent to useSumsubKycFlow instead of
deriving regionIntent from selectedRegion, and clear sessionRegionIntent in the
same callbacks that end the flow (onKycSuccess, onManualClose,
handleSumsubClose, closeVerificationProgressModal) so the intent remains stable
for token refreshes and status checks until the flow completes.

In `@src/constants/kyc.consts.ts`:
- Around line 1-59: KycStatusCategory and getKycStatusCategory are using
different vocabularies; update the KycStatusCategory type to match the helper's
outputs (change from 'approved'|'pending'|'failed' to
'completed'|'processing'|'failed') and change getKycStatusCategory's return type
to use KycStatusCategory so the type and implementation are consistent; ensure
any other usages of KycStatusCategory across the codebase are updated to the new
labels if necessary.

In `@src/hooks/useIdentityVerification.tsx`:
- Around line 174-178: Update the Bridge QR-only logic to avoid adding redundant
regions when Sumsub already grants full LATAM access: in the conditional that
currently reads using isBridgeApproved and !isMantecaApproved, include a check
for !isSumsubApproved so it becomes isBridgeApproved && !isMantecaApproved &&
!isSumsubApproved; this ensures you only push MANTECA_QR_ONLY_REGIONS and
BRIDGE_SUPPORTED_LATAM_COUNTRIES into unlocked when the user lacks both Manteca
and Sumsub approvals (refer to symbols isBridgeApproved, isMantecaApproved,
isSumsubApproved, unlocked, MANTECA_QR_ONLY_REGIONS,
BRIDGE_SUPPORTED_LATAM_COUNTRIES).

In `@src/hooks/useUnifiedKycStatus.ts`:
- Around line 26-29: The sumsubVerification logic in useUnifiedKycStatus uses
.find() which may pick an older SUMSUB entry; change it to either mirror
useQrKycGate.ts by using .some() when you only need to know if any SUMSUB
verification exists, or (preferably) select the most recent SUMSUB verification
by inspecting user?.user.kycVerifications and picking the entry with provider
=== 'SUMSUB' and the latest updatedAt/createdAt timestamp (e.g., via
Array.prototype.reduce or a timestamp-based sort) so sumsubVerification reflects
the newest attempt.

In `@src/hooks/useWebSocket.ts`:
- Around line 65-82: The dependency array in useWebSocket is malformed: remove
the stray trailing comma and extra blank lines so the array is a well-formed
single list of dependencies (e.g., [onHistoryEntry, onKycStatusUpdate,
onMantecaKycStatusUpdate, onSumsubKycStatusUpdate, onTosUpdate, onPendingPerk,
onConnect, onDisconnect, onError]) and tidy spacing/commas; while here ensure
each handler referenced (onHistoryEntry, onKycStatusUpdate,
onMantecaKycStatusUpdate, onSumsubKycStatusUpdate, onTosUpdate, onPendingPerk,
onConnect, onDisconnect, onError) is the intended dependency (or wrapped with
useCallback elsewhere) so the effect dependency list is syntactically correct
and consistent.
🧹 Nitpick comments (5)
src/components/Kyc/SumsubKycWrapper.tsx (3)

11-12: Address TODO: Move SDK URL to constants.

The TODO comment indicates this URL should be centralized. Consider moving it to a constants file for consistency with other external URLs in the codebase.

Would you like me to open an issue to track moving this constant?


56-74: Script element is never removed on unmount.

The dynamically created script element is appended to document.head but is never cleaned up when the component unmounts. While this is typically acceptable for SDK scripts (they're often designed to persist), it's worth noting that:

  1. If the component mounts/unmounts rapidly, the onload callback could fire after unmount, potentially causing state updates on an unmounted component.
  2. The script persists in the DOM even after the wrapper is no longer needed.

Consider adding a cleanup function or using an isMounted ref to guard against state updates after unmount.

♻️ Proposed fix to guard against unmounted state updates
     // load sumsub websdk script
     useEffect(() => {
+        let isMounted = true
         const existingScript = document.getElementById('sumsub-websdk')
         if (existingScript) {
             setSdkLoaded(true)
             return
         }
 
         const script = document.createElement('script')
         script.id = 'sumsub-websdk'
         script.src = SUMSUB_SDK_URL
         script.async = true
-        script.onload = () => setSdkLoaded(true)
+        script.onload = () => {
+            if (isMounted) setSdkLoaded(true)
+        }
         script.onerror = () => {
             console.error('[sumsub] failed to load websdk script')
-            setSdkLoadError(true)
+            if (isMounted) setSdkLoadError(true)
         }
         document.head.appendChild(script)
+
+        return () => {
+            isMounted = false
+        }
     }, [])

219-246: Fixed height percentages may cause layout issues.

The SDK container uses style={{ height: '85%' }} and the controls use h-[12%]. This leaves 3% unaccounted for, which could cause overflow or gaps on some screen sizes. Consider using flexbox with flex-grow instead of fixed percentages for more robust layout.

src/hooks/useIdentityVerification.tsx (1)

79-82: Case-sensitive region path matching.

The getRegionIntent function only matches 'latam' exactly (lowercase). If regionPath is 'LATAM' or 'Latam', it will return 'STANDARD' instead of 'LATAM'. Consider normalizing the input:

♻️ Proposed fix for case-insensitive matching
 /** maps a region path to the sumsub kyc template intent */
 export const getRegionIntent = (regionPath: string): KYCRegionIntent => {
-    return regionPath === 'latam' ? 'LATAM' : 'STANDARD'
+    return regionPath.toLowerCase() === 'latam' ? 'LATAM' : 'STANDARD'
 }
src/hooks/useUnifiedKycStatus.ts (1)

33-33: Unsafe type cast for sumsubStatus.

The status is cast to SumsubKycStatus without validation. If the backend returns an unexpected status string, this could cause issues downstream. Consider adding validation or keeping the type as string | null.

♻️ Option to validate status
+const VALID_SUMSUB_STATUSES = new Set(['NOT_STARTED', 'PENDING', 'IN_REVIEW', 'APPROVED', 'REJECTED', 'ACTION_REQUIRED'])
+
 const sumsubStatus = useMemo(() => {
-    return (sumsubVerification?.status as SumsubKycStatus) ?? null
+    const status = sumsubVerification?.status
+    if (status && VALID_SUMSUB_STATUSES.has(status)) {
+        return status as SumsubKycStatus
+    }
+    return null
 }, [sumsubVerification])

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useIdentityVerification.tsx (1)

127-143: ⚠️ Potential issue | 🟠 Major

Sumsub-approved users still fail isVerifiedForCountry.

useMemo now unlocks all regions for Sumsub, but isVerifiedForCountry still only checks Bridge/Manteca. If any flow uses this helper, Sumsub-approved users can be treated as unverified.

✅ Suggested fix
 const isVerifiedForCountry = useCallback(
     (code: string) => {
+        if (isUserSumsubKycApproved) return true
         const upper = code.toUpperCase()

         // Check if user has active Manteca verification for this specific country
         const mantecaActive =
             user?.user.kycVerifications?.some(
                 (v) =>
                     v.provider === 'MANTECA' &&
                     (v.mantecaGeo || '').toUpperCase() === upper &&
                     v.status === MantecaKycStatus.ACTIVE
             ) ?? false

         // Manteca countries need country-specific verification, others just need Bridge KYC
         return isMantecaSupportedCountry(upper) ? mantecaActive : isUserBridgeKycApproved
     },
-    [user, isUserBridgeKycApproved, isMantecaSupportedCountry]
+    [user, isUserBridgeKycApproved, isUserSumsubKycApproved, isMantecaSupportedCountry]
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useIdentityVerification.tsx` around lines 127 - 143,
isVerifiedForCountry currently only considers Manteca and Bridge KYC causing
Sumsub-approved users to be treated as unverified; add a Sumsub approval check
and include it in the non-Manteca branch. Inside isVerifiedForCountry compute
something like sumsubApproved = user?.user.kycVerifications?.some(v =>
v.provider === 'SUMSUB' && v.status === SumsubKycStatus.APPROVED) ?? false, then
return isMantecaSupportedCountry(upper) ? mantecaActive :
(isUserBridgeKycApproved || sumsubApproved); reference the isVerifiedForCountry
function, isMantecaSupportedCountry, isUserBridgeKycApproved and
user?.user.kycVerifications when making the change.
🧹 Nitpick comments (2)
src/components/Profile/views/RegionsVerification.view.tsx (1)

99-117: Please resolve the TODO before release.

Leaving TODOs in UI copy tends to be forgotten; consider updating the modal copy now or tracking it in an issue.

Want me to draft the updated modal copy or open a tracking issue?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Profile/views/RegionsVerification.view.tsx` around lines 99 -
117, Remove the inline TODO above the ActionModal and update the modal copy to
reflect the benefits of unlocking a region instead of leaving a developer note;
specifically edit the title/description used by ActionModal (title: `Unlock
${selectedRegion?.name ?? ''}` and description prop) to clearly state benefits
(e.g., "Verify your identity to send and receive money, access local currency
support, and enable faster payouts") and keep the CTA tied to
handleStartKyc/isLoading, or if you cannot update copy now create a tracked
issue and replace the TODO with a brief comment referencing that issue ID;
ensure changes are applied where ActionModal, selectedRegion, handleStartKyc,
and handleModalClose are used.
src/hooks/useIdentityVerification.tsx (1)

79-82: Normalize regionPath casing for resilience.

If this ever comes from URL params, mixed casing will map to STANDARD. Lowercasing avoids surprises.

♻️ Suggested tweak
 export const getRegionIntent = (regionPath: string): KYCRegionIntent => {
-    return regionPath === 'latam' ? 'LATAM' : 'STANDARD'
+    return regionPath.toLowerCase() === 'latam' ? 'LATAM' : 'STANDARD'
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useIdentityVerification.tsx` around lines 79 - 82, getRegionIntent
currently compares regionPath verbatim which misclassifies mixed-case input;
normalize the input first (e.g., trim() and toLowerCase()) before comparing so
values like "LatAm" or " LATAM " map to 'LATAM'. Update the getRegionIntent
function to canonicalize regionPath and then return KYCRegionIntent 'LATAM' when
the normalized value equals 'latam', otherwise return 'STANDARD'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Profile/views/RegionsVerification.view.tsx`:
- Around line 49-63: The current flow sets activeRegionIntent state then
immediately awaits handleInitiateKyc, causing a race where handleInitiateKyc
reads the old intent; update the hook useSumsubKycFlow so its initiate function
(handleInitiateKyc) accepts an optional intent parameter (e.g.,
handleInitiateKyc(intent?: RegionIntent)) and uses that intent if provided, and
then modify RegionsVerification.view.tsx's handleStartKyc to compute const
intent = getRegionIntent(selectedRegion.path), call
setActiveRegionIntent(intent) and then await handleInitiateKyc(intent) (instead
of relying on state); also update any other call sites of handleInitiateKyc to
pass intent where appropriate.

In `@src/constants/kyc.consts.ts`:
- Around line 21-35: ACTION_REQUIRED is currently present in both
FAILED_STATUSES and SUMSUB_IN_PROGRESS_STATUSES causing conflicting checks
(e.g., isKycStatusFailed vs isSumsubStatusInProgress); decide its intended
classification and remove it from the other set — for example, if
ACTION_REQUIRED should be considered "in progress" move/keep it only in
SUMSUB_IN_PROGRESS_STATUSES and delete it from FAILED_STATUSES (update the
ReadonlySet literal for FAILED_STATUSES and any related tests or docs); ensure
references like isKycStatusFailed and isSumsubStatusInProgress now return
mutually exclusive results for ACTION_REQUIRED.

---

Outside diff comments:
In `@src/hooks/useIdentityVerification.tsx`:
- Around line 127-143: isVerifiedForCountry currently only considers Manteca and
Bridge KYC causing Sumsub-approved users to be treated as unverified; add a
Sumsub approval check and include it in the non-Manteca branch. Inside
isVerifiedForCountry compute something like sumsubApproved =
user?.user.kycVerifications?.some(v => v.provider === 'SUMSUB' && v.status ===
SumsubKycStatus.APPROVED) ?? false, then return isMantecaSupportedCountry(upper)
? mantecaActive : (isUserBridgeKycApproved || sumsubApproved); reference the
isVerifiedForCountry function, isMantecaSupportedCountry,
isUserBridgeKycApproved and user?.user.kycVerifications when making the change.

---

Nitpick comments:
In `@src/components/Profile/views/RegionsVerification.view.tsx`:
- Around line 99-117: Remove the inline TODO above the ActionModal and update
the modal copy to reflect the benefits of unlocking a region instead of leaving
a developer note; specifically edit the title/description used by ActionModal
(title: `Unlock ${selectedRegion?.name ?? ''}` and description prop) to clearly
state benefits (e.g., "Verify your identity to send and receive money, access
local currency support, and enable faster payouts") and keep the CTA tied to
handleStartKyc/isLoading, or if you cannot update copy now create a tracked
issue and replace the TODO with a brief comment referencing that issue ID;
ensure changes are applied where ActionModal, selectedRegion, handleStartKyc,
and handleModalClose are used.

In `@src/hooks/useIdentityVerification.tsx`:
- Around line 79-82: getRegionIntent currently compares regionPath verbatim
which misclassifies mixed-case input; normalize the input first (e.g., trim()
and toLowerCase()) before comparing so values like "LatAm" or " LATAM " map to
'LATAM'. Update the getRegionIntent function to canonicalize regionPath and then
return KYCRegionIntent 'LATAM' when the normalized value equals 'latam',
otherwise return 'STANDARD'.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/constants/kyc.consts.ts (2)

46-51: Consider handling NOT_STARTED explicitly or aligning parameter types.

Two observations:

  1. getKycStatusCategory doesn't explicitly handle NOT_STARTED_STATUSES, returning 'processing' for them. If callers rely on isKycStatusNotStarted to skip rendering, this may be fine, but consider documenting this behavior or adding a fourth category.

  2. This function takes string while other helpers accept string | undefined | null. For consistency, consider aligning the signature or documenting why this function requires a defined value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/constants/kyc.consts.ts` around lines 46 - 51, getKycStatusCategory
currently treats all non-approved/failed statuses as 'processing' and requires a
non-null string; update it to explicitly handle NOT_STARTED_STATUSES (returning
a distinct category like 'not_started' or documented behavior) and align the
parameter type to accept string | undefined | null (or handle undefined/null at
top of getKycStatusCategory) so callers like isKycStatusNotStarted are
consistent; adjust checks to use APPROVED_STATUSES, FAILED_STATUSES,
NOT_STARTED_STATUSES and return 'completed'|'failed'|'not_started'|'processing'
(or add a comment explaining why null/undefined is disallowed) to keep behavior
clear and consistent.

8-8: Type union with string provides no compile-time narrowing.

Since string is a supertype, the union MantecaKycStatus | SumsubKycStatus | string collapses to just string, meaning callers won't get type narrowing benefits. If the intent is documentation, consider using a branded type or removing string to enforce known statuses at compile time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/constants/kyc.consts.ts` at line 8, The union type KycVerificationStatus
currently includes `string`, which collapses the union into a plain string and
prevents compile-time narrowing; update the declaration of KycVerificationStatus
by removing the raw `string` so it reads `export type KycVerificationStatus =
MantecaKycStatus | SumsubKycStatus`, or if you need to allow arbitrary strings,
replace with a branded/string-literal approach (e.g., define a Brand like `type
UnknownKyc = string & { __brand?: 'UnknownKyc' }` and use `MantecaKycStatus |
SumsubKycStatus | UnknownKyc`) and update all usages of KycVerificationStatus
accordingly to preserve type safety.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/constants/kyc.consts.ts`:
- Around line 14-25: ACTION_REQUIRED is present in both FAILED_STATUSES and
SUMSUB_IN_PROGRESS_STATUSES causing isKycStatusFailed('ACTION_REQUIRED') and
isSumsubStatusInProgress('ACTION_REQUIRED') to both be true; remove
'ACTION_REQUIRED' from FAILED_STATUSES so it is treated as in-progress for
sumsub. Update the constant FAILED_STATUSES (remove 'ACTION_REQUIRED') and
run/update any tests or logic that reference isKycStatusFailed,
isSumsubStatusInProgress, FAILED_STATUSES, or SUMSUB_IN_PROGRESS_STATUSES to
reflect the new classification.

---

Nitpick comments:
In `@src/constants/kyc.consts.ts`:
- Around line 46-51: getKycStatusCategory currently treats all
non-approved/failed statuses as 'processing' and requires a non-null string;
update it to explicitly handle NOT_STARTED_STATUSES (returning a distinct
category like 'not_started' or documented behavior) and align the parameter type
to accept string | undefined | null (or handle undefined/null at top of
getKycStatusCategory) so callers like isKycStatusNotStarted are consistent;
adjust checks to use APPROVED_STATUSES, FAILED_STATUSES, NOT_STARTED_STATUSES
and return 'completed'|'failed'|'not_started'|'processing' (or add a comment
explaining why null/undefined is disallowed) to keep behavior clear and
consistent.
- Line 8: The union type KycVerificationStatus currently includes `string`,
which collapses the union into a plain string and prevents compile-time
narrowing; update the declaration of KycVerificationStatus by removing the raw
`string` so it reads `export type KycVerificationStatus = MantecaKycStatus |
SumsubKycStatus`, or if you need to allow arbitrary strings, replace with a
branded/string-literal approach (e.g., define a Brand like `type UnknownKyc =
string & { __brand?: 'UnknownKyc' }` and use `MantecaKycStatus | SumsubKycStatus
| UnknownKyc`) and update all usages of KycVerificationStatus accordingly to
preserve type safety.

…e multi phase kyc verification tackling bridge tos acceptance
adds requires_documents display status to rail tracking so bridge
rails needing extra documents are distinguished from those needing
ToS acceptance.

new KycRequiresDocuments component shows human-readable requirement
descriptions and a submit button that opens the sumsub SDK with the
peanut-additional-docs level.

wires into KycStatusDrawer to show the additional docs UI when bridge
rails have REQUIRES_EXTRA_INFORMATION status, and extends
initiateSumsubKyc to accept a levelName parameter.
- fix icon name: document -> docs, remove unsafe IconName cast
- preserve levelName across token refresh via levelNameRef
- add explicit additionalRequirements type to IUserRail.metadata
- fix needsAdditionalDocs: derive from rail status, not empty requirements
- add fallback UI when requirements array is empty
…al states, format

- aggregate additionalRequirements across all bridge rails with Array.isArray guard
- don't let additional-docs view mask failed/action_required kyc states
- run prettier on KycRequiresDocuments
…ubmission

feat: bridge additional document collection UI
@kushagrasarathe kushagrasarathe merged commit c214dc2 into peanut-wallet-dev Feb 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants