+
Skip to content

Conversation

devkiran
Copy link
Collaborator

@devkiran devkiran commented Oct 2, 2025

Summary by CodeRabbit

  • New Features

    • Fast ACH payouts: 2 business days, $25 fee, UI toggle/banner, updated payout summaries, emails now show payment method.
    • Billing views surface payment method and a separate Fast ACH fee line when applicable.
    • Enhanced payment-method and cutoff-period selection UX in the payout sheet.
  • Bug Fixes

    • More reliable invoice PDF responses and clarified unauthorized message.
  • Chores

    • Backfilled missing invoice payment methods for historical accuracy.

Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d0a87d1 and b722721.

📒 Files selected for processing (20)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (5 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx (2 hunks)
  • apps/web/app/api/workspaces/[idOrSlug]/billing/invoices/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (4 hunks)
  • apps/web/lib/actions/partners/confirm-payouts.ts (6 hunks)
  • apps/web/lib/cron/verify-vercel.ts (1 hunks)
  • apps/web/lib/partners/constants.ts (2 hunks)
  • apps/web/lib/payment-methods.ts (0 hunks)
  • apps/web/lib/stripe/payment-methods.ts (1 hunks)
  • apps/web/lib/zod/schemas/invoices.ts (1 hunks)
  • apps/web/lib/zod/schemas/workspaces.ts (1 hunks)
  • apps/web/scripts/migrations/backfill-invoice-payment-method.ts (1 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (11 hunks)
  • packages/email/src/templates/partner-payout-confirmed.tsx (3 hunks)
  • packages/prisma/schema/invoice.prisma (1 hunks)
  • packages/prisma/schema/network.prisma (2 hunks)
  • packages/prisma/schema/partner.prisma (1 hunks)
  • packages/prisma/schema/program.prisma (1 hunks)
  • packages/prisma/schema/workspace.prisma (1 hunks)

Walkthrough

Adds Fast ACH payout support across DB, backend, UI, and emails: new PaymentMethod enum and invoice.paymentMethod, workspace fastDirectDebitPayouts flag, FAST_ACH_FEE_CENTS and normalization/utilities, UI/UX for selecting Fast ACH, backend validation, fee handling and invoice updates, migration to backfill payment methods, and email/template updates.

Changes

Cohort / File(s) Summary
Constants & Stripe utilities
apps/web/lib/partners/constants.ts, apps/web/lib/stripe/payment-methods.ts
Add FAST_ACH_FEE_CENTS, STRIPE_PAYMENT_METHOD_NORMALIZATION, INVOICE_PAYMENT_METHODS, INVOICE_AVAILABLE_PAYOUT_STATUSES; add calculatePayoutFeeForMethod and STRIPE_PAYMENT_METHODS.
Payment method catalog
apps/web/lib/payment-methods.ts
Export immutable PAYMENT_METHODS (adds ach_fast) and add icon imports.
DB schema & migrations
packages/prisma/schema/invoice.prisma, packages/prisma/schema/workspace.prisma, apps/web/scripts/migrations/backfill-invoice-payment-method.ts
Add PaymentMethod enum and Invoice.paymentMethod; add Project.fastDirectDebitPayouts; migration script to backfill invoice payment methods from Stripe metadata.
Zod schemas
apps/web/lib/zod/schemas/invoices.ts, apps/web/lib/zod/schemas/workspaces.ts
Add optional/nullable paymentMethod to Invoice schema; add fastDirectDebitPayouts boolean (default false) to Workspace schema.
Payout confirm flow (action)
apps/web/lib/actions/partners/confirm-payouts.ts
Add fastSettlement input; validate workspace capability and method type; normalize and store invoice.paymentMethod (set to ach_fast when fastSettlement).
Payout processing (cron)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts
Load invoice early, compute FAST_ACH_FEE_CENTS when ach_fast, include fastAchFee in fee/total, update invoice amounts, set Stripe preferred_settlement_speed for US bank intents, and pass paymentMethod through emails and descriptions.
Billing API & PDF route
apps/web/app/api/workspaces/[idOrSlug]/billing/invoices/route.ts, apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx
Include paymentMethod in invoice projection returned by billing API; minor PDF Response construction/punctuation tweak.
UI / Invoice presentation
apps/web/ui/partners/payout-invoice-sheet.tsx, apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx
Payout sheet: comboboxes, Fast ACH toggle, fast fee calculation, payload includes fastSettlement; partner payout invoice shows Fast ACH fee and guards; invoices page optionally displays payment method; component signatures updated to accept display/showing of payment method.
Email template
packages/email/src/templates/partner-payout-confirmed.tsx
Add paymentMethod to payout payload and render conditional processing time (2 business days for ach_fast, otherwise 5).
Prisma formatting-only edits
packages/prisma/schema/network.prisma, packages/prisma/schema/partner.prisma, packages/prisma/schema/program.prisma
Formatting/newline-only adjustments.
Misc
apps/web/lib/cron/verify-vercel.ts
Add blank line; no functional change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as Partner Manager
  participant UI as Payout Invoice Sheet
  participant Action as confirmPayoutsAction
  participant DB as Prisma
  participant Stripe as Stripe API

  User->>UI: Choose payment method and optional Fast ACH
  UI->>Action: confirmPayouts({ paymentMethodId, fastSettlement, ... })
  Action->>DB: Read workspace.fastDirectDebitPayouts
  alt fastSettlement == true
    Action->>Action: Validate workspace and method type (must be us_bank_account)
    Action->>DB: Create invoice with paymentMethod = ach_fast
  else
    Action->>Action: Normalize method via STRIPE_PAYMENT_METHOD_NORMALIZATION
    Action->>DB: Create invoice with normalized paymentMethod
  end
  Action-->>UI: Return success (invoice created)
Loading
sequenceDiagram
  autonumber
  participant Cron as Payouts Cron
  participant DB as Prisma (Invoice)
  participant Stripe as Stripe (PaymentIntent)
  participant Email as Email Service

  Cron->>DB: Load invoice by id (includes paymentMethod)
  Cron->>Cron: Compute fees (platform fee + fastAchFee if paymentMethod == ach_fast)
  alt paymentMethod in [ach, ach_fast] (us_bank_account)
    Cron->>Stripe: Create PaymentIntent (include preferred_settlement_speed if ach_fast)
  else paymentMethod in {sepa, acss}
    Cron->>Stripe: Create PaymentIntent (FX/currency handling)
  end
  Cron->>DB: Update invoice with finalized amount/fee/total
  Cron->>Email: Send payout emails including paymentMethod
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • TWilson023

Poem

"I hopped through ledgers, whiskers bright,
Fast ACH now dances into night.
Fees trimmed neat, the carrots cheer—
Payouts swift, the burrow near.
Ledger stamped, the rabbit grins: 'Paid!'" 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly summarizes the primary change, which is the introduction of a fast settlement option into the payout processing workflow, matching the detailed code, configuration, and UI updates throughout the PR.

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.

Copy link
Contributor

vercel bot commented Oct 2, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 7, 2025 10:24pm

…malize payment methods. Update related schemas and types for consistency.
@devkiran devkiran marked this pull request as ready for review October 7, 2025 16:48
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: 5

Caution

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

⚠️ Outside diff range comments (1)
apps/web/ui/partners/payout-invoice-sheet.tsx (1)

299-308: Add rel="noopener noreferrer" to external settings link.

Security best‑practice for target="_blank".

-            <a
+            <a
               href={`/${slug}/settings/billing`}
               className={cn(
                 buttonVariants({ variant: "secondary" }),
                 "flex items-center rounded-md border border-neutral-200 p-1.5 text-sm",
               )}
-              target="_blank"
+              target="_blank"
+              rel="noopener noreferrer"
             >
🧹 Nitpick comments (5)
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx (1)

64-64: Consider simplifying the Response construction.

The Uint8Array wrapper appears unnecessary since the Response constructor accepts Buffer directly in Node.js/Next.js. The current code works but adds an extra conversion step.

If simplification is desired, apply this diff:

-  return new Response(new Uint8Array(pdf), {
+  return new Response(pdf, {
     headers: {
       "Content-Type": "application/pdf",
       "Content-Disposition": `inline; filename="Invoice-${invoice.number}.pdf"`,
     },
   });
packages/email/src/types.ts (1)

10-10: Consider importing from Prisma to avoid type duplication.

The PaymentMethod type duplicates the PaymentMethod enum defined in packages/prisma/schema/invoice.prisma. Maintaining separate definitions risks divergence if one is updated without the other.

Apply this diff to import from Prisma instead:

-export type PaymentMethod = "card" | "ach" | "ach_fast" | "sepa" | "acss";
+export type { PaymentMethod } from "@dub/prisma/client";

This ensures type consistency across the codebase. If the email package intentionally avoids Prisma dependencies for architectural reasons, consider documenting that decision.

apps/web/lib/actions/partners/confirm-payouts.ts (1)

120-123: Align stored invoice.paymentMethod with UI keys or add a UI mapping.

You persist “ach/ach_fast/acss/sepa/card”. The UI PAYMENT_METHODS is keyed by “us_bank_account/acss_debit/sepa_debit/ach_fast/card”. Ensure UI maps these (ach→us_bank_account, acss→acss_debit, sepa→sepa_debit) or store UI‑friendly keys here.

Would you prefer to keep normalized values here and map in UI (safer), or change this to UI keys for direct lookup?

apps/web/ui/partners/payout-invoice-sheet.tsx (2)

363-369: Format percentage to avoid float artifacts (e.g., 2.999999%).

Use toFixed then Number to strip trailing zeros.

-          <SimpleTooltipContent
-            title={`${selectedPaymentMethod.fee * 100}% processing fee. ${!DIRECT_DEBIT_PAYMENT_METHOD_TYPES.includes(selectedPaymentMethod.type as Stripe.PaymentMethod.Type) ? " Switch to Direct Debit for a reduced fee." : ""}`}
+          <SimpleTooltipContent
+            title={`${Number((selectedPaymentMethod.fee * 100).toFixed(2))}% processing fee.${!DIRECT_DEBIT_PAYMENT_METHOD_TYPES.includes(selectedPaymentMethod.type as Stripe.PaymentMethod.Type) ? " Switch to Direct Debit for a reduced fee." : ""}`}
             cta="Learn more"
             href="https://d.to/payouts"
           />

286-294: Render selected icon safely for Icon or ReactNode.

icon can be a component or a node. Avoid assuming it’s always a component.

-                      <>
-                        <selectedPaymentMethodOption.icon className="size-4" />
-                        {selectedPaymentMethodOption.label}
-                      </>
+                      <>
+                        {typeof selectedPaymentMethodOption.icon === "function" ? (
+                          <selectedPaymentMethodOption.icon className="size-4" />
+                        ) : (
+                          selectedPaymentMethodOption.icon
+                        )}
+                        {selectedPaymentMethodOption.label}
+                      </>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ed46a7 and 6a5fc26.

📒 Files selected for processing (17)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (5 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx (2 hunks)
  • apps/web/app/api/workspaces/[idOrSlug]/billing/invoices/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (4 hunks)
  • apps/web/lib/actions/partners/confirm-payouts.ts (6 hunks)
  • apps/web/lib/cron/verify-vercel.ts (1 hunks)
  • apps/web/lib/partners/constants.ts (2 hunks)
  • apps/web/lib/payment-methods.ts (2 hunks)
  • apps/web/lib/zod/schemas/invoices.ts (1 hunks)
  • apps/web/lib/zod/schemas/workspaces.ts (1 hunks)
  • apps/web/scripts/migrations/backfill-invoice-payment-method.ts (1 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (12 hunks)
  • packages/email/src/templates/partner-payout-confirmed.tsx (4 hunks)
  • packages/email/src/types.ts (1 hunks)
  • packages/prisma/schema/invoice.prisma (1 hunks)
  • packages/prisma/schema/workspace.prisma (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
apps/web/lib/zod/schemas/invoices.ts (1)
packages/email/src/types.ts (1)
  • PaymentMethod (10-10)
apps/web/scripts/migrations/backfill-invoice-payment-method.ts (1)
apps/web/lib/partners/constants.ts (1)
  • STRIPE_PAYMENT_METHOD_NORMALIZATION (103-109)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (3)
apps/web/lib/types.ts (1)
  • InvoiceProps (510-510)
apps/web/lib/payment-methods.ts (1)
  • PAYMENT_METHODS (27-64)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/ui/partners/payout-invoice-sheet.tsx (4)
apps/web/lib/payment-methods.ts (2)
  • PAYMENT_METHODS (27-64)
  • calculatePayoutFeeForMethod (5-25)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
packages/ui/src/combobox/index.tsx (2)
  • Combobox (85-367)
  • ComboboxOption (29-37)
apps/web/lib/actions/partners/confirm-payouts.ts (1)
apps/web/lib/partners/constants.ts (1)
  • STRIPE_PAYMENT_METHOD_NORMALIZATION (103-109)
packages/email/src/templates/partner-payout-confirmed.tsx (1)
packages/email/src/types.ts (1)
  • PaymentMethod (10-10)
⏰ 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 (10)
packages/prisma/schema/workspace.prisma (1)

50-50: LGTM!

The fasterAchPayouts field is well-defined with an appropriate default value. The naming clearly conveys its purpose for enabling faster ACH settlement options.

packages/prisma/schema/invoice.prisma (3)

12-18: LGTM!

The PaymentMethod enum covers all supported payment method types, including the new ach_fast for fast settlement. The enum values are clear and align well with the payment processing domain.


27-27: LGTM!

Making paymentMethod optional (PaymentMethod?) is the right choice for backward compatibility with existing invoices that don't have this field populated.


40-41: LGTM!

The relations to Program and Project are properly configured with explicit field mappings, enabling proper foreign key constraints and data integrity.

apps/web/lib/zod/schemas/invoices.ts (1)

2-2: LGTM!

The schema correctly imports and uses the PaymentMethod enum from Prisma. The field definition with .nullable().optional() properly aligns with the Prisma schema's PaymentMethod? type.

Also applies to: 8-8

apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx (1)

39-39: LGTM!

Good attention to consistency by adding the period to match error message formatting standards.

apps/web/lib/partners/constants.ts (2)

12-12: LGTM!

The FAST_ACH_FEE_CENTS constant is clearly defined with a helpful inline dollar conversion comment ($25). This makes the business logic transparent.


103-109: LGTM!

The STRIPE_PAYMENT_METHOD_NORMALIZATION mapping is well-structured and correctly maps Stripe payment method types to the corresponding PaymentMethod enum values. Note that ach_fast is intentionally not included since it's a custom designation handled separately in the payout flow, not a Stripe payment method type.

apps/web/app/api/workspaces/[idOrSlug]/billing/invoices/route.ts (1)

73-73: LGTM!

Adding paymentMethod to the select clause properly exposes the new field in the API response, making it available to consumers alongside other invoice details.

apps/web/lib/zod/schemas/workspaces.ts (1)

175-175: LGTM!

The fasterAchPayouts field is correctly added to WorkspaceSchemaExtended with the appropriate type and default value, aligning perfectly with the Prisma schema definition.

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

Caution

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

⚠️ Outside diff range comments (1)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1)

173-182: Consider transaction safety for Stripe API failures.

The invoice is updated with finalized amounts before the Stripe payment intent is created (lines 184-203). If the Stripe API call fails, the invoice will contain incorrect amounts, potentially leaving the database in an inconsistent state.

Consider wrapping the invoice update and Stripe API call in a transaction, or implementing rollback logic to handle Stripe API failures gracefully.

🧹 Nitpick comments (1)
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (1)

138-139: Use a shared constant for "ach_fast"
The payment‐method literal "ach_fast" appears in actions, cron jobs, invoice pages, email templates, and types; define PAYMENT_METHOD_ACH_FAST in a central constants module and replace these occurrences.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5fc26 and de436a9.

📒 Files selected for processing (3)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (5 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/ui/partners/payout-invoice-sheet.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
⏰ 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 (7)
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2)

1-1: LGTM!

The import is appropriate for handling Fast ACH fees in the invoice.


150-157: LGTM!

The conditional rendering of the Fast ACH fees line item is implemented correctly. It only displays when applicable and uses consistent formatting.

apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (5)

5-5: LGTM!

The import of FAST_ACH_FEE_CENTS is correct and will be used to apply the flat fee for fast ACH payouts.


143-144: LGTM!

The fee calculation logic is correct:

  • Percentage-based fee is calculated first
  • Fast ACH flat fee is added separately
  • Total correctly includes both fees

The order ensures the fast ACH fee is not subject to the percentage calculation, which is the intended behavior.


189-196: No verification needed: ach_fast is only set when paymentMethod.type === "us_bank_account" via existing guard in confirm-payouts.ts.


269-269: Verify PartnerPayoutConfirmed email content for Fast ACH

Direct Debit emails are only sent when paymentMethod.type is in DIRECT_DEBIT_PAYMENT_METHOD_TYPES (["us_bank_account","acss_debit","sepa_debit"]), which covers Fast ACH (Stripe type "us_bank_account"). Fast ACH payouts will therefore receive the same 4-business-day notice template. Confirm the PartnerPayoutConfirmed email template and update or split it so Fast ACH partners see the correct settlement timeline.


134-141: PaymentMethod and ACH fee logic validated. Field exists and enum includes ach_fast; null defaults to 0. No concurrent modifications alter paymentMethod in this flow.

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

♻️ Duplicate comments (1)
apps/web/ui/partners/payout-invoice-sheet.tsx (1)

231-236: Round the calculated fee to cents before use.

amount is stored in cents while selectedPaymentMethod.fee is a percentage (e.g. 0.015). Multiplying them produces fractional cents, so fee and total can become non-integers; we also forward those raw values in the confirm payload. That leads to inconsistent invoice math and downstream rounding bugs. Compute the processing portion in cents and round it before adding the optional Fast ACH fee.

-    const fastAchFee = selectedPaymentMethod.fastSettlement
-      ? FAST_ACH_FEE_CENTS
-      : 0;
-
-    const fee = amount * selectedPaymentMethod.fee + fastAchFee;
-    const total = amount + fee;
+    const processingFeeCents = Math.round(
+      amount * selectedPaymentMethod.fee,
+    );
+    const fastAchFee = selectedPaymentMethod.fastSettlement
+      ? FAST_ACH_FEE_CENTS
+      : 0;
+
+    const fee = processingFeeCents + fastAchFee;
+    const total = amount + fee;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de436a9 and a98df15.

📒 Files selected for processing (7)
  • apps/web/lib/actions/partners/confirm-payouts.ts (6 hunks)
  • apps/web/lib/zod/schemas/workspaces.ts (1 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (11 hunks)
  • packages/prisma/schema/network.prisma (2 hunks)
  • packages/prisma/schema/partner.prisma (1 hunks)
  • packages/prisma/schema/program.prisma (1 hunks)
  • packages/prisma/schema/workspace.prisma (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/prisma/schema/partner.prisma
  • packages/prisma/schema/program.prisma
  • packages/prisma/schema/network.prisma
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/actions/partners/confirm-payouts.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/ui/partners/payout-invoice-sheet.tsx (4)
apps/web/lib/payment-methods.ts (2)
  • PAYMENT_METHODS (27-64)
  • calculatePayoutFeeForMethod (5-25)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/lib/partners/constants.ts (2)
  • FAST_ACH_FEE_CENTS (12-12)
  • DIRECT_DEBIT_PAYMENT_METHOD_TYPES (58-62)
packages/ui/src/combobox/index.tsx (2)
  • Combobox (85-367)
  • ComboboxOption (29-37)
⏰ 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 (1)
packages/prisma/schema/workspace.prisma (1)

48-48: LGTM! Prisma schema definition is correct.

The non-nullable Boolean @default(false) definition is appropriate for a feature flag. The field is logically placed with other boolean flags and the default value ensures safe migration for existing rows.

Note: The corresponding Zod schema in apps/web/lib/zod/schemas/workspaces.ts:175 should be updated to match this non-nullable definition.

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: 2

♻️ Duplicate comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (2)

131-140: Add rel="noopener noreferrer" to external invoice link.

The anchor opening the invoice PDF uses target="_blank" but lacks security attributes to prevent reverse-tabnabbing and referrer leakage.

Apply this diff:

               <a
                 href={invoice.pdfUrl}
                 target="_blank"
+                rel="noopener noreferrer"
                 className={cn(
                   buttonVariants({ variant: "secondary" }),
                   "flex h-9 items-center justify-center rounded-md border px-3 text-sm",
                 )}
               >
                 <span>View invoice</span>
               </a>

262-271: Add rel="noopener noreferrer" to external invoice link (desktop view).

Same security fix needed for the desktop layout.

Apply this diff:

             <a
               href={invoice.pdfUrl}
               target="_blank"
+              rel="noopener noreferrer"
               className={cn(
                 buttonVariants({ variant: "secondary" }),
                 "flex h-9 items-center justify-center rounded-md border px-3 text-sm",
               )}
             >
               <span>View invoice</span>
             </a>
🧹 Nitpick comments (1)
apps/web/lib/stripe/payment-methods.ts (1)

16-18: Extract magic number to a named constant.

The 0.03 fee surcharge for link/card methods is hard-coded. Consider extracting it to a named constant alongside FAST_ACH_FEE_CENTS for better maintainability and clarity.

Apply this diff:

+export const LINK_CARD_FEE_SURCHARGE = 0.03;
+
 export const calculatePayoutFeeForMethod = ({
   paymentMethod,
   payoutFee,
 }: {
   paymentMethod: Stripe.PaymentMethod.Type;
   payoutFee: number | undefined;
 }) => {
   if (!paymentMethod || payoutFee === undefined || payoutFee === null) {
     return null;
   }

   if (["link", "card"].includes(paymentMethod)) {
-    return payoutFee + 0.03;
+    return payoutFee + LINK_CARD_FEE_SURCHARGE;
   }

   if (DIRECT_DEBIT_PAYMENT_METHOD_TYPES.includes(paymentMethod)) {
     return payoutFee;
   }

   throw new Error(`Unsupported payment method ${paymentMethod}.`);
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a98df15 and 9f742a0.

📒 Files selected for processing (9)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (5 hunks)
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (4 hunks)
  • apps/web/lib/cron/verify-vercel.ts (1 hunks)
  • apps/web/lib/partners/constants.ts (3 hunks)
  • apps/web/lib/stripe/payment-methods.ts (1 hunks)
  • apps/web/lib/zod/schemas/workspaces.ts (1 hunks)
  • apps/web/ui/partners/payout-invoice-sheet.tsx (11 hunks)
  • packages/email/src/templates/partner-payout-confirmed.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/lib/partners/constants.ts
  • apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx
  • packages/email/src/templates/partner-payout-confirmed.tsx
  • apps/web/lib/cron/verify-vercel.ts
  • apps/web/lib/zod/schemas/workspaces.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-18T20:23:38.835Z
Learnt from: TWilson023
PR: dubinc/dub#2538
File: apps/web/ui/partners/overview/blocks/traffic-sources-block.tsx:50-82
Timestamp: 2025-06-18T20:23:38.835Z
Learning: Internal links within the same application that use target="_blank" may not require rel="noopener noreferrer" according to the team's security standards, even though it's generally considered a best practice for any target="_blank" link.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx
🧬 Code graph analysis (4)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (3)
apps/web/lib/types.ts (1)
  • InvoiceProps (510-510)
apps/web/lib/partners/constants.ts (1)
  • INVOICE_PAYMENT_METHODS (70-91)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/lib/stripe/payment-methods.ts (1)
apps/web/lib/partners/constants.ts (1)
  • DIRECT_DEBIT_PAYMENT_METHOD_TYPES (58-62)
apps/web/ui/partners/payout-invoice-sheet.tsx (4)
apps/web/lib/stripe/payment-methods.ts (2)
  • STRIPE_PAYMENT_METHODS (27-58)
  • calculatePayoutFeeForMethod (5-25)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
apps/web/lib/partners/constants.ts (2)
  • FAST_ACH_FEE_CENTS (12-12)
  • DIRECT_DEBIT_PAYMENT_METHOD_TYPES (58-62)
packages/ui/src/combobox/index.tsx (2)
  • Combobox (85-367)
  • ComboboxOption (29-37)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1)
apps/web/lib/partners/constants.ts (1)
  • FAST_ACH_FEE_CENTS (12-12)
🪛 Biome (2.1.2)
apps/web/ui/partners/payout-invoice-sheet.tsx

[error] 642-642: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

⏰ 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 (9)
apps/web/lib/stripe/payment-methods.ts (1)

27-58: LGTM!

The STRIPE_PAYMENT_METHODS constant is well-structured with appropriate metadata for each payment method. The use of Object.freeze ensures immutability.

apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (3)

134-141: LGTM!

The early invoice loading and Fast ACH fee calculation are correctly implemented. The condition checks for invoice.paymentMethod === "ach_fast" which aligns with the normalized payment method values used throughout the codebase.


189-196: LGTM!

The conditional Stripe payment intent configuration correctly sets preferred_settlement_speed based on the invoice payment method, enabling Fast ACH settlements when appropriate.


269-269: LGTM!

The email payload correctly includes the payment method with a sensible fallback to "ach" for backward compatibility.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (1)

104-110: LGTM!

The updated component signature with displayPaymentMethod parameter provides flexible payment method display control. The default value ensures backward compatibility.

apps/web/ui/partners/payout-invoice-sheet.tsx (4)

112-170: LGTM!

The payment method options generation correctly creates separate entries for standard and Fast ACH when enabled. The ID suffix approach (-fast) is properly handled throughout the flow.


217-244: LGTM!

The fee calculation correctly uses Math.round() to handle fractional cents that result from multiplying the payout amount by the fee rate. The Fast ACH fee is properly added as a fixed cost in cents.


250-336: LGTM!

The Combobox integration provides a better UX with rich option rendering, including icons and Fast ACH fee metadata. The implementation properly handles selection state and callbacks.


558-559: LGTM!

The payout confirmation correctly strips the -fast suffix and passes the fastSettlement flag separately, cleanly separating UI representation from the API payload.

@steven-tey
Copy link
Collaborator

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

✅ Actions performed

Full review triggered.

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: 0

🧹 Nitpick comments (1)
apps/web/lib/partners/constants.ts (1)

78-99: Consider adding type definitions for better type safety.

The INVOICE_PAYMENT_METHODS constant is well-structured with Object.freeze for immutability. However, consider defining an explicit type for the payment method keys and structure to improve type safety across the codebase.

Example type definitions:

type InvoicePaymentMethodKey = "card" | "ach" | "ach_fast" | "sepa" | "acss";

type InvoicePaymentMethodInfo = {
  label: string;
  duration: string;
};

export const INVOICE_PAYMENT_METHODS: Readonly<Record<InvoicePaymentMethodKey, InvoicePaymentMethodInfo>> = Object.freeze({
  // ... existing implementation
});

This would provide better autocomplete and catch mismatched keys at compile time.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f742a0 and 2091266.

📒 Files selected for processing (1)
  • apps/web/lib/partners/constants.ts (2 hunks)
⏰ 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 (3)
apps/web/lib/partners/constants.ts (3)

12-12: LGTM! Fast ACH fee constant is well-defined.

The FAST_ACH_FEE_CENTS constant follows the established pattern of other fee constants in this file and clearly represents the $25 fee for fast ACH transfers.


101-105: LGTM! Payout statuses are clearly defined.

The INVOICE_AVAILABLE_PAYOUT_STATUSES array appropriately defines the statuses for available payouts.


70-76: ACH normalization and fast‐ACH override verified.
STRIPE_PAYMENT_METHOD_NORMALIZATION is used for standard ACH, and in confirm-payouts the fastSettlement flag correctly switches to "ach_fast". Downstream consumers—including migrations, API handlers, and the UI—support both "ach" and "ach_fast".

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: 2

♻️ Duplicate comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (2)

131-140: Add rel="noopener noreferrer" to external invoice link (mobile).

The anchor opens an external PDF without security attributes, creating a reverse-tabnabbing risk.

Apply this diff:

               <a
                 href={invoice.pdfUrl}
                 target="_blank"
+                rel="noopener noreferrer"
                 className={cn(
                   buttonVariants({ variant: "secondary" }),
                   "flex h-9 items-center justify-center rounded-md border px-3 text-sm",
                 )}
               >

262-271: Add rel="noopener noreferrer" to external invoice link (desktop).

Same security issue as mobile view.

Apply this diff:

             <a
               href={invoice.pdfUrl}
               target="_blank"
+              rel="noopener noreferrer"
               className={cn(
                 buttonVariants({ variant: "secondary" }),
                 "flex h-9 items-center justify-center rounded-md border px-3 text-sm",
               )}
             >
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2091266 and b722721.

📒 Files selected for processing (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-18T20:23:38.835Z
Learnt from: TWilson023
PR: dubinc/dub#2538
File: apps/web/ui/partners/overview/blocks/traffic-sources-block.tsx:50-82
Timestamp: 2025-06-18T20:23:38.835Z
Learning: Internal links within the same application that use target="_blank" may not require rel="noopener noreferrer" according to the team's security standards, even though it's generally considered a best practice for any target="_blank" link.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx
🧬 Code graph analysis (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/billing/invoices/page-client.tsx (3)
apps/web/lib/types.ts (1)
  • InvoiceProps (510-510)
apps/web/lib/partners/constants.ts (1)
  • INVOICE_PAYMENT_METHODS (78-99)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (5-16)
⏰ 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

@steven-tey steven-tey merged commit 5d295bc into main Oct 7, 2025
9 checks passed
@steven-tey steven-tey deleted the fast-ach branch October 7, 2025 22:33
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

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