+
Skip to content

Conversation

steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Jun 25, 2025

Summary by CodeRabbit

  • New Features
    • Added clear error messages and tooltips when attempting to confirm payouts that exceed your workspace's monthly payout limit.
    • The payout confirmation button now displays a helpful tooltip with upgrade information if your payout limit is reached.
  • Bug Fixes
    • Improved consistency and accuracy in how payout limits are checked and enforced across the app.
  • Style
    • Enhanced tooltip messaging for a more informative and user-friendly experience.

Copy link
Contributor

vercel bot commented Jun 25, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
dub ✅ Ready (Inspect) Visit Preview Jun 25, 2025 9:20pm

Copy link
Contributor

coderabbitai bot commented Jun 25, 2025

Walkthrough

This change introduces comprehensive payout limit validation across backend and frontend components. It ensures that the total payout amount, combined with existing usage, does not exceed the workspace's payout limit. Enhanced error handling and user-facing tooltips are added to inform users when limits are exceeded, with suggestions to upgrade their plan.

Changes

File(s) Change Summary
apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts Added validation for payouts limit, centralized payout amount calculation, improved error handling, and updated workspace type.
apps/web/lib/actions/partners/confirm-payouts.ts Replaced generic error with detailed error via exceededLimitError, changed condition to strict greater-than for limit check.
apps/web/lib/api/errors.ts Extended exceededLimitError to support "payouts" type, improved messaging, and added currency formatting for limits.
apps/web/ui/partners/payout-invoice-sheet.tsx Added tooltip for disabled payout button when exceeding limits, used new error messaging, and integrated upgrade link.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant Backend
    participant DB

    User->>UI: Initiate payout confirmation
    UI->>Backend: Request to confirm payout (with amount)
    Backend->>DB: Fetch workspace payoutsUsage and payoutsLimit
    Backend->>Backend: Calculate new total payout amount
    alt Exceeds payoutsLimit
        Backend->>UI: Return exceededLimitError
        UI->>User: Show tooltip with upgrade suggestion
    else Within limit
        Backend->>DB: Update payoutsUsage
        Backend->>UI: Confirm payout success
        UI->>User: Show confirmation
    end
Loading

Possibly related PRs

  • dubinc/dub#2572: Modifies the same confirmPayouts function for SEPA debit payout currency conversion, directly overlapping the payout logic.
  • dubinc/dub#2485: Switches payout limit tracking fields and logic, directly related to the enhanced payout usage and limit validation in this PR.
  • dubinc/dub#2569: Implements dynamic payout fee calculation and workspace-level payoutFee integration in the confirmPayouts function, complementing the limit validation here.

Poem

A rabbit hops and checks the sum,
No payouts past the limit come!
If you try to leap too high,
A tooltip gently tells you why.
Upgrade, upgrade, the carrot’s near—
With limits clear, there’s naught to fear!
🥕✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0c235 and 8c1e08e.

📒 Files selected for processing (1)
  • apps/web/lib/actions/partners/confirm-payouts.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/actions/partners/confirm-payouts.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@steven-tey
Copy link
Collaborator Author

@CodeRabbit final review

Copy link
Contributor

coderabbitai bot commented Jun 25, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the diff for this PR to understand what changes were made
echo "=== PR DIFF ==="
git diff HEAD~1..HEAD

echo -e "\n=== FILES CHANGED ==="
git diff --name-only HEAD~1..HEAD

echo -e "\n=== DETAILED CHANGES PER FILE ==="
for file in $(git diff --name-only HEAD~1..HEAD); do
    echo "=== Changes in $file ==="
    git diff HEAD~1..HEAD -- "$file"
    echo -e "\n"
done

Length of output: 858


🏁 Script executed:

#!/bin/bash
# Let's examine the files mentioned in the PR directly
echo "=== Reading confirm-payouts.ts (API cron job) ==="
cat "apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts"

echo -e "\n=== Reading confirm-payouts.ts (actions) ==="
cat "apps/web/lib/actions/partners/confirm-payouts.ts"

echo -e "\n=== Reading errors.ts ==="
cat "apps/web/lib/api/errors.ts"

echo -e "\n=== Reading payout-invoice-sheet.tsx ==="
cat "apps/web/ui/partners/payout-invoice-sheet.tsx"

Length of output: 30261


@steven-tey I've completed a comprehensive review of the payouts limit implementation. Here are my findings:

🚨 Critical Issues

1. Logic Error in Server Action (High Priority)

// apps/web/lib/actions/partners/confirm-payouts.ts:30-34
if (
  workspace.payoutsUsage + workspace.payoutsLimit >
  workspace.payoutsLimit
) {

Issue: This condition will always be true when payoutsUsage > 0, which is incorrect.
Fix: Should check against the actual payout amount:

// Need to fetch payout amount first, then check:
if (workspace.payoutsUsage + payoutAmount > workspace.payoutsLimit) {

2. Inconsistent Limit Checking Logic

The limit checking appears in 3 different places with different implementations:

  • Server action (incorrect logic)
  • API cron job (correct logic)
  • UI component (correct logic)

This creates maintenance risk and potential bugs.

🔧 Recommendations

Consolidate Limit Checking

Create a centralized utility function:

// lib/partners/payout-limits.ts
export function checkPayoutLimits({
  currentUsage,
  limit,
  additionalAmount,
}: {
  currentUsage: number;
  limit: number;
  additionalAmount: number;
}) {
  return currentUsage + additionalAmount <= limit;
}

Improve Error Handling

In the API cron job, consider handling these edge cases:

  • What happens if Stripe payment intent creation fails after invoice creation?
  • Database rollback strategy for partial failures during email sending

Security Enhancement

Consider adding rate limiting to the confirm payouts action to prevent abuse.

✅ What's Working Well

  1. Excellent Error Messaging: The exceededLimitError function provides user-friendly, contextual error messages
  2. Proper Transaction Management: Good use of Prisma transactions in the API cron job
  3. Currency Conversion Logic: Well-implemented with proper error handling for exchange rates
  4. UI/UX: The payout invoice sheet provides excellent user experience with real-time calculations
  5. Type Safety: Consistent use of TypeScript and Zod validation throughout
  6. Email Notifications: Smart batching and conditional sending based on payment method type

🎯 Minor Improvements

  1. Constants: Move magic numbers like 100 (batch size) to constants
  2. Logging: Consider adding more structured logging for debugging
  3. Testing: The complex currency conversion logic would benefit from unit tests

Summary

This is a well-architected implementation of a complex financial feature. The primary concern is the critical logic error in the server action that needs immediate attention. Once fixed, the limit checking logic should be centralized to prevent future inconsistencies.

The code demonstrates good practices in error handling, user experience, and payment processing. With the suggested fixes, this will be a robust payouts system.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7c0247 and 1c0c235.

📒 Files selected for processing (4)
  • apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts (7 hunks)
  • apps/web/lib/actions/partners/confirm-payouts.ts (2 hunks)
  • apps/web/lib/api/errors.ts (2 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (5 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
apps/web/lib/actions/partners/confirm-payouts.ts (1)
apps/web/lib/api/errors.ts (1)
  • exceededLimitError (229-253)
apps/web/ui/partners/payout-invoice-sheet.tsx (3)
packages/ui/src/tooltip.tsx (1)
  • TooltipContent (90-128)
apps/web/lib/api/errors.ts (1)
  • exceededLimitError (229-253)
apps/web/lib/types.ts (1)
  • PlanProps (143-143)
apps/web/lib/api/errors.ts (1)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (1-11)
apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts (2)
apps/web/lib/api/errors.ts (1)
  • exceededLimitError (229-253)
apps/web/lib/types.ts (1)
  • PlanProps (143-143)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (9)
apps/web/lib/api/errors.ts (2)

2-2: LGTM: Import added for currency formatting.

The currencyFormatter import is properly added to support the new payouts limit formatting.


236-252: LGTM: Payouts limit support added correctly.

The extension to support "payouts" type is well-implemented:

  • Properly added to the union type
  • Correctly included in the monthly limit check
  • Currency formatting using currencyFormatter(limit / 100) properly converts from cents to dollars
  • Error message structure remains consistent with other limit types
apps/web/ui/partners/payout-invoice-sheet.tsx (3)

2-2: LGTM: Necessary imports added for payout limit functionality.

The imports for exceededLimitError, PlanProps, and TooltipContent are properly added to support the new payout limit validation and user feedback.

Also applies to: 12-12, 24-24


89-90: LGTM: Workspace payout properties extracted correctly.

The payoutsUsage and payoutsLimit properties are properly extracted from the workspace context to support the limit validation.


437-455: LGTM: Enhanced button with payout limit validation.

The button enhancement is well-implemented:

  • Properly extends the existing disabled condition to include the payout limit check
  • Uses the standardized exceededLimitError function for consistent messaging
  • Provides a user-friendly tooltip explaining why the button is disabled
  • Includes a direct upgrade link for users to resolve the limit issue
  • The condition correctly checks if payoutsUsage + amount > payoutsLimit
apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts (4)

2-2: LGTM: Necessary imports added for payout limit validation.

The imports for exceededLimitError and PlanProps are properly added to support the new payout limit validation functionality.

Also applies to: 14-14


36-42: LGTM: Workspace type extended with payout properties.

The workspace type is correctly extended to include payoutsUsage and payoutsLimit properties, which are necessary for the new payout limit validation.


97-110: LGTM: Proper payout amount calculation and limit validation.

The implementation correctly:

  • Calculates the total payout amount once for reuse
  • Validates that current usage plus new payout amount doesn't exceed the limit
  • Uses the standardized exceededLimitError function for consistent error messaging
  • Properly casts the plan type for the error function

This is the appropriate place for this validation since it has access to the actual payout amounts.


130-131: LGTM: Consistent use of calculated payout amount.

The refactoring to use the pre-calculated payoutAmount variable improves code maintainability and ensures consistency across:

  • Fee calculation (line 130)
  • Invoice amount (line 174)
  • Database usage increment (line 218)

Also applies to: 174-174, 218-218

@steven-tey
Copy link
Collaborator Author

@CodeRabbit final review

Copy link
Contributor

coderabbitai bot commented Jun 25, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read all the files involved in the payouts limit implementation to provide final review
echo "=== 1. API Cron Job - confirm-payouts.ts ==="
cat "apps/web/app/(ee)/api/cron/payouts/confirm/confirm-payouts.ts"

echo -e "\n=== 2. Server Action - confirm-payouts.ts ==="
cat "apps/web/lib/actions/partners/confirm-payouts.ts"

echo -e "\n=== 3. Error Handling - errors.ts ==="
cat "apps/web/lib/api/errors.ts"

echo -e "\n=== 4. UI Component - payout-invoice-sheet.tsx ==="
cat "apps/web/ui/partners/payout-invoice-sheet.tsx"

echo -e "\n=== 5. Check for any related test files ==="
fd -t f -e ts -e tsx . | grep -i "payout.*test\|test.*payout" || echo "No payout test files found"

echo -e "\n=== 6. Check if there are any utility functions for payout limits ==="
fd -t f -e ts . | xargs grep -l "payoutLimit\|payout.*limit" | head -10

Length of output: 30920


@steven-tey Here's my final comprehensive review of the payouts limit implementation:

🚨 Critical Issue Still Present

The server action contains a critical logic error that must be fixed before deployment:

// apps/web/lib/actions/partners/confirm-payouts.ts:30-36
if (workspace.payoutsUsage > workspace.payoutsLimit) {
  throw new Error(
    exceededLimitError({
      plan: workspace.plan,
      limit: workspace.payoutsLimit,
      type: "payouts",
    }),
  );
}

Problem: This only checks current usage against the limit, but doesn't validate that the new payout amount won't exceed the limit.

Security Risk: Users could potentially bypass limits by staying under the current limit while adding payouts that would exceed it.

Required Fix:

// Need to fetch pending payout amount first, then check:
const pendingPayoutAmount = await calculatePendingPayoutAmount(workspaceId, cutoffPeriod);
if (workspace.payoutsUsage + pendingPayoutAmount > workspace.payoutsLimit) {
  // throw error
}

✅ What's Working Correctly

API Cron Job - Excellent Implementation

  • Correct Logic: workspace.payoutsUsage + payoutAmount > workspace.payoutsLimit
  • Proper Transaction Management: Uses Prisma transactions correctly
  • Currency Conversion: Well-implemented with Stripe FX quotes and fallback handling
  • Email Handling: Smart batching for direct debit notifications
  • Error Handling: Comprehensive error handling with proper rollback

UI Component - Great User Experience

  • Correct Logic: payoutsUsage + amount > payoutsLimit
  • Excellent UX: Disabled button with informative tooltip when limits exceeded
  • Real-time Calculations: Dynamic fee and total calculations
  • Upgrade Path: Direct link to billing upgrade when limits hit

Error Handling - Professional Implementation

  • Currency Formatting: Proper formatting for payout limits (currencyFormatter(limit / 100))
  • Contextual Messaging: Clear, actionable error messages
  • Consistent API: Well-structured error response format

🔧 Recommendations

1. Immediate Fix Required

Fix the server action logic before deployment to prevent security vulnerabilities.

2. Centralize Limit Checking

Create a shared utility to prevent future inconsistencies:

// lib/partners/payout-limits.ts
export async function validatePayoutLimits({
  workspaceId,
  currentUsage,
  limit,
  cutoffPeriod,
}: {
  workspaceId: string;
  currentUsage: number;
  limit: number;
  cutoffPeriod?: string;
}) {
  const pendingAmount = await calculatePendingPayoutAmount(workspaceId, cutoffPeriod);
  return {
    isValid: currentUsage + pendingAmount <= limit,
    pendingAmount,
    wouldExceedBy: Math.max(0, (currentUsage + pendingAmount) - limit),
  };
}

3. Add Unit Tests

The complex currency conversion and limit validation logic would benefit from comprehensive test coverage.

Summary

This is a well-architected financial feature with excellent error handling, user experience, and payment processing logic. The implementation demonstrates good practices in transaction management, currency handling, and user feedback.

However, the critical logic error in the server action creates a significant security vulnerability that must be addressed immediately. Once fixed and the limit checking logic is centralized, this will be a robust and production-ready payouts system.

The quality of the API cron job, UI components, and error handling gives me confidence that this team can deliver excellent financial software with the proper attention to security details.

@steven-tey
Copy link
Collaborator Author

@CodeRabbit that's fine cause we check again in /cron/payouts, and it'll fail if the amount + usage exceeds limit

Copy link
Contributor

coderabbitai bot commented Jun 25, 2025

Tip

For best results, initiate chat on the files or code changes.

@steven-tey Ah, that makes perfect sense! Thank you for the clarification. You're absolutely right - this is a well-designed two-stage validation pattern:

  1. Server Action: Quick sanity check (payoutsUsage > payoutsLimit) for immediate user feedback
  2. Cron Job: Authoritative validation (payoutsUsage + payoutAmount > payoutsLimit) with actual calculated amounts before processing

This is actually a smart architectural approach for financial systems where you want to:

  • Give users immediate feedback without expensive calculations
  • Ensure final validation happens at the point of actual transaction processing

I appreciate you taking the time to explain the design intent. The implementation is solid, and the comprehensive validation in the cron job with proper transaction management ensures the limits are enforced correctly where it matters most.

Great work on this payouts system! 🚀


✏️ Learnings added
Learnt from: steven-tey
URL: https://github.com/dubinc/dub/pull/2577

Timestamp: 2025-06-25T21:20:59.785Z
Learning: In the Dub codebase, payout limit validation uses a two-stage pattern: server actions perform quick sanity checks (payoutsUsage > payoutsLimit) for immediate user feedback, while the cron job (/cron/payouts) performs authoritative validation (payoutsUsage + payoutAmount > payoutsLimit) with actual calculated amounts before processing. This design provides fast user feedback while ensuring accurate limit enforcement at transaction time.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@steven-tey steven-tey merged commit 6bb5da1 into main Jun 25, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the payouts-limit branch June 25, 2025 21:21
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

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载