-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fingerprint integration #3160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fingerprint integration #3160
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR introduces fingerprint-based device identification for partners during onboarding and login, refactors fraud risk detection with new device- and geolocation-based rules, consolidates hardcoded enrollment status arrays into shared constants, and adds plan-based gating to risk evaluation endpoints. Changes
Sequence Diagram(s)sequenceDiagram
actor Partner
participant LoginPage as Login Page
participant FingerprintJS as FingerprintJS Pro
participant Backend as Backend API
participant DB as Database
Partner->>LoginPage: Visit partner login
LoginPage->>FingerprintJS: Initialize FingerprintProvider
FingerprintJS->>FingerprintJS: Generate device fingerprint
Partner->>LoginPage: Submit login (email/password)
LoginPage->>Backend: POST /auth/login with requestId
Backend->>DB: Fetch partner by email
DB-->>Backend: Partner record (if exists)
Backend->>Backend: Generate session token
Backend-->>LoginPage: Redirect to dashboard
Partner->>Partner: Navigate to onboarding
participant OnboardingPage as Onboarding Page
Partner->>OnboardingPage: View onboarding form
OnboardingPage->>FingerprintJS: Get visitor data
FingerprintJS-->>OnboardingPage: visitorId, visitorCountry
OnboardingPage->>OnboardingPage: Store in form state
Partner->>OnboardingPage: Submit onboarding form
OnboardingPage->>Backend: POST /onboard-partner with requestId
Backend->>Backend: fetchVisitorFingerprint(requestId)
Backend->>FingerprintJS: Query Fingerprint API for visitor data
FingerprintJS-->>Backend: visitorId, visitorCountry
Backend->>DB: Store Partner with fingerprint data
Backend->>Backend: getApplicationRiskSignals(program, partner)
Backend->>DB: Query for cross-program bans, duplicates
DB-->>Backend: Risk signal results
Backend->>Backend: Compute risk severity
Backend-->>OnboardingPage: Success with risk assessment
sequenceDiagram
participant Cron as Auto-Approve Cron
participant API as Backend API
participant DB as Database
participant FraudAPI as Fraud Detection
Cron->>API: Trigger auto-approve-partner
API->>DB: Fetch pending program enrollments
DB-->>API: List of pending enrollments
loop For each enrollment
API->>API: getPlanCapabilities(workspace)
alt canManageFraudEvents = true
API->>FraudAPI: getApplicationRiskSignals(program, partner)
FraudAPI->>DB: Query fraud indicators
DB-->>FraudAPI: Risk data
FraudAPI-->>API: riskSignals, severity
alt severity = "high"
API->>API: Skip auto-approval, log warning
else
API->>DB: Update enrollment to approved
DB-->>API: Success
end
else
API->>DB: Auto-approve (no fraud checks)
DB-->>API: Success
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used🧠 Learnings (4)📚 Learning: 2025-11-24T09:10:12.536ZApplied to files:
📚 Learning: 2025-11-12T22:23:10.414ZApplied to files:
📚 Learning: 2025-08-16T11:14:00.667ZApplied to files:
📚 Learning: 2025-10-15T01:52:37.048ZApplied to files:
🧬 Code graph analysis (1)apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Introduced `checkPartnerCountryMismatch` and `checkPartnerDuplicateAccount` functions to enhance fraud detection. - Updated the `FRAUD_RULES` to include new checks for country mismatch and duplicate accounts. - Modified the onboarding process to capture visitor country and adjust payload accordingly. - Refactored visitor fingerprint response to include visitor country information. - Adjusted Prisma schema to accommodate new visitor country field.
- Replaced `getPartnerHighRiskSignals` with `getApplicationRiskSignals` to streamline risk assessment for partners. - Updated the auto-approval logic to evaluate risk signals based on workspace plan capabilities. - Removed deprecated functions and adjusted related API routes to enhance clarity and maintainability. - Introduced new risk signal checks for improved fraud detection accuracy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (5)
apps/web/.env.example (1)
160-162: Address formatting issues flagged by static analysis.Two minor formatting issues to fix:
- Add a trailing newline at the end of the file
- Consider reordering keys (FINGERPRINT_SECRET_KEY before NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY) for consistency with the pattern of non-public keys before public ones
Apply this diff to fix:
# Fingerprint -NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY= FINGERPRINT_SECRET_KEY= +NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY= +apps/web/lib/api/fraud/rules/check-partner-country-mismatch.ts (1)
4-16: Logic is sound, consider adding country code validation.The function correctly handles missing data and performs case-insensitive comparison. However, consider validating that both country codes are valid ISO 3166-1 codes before comparison to catch data quality issues early.
Example enhancement:
import { COUNTRY_CODES } from "@dub/utils"; export function checkPartnerCountryMismatch( partner: Pick<Partner, "country" | "visitorCountry">, ) { if (!partner.visitorCountry || !partner.country) { return false; } const normalizedVisitorCountry = partner.visitorCountry.toUpperCase().trim(); const normalizedCountry = partner.country.toUpperCase().trim(); // Optionally validate against COUNTRY_CODES if you want to catch invalid codes // if (!COUNTRY_CODES.includes(normalizedVisitorCountry) || !COUNTRY_CODES.includes(normalizedCountry)) { // return false; // } return normalizedVisitorCountry !== normalizedCountry; }apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx (1)
1-21: FingerprintProvider wiring looks correct; consider guarding the API keyThe
FpjsProvidersetup (apiKey + default endpoints) looks aligned with the library’s pattern. To avoid accidentally passing"undefined"when the env var is missing, you may want to normalize/guard:-export function FingerprintProvider({ children }: { children: ReactNode }) { - return ( - <FpjsProvider - loadOptions={{ - apiKey: `${process.env.NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY}`, +export function FingerprintProvider({ children }: { children: ReactNode }) { + const apiKey = process.env.NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY ?? ""; + + return ( + <FpjsProvider + loadOptions={{ + apiKey, endpoint: [FingerprintJSPro.defaultEndpoint], scriptUrlPattern: [FingerprintJSPro.defaultScriptUrlPattern], }} > {children} </FpjsProvider> ); }You can also add a dev-time warning if
apiKeyis empty.apps/web/app/(ee)/partners.dub.co/(auth)/login/page.tsx (1)
7-7: Login page now wraps PartnerLoginForm with FingerprintProvider, but form still has TODOThe provider integration here looks fine, but
PartnerLoginForm(per its TODO) still doesn’t actually use the fingerprint context/hooks, so this change alone won’t collect any fingerprint data on login. Either wirePartnerLoginFormup touseVisitorData(or similar) in this PR or explicitly track that as a follow-up.Also applies to: 9-9, 66-70
apps/web/lib/api/fraud/fingerprint.ts (1)
77-84: Document error status handling behavior.The catch block returns
{ visitorId: null, status: "error" }, which based on the onboarding action code, currently allows onboarding to proceed (onlynot_foundblocks). This is inconsistent with the explicit throw for server errors (lines 58-60).Consider adding a comment explaining this behavior:
} catch (error) { console.error("[Fingerprint] Error fetching visitor ID.", error); + // Return error status to allow onboarding to proceed despite fingerprint service issues + // This prevents legitimate users from being blocked by transient service failures return { visitorId: null, status: "error", }; }Alternatively, if "error" should also block onboarding, update the onboarding action to check for
status !== "valid"instead of juststatus === "not_found".
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
apps/web/.env.example(1 hunks)apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts(3 hunks)apps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts(2 hunks)apps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.ts(2 hunks)apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts(2 hunks)apps/web/app/(ee)/api/stripe/connect/webhook/account-updated.ts(2 hunks)apps/web/app/(ee)/partners.dub.co/(auth)/login/page.tsx(2 hunks)apps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx(1 hunks)apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx(6 hunks)apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/page.tsx(2 hunks)apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx(1 hunks)apps/web/lib/actions/partners/onboard-partner.ts(5 hunks)apps/web/lib/actions/partners/reject-partner-application.ts(2 hunks)apps/web/lib/api/fraud/constants.ts(3 hunks)apps/web/lib/api/fraud/fingerprint.ts(1 hunks)apps/web/lib/api/fraud/get-application-risk-signals.ts(1 hunks)apps/web/lib/api/fraud/get-partner-high-risk-signals.ts(0 hunks)apps/web/lib/api/fraud/rules/check-partner-country-mismatch.ts(1 hunks)apps/web/lib/types.ts(2 hunks)apps/web/lib/zod/schemas/partners.ts(2 hunks)apps/web/package.json(1 hunks)apps/web/ui/partners/partner-info-cards.tsx(2 hunks)packages/prisma/schema/partner.prisma(2 hunks)
💤 Files with no reviewable changes (1)
- apps/web/lib/api/fraud/get-partner-high-risk-signals.ts
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
📚 Learning: 2025-09-24T16:13:00.387Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: packages/prisma/schema/partner.prisma:151-153
Timestamp: 2025-09-24T16:13:00.387Z
Learning: In the Dub codebase, Prisma schemas use single-column indexes without brackets (e.g., `@index(partnerId)`) and multi-column indexes with brackets (e.g., `@index([programId, partnerId])`). This syntax pattern is consistently used throughout their schema files and works correctly with their Prisma version.
Applied to files:
packages/prisma/schema/partner.prisma
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.
Applied to files:
apps/web/lib/zod/schemas/partners.tsapps/web/app/(ee)/api/stripe/connect/webhook/account-updated.tsapps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/ui/partners/partner-info-cards.tsxapps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
Applied to files:
apps/web/lib/zod/schemas/partners.tsapps/web/app/(ee)/api/stripe/connect/webhook/account-updated.tsapps/web/lib/api/fraud/rules/check-partner-country-mismatch.tsapps/web/lib/api/fraud/get-application-risk-signals.tsapps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.tsapps/web/lib/api/fraud/fingerprint.tsapps/web/lib/types.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/app/(ee)/api/cron/auto-approve-partner/route.tsapps/web/lib/api/fraud/constants.tsapps/web/lib/actions/partners/onboard-partner.ts
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.
Applied to files:
apps/web/lib/zod/schemas/partners.tsapps/web/lib/api/fraud/rules/check-partner-country-mismatch.tsapps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/ui/partners/partner-info-cards.tsxapps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/lib/zod/schemas/partners.tsapps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.tsapps/web/ui/partners/partner-info-cards.tsxapps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts
📚 Learning: 2025-08-25T21:03:24.285Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/bounty-card.tsx:1-1
Timestamp: 2025-08-25T21:03:24.285Z
Learning: In Next.js App Router, Server Components that use hooks can work without "use client" directive if they are only imported by Client Components, as they get "promoted" to run on the client side within the Client Component boundary.
Applied to files:
apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsxapps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx
📚 Learning: 2025-09-18T16:33:17.719Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2858
File: apps/web/ui/partners/partner-application-tabs.tsx:1-1
Timestamp: 2025-09-18T16:33:17.719Z
Learning: When a React component in Next.js App Router uses non-serializable props (like setState functions), adding "use client" directive can cause serialization warnings. If the component is only imported by Client Components, it's better to omit the "use client" directive to avoid these warnings while still getting client-side execution through promotion.
Applied to files:
apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsxapps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx
📚 Learning: 2025-11-24T08:55:31.332Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/app/(ee)/api/fraud-rules/route.ts:71-87
Timestamp: 2025-11-24T08:55:31.332Z
Learning: In apps/web/app/(ee)/api/fraud-rules/route.ts, fraud rules cannot be created in a disabled state. When using prisma.fraudRule.upsert, the create branch intentionally omits the disabledAt field (defaulting to null, meaning enabled), while the update branch allows toggling enabled/disabled state via the disabledAt field. This is a business logic constraint.
Applied to files:
apps/web/app/(ee)/api/stripe/connect/webhook/account-updated.tsapps/web/lib/api/fraud/rules/check-partner-country-mismatch.tsapps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.tsapps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.tsapps/web/lib/types.tsapps/web/lib/actions/partners/reject-partner-application.tsapps/web/lib/api/fraud/constants.tsapps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
Applied to files:
apps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.tsapps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/lib/actions/partners/reject-partner-application.tsapps/web/ui/partners/partner-info-cards.tsx
📚 Learning: 2025-07-30T15:25:13.936Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/actions/partners/reject-partner-application.tsapps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx
📚 Learning: 2025-06-04T15:09:51.562Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2471
File: apps/web/ui/auth/reset-password-form.tsx:52-65
Timestamp: 2025-06-04T15:09:51.562Z
Learning: In the Dub codebase, server-side validation errors for password fields are handled via toast notifications rather than using react-hook-form's setError method to display field-specific errors. This architectural pattern separates client-side validation feedback (inline) from server-side error handling (toast).
Applied to files:
apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx
📚 Learning: 2025-08-14T05:00:23.224Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/app/api/og/program/route.tsx:63-64
Timestamp: 2025-08-14T05:00:23.224Z
Learning: In Dub's partner program system, the default partner group will always exist. When programs are created, a default partner group is automatically upserted using DEFAULT_PARTNER_GROUP constant, so accessing program.groups[0] in contexts where the default group is queried is safe.
Applied to files:
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
🧬 Code graph analysis (12)
apps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx (1)
apps/web/ui/auth/login/login-form.tsx (1)
LoginForm(72-207)
apps/web/app/(ee)/api/stripe/connect/webhook/account-updated.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
INACTIVE_PROGRAM_ENROLLMENT_STATUSES(26-30)
apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/page.tsx (2)
apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx (1)
FingerprintProvider(9-21)apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx (1)
OnboardingForm(33-327)
apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts (2)
apps/web/lib/api/fraud/get-application-risk-signals.ts (1)
getApplicationRiskSignals(12-85)apps/web/lib/plan-capabilities.ts (1)
getPlanCapabilities(4-22)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
INACTIVE_PROGRAM_ENROLLMENT_STATUSES(26-30)
apps/web/lib/actions/partners/reject-partner-application.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
INACTIVE_PROGRAM_ENROLLMENT_STATUSES(26-30)
apps/web/ui/partners/partner-info-cards.tsx (1)
apps/web/lib/zod/schemas/partners.ts (1)
INACTIVE_PROGRAM_ENROLLMENT_STATUSES(26-30)
apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx (1)
apps/web/lib/actions/parse-action-errors.ts (1)
parseActionError(3-29)
apps/web/app/(ee)/partners.dub.co/(auth)/login/page.tsx (2)
apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx (1)
FingerprintProvider(9-21)apps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx (1)
PartnerLoginForm(5-10)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (2)
apps/web/lib/plan-capabilities.ts (1)
getPlanCapabilities(4-22)apps/web/lib/api/fraud/get-application-risk-signals.ts (1)
getApplicationRiskSignals(12-85)
apps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
INACTIVE_PROGRAM_ENROLLMENT_STATUSES(26-30)
apps/web/lib/actions/partners/onboard-partner.ts (2)
apps/web/lib/api/fraud/fingerprint.ts (1)
fetchVisitorFingerprint(26-85)apps/web/lib/partners/complete-program-applications.ts (1)
completeProgramApplications(19-230)
🪛 dotenv-linter (4.0.0)
apps/web/.env.example
[warning] 162-162: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 162-162: [UnorderedKey] The FINGERPRINT_SECRET_KEY key should go before the NEXT_PUBLIC_FINGERPRINT_PUBLIC_KEY key
(UnorderedKey)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (21)
packages/prisma/schema/partner.prisma (2)
65-67: LGTM! Fingerprint fields added appropriately.The optional
visitorIdandvisitorCountryfields enable device-based fraud detection while allowing gradual rollout. Making them nullable ensures backward compatibility with existing partners.
91-91: Index on visitorId will support fraud detection queries.The index follows the single-column syntax convention and will optimize lookups when checking for duplicate accounts from the same device.
apps/web/lib/types.ts (1)
683-685: New fraud rule types added correctly.The
partnerDuplicateAccountandpartnerCountryMismatchtypes align with the corresponding fraud rule definitions in constants.ts and support the Fingerprint integration.apps/web/lib/api/fraud/constants.ts (4)
38-38: Comment clarifies severity-based ordering.The updated comment accurately reflects that partner rules are now ordered by severity (high → medium → low).
67-74: High severity assignment for duplicate account detection is appropriate.Detecting multiple accounts from the same device (
partnerDuplicateAccount) is correctly classified as high severity since it strongly indicates fraudulent behavior.
76-83: Medium severity for geolocation mismatch is reasonable.The
partnerCountryMismatchrule is appropriately set to medium severity since location mismatches could result from VPNs, proxies, or legitimate travel scenarios, making it less definitive than device-based duplicate detection.
93-109: Original rules correctly preserved.Good that
partnerEmailDomainMismatchandpartnerEmailMaskedwere re-added to maintain existing fraud detection capabilities alongside the new device-based rules.apps/web/ui/partners/partner-info-cards.tsx (1)
10-10: Centralizing inactive-status logic via shared constant looks goodUsing
INACTIVE_PROGRAM_ENROLLMENT_STATUSES.includes(partner.status)keeps UI behavior aligned with backend/business rules and avoids drift from ad-hoc status lists.Also applies to: 300-302
apps/web/package.json (1)
32-32: Dependency addition for FingerprintJS Pro ReactAdding
@fingerprintjs/fingerprintjs-pro-reacthere matches the new provider/hooks usage and looks correct; just double-check this version’s compatibility with your current React (19.1.1) and Next (15.5.4) stack.apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx (1)
3-3: Onboarding form: fingerprint requestId + error handling wiring looks solidUsing
useVisitorDatato populaterequestIdinto the form and switching toparseActionErrorfor toasts fits cleanly with the new fraud/fingerprint pipeline; the async submit wrapper is also fine.Also applies to: 20-20, 54-61, 97-101, 116-117, 127-129
apps/web/app/(ee)/api/partner-profile/programs/[programId]/links/route.ts (1)
9-12: UsingINACTIVE_PROGRAM_ENROLLMENT_STATUSESfor link-creation guard is a good centralizationSwitching the forbidden check to
INACTIVE_PROGRAM_ENROLLMENT_STATUSES.includes(status)keeps this API aligned with the single source of truth for inactive enrollment statuses and avoids future drift if new inactive states are introduced.Also applies to: 68-72
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (1)
88-105: LGTM! Clean refactor to centralized risk evaluation.The plan-gated risk evaluation approach is well-structured. It correctly:
- Gates risk checks behind the
canManageFraudEventscapability- Uses the centralized
getApplicationRiskSignalsfunction- Blocks auto-approval only for high-severity risks
apps/web/app/(ee)/api/embed/referrals/links/[linkId]/route.ts (1)
8-11: LGTM! Good consolidation of shared constants.Replacing the hardcoded status array with
INACTIVE_PROGRAM_ENROLLMENT_STATUSESimproves maintainability and ensures consistency across enrollment status checks throughout the codebase.Also applies to: 26-26
apps/web/lib/actions/partners/onboard-partner.ts (2)
41-47: Verify handling of fingerprint "error" status.The code blocks onboarding when
result.status === "not_found"but allows it to proceed when status is "error". Based on the fingerprint API implementation, "error" status indicates a caught exception during the fetch.Should onboarding be blocked for "error" status as well, or is it intentional to allow onboarding to proceed when fingerprint verification fails due to technical errors (to avoid blocking legitimate users)?
If "error" should also block onboarding, apply this diff:
-if (result.status === "not_found") { +if (result.status !== "valid") { throw new Error( "We're having trouble verifying your request. Please refresh the page and try again.", ); }Otherwise, consider adding a comment explaining why "error" doesn't block onboarding:
+// Allow onboarding to proceed on "error" status to avoid blocking legitimate users +// when fingerprint service is unavailable if (result.status === "not_found") {
59-60: LGTM! Clean conditional payload enrichment.The fingerprint data is correctly added to the partner payload only when available. The conditional spread syntax properly handles the optional nature of
visitorIdandvisitorCountry.apps/web/lib/api/fraud/get-application-risk-signals.ts (3)
33-62: LGTM! Efficient parallel risk data collection.The database queries are well-structured:
- Uses
Promise.allfor parallel execution- Properly filters by partnerId and programId
- Conditionally executes duplicate account query only when visitorId is present
- Returns appropriate defaults (0) when data is unavailable
64-73: LGTM! Risk signal thresholds are correctly implemented.The risk signal logic correctly uses:
> 0for cross-program bans (any ban is a risk)> 1for duplicate payout/account (count of 1 = current partner only, >1 = actual duplicates)- Direct boolean checks for partner validation rules
75-84: LGTM! Clean severity computation and return value.The function correctly:
- Filters triggered rules based on risk signals
- Computes the highest severity from triggered rules
- Returns both the detailed signals and overall severity for downstream use
apps/web/lib/api/fraud/fingerprint.ts (2)
5-22: LGTM! Well-designed schema and type definitions.The Zod schema properly validates the nested Fingerprint API response structure, and the
FingerprintVisitorResponsediscriminated union provides excellent type safety with thestatusdiscriminator and appropriate field types for each variant.
50-67: Verify error handling strategy for fingerprint failures.The function throws errors for status >= 500 (server errors) but returns
not_foundfor 4xx errors. This means:
- Server errors will propagate and block onboarding
- Client errors (e.g., invalid requestId) return gracefully
Based on the onboarding action code, this will:
- Block onboarding for
not_foundstatus- Block onboarding when errors are thrown (server errors >= 500)
This seems correct, but consider if you want to distinguish between "invalid requestId" (should block) vs "server unavailable" (might want to allow onboarding with degraded fraud detection).
The current behavior blocks onboarding for both fingerprint validation failures and service outages. Is this the intended UX, or should service outages allow onboarding to proceed without fingerprint verification?
apps/web/app/(ee)/partners.dub.co/(auth)/login/partner-login-form.tsx (1)
5-10: Remove the stale TODO comment—FingerprintProvider is already configured at the parent level but not actually used.The parent
page.tsxwrapsPartnerLoginFormwithFingerprintProvider, which provides access to fingerprint context for any child components. However, neitherPartnerLoginFormnor theLoginFormcomponent it renders actually use fingerprint functionality (nouseFingerprintJShook calls). The TODO appears incomplete or abandoned. Either remove the comment if fingerprint integration is no longer planned, or clarify what specific fingerprint functionality is needed and implement it using the already-available context.
| import { getSession } from "@/lib/auth"; | ||
| import { prisma } from "@dub/prisma"; | ||
| import { Suspense } from "react"; | ||
| import { FingerprintProvider } from "../../fingerprint-provider"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and read the target file
fd -t f "page.tsx" --path "*onboarding*" | head -20Repository: dubinc/dub
Length of output: 287
🏁 Script executed:
# Correct fd syntax to find the onboarding page.tsx
fd -t f "page.tsx" | grep -i onboardingRepository: dubinc/dub
Length of output: 1061
🏁 Script executed:
# Read the target file to examine its current structure
cat -n apps/web/app/\(ee\)/partners.dub.co/\(onboarding\)/onboarding/page.tsxRepository: dubinc/dub
Length of output: 1470
🏁 Script executed:
# Find the OnboardingForm component
fd -t f "onboarding-form" | grep -v node_modulesRepository: dubinc/dub
Length of output: 131
🏁 Script executed:
# Read the OnboardingForm component to check for hook usage
cat -n "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"Repository: dubinc/dub
Length of output: 13193
🏁 Script executed:
# Find and read the FingerprintProvider component
fd -t f "fingerprint-provider" | grep -v node_modulesRepository: dubinc/dub
Length of output: 112
🏁 Script executed:
# Read the FingerprintProvider to confirm it's a context provider
cat -n "apps/web/app/(ee)/partners.dub.co/fingerprint-provider.tsx"Repository: dubinc/dub
Length of output: 724
Based on my verification, the review comment is accurate and identifies a genuine issue:
Wrap the Suspense fallback in FingerprintProvider
OnboardingForm uses the useVisitorData hook from @fingerprintjs/fingerprintjs-pro-react (line 54 in onboarding-form.tsx), which requires the FpjsProvider context (wrapped by FingerprintProvider). Currently, the normal render path (lines 31-35) wraps OnboardingForm with FingerprintProvider, but the Suspense fallback (line 14) renders it without the provider. If the fallback triggers, useVisitorData will fail since it won't have the required context.
- <div className="animate-slide-up-fade w-full rounded-xl p-8 [--offset:10px] [animation-delay:500ms] [animation-duration:1s] [animation-fill-mode:both]">
- <Suspense fallback={<OnboardingForm />}>
- <OnboardingFormRSC />
- </Suspense>
- </div>
+ <div className="animate-slide-up-fade w-full rounded-xl p-8 [--offset:10px] [animation-delay:500ms] [animation-duration:1s] [animation-fill-mode:both]">
+ <Suspense
+ fallback={
+ <FingerprintProvider>
+ <OnboardingForm />
+ </FingerprintProvider>
+ }
+ >
+ <OnboardingFormRSC />
+ </Suspense>
+ </div>This ensures both the fallback and resolved tree render under the same provider context.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/page.tsx around
line 4 (and affecting the Suspense at ~line 14 and the normal render at ~lines
31-35), the Suspense fallback renders OnboardingForm without the
FingerprintProvider which breaks useVisitorData; wrap the Suspense fallback (or
move FingerprintProvider to be above the Suspense) so that both the fallback and
the resolved tree are rendered inside FingerprintProvider (i.e., ensure
FingerprintProvider is an ancestor of the Suspense and/or its fallback
component).
| requestId: z | ||
| .string({ required_error: "Request ID is required" }) | ||
| .describe("The Fingerprint request ID of the partner."), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for requestId usage in onboarding-related files
echo "=== Searching for requestId in onboarding files ==="
rg -n -A5 -B5 'requestId' --type=ts --type=tsx -g '*onboard*'
echo ""
echo "=== Searching for Fingerprint integration in onboarding files ==="
rg -n -A5 -B5 'fingerprint|Fingerprint' --type=ts --type=tsx -g '*onboard*'Repository: dubinc/dub
Length of output: 245
🏁 Script executed:
#!/bin/bash
# Find onboarding-related files
echo "=== Finding onboarding-related files ==="
fd -e ts -e tsx 'onboard' | head -20
echo ""
echo "=== Searching for requestId in TypeScript files ==="
rg -n -A5 -B5 'requestId' --type=ts -g '*onboard*' | head -100
echo ""
echo "=== Searching for Fingerprint in TypeScript files ==="
rg -n -A5 -B5 'Fingerprint|fingerprint' --type=ts -g '*onboard*' | head -100
echo ""
echo "=== Broad search for requestId in all TypeScript files ==="
rg -n 'requestId' --type=ts | head -50Repository: dubinc/dub
Length of output: 8316
🏁 Script executed:
#!/bin/bash
# Read the onboarding form to see visitorData initialization
echo "=== Reading onboarding-form.tsx (first 150 lines) ==="
head -150 "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"
echo ""
echo "=== Reading lines 90-110 of onboarding-form.tsx ==="
sed -n '90,110p' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"
echo ""
echo "=== Reading fingerprint.ts ==="
cat "apps/web/lib/api/fraud/fingerprint.ts"
echo ""
echo "=== Reading onboard-partner.ts ==="
cat "apps/web/lib/actions/partners/onboard-partner.ts"Repository: dubinc/dub
Length of output: 10960
🏁 Script executed:
#!/bin/bash
# Read the partners schema around line 602
echo "=== Reading partners.ts schema (lines 590-620) ==="
sed -n '590,620p' "apps/web/lib/zod/schemas/partners.ts"
echo ""
echo "=== Search for onboardPartnerSchema definition ==="
rg -n 'onboardPartnerSchema' --type=ts -A20 -B5 | head -80
echo ""
echo "=== Search for submit button in onboarding-form.tsx ==="
rg -n 'submit\|Submit\|disable' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" | head -30Repository: dubinc/dub
Length of output: 7724
🏁 Script executed:
#!/bin/bash
# Search for submit button in the form
echo "=== Finding submit button in onboarding-form.tsx ==="
rg -n 'Button\|button' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" -A2 -B2
echo ""
echo "=== Check if there's validation before submission ==="
rg -n 'isSubmitting\|isPending\|disabled' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" -A2 -B2
echo ""
echo "=== Get line count of onboarding-form.tsx ==="
wc -l "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"
echo ""
echo "=== Read the end of onboarding-form.tsx to see button implementation ==="
tail -80 "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"Repository: dubinc/dub
Length of output: 3327
🏁 Script executed:
#!/bin/bash
# Search for requestId field rendering
echo "=== Search for requestId field in form ==="
rg -n 'requestId' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" -B5 -A5
echo ""
echo "=== Check for error display and validation messages ==="
rg -n 'errors\.' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" | head -20
echo ""
echo "=== Check useVisitorData hook loading state ==="
rg -n 'visitorData\|useVisitorData' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" -A3 -B3Repository: dubinc/dub
Length of output: 1034
🏁 Script executed:
#!/bin/bash
# Check if useVisitorData is used elsewhere or if there's loading state
echo "=== Search for useVisitorData usage and loading state ==="
rg -n 'isLoading\|isPending\|loading' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx" -B2 -A2
echo ""
echo "=== Check full useVisitorData initialization ==="
sed -n '50,56p' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"
echo ""
echo "=== Check default values and form initialization ==="
sed -n '60,75p' "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/onboarding-form.tsx"Repository: dubinc/dub
Length of output: 820
Based on my verification, I've found that the requestId field is properly validated and used in the onboarding flow, but there's a notable UX consideration. Let me provide the rewritten review comment:
Add loading state indicator while Fingerprint request ID is being retrieved.
The requestId field is correctly validated and properly integrated: it's populated from the Fingerprint SDK (via useVisitorData hook), set into the form via setValue in onboarding-form.tsx:99, validated by the Zod schema as required, and verified server-side in onboard-partner.ts:41 using fetchVisitorFingerprint(). However, the form lacks a loading state while the Fingerprint data is being retrieved. Since requestId is a hidden field, if a user submits the form before visitorData?.requestId is populated, they'll encounter a "Request ID is required" validation error without understanding why. Consider:
- Adding a loading indicator while
useVisitorDataretrieves the Fingerprint data - Disabling the submit button until
visitorData?.requestIdis available - Or set
requestIdwith a fallback value indefaultValuesto clarify its status
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts (1)
26-34: Re‑evaluate who is allowed to see detailed fraud signals and severityThis handler currently returns
riskSignalsandseverityfor any caller that passeswithWorkspacechecks and is on anadvanced/enterpriseplan; there is norequiredPermissionsgating or in‑handler permission check, and nothing redacts the response for lower‑privilege workspace members.If these fraud signals and aggregate severity are considered sensitive (as implied by earlier discussion about hiding fraud details), consider:
- Requiring an explicit permission in the
withWorkspaceoptions (e.g., whatever you use for fraud/program management), and/or- Conditioning the response on the caller’s permissions, returning a redacted structure (empty
risksDetected,null/sentinelriskSeverity) for users who shouldn’t see fraud outcomes, and optionally skippinggetApplicationRiskSignalsentirely for those callers.This is conceptually similar to the prior suggestion about not leaking fraud details via
riskSeverity.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts(3 hunks)apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts(1 hunks)apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx(4 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
Applied to files:
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsxapps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.tsapps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.
Applied to files:
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.
Applied to files:
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
📚 Learning: 2025-08-16T11:14:00.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2754
File: apps/web/lib/partnerstack/schemas.ts:47-52
Timestamp: 2025-08-16T11:14:00.667Z
Learning: The PartnerStack API always includes the `group` field in partner responses, so the schema should use `.nullable()` rather than `.nullish()` since the field is never omitted/undefined.
Applied to files:
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts
🧬 Code graph analysis (3)
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (3)
apps/web/ui/partners/partners-upgrade-modal.tsx (1)
usePartnersUpgradeModal(285-304)apps/web/lib/types.ts (1)
FraudSeverity(687-687)apps/web/ui/partners/fraud-risks/partner-application-fraud-severity-indicator.tsx (1)
PartnerApplicationFraudSeverityIndicator(7-42)
apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts (2)
apps/web/lib/auth/workspace.ts (1)
withWorkspace(55-488)apps/web/lib/api/fraud/get-application-risk-signals.ts (1)
getApplicationRiskSignals(12-85)
apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (2)
apps/web/lib/plan-capabilities.ts (1)
getPlanCapabilities(4-22)apps/web/lib/api/fraud/get-application-risk-signals.ts (1)
getApplicationRiskSignals(12-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (6)
apps/web/app/(ee)/api/partners/[partnerId]/application-risks/route.ts (1)
1-24: Centralizing application risk computation viagetApplicationRiskSignalslooks goodFetching the enrollment with
getProgramEnrollmentOrThrow(including fullpartner) and then delegating togetApplicationRiskSignals({ program: { id: programId }, partner })is a clean consolidation of partner-scoped rules (cross‑program bans, duplicate payout/account, country/social/email checks). The call site matches the helper’s expected shape and the “application/onboarding” responsibility split for partner rules.Based on learnings, this aligns with partner-specific rules being enforced in application flows rather than via the generic FRAUD_RULES registry.
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (4)
40-40: LGTM! Consistent with signature change.The call site correctly updated to not pass the
severityprop, matching the refactoredPartnerApplicationRiskSummaryUpsellcomponent signature.
110-122: LGTM! Appropriate hardcoding for upsell component.The removal of the
severityparameter and hardcoding to"medium"makes sense since this upsell component is shown when users lack access to fraud management features. Using dummy data with a fixed severity is appropriate for demo/preview purposes.
135-135: LGTM! Consistent hardcoded severity for demo UI.The hardcoded
"medium"severity is consistent with theseverityConfigat line 121 and appropriate for the blurred demo content shown in the upsell overlay.
155-160: LGTM! UI structure improved for better visual hierarchy.The new overlay structure with a dedicated header section creates better visual prominence for the upsell message. The negative margins and layering (
isolate z-10) create a "cut-through" effect where the header appears above the blurred content.Verify the visual rendering to ensure the header aligns properly with the parent container and the negative margins (
-mx-1 -mt-1) work as intended with the parent's padding.apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts (1)
2-5: Fraud capability gating + application risk signals integration looks solid; confirm severity threshold.The changes to:
- Select the full
partnerobject from Prisma (Lines 54–55),- Gate fraud checks on
canManageFraudEventsderived fromgetPlanCapabilities(Lines 94–98), and- Use
getApplicationRiskSignalsto block auto-approval only whenseverity === "high"(Lines 100–110),are consistent with the
getPlanCapabilitiesandgetApplicationRiskSignalscontracts and keep risk evaluation scoped to plans that can manage fraud events.Please double‑check that the product requirement is to only block auto‑approval on
"high"severity; as written,"medium"and"low"severities will still be auto‑approved.Also applies to: 54-55, 94-110
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/lib/swr/use-partner-application-risks.ts(1 hunks)apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx(5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
📚 Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.
Applied to files:
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (3)
apps/web/ui/partners/partners-upgrade-modal.tsx (1)
usePartnersUpgradeModal(285-304)apps/web/lib/types.ts (1)
FraudSeverity(687-687)apps/web/ui/partners/fraud-risks/partner-application-fraud-severity-indicator.tsx (1)
PartnerApplicationFraudSeverityIndicator(7-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (2)
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx (2)
40-40: LGTM - Consistent with function signature change.The call to
PartnerApplicationRiskSummaryUpsellwithout props is consistent with the updated function signature that no longer accepts a severity parameter.
155-191: LGTM - Clear upsell UI with improved messaging.The updated overlay structure with a dedicated header (lines 156-160) and revised copy mentioning the "Advanced plan" (line 174) creates a clear and actionable upgrade prompt. The visual hierarchy and messaging effectively communicate the value proposition.
apps/web/ui/partners/fraud-risks/partner-application-risk-summary.tsx
Outdated
Show resolved
Hide resolved
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Summary by CodeRabbit
Release Notes
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.