From 6c9eec7632355ca211d3e6397b947e83396f05f2 Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:52:02 +0530
Subject: [PATCH 1/7] fix: surface bridge docs requirement and allow
additional-docs SDK flow
KycStatusItem: check for REQUIRES_EXTRA_INFORMATION bridge rails and
show "Action needed" with pending pill instead of "Verified".
useSumsubKycFlow: don't short-circuit on APPROVED when a token is
returned (additional-docs flow needs the SDK to open). Don't sync
APPROVED status when token present to prevent useEffect from firing
onKycSuccess prematurely.
SumsubKycWrapper: ignore onApplicantStatusChanged events within 3s of
SDK init to prevent pre-existing APPROVED status from auto-closing.
---
src/components/Kyc/KycStatusItem.tsx | 21 +++++++++++++++++----
src/components/Kyc/SumsubKycWrapper.tsx | 10 ++++++++++
src/hooks/useSumsubKycFlow.ts | 12 ++++++++----
3 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/src/components/Kyc/KycStatusItem.tsx b/src/components/Kyc/KycStatusItem.tsx
index 45e04b036..320de4e37 100644
--- a/src/components/Kyc/KycStatusItem.tsx
+++ b/src/components/Kyc/KycStatusItem.tsx
@@ -71,6 +71,15 @@ export const KycStatusItem = ({
const finalBridgeKycStatus = wsBridgeKycStatus || bridgeKycStatus || user?.user?.bridgeKycStatus
const kycStatus = verification ? verification.status : finalBridgeKycStatus
+ // check if any bridge rail needs additional documents
+ const hasBridgeDocsNeeded = useMemo(
+ () =>
+ (user?.rails ?? []).some(
+ (r) => r.status === 'REQUIRES_EXTRA_INFORMATION' && r.rail.provider.code === 'BRIDGE'
+ ),
+ [user?.rails]
+ )
+
const isApproved = isKycStatusApproved(kycStatus)
const isPending = isKycStatusPending(kycStatus)
const isRejected = isKycStatusFailed(kycStatus)
@@ -80,13 +89,14 @@ export const KycStatusItem = ({
const isInitiatedButNotStarted = !!verification && isKycStatusNotStarted(kycStatus)
const subtitle = useMemo(() => {
+ if (hasBridgeDocsNeeded) return 'Action needed'
if (isInitiatedButNotStarted) return 'Not completed'
if (isActionRequired) return 'Action needed'
if (isPending) return 'Processing'
- if (isApproved) return 'Completed'
+ if (isApproved) return 'Verified'
if (isRejected) return 'Failed'
return 'Unknown'
- }, [isInitiatedButNotStarted, isActionRequired, isPending, isApproved, isRejected])
+ }, [hasBridgeDocsNeeded, isInitiatedButNotStarted, isActionRequired, isPending, isApproved, isRejected])
const title = useMemo(() => {
if (region === 'LATAM') return 'LATAM verification'
@@ -95,7 +105,7 @@ export const KycStatusItem = ({
// only hide for bridge's default "not_started" state.
// if a verification record exists, the user has initiated KYC — show it.
- if (!verification && isKycStatusNotStarted(kycStatus)) {
+ if (!verification && !hasBridgeDocsNeeded && isKycStatusNotStarted(kycStatus)) {
return null
}
@@ -117,7 +127,10 @@ export const KycStatusItem = ({
{subtitle}
{
console.log('[sumsub] onApplicantSubmitted fired')
stableOnComplete()
@@ -103,6 +107,12 @@ export const SumsubKycWrapper = ({
reviewResult?: { reviewAnswer?: string }
}) => {
console.log('[sumsub] onApplicantStatusChanged fired', payload)
+ // ignore status events that fire within 3s of sdk init — these reflect
+ // pre-existing state (e.g. user already approved), not a new submission
+ if (Date.now() - sdkInitTime < 3000) {
+ console.log('[sumsub] ignoring early onApplicantStatusChanged (pre-existing state)')
+ return
+ }
// auto-close when sumsub shows success screen
if (payload?.reviewStatus === 'completed' && payload?.reviewResult?.reviewAnswer === 'GREEN') {
stableOnComplete()
diff --git a/src/hooks/useSumsubKycFlow.ts b/src/hooks/useSumsubKycFlow.ts
index 2c107db32..4dc80f996 100644
--- a/src/hooks/useSumsubKycFlow.ts
+++ b/src/hooks/useSumsubKycFlow.ts
@@ -126,8 +126,11 @@ export const useSumsubKycFlow = ({ onKycSuccess, onManualClose, regionIntent }:
return
}
- // sync status from api response
- if (response.data?.status) {
+ // sync status from api response, but skip when a token is returned
+ // alongside APPROVED — that means the SDK should open (e.g. additional-docs flow),
+ // not that kyc is finished. syncing APPROVED here would trigger the useEffect
+ // which fires onKycSuccess and closes everything before the SDK opens.
+ if (response.data?.status && !(response.data.status === 'APPROVED' && response.data.token)) {
setLiveKycStatus(response.data.status)
}
@@ -136,9 +139,10 @@ export const useSumsubKycFlow = ({ onKycSuccess, onManualClose, regionIntent }:
if (effectiveIntent) regionIntentRef.current = effectiveIntent
levelNameRef.current = levelName
- // if already approved, no token is returned.
+ // if already approved and no token returned, kyc is done.
// set prevStatusRef so the transition effect doesn't fire onKycSuccess a second time.
- if (response.data?.status === 'APPROVED') {
+ // when a token IS returned (e.g. additional-docs flow), we still need to show the SDK.
+ if (response.data?.status === 'APPROVED' && !response.data?.token) {
prevStatusRef.current = 'APPROVED'
onKycSuccess?.()
return
From f3eb36dbbbffa342689a86e358a936bc1c97ede7 Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:56:46 +0530
Subject: [PATCH 2/7] fix: improve KYC UI states, copy, and requirement labels
- update verification-in-progress modal title and description
- use InfoCard for additional docs requirements display
- add DUPLICATE_EMAIL reject label for bridge submission errors
- fix country flag and region label ordering (US/EU/UK/MX)
- update fallback requirement description copy
---
src/components/Kyc/CountryFlagAndName.tsx | 4 ++--
.../Kyc/KycVerificationInProgressModal.tsx | 4 ++--
.../Kyc/modals/KycProcessingModal.tsx | 2 +-
src/components/Kyc/states/KycCompleted.tsx | 2 +-
.../Kyc/states/KycRequiresDocuments.tsx | 24 +++++++++++--------
src/constants/bridge-requirements.consts.ts | 2 +-
src/constants/sumsub-reject-labels.consts.ts | 7 ++++++
7 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/src/components/Kyc/CountryFlagAndName.tsx b/src/components/Kyc/CountryFlagAndName.tsx
index fec797e3c..3398a38bf 100644
--- a/src/components/Kyc/CountryFlagAndName.tsx
+++ b/src/components/Kyc/CountryFlagAndName.tsx
@@ -16,8 +16,8 @@ export const CountryFlagAndName = ({ countryCode, isBridgeRegion }: CountryFlagA
icons={[
'https://flagcdn.com/w160/us.png',
'https://flagcdn.com/w160/eu.png',
- 'https://flagcdn.com/w160/mx.png',
'https://flagcdn.com/w160/gb.png',
+ 'https://flagcdn.com/w160/mx.png',
]}
iconSize={80}
imageClassName="h-5 w-5 min-h-5 min-w-5 rounded-full object-cover object-center shadow-sm"
@@ -32,7 +32,7 @@ export const CountryFlagAndName = ({ countryCode, isBridgeRegion }: CountryFlagA
loading="lazy"
/>
)}
- {isBridgeRegion ? 'US/EU/MX/UK' : countryName}
+ {isBridgeRegion ? 'US/EU/UK/MX' : countryName}
)
}
diff --git a/src/components/Kyc/KycVerificationInProgressModal.tsx b/src/components/Kyc/KycVerificationInProgressModal.tsx
index 40095eeea..38c0385aa 100644
--- a/src/components/Kyc/KycVerificationInProgressModal.tsx
+++ b/src/components/Kyc/KycVerificationInProgressModal.tsx
@@ -73,11 +73,11 @@ export const KycVerificationInProgressModal = ({
onClose={onClose}
isLoadingIcon
iconContainerClassName="bg-yellow-1 text-black"
- title="Identity verified!"
+ title={'Verfication in progress'}
description={
preparingTimedOut
? "This is taking longer than expected. You can continue and we'll notify you when it's ready."
- : 'Preparing your account...'
+ : 'Submitting your information and preparing your account. This usually takes less than a minute.'
}
ctas={
preparingTimedOut
diff --git a/src/components/Kyc/modals/KycProcessingModal.tsx b/src/components/Kyc/modals/KycProcessingModal.tsx
index c904bd12d..f2e996c21 100644
--- a/src/components/Kyc/modals/KycProcessingModal.tsx
+++ b/src/components/Kyc/modals/KycProcessingModal.tsx
@@ -12,7 +12,7 @@ export const KycProcessingModal = ({ visible, onClose }: KycProcessingModalProps
visible={visible}
onClose={onClose}
icon="clock"
- iconContainerClassName="bg-purple-3"
+ iconContainerClassName="bg-yellow-1"
title="Verification in progress"
description="We're reviewing your identity. This usually takes less than a minute."
ctas={[
diff --git a/src/components/Kyc/states/KycCompleted.tsx b/src/components/Kyc/states/KycCompleted.tsx
index 0c59259b9..268ad9bfb 100644
--- a/src/components/Kyc/states/KycCompleted.tsx
+++ b/src/components/Kyc/states/KycCompleted.tsx
@@ -33,7 +33,7 @@ export const KycCompleted = ({
return (
-
+
diff --git a/src/components/Kyc/states/KycRequiresDocuments.tsx b/src/components/Kyc/states/KycRequiresDocuments.tsx
index 16407bcea..c9a751615 100644
--- a/src/components/Kyc/states/KycRequiresDocuments.tsx
+++ b/src/components/Kyc/states/KycRequiresDocuments.tsx
@@ -1,5 +1,6 @@
import { KYCStatusDrawerItem } from '../KYCStatusDrawerItem'
import { Button } from '@/components/0_Bruddle/Button'
+import InfoCard from '@/components/Global/InfoCard'
import { getRequirementLabel } from '@/constants/bridge-requirements.consts'
// shows when a payment provider (bridge) needs additional documents from the user.
@@ -15,25 +16,28 @@ export const KycRequiresDocuments = ({
}) => {
return (
-
+
-
Your payment provider requires additional verification documents.
+
Our payment provider requires additional verification documents.
{requirements.length > 0 ? (
requirements.map((req) => {
const label = getRequirementLabel(req)
return (
-
-
{label.title}
-
{label.description}
-
+
)
})
) : (
-
-
Additional Document
-
Please provide the requested document.
-
+
)}
diff --git a/src/constants/bridge-requirements.consts.ts b/src/constants/bridge-requirements.consts.ts
index d50ae2868..d2adebd9d 100644
--- a/src/constants/bridge-requirements.consts.ts
+++ b/src/constants/bridge-requirements.consts.ts
@@ -26,7 +26,7 @@ const BRIDGE_REQUIREMENT_LABELS: Record
= {
const FALLBACK_LABEL: RequirementLabelInfo = {
title: 'Additional Document',
- description: 'Please provide the requested document.',
+ description: 'Please resubmit your identity document.',
}
/** get human-readable label for a bridge additional requirement */
diff --git a/src/constants/sumsub-reject-labels.consts.ts b/src/constants/sumsub-reject-labels.consts.ts
index 948ce2f3f..7c4fc2850 100644
--- a/src/constants/sumsub-reject-labels.consts.ts
+++ b/src/constants/sumsub-reject-labels.consts.ts
@@ -270,6 +270,13 @@ const REJECT_LABEL_MAP: Record = {
title: 'Verification temporarily unavailable',
description: 'The verification database is currently unavailable. Please try again later.',
},
+
+ // --- provider submission errors (retryable) ---
+ DUPLICATE_EMAIL: {
+ title: 'Email already in use',
+ description:
+ 'The email you entered is already associated with another account. Please verify again with a different email.',
+ },
}
const FALLBACK_LABEL_INFO: RejectLabelInfo = {
From f79bbe235971e343892f033b8b5afcfdda694fb8 Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:57:16 +0530
Subject: [PATCH 3/7] refactor: replace inline KYC websocket with useKycStatus
hook
remove duplicated bridgeKycStatus websocket listeners and manual
status tracking in favor of the centralized useKycStatus hook.
---
.../add-money/[country]/bank/page.tsx | 23 ++------------
.../AddWithdraw/AddWithdrawCountriesList.tsx | 31 +++----------------
.../Claim/Link/views/BankFlowManager.view.tsx | 23 +-------------
3 files changed, 9 insertions(+), 68 deletions(-)
diff --git a/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx b/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
index 3e773c247..58bcf7b69 100644
--- a/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
+++ b/src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
@@ -9,9 +9,8 @@ import { useOnrampFlow } from '@/context/OnrampFlowContext'
import { useWallet } from '@/hooks/wallet/useWallet'
import { formatAmount } from '@/utils/general.utils'
import { countryData } from '@/components/AddMoney/consts'
-import { type BridgeKycStatus } from '@/utils/bridge-accounts.utils'
-import { useWebSocket } from '@/hooks/useWebSocket'
import { useAuth } from '@/context/authContext'
+import useKycStatus from '@/hooks/useKycStatus'
import { useCreateOnramp } from '@/hooks/useCreateOnramp'
import { useRouter, useParams } from 'next/navigation'
import { useCallback, useEffect, useMemo, useState } from 'react'
@@ -56,7 +55,6 @@ export default function OnrampBankPage() {
const [showWarningModal, setShowWarningModal] = useState(false)
const [showKycModal, setShowKycModal] = useState(false)
const [isRiskAccepted, setIsRiskAccepted] = useState(false)
- const [liveKycStatus, setLiveKycStatus] = useState(undefined)
const { setError, error, setOnrampData, onrampData } = useOnrampFlow()
const { balance } = useWallet()
@@ -91,19 +89,7 @@ export default function OnrampBankPage() {
// uk-specific check
const isUK = isUKCountry(selectedCountryPath)
- useWebSocket({
- username: user?.user.username ?? undefined,
- autoConnect: !!user?.user.username,
- onKycStatusUpdate: (newStatus) => {
- setLiveKycStatus(newStatus as BridgeKycStatus)
- },
- })
-
- useEffect(() => {
- if (user?.user.bridgeKycStatus) {
- setLiveKycStatus(user.user.bridgeKycStatus as BridgeKycStatus)
- }
- }, [user?.user.bridgeKycStatus])
+ const { isUserKycApproved } = useKycStatus()
useEffect(() => {
fetchUser()
@@ -203,10 +189,7 @@ export default function OnrampBankPage() {
const handleAmountContinue = () => {
if (!validateAmount(rawTokenAmount)) return
- const currentKycStatus = liveKycStatus || user?.user.bridgeKycStatus
- const isUserKycVerified = currentKycStatus === 'approved'
-
- if (!isUserKycVerified) {
+ if (!isUserKycApproved) {
setShowKycModal(true)
return
}
diff --git a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
index 0472cdffa..403f9cb39 100644
--- a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
+++ b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
@@ -10,12 +10,10 @@ import Image, { type StaticImageData } from 'next/image'
import { useParams, useRouter, useSearchParams } from 'next/navigation'
import EmptyState from '../Global/EmptyStates/EmptyState'
import { useAuth } from '@/context/authContext'
-import { useEffect, useMemo, useRef, useState } from 'react'
+import { useMemo, useRef, useState } from 'react'
import { DynamicBankAccountForm, type IBankAccountDetails } from './DynamicBankAccountForm'
import { addBankAccount } from '@/app/actions/users'
-import { type BridgeKycStatus } from '@/utils/bridge-accounts.utils'
import { type AddBankAccountPayload } from '@/app/actions/types/users.types'
-import { useWebSocket } from '@/hooks/useWebSocket'
import { useWithdrawFlow } from '@/context/WithdrawFlowContext'
import { type Account } from '@/interfaces'
import { getCountryCodeForWithdraw } from '@/utils/withdraw.utils'
@@ -64,28 +62,11 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
const [view, setView] = useState<'list' | 'form'>(flow === 'withdraw' && amountToWithdraw ? 'form' : 'list')
const [isKycModalOpen, setIsKycModalOpen] = useState(false)
const formRef = useRef<{ handleSubmit: () => void }>(null)
- const [liveKycStatus, setLiveKycStatus] = useState(
- user?.user?.bridgeKycStatus as BridgeKycStatus
- )
const [isSupportedTokensModalOpen, setIsSupportedTokensModalOpen] = useState(false)
- const { isUserBridgeKycUnderReview } = useKycStatus()
+ const { isUserKycApproved, isUserBridgeKycUnderReview } = useKycStatus()
const [showKycStatusModal, setShowKycStatusModal] = useState(false)
- useWebSocket({
- username: user?.user.username ?? undefined,
- autoConnect: !!user?.user.username,
- onKycStatusUpdate: (newStatus) => {
- setLiveKycStatus(newStatus as BridgeKycStatus)
- },
- })
-
- useEffect(() => {
- if (user?.user.bridgeKycStatus) {
- setLiveKycStatus(user.user.bridgeKycStatus as BridgeKycStatus)
- }
- }, [user?.user.bridgeKycStatus])
-
const countryPathParts = Array.isArray(params.country) ? params.country : [params.country]
const isBankPage = countryPathParts[countryPathParts.length - 1] === 'bank'
const countrySlugFromUrl = isBankPage ? countryPathParts.slice(0, -1).join('-') : countryPathParts.join('-')
@@ -100,13 +81,11 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
): Promise<{ error?: string }> => {
// re-fetch user to ensure we have the latest KYC status
// (the multi-phase flow may have completed but websocket/state not yet propagated)
- const freshUser = await fetchUser()
- const currentKycStatus = freshUser?.user?.bridgeKycStatus || liveKycStatus || user?.user.bridgeKycStatus
- const isUserKycVerified = currentKycStatus === 'approved'
+ await fetchUser()
// scenario (1): happy path: if the user has already completed kyc, we can add the bank account directly
// email and name are now collected by sumsub — no need to check them here
- if (isUserKycVerified) {
+ if (isUserKycApproved) {
const currentAccountIds = new Set((freshUser?.accounts ?? user?.accounts ?? []).map((acc) => acc.id))
const result = await addBankAccount(payload)
@@ -149,7 +128,7 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
// scenario (2): if the user hasn't completed kyc yet
// name and email are now collected by sumsub sdk — no need to save them beforehand
- if (!isUserKycVerified) {
+ if (!isUserKycApproved) {
await sumsubFlow.handleInitiateKyc('STANDARD')
}
diff --git a/src/components/Claim/Link/views/BankFlowManager.view.tsx b/src/components/Claim/Link/views/BankFlowManager.view.tsx
index 6d601ac8e..d7da95828 100644
--- a/src/components/Claim/Link/views/BankFlowManager.view.tsx
+++ b/src/components/Claim/Link/views/BankFlowManager.view.tsx
@@ -3,7 +3,7 @@
import { type IClaimScreenProps } from '../../Claim.consts'
import { DynamicBankAccountForm, type IBankAccountDetails } from '@/components/AddWithdraw/DynamicBankAccountForm'
import { ClaimBankFlowStep, useClaimBankFlow } from '@/context/ClaimBankFlowContext'
-import { useCallback, useContext, useState, useRef, useEffect } from 'react'
+import { useCallback, useContext, useState, useRef } from 'react'
import { loadingStateContext } from '@/context'
import { createBridgeExternalAccountForGuest } from '@/app/actions/external-accounts'
import { confirmOfframp, createOfframp, createOfframpForGuest } from '@/app/actions/offramp'
@@ -25,8 +25,6 @@ import useSavedAccounts from '@/hooks/useSavedAccounts'
import { ConfirmBankClaimView } from './Confirm.bank-claim.view'
import { CountryListRouter } from '@/components/Common/CountryListRouter'
import NavHeader from '@/components/Global/NavHeader'
-import { useWebSocket } from '@/hooks/useWebSocket'
-import { type BridgeKycStatus } from '@/utils/bridge-accounts.utils'
import { getCountryCodeForWithdraw } from '@/utils/withdraw.utils'
import { useAppDispatch } from '@/redux/hooks'
import { bankFormActions } from '@/redux/slices/bank-form-slice'
@@ -96,28 +94,9 @@ export const BankFlowManager = (props: IClaimScreenProps) => {
const [receiverFullName, setReceiverFullName] = useState('')
const [error, setError] = useState(null)
const formRef = useRef<{ handleSubmit: () => void }>(null)
- const [liveKycStatus, setLiveKycStatus] = useState(
- user?.user?.bridgeKycStatus as BridgeKycStatus
- )
const [isProcessingKycSuccess, setIsProcessingKycSuccess] = useState(false)
const [offrampData, setOfframpData] = useState(null)
- // websocket for real-time KYC status updates
- useWebSocket({
- username: user?.user.username ?? undefined,
- autoConnect: !!user?.user.username,
- onKycStatusUpdate: (newStatus) => {
- setLiveKycStatus(newStatus as BridgeKycStatus)
- },
- })
-
- // effect to update live KYC status from user object
- useEffect(() => {
- if (user?.user.bridgeKycStatus) {
- setLiveKycStatus(user.user.bridgeKycStatus as BridgeKycStatus)
- }
- }, [user?.user.bridgeKycStatus])
-
/**
* @name handleConfirmClaim
* @description claims the link to the deposit address provided by the off-ramp api and confirms the transfer.
From 7dc41e8310170ec839993d7d43c84cc421ec888b Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:57:48 +0530
Subject: [PATCH 4/7] fix: refresh user on sumsub KYC websocket status updates
---
src/app/(mobile-ui)/history/page.tsx | 4 ++++
src/components/Home/HomeHistory.tsx | 7 +++++++
2 files changed, 11 insertions(+)
diff --git a/src/app/(mobile-ui)/history/page.tsx b/src/app/(mobile-ui)/history/page.tsx
index 2caf96b09..534e4aa1f 100644
--- a/src/app/(mobile-ui)/history/page.tsx
+++ b/src/app/(mobile-ui)/history/page.tsx
@@ -141,6 +141,10 @@ const HistoryPage = () => {
console.log('KYC status updated via WebSocket:', newStatus)
await fetchUser()
},
+ onSumsubKycStatusUpdate: async (newStatus: string) => {
+ console.log('Sumsub KYC status updated via WebSocket:', newStatus)
+ await fetchUser()
+ },
})
const allEntries = useMemo(() => historyData?.pages.flatMap((page) => page.entries) ?? [], [historyData])
diff --git a/src/components/Home/HomeHistory.tsx b/src/components/Home/HomeHistory.tsx
index 0cd6d7027..a1992a528 100644
--- a/src/components/Home/HomeHistory.tsx
+++ b/src/components/Home/HomeHistory.tsx
@@ -71,6 +71,13 @@ const HomeHistory = ({ username, hideTxnAmount = false }: { username?: string; h
},
[fetchUser]
),
+ onSumsubKycStatusUpdate: useCallback(
+ async (newStatus: string) => {
+ console.log('Sumsub KYC status updated via WebSocket:', newStatus)
+ await fetchUser()
+ },
+ [fetchUser]
+ ),
})
// Combine fetched history with real-time updates
From 44343234b056d3f252d146c43af7ef4256ed0f32 Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:57:55 +0530
Subject: [PATCH 5/7] fix: DRY verification modal unlock items and improve
onramp error handling
- deduplicate BRIDGE_UNLOCK_ITEMS across regions
- preserve specific error messages from backend in useCreateOnramp
---
.../StartVerificationModal.tsx | 56 ++++++++++---------
src/hooks/useCreateOnramp.ts | 14 +++--
2 files changed, 38 insertions(+), 32 deletions(-)
diff --git a/src/components/IdentityVerification/StartVerificationModal.tsx b/src/components/IdentityVerification/StartVerificationModal.tsx
index 045760bf8..41e4a105b 100644
--- a/src/components/IdentityVerification/StartVerificationModal.tsx
+++ b/src/components/IdentityVerification/StartVerificationModal.tsx
@@ -6,40 +6,42 @@ import { Icon } from '../Global/Icons/Icon'
import { type Region } from '@/hooks/useIdentityVerification'
import React from 'react'
+const QR_PAYMENTS = (
+
+ QR Payments in Argentina and Brazil
+
+)
+
+const BRIDGE_UNLOCK_ITEMS: Array = [
+
+ Europe SEPA transfers (+30 countries)
+
,
+
+ UK Faster payment transfers
+
,
+
+ United States ACH and Wire transfers
+
,
+
+ Mexico SPEI transfers
+
,
+ QR_PAYMENTS,
+]
+
// unlock benefits shown per region
const REGION_UNLOCK_ITEMS: Record> = {
latam: [
Bank transfers to your own accounts in LATAM
,
-
- QR Payments in Argentina and Brazil
-
,
- ],
- europe: [
-
- Europe SEPA transfers (+30 countries)
-
,
-
- QR Payments in Argentina and Brazil
-
,
- ],
- 'north-america': [
-
- United States ACH and Wire transfers
-
,
-
- Mexico SPEI transfers
-
,
-
- QR Payments in Argentina and Brazil
-
,
- ],
- 'rest-of-the-world': [
-
- QR Payments in Argentina and Brazil
-
,
+ QR_PAYMENTS,
],
+
+ europe: BRIDGE_UNLOCK_ITEMS,
+
+ 'north-america': BRIDGE_UNLOCK_ITEMS,
+
+ 'rest-of-the-world': [QR_PAYMENTS],
}
const DEFAULT_UNLOCK_ITEMS = [Bank transfers and local payment methods
]
diff --git a/src/hooks/useCreateOnramp.ts b/src/hooks/useCreateOnramp.ts
index f88360fad..37ac606ca 100644
--- a/src/hooks/useCreateOnramp.ts
+++ b/src/hooks/useCreateOnramp.ts
@@ -67,15 +67,19 @@ export const useCreateOnramp = (): UseCreateOnrampReturn => {
})
if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`)
+ // parse error body from backend to get specific message
+ let errorMessage = 'Failed to create bank transfer. Please try again or contact support.'
+ setError(errorMessage)
+ throw new Error(errorMessage)
}
const onrampData = await response.json()
return onrampData
- } catch (error) {
- console.error('Error creating onramp:', error)
- setError('Failed to create bank transfer. Please try again or contact support.')
- throw error
+ } catch (err) {
+ console.error('Error creating onramp:', err)
+ // only set generic fallback if no specific error was already set by the !response.ok block
+ setError((prev) => prev ?? 'Failed to create bank transfer. Please try again or contact support.')
+ throw err
} finally {
setIsLoading(false)
}
From 5c1ee0adbf0df9663b59fdb33d100c033c6c22e2 Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 4 Mar 2026 23:58:20 +0530
Subject: [PATCH 6/7] chore: format
---
src/components/Kyc/KycStatusItem.tsx | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/components/Kyc/KycStatusItem.tsx b/src/components/Kyc/KycStatusItem.tsx
index 320de4e37..93f2db865 100644
--- a/src/components/Kyc/KycStatusItem.tsx
+++ b/src/components/Kyc/KycStatusItem.tsx
@@ -127,10 +127,7 @@ export const KycStatusItem = ({
{subtitle}
Date: Thu, 5 Mar 2026 13:32:02 +0530
Subject: [PATCH 7/7] =?UTF-8?q?fix:=20address=20CR=20feedback=20=E2=80=94?=
=?UTF-8?q?=20freshUser=20ref,=20typo,=20fallback=20copy?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- remove stale freshUser reference in AddWithdrawCountriesList (bug)
- fix "Verfication" typo in KycVerificationInProgressModal
- revert fallback requirement description to generic copy
---
src/components/AddWithdraw/AddWithdrawCountriesList.tsx | 2 +-
src/components/Kyc/KycVerificationInProgressModal.tsx | 2 +-
src/constants/bridge-requirements.consts.ts | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
index 403f9cb39..8ff2b9480 100644
--- a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
+++ b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
@@ -86,7 +86,7 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
// scenario (1): happy path: if the user has already completed kyc, we can add the bank account directly
// email and name are now collected by sumsub — no need to check them here
if (isUserKycApproved) {
- const currentAccountIds = new Set((freshUser?.accounts ?? user?.accounts ?? []).map((acc) => acc.id))
+ const currentAccountIds = new Set((user?.accounts ?? []).map((acc) => acc.id))
const result = await addBankAccount(payload)
if (result.error) {
diff --git a/src/components/Kyc/KycVerificationInProgressModal.tsx b/src/components/Kyc/KycVerificationInProgressModal.tsx
index 38c0385aa..b314c53b9 100644
--- a/src/components/Kyc/KycVerificationInProgressModal.tsx
+++ b/src/components/Kyc/KycVerificationInProgressModal.tsx
@@ -73,7 +73,7 @@ export const KycVerificationInProgressModal = ({
onClose={onClose}
isLoadingIcon
iconContainerClassName="bg-yellow-1 text-black"
- title={'Verfication in progress'}
+ title="Verification in progress"
description={
preparingTimedOut
? "This is taking longer than expected. You can continue and we'll notify you when it's ready."
diff --git a/src/constants/bridge-requirements.consts.ts b/src/constants/bridge-requirements.consts.ts
index d2adebd9d..d50ae2868 100644
--- a/src/constants/bridge-requirements.consts.ts
+++ b/src/constants/bridge-requirements.consts.ts
@@ -26,7 +26,7 @@ const BRIDGE_REQUIREMENT_LABELS: Record = {
const FALLBACK_LABEL: RequirementLabelInfo = {
title: 'Additional Document',
- description: 'Please resubmit your identity document.',
+ description: 'Please provide the requested document.',
}
/** get human-readable label for a bridge additional requirement */