Skip to content

Create referral commissions in workflows#3969

Open
devkiran wants to merge 40 commits into
mainfrom
realtime-referral-commissions
Open

Create referral commissions in workflows#3969
devkiran wants to merge 40 commits into
mainfrom
realtime-referral-commissions

Conversation

@devkiran

@devkiran devkiran commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Add voiding of referral commissions when related source commissions are refunded or moved out of pending.
    • Referral creation now accepts discriminated triggers and returns structured success/skip reasons.
    • New cron endpoint to create network referral commissions.
  • Refactor

    • Cron flows and job targets simplified to use the network commission path.
    • Workflows/approval flows now conditionally create referrals based on context and commission type.
  • Performance

    • Added DB index to speed referral commission lookups.

@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Jun 3, 2026 5:24pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Walkthrough

Refactors partner-referral creation to use a discriminated args/structured-result API, adds voidReferralCommissions to bulk-void downstream referral commissions transactionally, wires these into cron endpoints, workflows, webhooks, and commission updates, and adds a DB index on sourceCommissionId.

Changes

Referral Commission Void and Return Propagation

Layer / File(s) Summary
Refactored createReferralCommission contract and implementation
apps/web/lib/partner-referrals/create-referral-commission.ts
createReferralCommission now accepts a discriminated CreateReferralCommissionArgs union (`source: "commission"
New voidReferralCommissions function
apps/web/lib/api/commissions/void-referral-commissions.ts
Exports voidReferralCommissions which selects referral commissions by source IDs and mutable status, bulk-updates status and clears payoutId in a transaction, re-queries the voided set, and runs post-update side effects: reconcile payouts, sync partner totals, and record activity.
Integrating void calls into commission mutations and webhook
apps/web/lib/api/commissions/update-partner-commission.ts, apps/web/lib/api/commissions/bulk-update-partner-commissions.ts, apps/web/app/(ee)/api/stripe/integration/webhook/charge-refunded.ts
Wires voidReferralCommissions into update, bulk-update, and Stripe refund flows to propagate void/refunded status to downstream referral commissions when source commissions change state or are refunded.
Entry points using new createReferralCommission args and network cron
apps/web/app/(ee)/api/cron/commissions/referrals/create/route.ts, apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts, apps/web/app/(ee)/api/cron/commissions/referrals/network/route.ts, apps/web/app/(ee)/api/workflows/create-partner-commission/route.ts, apps/web/app/(ee)/api/workflows/partner-approved/route.ts, apps/web/lib/partners/*, apps/web/app/(ee)/api/cron/payouts/charge-succeeded/send-paypal-payouts.ts
Cron and workflow routes now build and pass the discriminated CreateReferralCommissionArgs; the queue path was simplified and a new /referrals/network cron endpoint added; create-partner-commission conditionally triggers referral creation for sale commissions; partner-approved calls referral creation only when referral info exists; enqueue URLs updated to the /network endpoint.
Database schema update
packages/prisma/schema/commission.prisma
Adds @@index(sourceCommissionId) to the Commission model for improved referral-commission lookup performance.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • dubinc/dub#3925: Also touches partner-referral creation/backfill and eligibility logic interacting with createReferralCommission.
  • dubinc/dub#3858: Prior changes to referral-commission cron endpoints and creation flow that this PR builds upon.
  • dubinc/dub#3961: Related workflow changes for partner-commission creation that intersect with this PR's workflow wiring.

Suggested reviewers

  • pepeladeira
  • steven-tey

Poem

🐰 I hopped through code with nimble paws,
Discriminated args replaced old laws,
Downstream commissions now voided neat,
Crons and webhooks hum in sync and beat,
A rabbit cheers — the ledger's tidy and sweet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.
Title check ✅ Passed The pull request title accurately and concisely summarizes the primary change: creating referral commissions in workflows instead of payout cron jobs, which is reflected across multiple workflow route changes and the simplification of cron job handlers.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch realtime-referral-commissions

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.

@devkiran devkiran marked this pull request as ready for review June 2, 2026 10:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
apps/web/lib/partner-referrals/create-referral-commission.ts (1)

376-490: 💤 Low value

Add explicit return or exhaustiveness check at end of resolveReferralContext.

The function implicitly returns undefined when control flow falls through after the two if blocks. While the discriminated union ensures only "commission" or "partner" are valid at compile time, adding an explicit exhaustiveness check improves maintainability if new source types are added later.

♻️ Suggested exhaustiveness pattern
     return {
       sourceCommission: null,
       programId,
       partnerId,
       referredByPartnerId,
     };
   }
+
+  // Exhaustiveness check - should never be reached
+  const _exhaustiveCheck: never = args;
+  return _exhaustiveCheck;
 }
🤖 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 `@apps/web/lib/partner-referrals/create-referral-commission.ts` around lines
376 - 490, resolveReferralContext can implicitly return undefined if args.source
is neither "commission" nor "partner"; add an explicit exhaustiveness check at
the end of the function to return a stable value (e.g., null) or throw an error.
Locate the resolveReferralContext function and after the existing if
(args.source === "partner") block add a final branch that handles unexpected
args.source (use a never-exhaustive check or a default case referencing
args.source) and return null or throw with a clear message so callers never
receive undefined.
apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts (1)

35-51: 💤 Low value

Remove unused Prisma selections in referral queue

In apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts, payout.programEnrollment and payout.commissions are selected but never used in the route logic, and createNetworkReferralCommission (apps/web/lib/partner-referrals/create-network-referral-commission.ts) only relies on payout.id/amount/programId (it re-fetches network programEnrollment itself). Remove these fields from the Prisma select to avoid unnecessary DB work.

🤖 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 `@apps/web/app/`(ee)/api/cron/commissions/referrals/queue/route.ts around lines
35 - 51, The Prisma query in route.ts is selecting payout.programEnrollment and
payout.commissions even though the route logic and
createNetworkReferralCommission only need payout.id, payout.amount, and
payout.programId; remove the nested selects for programEnrollment and
commissions from the payout selection in
apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts so the query
only selects the necessary payout fields (id, amount, programId), leaving
createNetworkReferralCommission
(apps/web/lib/partner-referrals/create-network-referral-commission.ts) to
refetch programEnrollment itself.
🤖 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 `@apps/web/lib/api/commissions/update-partner-commission.ts`:
- Around line 320-328: The call to voidReferralCommissions should only happen
for terminal void statuses; wrap the invocation of voidReferralCommissions (the
call passing sourceCommissionStatus: finalStatus!) in a conditional that checks
finalStatus is one of ["refunded","duplicate","fraud","canceled"] and skip it
when finalStatus is undefined or "pending" (or any non-terminal status). Update
the code around the voidReferralCommissions call so you only pass
sourceCommissionStatus when inside that conditional to avoid nulling payoutIds
or forwarding invalid statuses.

In `@apps/web/lib/api/commissions/void-referral-commissions.ts`:
- Around line 35-62: The current flow reads mutable commissions via
prisma.commission.findMany but then updates by id without re-checking
eligibility, which can overwrite immutable rows; wrap the read+write in a single
transaction and perform the update with the same eligibility predicates (the
original where block: sourceCommissionId in sourceCommissionIds AND (status =
"pending" OR (status = "processed" AND payout.status in
MUTABLE_PAYOUT_STATUSES))) using prisma.commission.updateMany (or include the
same predicates in the update call) so only eligible rows are modified, then
reconcile/log based on the update result (rowsAffected/returned rows) to act
only on actually changed commissions (refer to the existing
prisma.commission.findMany selection and the update logic near the
updateById/updateMany calls).

---

Nitpick comments:
In `@apps/web/app/`(ee)/api/cron/commissions/referrals/queue/route.ts:
- Around line 35-51: The Prisma query in route.ts is selecting
payout.programEnrollment and payout.commissions even though the route logic and
createNetworkReferralCommission only need payout.id, payout.amount, and
payout.programId; remove the nested selects for programEnrollment and
commissions from the payout selection in
apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts so the query
only selects the necessary payout fields (id, amount, programId), leaving
createNetworkReferralCommission
(apps/web/lib/partner-referrals/create-network-referral-commission.ts) to
refetch programEnrollment itself.

In `@apps/web/lib/partner-referrals/create-referral-commission.ts`:
- Around line 376-490: resolveReferralContext can implicitly return undefined if
args.source is neither "commission" nor "partner"; add an explicit
exhaustiveness check at the end of the function to return a stable value (e.g.,
null) or throw an error. Locate the resolveReferralContext function and after
the existing if (args.source === "partner") block add a final branch that
handles unexpected args.source (use a never-exhaustive check or a default case
referencing args.source) and return null or throw with a clear message so
callers never receive undefined.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9570db4f-08a2-40e2-8f13-2f9f291cc474

📥 Commits

Reviewing files that changed from the base of the PR and between d536779 and b3de67f.

📒 Files selected for processing (11)
  • apps/web/app/(ee)/api/cron/commissions/referrals/create/route.ts
  • apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts
  • apps/web/app/(ee)/api/stripe/integration/webhook/charge-refunded.ts
  • apps/web/app/(ee)/api/workflows/create-partner-commission/route.ts
  • apps/web/app/(ee)/api/workflows/partner-approved/route.ts
  • apps/web/lib/api/commissions/bulk-update-partner-commissions.ts
  • apps/web/lib/api/commissions/update-partner-commission.ts
  • apps/web/lib/api/commissions/void-referral-commissions.ts
  • apps/web/lib/partner-referrals/create-referral-commission.ts
  • apps/web/scripts/dev/test-partner-referrals.ts
  • packages/prisma/schema/commission.prisma
💤 Files with no reviewable changes (1)
  • apps/web/scripts/dev/test-partner-referrals.ts

Comment thread apps/web/lib/api/commissions/update-partner-commission.ts Outdated
Comment thread apps/web/lib/api/commissions/void-referral-commissions.ts Outdated
Gate referral creation on sale commission type, return createReferralCommission from partner-approved workflow, and count paid sales toward referral earnings limits.
@devkiran

devkiran commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@devkiran

devkiran commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review please

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

@devkiran Sure, triggering a full review of PR #3969 now!

✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/lib/partner-referrals/create-referral-commission.ts (1)

181-200: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Threshold referrals stop firing when the threshold is actually crossed.

This branch is now reached from apps/web/app/(ee)/api/workflows/create-partner-commission/route.ts right after the sale commission is created, but the aggregate still only counts source commissions with status: "paid". That means the sale that crosses the threshold is usually invisible here, and once it later transitions to paid this slice no longer shows any non-network path that re-runs createReferralCommission. The referral bonus will be delayed until some later sale, or never created if no later sale arrives.

Either re-trigger commissionThreshold when source commissions become paid, or change the threshold calculation to include the current source commission at creation time if that is the intended business rule.

🤖 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 `@apps/web/lib/partner-referrals/create-referral-commission.ts` around lines
181 - 200, The threshold branch in create-referral-commission.ts checks
prisma.commission.aggregate with status: "paid", which ignores the just-created
source commission and delays/refuses the referral bonus; fix by including the
current source commission when computing total (or alternatively ensure this
function is retriggered when a source commission transitions to "paid").
Concretely, update the commissionThreshold branch that uses
prisma.commission.aggregate to add the current commission's earnings into the
total before comparing to commissionsThresholdInCents (e.g., add a
currentCommissionEarnings variable from the incoming/created commission and sum
it with totalCommissionsEarned) or change the caller/workflow to invoke
createReferralCommission when a commission status flips to "paid" so the
aggregate with status: "paid" includes the new sale. Ensure references to
partnerId, programId, prisma.commission.aggregate, commissionsThresholdInCents
and referredByPartnerId are used to locate the code to modify.
🤖 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 `@apps/web/lib/api/commissions/bulk-update-partner-commissions.ts`:
- Around line 171-176: The call to voidReferralCommissions is missing the acting
user so downstream referral status changes lose actor attribution; update the
invocation in bulk-update-partner-commissions (where you currently pass
workspaceId, programId, sourceCommissionIds: commissionIds,
sourceCommissionStatus: status) to also pass the userId (or actingUserId) from
this bulk path so voidReferralCommissions receives the actor and records it on
the referral commission updates.

In `@apps/web/lib/api/commissions/update-partner-commission.ts`:
- Around line 322-329: The call to voidReferralCommissions is omitting the actor
so the side-effect activity log loses the user attribution; update the call site
to pass the parent mutation's userId into voidReferralCommissions (add userId:
userId) and ensure the voidReferralCommissions function signature accepts and
forwards that userId to the activity/logging logic so referral commission
updates are attributed correctly.

In `@apps/web/lib/api/commissions/void-referral-commissions.ts`:
- Around line 91-97: The current tx.commission.findMany call queries by
sourceCommissionId and status (using sourceCommissionIds and
sourceCommissionStatus), which can return rows that were already in that status;
instead, capture the exact referral commission IDs changed by the mutation (from
the update result) and re-query tx.commission.findMany using where: { id: { in:
updatedReferralCommissionIds } } so you only fetch commissions actually modified
(reference the tx.commission.findMany call and the variable that holds the
update result/IDs).

---

Outside diff comments:
In `@apps/web/lib/partner-referrals/create-referral-commission.ts`:
- Around line 181-200: The threshold branch in create-referral-commission.ts
checks prisma.commission.aggregate with status: "paid", which ignores the
just-created source commission and delays/refuses the referral bonus; fix by
including the current source commission when computing total (or alternatively
ensure this function is retriggered when a source commission transitions to
"paid"). Concretely, update the commissionThreshold branch that uses
prisma.commission.aggregate to add the current commission's earnings into the
total before comparing to commissionsThresholdInCents (e.g., add a
currentCommissionEarnings variable from the incoming/created commission and sum
it with totalCommissionsEarned) or change the caller/workflow to invoke
createReferralCommission when a commission status flips to "paid" so the
aggregate with status: "paid" includes the new sale. Ensure references to
partnerId, programId, prisma.commission.aggregate, commissionsThresholdInCents
and referredByPartnerId are used to locate the code to modify.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: aed61b89-fe36-46ff-9451-44c3ca3ff925

📥 Commits

Reviewing files that changed from the base of the PR and between d536779 and f68af1b.

📒 Files selected for processing (20)
  • apps/web/app/(ee)/api/admin/payouts/get-payouts-timeseries.ts
  • apps/web/app/(ee)/api/cron/commissions/referrals/create/route.ts
  • apps/web/app/(ee)/api/cron/commissions/referrals/network/route.ts
  • apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts
  • apps/web/app/(ee)/api/cron/payouts/charge-succeeded/send-paypal-payouts.ts
  • apps/web/app/(ee)/api/stripe/integration/webhook/charge-refunded.ts
  • apps/web/app/(ee)/api/workflows/create-partner-commission/route.ts
  • apps/web/app/(ee)/api/workflows/partner-approved/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/webhooks/[webhookId]/page-client.tsx
  • apps/web/app/providers.tsx
  • apps/web/lib/api/commissions/bulk-update-partner-commissions.ts
  • apps/web/lib/api/commissions/update-partner-commission.ts
  • apps/web/lib/api/commissions/void-referral-commissions.ts
  • apps/web/lib/partner-referrals/create-referral-commission.ts
  • apps/web/lib/partners/create-stablecoin-payout.ts
  • apps/web/lib/partners/create-stripe-transfer.ts
  • apps/web/playwright/workspaces/billing-trial.spec.ts
  • apps/web/scripts/dev/test-partner-referrals.ts
  • apps/web/ui/partners/partner-link-selector.tsx
  • packages/prisma/schema/commission.prisma
💤 Files with no reviewable changes (2)
  • apps/web/scripts/dev/test-partner-referrals.ts
  • apps/web/app/(ee)/api/cron/commissions/referrals/queue/route.ts

Comment thread apps/web/lib/api/commissions/bulk-update-partner-commissions.ts Outdated
Comment thread apps/web/lib/api/commissions/update-partner-commission.ts Outdated
Comment thread apps/web/lib/api/commissions/void-referral-commissions.ts Outdated
@devkiran devkiran changed the title Create referral commissions in workflows instead of payout cron jobs Create referral commissions in workflows Jun 3, 2026
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.

2 participants