Skip to content

feat(affiliate): require partnerCode at registration#12392

Merged
kaladinlight merged 11 commits into
developfrom
feat/affiliate-register-with-code
May 28, 2026
Merged

feat(affiliate): require partnerCode at registration#12392
kaladinlight merged 11 commits into
developfrom
feat/affiliate-register-with-code

Conversation

@kaladinlight
Copy link
Copy Markdown
Member

@kaladinlight kaladinlight commented May 28, 2026

Description

Unifies affiliate registration into a single POST /v1/affiliate call requiring both partnerCode and bps, replacing the prior two-step flow (register → claim-code). The standalone claim-code route and its dashboard UI are removed.

Validation tightened: partnerCode is now /^[a-z0-9]{3,32}$/ — lowercase letters and digits only, 3–32 chars (no kebab-case, no uppercase). Enforced consistently across the dashboard form, public-api request schema, and swap-service DTO. Closes a prior bypass where swap-service accepted codes the dashboard rejected.

Also: dashboard now surfaces NestJS-style body.message (e.g. "Partner code already taken") instead of just the error label ("Conflict"). Extracted parsePartnerCode into lib/format to mirror parseBps.

Companion swap-service change (regex tightening + message wording) lives on a separate microservices branch.

Issue (if applicable)

closes #

Risk

Low. No on-chain transactions, no wallet interactions, no state-management changes. Touches the affiliate-dashboard UI and the public-api affiliate routes only. The removed claim-code route returns 404 going forward — partners using it would need to migrate to the unified register call (no known external consumers).

Testing

Engineering

  • Bring up affiliate-dashboard locally pointed at a public-api instance with the matching swap-service.
  • Happy path: SIWE sign in → Settings → fill Partner Code + Affiliate BPS → Register → confirm row appears in dashboard and config invalidates.
  • Validation rejects (each should show the inline error and not POST):
    • ab (too short)
    • 33-character string (too long)
    • Acme (uppercase)
    • my-code (hyphen)
    • my_code (underscore)
    • my code (space)
  • Whitespace tolerance: paste " acme " → trimmed to acme, accepted.
  • Conflict path: register the same code twice → second attempt shows "Partner code already taken" (not "Conflict").
  • BPS validation: 0, 1000 accepted; -1, 1001, empty all rejected with the BPS error.
  • Confirm POST /v1/affiliate/claim-code returns 404 (route removed).

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

Summary by CodeRabbit

  • New Features

    • Affiliate registration now requires a partner code during initial setup (3–32 lowercase alphanumeric) and accepts BPS at creation.
  • Bug Fixes

    • Improved error message extraction for clearer API error feedback.
  • Refactor

    • Removed the separate "Claim Partner Code" flow and UI; partner codes are provided during registration rather than claimed afterward.

Review Change Stack

kaladinlight and others added 9 commits May 28, 2026 14:35
Drop kebab-case support: regex is now /^[a-z0-9]{3,32}$/ in both the
affiliate-dashboard form and the public-api request schema. Extract
parsePartnerCode into lib/format alongside parseBps so RegisterCard
mirrors the BPS validation shape.

Also surface body.message in throwFromResponse so NestJS-style error
responses (e.g. "Partner code already taken") render instead of just
the error label ("Conflict").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kaladinlight kaladinlight requested a review from a team as a code owner May 28, 2026 22:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Warning

Review limit reached

@kaladinlight, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0eb12934-98f8-465b-a046-d57d339c96c1

📥 Commits

Reviewing files that changed from the base of the PR and between 45fc378 and af2c06e.

📒 Files selected for processing (1)
  • packages/public-api/src/routes/affiliate/types.ts
📝 Walkthrough

Walkthrough

Partner-code claim was removed; partnerCode and bps are validated and submitted during registration. UI, hook, API schemas, and public API route exports were updated to reflect the single-step registration flow.

Changes

Partner Code Registration Refactoring

Layer / File(s) Summary
Partner code validation and error extraction
packages/affiliate-dashboard/src/lib/format.ts, packages/affiliate-dashboard/src/lib/api.ts
Add PARTNER_CODE_REGEX and parsePartnerCode(); extend ErrorBody and throwFromResponse() to prefer body.details[0].message, then body.message, then body.error.
API schema and contract updates
packages/public-api/src/routes/affiliate/types.ts
CreateAffiliateRequestSchema requires partnerCode (trimmed, 3–32 lowercase alphanumeric) and bps (int 0–1000). UpdateAffiliateRequestSchema no longer includes partnerCode. Removed ClaimPartnerCodeRequestSchema and inferred type.
Hook registration logic
packages/affiliate-dashboard/src/hooks/useAffiliateActions.ts
Add exported RegisterArgs { bps, partnerCode }. Update register to accept the object, send bps and partnerCode in the mutation, and remove claimCode pending from isLoading.
RegisterCard component integration
packages/affiliate-dashboard/src/components/settings/RegisterCard.tsx
Props now include onRegister: ({ bps, partnerCode }) and onValidationError. Add partner code input state and client-side parsing/validation; disable Register until inputs valid; submit validated args.
SettingsTab wiring & ClaimCodeCard removal
packages/affiliate-dashboard/src/components/settings/SettingsTab.tsx
Removed ClaimCodeCard import and rendering; forward RegisterCard args directly to actions.register.
API endpoint deprecation
packages/public-api/src/index.ts, packages/public-api/src/routes/affiliate/index.ts
Removed claimPartnerCode import, its POST /v1/affiliate/claim-code route registration, and the claimPartnerCode re-export from affiliate routes.

"🐰
I hopped through code to stitch one flow,
Partner codes now join where new users go,
One form, one post, no extra claim to sow,
A tiny carrot dance — concise and aglow!" 🥕✨

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: requiring partnerCode at affiliate registration, which is the primary objective of this changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/affiliate-register-with-code

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.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
packages/affiliate-dashboard/src/components/settings/RegisterCard.tsx (1)

48-67: ⚡ Quick win

Memoize derived values and the click handler per project React guidelines.

handleClick is an event handler passed to Button (Line 75), and parsedBps/parsedCode/disabled are derived values recomputed every render.

♻️ Wrap with useMemo/useCallback
-import { useState } from 'react'
+import { useCallback, useMemo, useState } from 'react'
-  const parsedBps = parseBps(bps)
-  const parsedCode = parsePartnerCode(partnerCode)
-
-  const disabled = partnerCode === '' || bps === ''
-
-  const handleClick = (): void => {
+  const parsedBps = useMemo(() => parseBps(bps), [bps])
+  const parsedCode = useMemo(() => parsePartnerCode(partnerCode), [partnerCode])
+
+  const disabled = useMemo(() => partnerCode === '' || bps === '', [partnerCode, bps])
+
+  const handleClick = useCallback((): void => {
     if (parsedCode === null) {
       return onValidationError({
         type: 'error',
         text: `Partner code must be 3–32 lowercase letters or numbers (e.g. mypartnercode)`,
       })
     }
     if (parsedBps === null) {
       return onValidationError({
         type: 'error',
         text: `Affiliate BPS must be a number between ${MIN_BPS} and ${MAX_BPS}`,
       })
     }
     onRegister({ bps: parsedBps, partnerCode: parsedCode })
-  }
+  }, [parsedCode, parsedBps, onRegister, onValidationError])
As per coding guidelines: "ALWAYS use `useCallback` for event handlers and functions passed as props" and "ALWAYS use `useMemo` for derived values and computed properties".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/affiliate-dashboard/src/components/settings/RegisterCard.tsx` around
lines 48 - 67, The derived values parsedBps, parsedCode and disabled should be
memoized and the event handler handleClick should be wrapped in useCallback to
avoid recreating them each render; wrap parsedBps = parseBps(bps) and parsedCode
= parsePartnerCode(partnerCode) in useMemo (dependent on bps/partnerCode),
compute disabled via useMemo (dependent on partnerCode and bps or the memoized
parsed values), and wrap handleClick in useCallback (dependent on parsedBps,
parsedCode, onValidationError, onRegister, MIN_BPS, MAX_BPS) before passing it
to the Button to satisfy the project guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/public-api/src/routes/affiliate/types.ts`:
- Around line 40-46: Update UpdateAffiliateRequestSchema so its bps field
enforces the same upper bound as creation: add .max(1000) to the optional bps
validator (i.e., change the bps definition on UpdateAffiliateRequestSchema to
mirror CreateAffiliateRequestSchema). This ensures requests validated by
UpdateAffiliateRequestSchema (used via UpdateAffiliateRequestSchema.safeParse in
updateAffiliate.ts) cannot set bps > 1000 before forwarding upstream.

---

Nitpick comments:
In `@packages/affiliate-dashboard/src/components/settings/RegisterCard.tsx`:
- Around line 48-67: The derived values parsedBps, parsedCode and disabled
should be memoized and the event handler handleClick should be wrapped in
useCallback to avoid recreating them each render; wrap parsedBps = parseBps(bps)
and parsedCode = parsePartnerCode(partnerCode) in useMemo (dependent on
bps/partnerCode), compute disabled via useMemo (dependent on partnerCode and bps
or the memoized parsed values), and wrap handleClick in useCallback (dependent
on parsedBps, parsedCode, onValidationError, onRegister, MIN_BPS, MAX_BPS)
before passing it to the Button to satisfy the project guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f08d719-fab9-45d3-8975-b18fe7fbea90

📥 Commits

Reviewing files that changed from the base of the PR and between 6c36490 and d7a5be4.

📒 Files selected for processing (10)
  • packages/affiliate-dashboard/src/components/settings/ClaimCodeCard.tsx
  • packages/affiliate-dashboard/src/components/settings/RegisterCard.tsx
  • packages/affiliate-dashboard/src/components/settings/SettingsTab.tsx
  • packages/affiliate-dashboard/src/hooks/useAffiliateActions.ts
  • packages/affiliate-dashboard/src/lib/api.ts
  • packages/affiliate-dashboard/src/lib/format.ts
  • packages/public-api/src/index.ts
  • packages/public-api/src/routes/affiliate/claimPartnerCode.ts
  • packages/public-api/src/routes/affiliate/index.ts
  • packages/public-api/src/routes/affiliate/types.ts
💤 Files with no reviewable changes (4)
  • packages/public-api/src/routes/affiliate/claimPartnerCode.ts
  • packages/public-api/src/routes/affiliate/index.ts
  • packages/public-api/src/index.ts
  • packages/affiliate-dashboard/src/components/settings/ClaimCodeCard.tsx

Comment thread packages/public-api/src/routes/affiliate/types.ts
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

UpdateAffiliateRequestSchema was missing the .max(1000) constraint that
CreateAffiliateRequestSchema enforces, letting PATCH bypass the cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kaladinlight kaladinlight enabled auto-merge (squash) May 28, 2026 22:20
@kaladinlight kaladinlight merged commit aadf06e into develop May 28, 2026
4 checks passed
@kaladinlight kaladinlight deleted the feat/affiliate-register-with-code branch May 28, 2026 22:29
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