Skip to content

✨ app: enable kyc onboarding from add-funds#941

Open
franm91 wants to merge 1 commit intomainfrom
kyc
Open

✨ app: enable kyc onboarding from add-funds#941
franm91 wants to merge 1 commit intomainfrom
kyc

Conversation

@franm91
Copy link
Copy Markdown
Member

@franm91 franm91 commented Apr 8, 2026


Open with Devin

Summary by CodeRabbit

  • New Features

    • KYC onboarding can now be started from the Add Funds flow to enable additional payment methods and networks.
  • Improvements

    • Bank transfers hidden on base chains and gated until identity is approved; shows loading state during KYC initiation.
    • Fiat flow waits for verification; crypto flow surfaces a verification prompt and launches KYC when requested.
    • KYC completion refreshes account/country data without unexpected navigation.
  • Localization

    • Added Spanish (es, es-AR) and Portuguese translations for verification prompts and CTA.
  • Chores

    • Prepared a patch release entry enabling the KYC onboarding change.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 8, 2026

🦋 Changeset detected

Latest commit: 2741dce

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 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

Fetches KYC status and gates add-funds flows; adds KYC initiation hooks and UI loading/disable states; unifies KYC result typing, removes persona navigation side-effects, invalidates user/country on KYC completion, and adds i18n and a changeset for a patch release.

Changes

Cohort / File(s) Summary
KYC core & hook
src/utils/persona.ts, src/utils/useBeginKYC.ts
Unify KYCResult typing, return startKYC() from mutation, remove expo-router navigation from persona handlers, and invalidate ["user","country"] on KYC complete; retain error filtering and query invalidation.
Add Funds UI
src/components/add-funds/AddFunds.tsx, src/components/add-funds/AddFundsOption.tsx
Query ["kyc","status"], integrate useBeginKYC(); hide/disable bank/fiat options when KYC not approved; add loading prop and spinner for options; show InfoAlert for unverified crypto users and fiat loading skeleton when ramp providers pending.
Onboarding / Getting started
src/components/getting-started/GettingStarted.tsx
Use full useBeginKYC() return value; call mutateAsync() and only navigate when result.status !== "cancel"; add explicit catch for rejected promise.
Localization & spellcheck
src/i18n/es-AR.json, src/i18n/es.json, src/i18n/pt.json, cspell.json
Add identity-check prompt and “Get verified” translations (ES / ES-AR / PT); update cspell overrides with new Spanish forms.
Release metadata
.changeset/stale-teams-say.md
Add changeset for a patch release of @exactly/mobile noting KYC onboarding from add-funds.

Sequence Diagram

sequenceDiagram
    participant User
    participant AddFunds as AddFunds Component
    participant KYCQuery as KYC Status Query
    participant BeginKYC as useBeginKYC Hook
    participant Persona as persona.startKYC()
    participant Cache as Query Cache
    participant Ramp as Ramp Providers Query

    User->>AddFunds: Open add-funds / select option
    AddFunds->>KYCQuery: Read ["kyc","status"]
    alt KYC approved
        AddFunds->>AddFunds: Navigate to chosen flow (fiat/crypto)
    else KYC not approved
        AddFunds->>BeginKYC: Call mutate() / mutateAsync()
        BeginKYC->>Persona: startKYC()
        Persona->>Persona: External KYC flow (complete / cancel / error)
        Persona->>Cache: Invalidate ["kyc","status"]
        Persona->>Cache: Invalidate ["user","country"] on complete
        Persona-->>BeginKYC: Resolve with KYCResult
        BeginKYC-->>AddFunds: Promise settles
        AddFunds->>Ramp: Invalidate / refetch ramp providers (if applicable)
        AddFunds->>AddFunds: Proceed to flow unless cancelled
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • diegueguille
  • cruzdanilo
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the changeset: enabling KYC onboarding flow from the add-funds component, which is reflected across multiple modified files including AddFunds.tsx, persona.ts, useBeginKYC.ts, and GettingStarted.tsx.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kyc

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.

gemini-code-assist[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@sentry
Copy link
Copy Markdown

sentry bot commented Apr 8, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
619 1 618 1
View the full list of 1 ❄️ flaky test(s)
local::local

Flake rate in main: 100.00% (Passed 0 times, Failed 1 times)

Stack Traces | 345s run time
No visible element found: "Credit limit"

To view more test analytics, go to the Prevent Tests Dashboard

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@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.

♻️ Duplicate comments (2)
src/components/getting-started/GettingStarted.tsx (1)

152-157: 🧹 Nitpick | 🔵 Trivial

Clarify the navigation condition for explicitness.

The basic KYC flow rejects (rather than resolves) on error, so result?.status !== "cancel" effectively means result?.status === "complete". However, making this explicit improves readability and guards against future changes to the error handling pattern.

💡 Suggested clarification
         beginKYC
           .mutateAsync()
           .then((result) => {
-            if (result?.status !== "cancel") router.replace("/(main)/(home)");
+            if (!result || result.status === "complete") router.replace("/(main)/(home)");
           })
           .catch(() => {}); // eslint-disable-line `@typescript-eslint/no-empty-function` -- error handled by useBeginKYC
src/components/add-funds/AddFunds.tsx (1)

209-213: ⚠️ Potential issue | 🟡 Minor

Consider showing a loading state while countryCode is unresolved.

When countryCode is falsy (empty string or undefined), neither the skeleton nor the providers content renders, leaving a blank content area. This can occur during the initial countryCode fetch or on direct navigation to the fiat URL.

You could destructure isLoading from the countryCode query and show a skeleton while it resolves:

- const { data: countryCode } = useQuery({
+ const { data: countryCode, isLoading: isCountryLoading } = useQuery({

Then update the fiat skeleton condition:

- {type === "fiat" && countryCode && isPending && (
+ {type === "fiat" && (isCountryLoading || (countryCode && isPending)) && (

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4b9b1a08-1801-4366-98bc-f6d2748ac4a0

📥 Commits

Reviewing files that changed from the base of the PR and between 1ecb585 and fd1c662.

📒 Files selected for processing (10)
  • .changeset/stale-teams-say.md
  • cspell.json
  • src/components/add-funds/AddFunds.tsx
  • src/components/add-funds/AddFundsOption.tsx
  • src/components/getting-started/GettingStarted.tsx
  • src/i18n/es-AR.json
  • src/i18n/es.json
  • src/i18n/pt.json
  • src/utils/persona.ts
  • src/utils/useBeginKYC.ts

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 9 additional findings in Devin Review.

Open in Devin Review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 11 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cef13507bd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

title={t("Bank transfers")}
subtitle={t("From a bank account")}
disabled={!hasFiat}
disabled={(isKYCApproved && !hasFiat) || beginKYC.isPending}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep bank-transfer CTA disabled until providers load

The new disabled condition enables Bank transfers for users who are not KYC-approved while hasFiat is still undefined (providers query still loading). In that state, tapping immediately starts KYC before we know whether fiat is actually available for the user’s country, so unsupported users can be sent through verification and still end up with no fiat option. This is a regression from the previous behavior where the CTA stayed disabled until hasFiat resolved.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 13 additional findings in Devin Review.

Open in Devin Review

const { data: method } = useQuery<AuthMethod>({ queryKey: ["method"] });
const { data: kycStatus } = useQuery<KYCStatus>({ queryKey: ["kyc", "status"] });
const beginKYC = useBeginKYC();
const isKYCApproved = kycStatus?.code === "ok" || kycStatus?.code === "legacy kyc";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Inconsistent isKYCApproved pattern — missing "code" in guard used everywhere else

At src/components/add-funds/AddFunds.tsx:45, isKYCApproved is computed as kycStatus?.code === "ok" || kycStatus?.code === "legacy kyc", skipping the "code" in kycStatus narrowing guard. Every other usage in the codebase (10+ sites) follows the pattern kycStatus && "code" in kycStatus && (kycStatus.code === "ok" || ...) — including line 165 in the same file. This violates the AGENTS.md consistency rule: "consistency is more important than personal preference. adhere to the established patterns in the codebase."

All other usages follow the established pattern
  • src/components/card/Card.tsx:94-96
  • src/components/home/Home.tsx:130-131
  • src/components/getting-started/GettingStarted.tsx:31-36
  • src/components/add-funds/AddFunds.tsx:165 (same file!)
  • src/utils/useBeginKYC.ts:24
  • src/components/home/card-upgrade/VerifyIdentity.tsx:30
Suggested change
const isKYCApproved = kycStatus?.code === "ok" || kycStatus?.code === "legacy kyc";
const isKYCApproved = Boolean(kycStatus && "code" in kycStatus && (kycStatus.code === "ok" || kycStatus.code === "legacy kyc"));
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 13 additional findings in Devin Review.

Open in Devin Review

Comment on lines +157 to +170
beginKYC
.mutateAsync()
.then(async (result) => {
if (result?.status === "cancel") return;
const status = await queryClient.fetchQuery<KYCStatus>({
queryKey: ["kyc", "status"],
staleTime: 0,
});
if ("code" in status && (status.code === "ok" || status.code === "legacy kyc")) {
await queryClient.invalidateQueries({ queryKey: ["ramp", "providers"] });
router.push({ pathname: "/add-funds", params: { type: "fiat" } });
}
})
.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function -- error handled by useBeginKYC
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚩 Silent failure when KYC webhook processing is delayed

In AddFunds.tsx:157-170, after KYC completes, the code fetches KYC status and only navigates to fiat if the status is "ok" or "legacy kyc". If the server-side webhook hasn't been processed yet (race between persona completion and server webhook), fetchQuery could return a non-ok status (which throws APIError from getKYCStatus), caught silently by .catch(() => {}) at line 170. The user would see no feedback — no navigation and no error toast. The useBeginKYC onError handler only covers errors from mutationFn, not from the .then() chain. This is an unlikely edge case but could cause user confusion.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}}
/>
{hasFiat !== false && (
{hasFiat !== false && chain.id !== base.id && (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚩 Bank transfers option hidden on base chain is a new restriction

Line 145 adds chain.id !== base.id to hide the bank transfers option entirely on base chain. Previously, the condition was only hasFiat !== false. This means base chain users will never see the bank transfers option regardless of whether fiat providers are available. This appears intentional (the same pattern is applied to the KYC info alert at line 178), but it's a meaningful behavioral change worth confirming is deliberate.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant