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
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
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export default function UserInfo({ data }: { data: UserInfoProps }) {
</span>
<span className="text-neutral-500">
{item.isCurrency
? `$${nFormatter(program[item.id], { full: true })}`
? `$${nFormatter(program[item.id] / 100, { full: true })}`
: nFormatter(program[item.id], { full: true })}
</span>
</div>
Expand Down
63 changes: 34 additions & 29 deletions apps/web/app/(ee)/api/cron/payouts/balance-available/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { handleAndReturnErrorResponse } from "@/lib/api/errors";
import { qstash } from "@/lib/cron";
import { verifyQstashSignature } from "@/lib/cron/verify-qstash";
import { stripe } from "@/lib/stripe";
import { sendEmail } from "@dub/email";
import PartnerPayoutWithdrawalInitiated from "@dub/email/templates/partner-payout-withdrawal-initiated";
import { prisma } from "@dub/prisma";
import { currencyFormatter, formatDate, log } from "@dub/utils";
import {
APP_DOMAIN_WITH_NGROK,
currencyFormatter,
formatDate,
log,
} from "@dub/utils";
import { z } from "zod";
import { logAndRespond } from "../../utils";
export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -44,43 +50,42 @@ export async function POST(req: Request) {
stripeAccount,
});

// Check if there's any available balance
if (balance.available.length === 0 || balance.available[0].amount === 0) {
if (balance.available.length === 0) {
// should never happen, but just in case
return logAndRespond(
`No available balance found for partner ${partner.email} (${stripeAccount}). Skipping...`,
`Partner ${partner.email} (${stripeAccount}) has no available balance. Skipping...`,
{
logLevel: "error",
},
);
}

const { amount, currency } = balance.available[0];
let { amount: availableBalance, currency } = balance.available[0];

const { data: stripePayouts } = await stripe.payouts.list(
{
status: "pending",
},
{
stripeAccount,
},
);

let availableBalance = amount;

// Subtract the pending/in-transit payouts from the available balance
if (stripePayouts.length > 0) {
const pendingOrInTransitPayouts = stripePayouts.filter(
({ status }) => status === "pending" || status === "in_transit",
);
// if available balance is 0, check if there's any pending balance
if (availableBalance === 0) {
const pendingBalance = balance.pending[0].amount;

const alreadyPaidOutAmount = pendingOrInTransitPayouts.reduce(
(acc, payout) => acc + payout.amount,
0,
);
// if there's a pending balance, schedule another check in 1 hour
if (pendingBalance > 0) {
const res = await qstash.publishJSON({
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/payouts/balance-available`,
delay: 60 * 60, // check again in 1 hour
body: {
stripeAccount,
},
});
console.log(
`Scheduled another check for partner ${partner.email} (${stripeAccount}) in 1 hour: ${res.messageId}`,
);

availableBalance = availableBalance - alreadyPaidOutAmount;
}
return logAndRespond(
`Pending balance found for partner ${partner.email} (${stripeAccount}): ${currencyFormatter(pendingBalance / 100, { currency: "USD" })}. Scheduling another check in 1 hour...`,
);
}

if (availableBalance <= 0) {
return logAndRespond(
`The available balance (${currencyFormatter(availableBalance / 100, { currency })}) for partner ${partner.email} (${stripeAccount}) is less than or equal to 0 after subtracting pending payouts. Skipping...`,
`Partner ${partner.email} (${stripeAccount})'s available balance is 0. Skipping...`,
);
}

Expand Down