Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b5515c5
Add fast settlement option to payout processing
devkiran Oct 2, 2025
3124033
add FastAchPayoutToggle
devkiran Oct 2, 2025
be8324c
Update payout-invoice-sheet.tsx
devkiran Oct 2, 2025
6bd1ce4
Add fast ACH payout support and update schema
devkiran Oct 2, 2025
892f43b
Update payout-invoice-sheet.tsx
devkiran Oct 2, 2025
6b0ec3e
Merge branch 'main' into fast-ach
devkiran Oct 6, 2025
dd25949
Refactor payout processing to remove fastSettlement parameter and nor…
devkiran Oct 6, 2025
70ad535
Add script to backfill invoice payment methods
devkiran Oct 6, 2025
49f42cc
Add payment method display to invoices and centralize config
devkiran Oct 6, 2025
49bf3d8
simplify the display
devkiran Oct 6, 2025
01c0e43
Update partner-payout-confirmed.tsx
devkiran Oct 6, 2025
a607615
Update workspace.prisma
devkiran Oct 6, 2025
099e76f
update invoice with Fast ACH fees
devkiran Oct 6, 2025
79bb706
Merge branch 'main' into fast-ach
devkiran Oct 7, 2025
25a5454
replace payment methods with Combobox
devkiran Oct 7, 2025
25c796b
replace select with combobox for Cutoff Period
devkiran Oct 7, 2025
07c7fd2
fix invoice sheet
devkiran Oct 7, 2025
b9550ff
display the selected icon
devkiran Oct 7, 2025
fa017e7
Add dynamic Fast ACH fee handling for partner payouts
devkiran Oct 7, 2025
5cd71a5
Update process-payouts.ts
devkiran Oct 7, 2025
a34b964
Update payout-invoice-sheet.tsx
devkiran Oct 7, 2025
4eb9948
fix mobile fee
devkiran Oct 7, 2025
18f5576
Add FAST ACH fee to payout total calculation
devkiran Oct 7, 2025
745c227
Update page-client.tsx
devkiran Oct 7, 2025
6a5fc26
Merge branch 'main' into fast-ach
devkiran Oct 7, 2025
de436a9
fix fee calculation
devkiran Oct 7, 2025
f2e047f
Merge branch 'main' into fast-ach
steven-tey Oct 7, 2025
a98df15
fasterAchPayouts → fastDirectDebitPayouts
steven-tey Oct 7, 2025
a17710d
finalize enums / types
steven-tey Oct 7, 2025
9f742a0
address coderabbit feedback
steven-tey Oct 7, 2025
2091266
finalize constants
steven-tey Oct 7, 2025
b722721
fix typo
steven-tey Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { recordAuditLog } from "@/lib/api/audit-logs/record-audit-log";
import { exceededLimitError } from "@/lib/api/errors";
import {
DIRECT_DEBIT_PAYMENT_METHOD_TYPES,
FAST_ACH_FEE_CENTS,
FOREX_MARKUP_RATE,
} from "@/lib/partners/constants";
import {
CUTOFF_PERIOD,
CUTOFF_PERIOD_TYPES,
} from "@/lib/partners/cutoff-period";
import { calculatePayoutFeeForMethod } from "@/lib/payment-methods";
import { stripe } from "@/lib/stripe";
import { createFxQuote } from "@/lib/stripe/create-fx-quote";
import { calculatePayoutFeeForMethod } from "@/lib/stripe/payment-methods";
import { PlanProps } from "@/lib/types";
import { sendBatchEmail } from "@dub/email";
import { resend } from "@dub/email/resend";
Expand Down Expand Up @@ -130,8 +131,16 @@ export async function processPayouts({
`Using payout fee of ${payoutFee} for payment method ${paymentMethod.type}`,
);

let invoice = await prisma.invoice.findUniqueOrThrow({
where: {
id: invoiceId,
},
});

const fastAchFee =
invoice.paymentMethod === "ach_fast" ? FAST_ACH_FEE_CENTS : 0;
const currency = paymentMethodToCurrency[paymentMethod.type] || "usd";
const totalFee = Math.round(payoutAmount * payoutFee);
const totalFee = Math.round(payoutAmount * payoutFee) + fastAchFee;
const total = payoutAmount + totalFee;
let convertedTotal = total;

Expand Down Expand Up @@ -161,7 +170,7 @@ export async function processPayouts({
}

// Update the invoice with the finalized payout amount, fee, and total
const invoice = await prisma.invoice.update({
invoice = await prisma.invoice.update({
where: {
id: invoiceId,
},
Expand All @@ -177,6 +186,14 @@ export async function processPayouts({
customer: workspace.stripeId!,
payment_method_types: [paymentMethod.type],
payment_method: paymentMethod.id,
...(paymentMethod.type === "us_bank_account" && {
payment_method_options: {
us_bank_account: {
preferred_settlement_speed:
invoice.paymentMethod === "ach_fast" ? "fastest" : "standard",
},
},
}),
currency,
confirmation_method: "automatic",
confirm: true,
Expand Down Expand Up @@ -249,6 +266,7 @@ export async function processPayouts({
amount: payout.amount,
startDate: payout.periodStart,
endDate: payout.periodEnd,
paymentMethod: invoice.paymentMethod ?? "ach",
},
}),
})),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FAST_ACH_FEE_CENTS } from "@/lib/partners/constants";
import { stripe } from "@/lib/stripe";
import { prisma } from "@dub/prisma";
import {
Expand Down Expand Up @@ -134,15 +135,34 @@ export async function PartnerPayoutInvoice({
})})`
: "";

const fastAchFee =
invoice.paymentMethod === "ach_fast" ? FAST_ACH_FEE_CENTS : 0;

// guard against invalid invoice amounts
if (invoice.amount === 0) {
throw new Error("Invoice amount cannot be zero");
}
if (invoice.fee < fastAchFee) {
throw new Error("Invoice fee cannot be less than Fast ACH fee");
}

const invoiceSummaryDetails = [
{
label: "Invoice amount",
value: currencyFormatter(invoice.amount / 100),
},
{
label: `Platform fees (${Math.round((invoice.fee / invoice.amount) * 100)}%)`,
value: `${currencyFormatter(invoice.fee / 100)}`,
label: `Platform fees (${Math.round(((invoice.fee - fastAchFee) / invoice.amount) * 100)}%)`,
value: `${currencyFormatter((invoice.fee - fastAchFee) / 100)}`,
},
...(fastAchFee > 0
? [
{
label: "Fast ACH fees",
value: currencyFormatter(fastAchFee / 100),
},
]
: []),
{
label: "Invoice total",
value: `${currencyFormatter(invoice.total / 100)}${nonUsdTransactionDisplay}`,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const GET = withSession(async ({ session, params }) => {
if (!userInWorkspace) {
throw new DubApiError({
code: "unauthorized",
message: "You are not authorized to view this invoice",
message: "You are not authorized to view this invoice.",
});
}

Expand All @@ -61,7 +61,7 @@ export const GET = withSession(async ({ session, params }) => {
});
}

return new Response(pdf, {
return new Response(new Uint8Array(pdf), {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `inline; filename="Invoice-${invoice.number}.pdf"`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const otherInvoices = async ({
total: true,
createdAt: true,
status: true,
paymentMethod: true,
failedReason: true,
},
orderBy: {
Expand Down
Loading