Skip to content

feat: handle manteca qr-pay kyc gating#1225

Merged
jjramirezn merged 2 commits intofeat/manteca-integrationfrom
feat/manteca-qr-kyc
Sep 17, 2025
Merged

feat: handle manteca qr-pay kyc gating#1225
jjramirezn merged 2 commits intofeat/manteca-integrationfrom
feat/manteca-qr-kyc

Conversation

@kushagrasarathe
Copy link
Contributor

@kushagrasarathe kushagrasarathe commented Sep 17, 2025

  • contributes to TASK-12893: handle manteca qr-pay kyc gating
Screenshot 2025-09-17 at 2 48 54 PM

@notion-workspace
Copy link

Implement 2nd KYC Flow

@vercel
Copy link

vercel bot commented Sep 17, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Sep 17, 2025 9:21am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Warning

Rate limit exceeded

@kushagrasarathe has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 58 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 81c4b14 and 7ad0fe0.

📒 Files selected for processing (1)
  • src/hooks/useQrKycGate.ts (1 hunks)

Walkthrough

Adds a KYC gate to the QR pay flow using a new hook. Defers payment fetching until gate allows. Introduces a server action to resolve Bridge customer country, redirect URL utilities, KYC modal success callbacks, WebSocket-driven KYC updates, identity verification redirect handling, and minor form cleanup.

Changes

Cohort / File(s) Summary
QR Pay KYC gating
src/app/(mobile-ui)/qr-pay/page.tsx, src/hooks/useQrKycGate.ts
Adds KYC gate state machine (QrKycState) and hook to block/allow payments; defers payment lock fetch until PROCEED_TO_PAY; disables Pay when gated; shows Manteca/identity modals when required.
Bridge customer country action
src/app/actions/bridge/get-customer.ts
New server action to fetch Bridge customer and normalize residential country (ISO3→ISO2), cached for 5 minutes; returns normalized and raw values.
KYC flows and modals
src/components/Kyc/InitiateMantecaKYCModal.tsx, src/hooks/useMantecaKycFlow.ts, src/components/Profile/views/IdentityVerification.view.tsx
Adds onKycSuccess prop to MantecaGeoSpecificKycModal; centralizes iframe close handling and listens via WebSocket for KYC status to refresh user and close; identity view integrates redirect on KYC success using new utilities.
Redirect utilities
src/utils/general.utils.ts
Adds getRedirectUrl and clearRedirectUrl to read/clear stored redirect; complements existing saveRedirectUrl.
Form cleanup
src/components/AddMoney/UserDetailsForm.tsx
Removes isSubmitting prop usage and isValidating from formState; retains onValidChange behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Zishan-7
  • jjramirezn

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.

❤️ Share

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

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: handle manteca qr-pay kyc gating" is concise, follows conventional commit style, and directly summarizes the primary change in the changeset (adding KYC gating for the Manteca QR pay flow such as useQrKycGate, gating UI, and deferred payment fetch). It is specific enough for a reviewer scanning history and does not include noise or vague terms. Overall it accurately reflects the main intent of the PR.
Description Check ✅ Passed The PR description "handle manteca qr-pay kyc gating" directly matches the changes in this branch—adding QR KYC gating, Manteca-specific KYC modal/flow, and related hooks/utilities—so it is on-topic and related to the changeset. Although brief, it correctly identifies the primary intent of the code changes.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Sep 17, 2025
@kushagrasarathe kushagrasarathe changed the base branch from feat/kyc-activity to feat/manteca-kyc September 17, 2025 09:02
@kushagrasarathe kushagrasarathe changed the base branch from feat/manteca-kyc to feat/manteca-integration September 17, 2025 09:06
@notion-workspace
Copy link

Manteca kyc updates

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: 4

🧹 Nitpick comments (8)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

77-84: Align prop optionality with usage (?.)

onKycSuccess is required in the prop type but invoked with optional chaining. Make it optional for consistency and backward compatibility.

Apply:

-    onKycSuccess: () => void
+    onKycSuccess?: () => void
src/components/AddMoney/UserDetailsForm.tsx (1)

20-21: isSubmitting prop is ignored — either remove it or wire it to inputs

Parent passes isSubmitting, but the component no longer uses it. Either drop the prop across call sites or use it to disable inputs to prevent edits during submit.

Apply:

-export const UserDetailsForm = forwardRef<{ handleSubmit: () => void }, UserDetailsFormProps>(
-    ({ onSubmit, onValidChange, initialData }, ref) => {
+export const UserDetailsForm = forwardRef<{ handleSubmit: () => void }, UserDetailsFormProps>(
+    ({ onSubmit, onValidChange, initialData, isSubmitting }, ref) => {
...
-                                <BaseInput
+                                <BaseInput
                                     {...field}
                                     type={type}
                                     placeholder={placeholder}
+                                    disabled={!!isSubmitting}
                                     className="h-12 w-full rounded-sm border border-n-1 bg-white px-4 text-sm"
                                 />

Also applies to: 63-69

src/hooks/useMantecaKycFlow.ts (1)

46-55: Avoid magic string for completion status

'WIDGET_FINISHED' is a raw string; prefer a single source of truth to prevent typos and ease future changes.

Apply:

+    const MANTECA_COMPLETION_STATUSES = new Set<string>([MantecaKycStatus.ACTIVE, 'WIDGET_FINISHED'])
...
-        onMantecaKycStatusUpdate: async (status) => {
-            if (status === MantecaKycStatus.ACTIVE || status === 'WIDGET_FINISHED') {
+        onMantecaKycStatusUpdate: async (status) => {
+            if (MANTECA_COMPLETION_STATUSES.has(status)) {
                 await fetchUser()
                 handleIframeClose('completed')
             }
         },

Also consider lifting the string into a typed union or enum in @/interfaces when feasible.

src/utils/general.utils.ts (1)

1220-1228: Tighten types and clear expiry key as well

  • Return string | null for clarity.
  • Remove the paired -expiry key to avoid stale entries if expiry is ever used.

Apply:

-export const getRedirectUrl = () => {
-    return getFromLocalStorage('redirect')
-}
+export const getRedirectUrl = (): string | null => {
+    return (getFromLocalStorage('redirect') as string | null) ?? null
+}

-export const clearRedirectUrl = () => {
-    if (typeof localStorage !== 'undefined') {
-        localStorage.removeItem('redirect')
-    }
-}
+export const clearRedirectUrl = () => {
+    if (typeof localStorage !== 'undefined') {
+        localStorage.removeItem('redirect')
+        localStorage.removeItem('redirect-expiry')
+    }
+}
src/components/Profile/views/IdentityVerification.view.tsx (1)

23-23: Sanitize stored redirect before navigation

Even though you store a relative URL, sanitize the value at use to guard against unexpected contents.

Apply:

-import { getRedirectUrl, clearRedirectUrl } from '@/utils/general.utils'
+import { getRedirectUrl, clearRedirectUrl, sanitizeRedirectURL } from '@/utils/general.utils'
...
-    const handleRedirect = useCallback(() => {
+    const handleRedirect = useCallback(() => {
         const redirectUrl = getRedirectUrl()
         if (redirectUrl) {
             clearRedirectUrl()
-            router.push(redirectUrl)
+            router.push(sanitizeRedirectURL(redirectUrl))
         } else {
             router.replace('/profile')
         }
     }, [router])

Also applies to: 38-46

src/app/actions/bridge/get-customer.ts (1)

58-58: Remove debug console.log.

Leftover debug string in server logs; keep noise out.

-            console.log('normalized kushagra', normalized)
src/app/(mobile-ui)/qr-pay/page.tsx (1)

236-284: Show a loader during KYC gate LOADING.

Currently renders header + empty body. Display a spinner for better UX.

-    if (shouldBlockPay) {
+    if (shouldBlockPay) {
+        if (kycGateState === QrKycState.LOADING) {
+            return <PeanutLoading />
+        }
         return (
             <div className="flex min-h-[inherit] flex-col gap-8">
src/hooks/useQrKycGate.ts (1)

18-22: Doc nit: The hook doesn’t inspect “country of the QR code.”

It checks user KYC and Bridge country. Tweak wording to avoid confusion.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4226d4 and 81c4b14.

📒 Files selected for processing (8)
  • src/app/(mobile-ui)/qr-pay/page.tsx (8 hunks)
  • src/app/actions/bridge/get-customer.ts (1 hunks)
  • src/components/AddMoney/UserDetailsForm.tsx (1 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (2 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (4 hunks)
  • src/hooks/useMantecaKycFlow.ts (2 hunks)
  • src/hooks/useQrKycGate.ts (1 hunks)
  • src/utils/general.utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-05-15T14:47:26.891Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#857
File: src/hooks/useWebSocket.ts:77-82
Timestamp: 2025-05-15T14:47:26.891Z
Learning: The useWebSocket hook in src/hooks/useWebSocket.ts is designed to provide raw history entries, while the components using it (such as HomeHistory.tsx) are responsible for implementing deduplication logic based on UUID to prevent duplicate entries when combining WebSocket data with other data sources.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
📚 Learning: 2025-05-15T14:45:45.632Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#857
File: src/hooks/useWebSocket.ts:5-5
Timestamp: 2025-05-15T14:45:45.632Z
Learning: For the WebSocketStatus type in useWebSocket.ts, it was decided to keep it as a non-exported type until there's a specific need to export it. The team prefers to address potential issues when they become actual requirements rather than preemptively.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
  • src/app/(mobile-ui)/qr-pay/page.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page.

Applied to files:

  • src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.

Applied to files:

  • src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/AddMoney/UserDetailsForm.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/app/actions/bridge/get-customer.ts
🧬 Code graph analysis (5)
src/hooks/useMantecaKycFlow.ts (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useWebSocket.ts (1)
  • useWebSocket (19-186)
src/app/(mobile-ui)/qr-pay/page.tsx (5)
src/hooks/useQrKycGate.ts (1)
  • useQrKycGate (23-71)
src/components/Global/DirectSendQR/utils.ts (1)
  • isPaymentProcessorQR (100-107)
src/services/manteca.ts (1)
  • mantecaApi (89-166)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • MantecaGeoSpecificKycModal (72-126)
src/utils/general.utils.ts (3)
  • saveRedirectUrl (1214-1218)
  • getRedirectUrl (1220-1222)
  • clearRedirectUrl (1224-1228)
src/hooks/useQrKycGate.ts (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/app/actions/bridge/get-customer.ts (1)
  • getBridgeCustomerCountry (38-66)
src/app/actions/bridge/get-customer.ts (3)
src/components/AddMoney/consts/index.ts (1)
  • countryData (271-2439)
src/utils/__mocks__/next-cache.ts (1)
  • unstable_cache (1-1)
src/constants/general.consts.ts (2)
  • PEANUT_API_URL (43-47)
  • PEANUT_API_KEY (49-49)
src/components/Profile/views/IdentityVerification.view.tsx (2)
src/utils/general.utils.ts (2)
  • getRedirectUrl (1220-1222)
  • clearRedirectUrl (1224-1228)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (30-180)
🔇 Additional comments (12)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

118-121: Close-then-callback sequencing looks good

Closing the modal before invoking onKycSuccess avoids UI flicker and duplicate actions. LGTM.

src/components/AddMoney/UserDetailsForm.tsx (1)

26-27: Form-state narrowing is fine

Using only { errors, isValid } is sufficient here and reduces unnecessary re-renders.

src/hooks/useMantecaKycFlow.ts (2)

30-45: Centralized iframe close handler is correct

Clear branching: completed → onSuccess, manual → onManualClose, fallback → onClose. This removes duplication and clarifies intent. LGTM.


25-26: Fetch-before-success matches prior KYC loop fix

Calling await fetchUser() before firing success prevents the known re-fetch loop. Good alignment with prior learnings.

src/components/Profile/views/IdentityVerification.view.tsx (3)

48-52: Bridge KYC: fetch-then-redirect is correct

Ensures fresh user state before post-KYC routing. Matches documented sequencing.


53-56: Confirm Manteca KYC fetch timing

useMantecaKycFlow fetches user on completion before invoking onKycSuccess. Please confirm no additional fetch is required here and that this doesn’t re-trigger any effects downstream.


255-256: Wiring onKycSuccess to Manteca modal

Hooking the redirect callback into the geo-specific modal is correct and completes the flow. LGTM.

src/app/actions/bridge/get-customer.ts (2)

18-29: Good: ISO3→ISO2 map built once with uppercase normalization.

This aligns with our prior learning about 3-letter keys mapping to 2-letter codes and avoids lookup misses.


31-36: Good: Country normalization handles ISO2 and ISO3 cleanly.

Straightforward and returns null on unknowns.

src/app/(mobile-ui)/qr-pay/page.tsx (2)

136-149: LGTM: Deferring payment-lock fetch until KYC gate passes.

Prevents premature requests and aligns UI flow with gating.


420-430: LGTM: Pay button disabled while gated or invalid.

Good guardrail to prevent premature actions.

src/hooks/useQrKycGate.ts (1)

34-41: LGTM: Short‑circuit when Manteca KYC is active.

Correctly unblocks flow early for verified users.

Copy link
Contributor

@jjramirezn jjramirezn left a comment

Choose a reason for hiding this comment

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

Approved! Nice hook the useQrKycGate

* It checks the user's KYC status and the country of the QR code to determine the appropriate action.
* @returns {QrKycGateResult} An object with the KYC gate state and a boolean indicating if the user should be blocked from paying.
*/
export function useQrKycGate(): QrKycGateResult {
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: I like this hook!!!

@jjramirezn jjramirezn merged commit 9e952b5 into feat/manteca-integration Sep 17, 2025
4 checks passed
This was referenced Nov 5, 2025
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