Tremendous - #3952
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces Tremendous gift card payouts as a new partner payout method. The change spans database schema extensions, API configuration, OTP email verification endpoints, referrals embed UI components with settings tab, payout execution and campaign provisioning, cron-based job queuing, eligibility rules with payout caps, and payout-method surfaces across the admin and partner-facing dashboards. ChangesTremendous Payout Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai final review, any bugs/breaking changes? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
echo "=== withCron implementation ==="
rg "withCron" --type ts -n -C5 apps/web/lib 2>/dev/null | head -80
echo "=== old disposable-emails route for reference ==="
git diff HEAD~1 -- "apps/web/app/(ee)/api/cron/disposable-emails/route.ts" 2>/dev/null | head -100
echo "=== current disposable-emails route ==="
cat "apps/web/app/(ee)/api/cron/disposable-emails/route.ts" 2>/dev/null
echo "=== forceWithdrawalAction before change ==="
git log --oneline -5 -- "apps/web/lib/actions/partners/force-withdrawal.ts" 2>/dev/null
echo "=== current force-withdrawal ==="
cat "apps/web/lib/actions/partners/force-withdrawal.ts" 2>/dev/null
echo "=== check all callers of recomputePartnerPayoutState for tremendousEmail ==="
rg "recomputePartnerPayoutState" --type ts -n -B5 -A10 | head -200
echo "=== verify-otp P2025 handling ==="
cat "apps/web/app/(ee)/api/embed/referrals/tremendous/verify-otp/route.ts" 2>/dev/null
echo "=== send-tremendous-payouts cast ==="
rg "CreateOrder200Response" --type ts -n -C5
echo "=== tremendousEnv configuration ==="
cat "apps/web/lib/tremendous/configuration.ts" 2>/dev/null
echo "=== check if any callers of recomputePartnerPayoutState miss tremendousEmail ==="
rg "recomputePartnerPayoutState" --type ts -n -lLength of output: 34648 🏁 Script executed: #!/bin/bash
echo "=== withCron full implementation ==="
cat "apps/web/lib/cron/with-cron.ts" 2>/dev/null
echo "=== set-default-payout-method.ts full ==="
cat "apps/web/lib/actions/partners/set-default-payout-method.ts" 2>/dev/null
echo "=== forceWithdrawal - check for sendTremendousPayouts ==="
rg "sendTremendousPayouts" --type ts -n -C5
echo "=== paypal callback full partner select ==="
cat "apps/web/app/(ee)/api/paypal/callback/route.ts" 2>/dev/null | head -100
echo "=== tremendousEmail in set-default-payout-method ==="
rg "tremendousEmail" apps/web/lib/actions/partners --type ts -n -C3
echo "=== the authPartnerActionClient ctx partner select ==="
rg "authPartnerActionClient" --type ts -n -C3 apps/web/lib/auth | head -60
echo "=== partnerUserMiddleware or partner select in safe-action ==="
fd "safe-action.ts" apps/web/lib --exec cat {}Length of output: 14997
✅ Previously flagged items that are already handled
🔴 Real bug:
|
| Model | New field |
|---|---|
Partner |
tremendousEmail String? @unique`` |
Program |
tremendousCampaignId String? |
Payout |
tremendousOrderId String? |
Ensure the migration is applied before the new code is deployed.
TL;DR: The force-withdrawal silent no-op is the only concrete functional bug. The unsafe API cast is a latent crash risk. Everything else previously flagged is already handled correctly.
|
@coderabbitai addressed your feedback, thank you! Final review please 🙏 |
|
(ミ・ω・ミ) ✅ Action performedFull review finished. |
|
@coderabbitai also, any breaking changes to existing non-Tremendous/giftcard payout flows? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
echo "=== recomputePartnerPayoutState - full implementation ==="
cat "apps/web/lib/payouts/recompute-partner-payout-state.ts" 2>/dev/null
echo "=== get-partner-payout-methods.ts - full ==="
cat "apps/web/lib/payouts/get-partner-payout-methods.ts" 2>/dev/null
echo "=== payout-eligibility-filter.ts ==="
cat "apps/web/lib/api/payouts/payout-eligibility-filter.ts" 2>/dev/null
echo "=== charge-succeeded route ==="
cat "apps/web/app/(ee)/api/cron/payouts/charge-succeeded/route.ts" 2>/dev/null
echo "=== force-withdrawal.ts full ==="
cat "apps/web/lib/actions/partners/force-withdrawal.ts" 2>/dev/null
echo "=== ALL callers of recomputePartnerPayoutState - check selects ==="
rg "recomputePartnerPayoutState" --type ts -n -l
echo "=== recipient-account-closed.ts ==="
cat "apps/web/app/(ee)/api/stripe/connect/v2/webhook/recipient-account-closed.ts" 2>/dev/null
echo "=== recipient-configuration-updated.ts ==="
cat "apps/web/app/(ee)/api/stripe/connect/v2/webhook/recipient-configuration-updated.ts" 2>/dev/null
echo "=== account-application-deauthorized.ts ==="
cat "apps/web/app/(ee)/api/stripe/connect/webhook/account-application-deauthorized.ts" 2>/dev/null
echo "=== account-updated.ts ==="
cat "apps/web/app/(ee)/api/stripe/connect/webhook/account-updated.ts" 2>/dev/null
echo "=== partner onboarding payouts page ==="
cat "apps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/payouts/page.tsx" 2>/dev/null
echo "=== set-default-payout-method ==="
cat "apps/web/lib/actions/partners/set-default-payout-method.ts" 2>/dev/null
echo "=== all other places that call recomputePartnerPayoutState ==="
rg "recomputePartnerPayoutState" --type ts -n -A5 -B10
echo "=== queue-tremendous-payouts - qstash batch errors handling ==="
cat "apps/web/app/(ee)/api/cron/payouts/charge-succeeded/queue-tremendous-payouts.ts" 2>/dev/null
echo "=== any calls to forceWithdrawal ==="
rg "forceWithdrawal" --type ts -n -C5 apps/web 2>/dev/null | head -100Length of output: 66085
Everything looks good. Existing payout flows are fully isolated from the Tremendous integration. 🐇✨ |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (4)
apps/web/app/(ee)/api/embed/referrals/tremendous/verify-otp/route.ts (1)
188-197: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winNarrow Prisma error before reading
.code.The catch block accesses
error.codewithout first verifying the error type. While the project's tsconfig may not enforce strict catch variable typing, the established codebase pattern is to narrow withinstanceof Prisma.PrismaClientKnownRequestErrorbefore accessing.code. This prevents potential runtime issues if a different error type is thrown.🔧 Proposed fix
Add the import near existing Prisma imports:
import { prisma } from "`@dub/prisma`"; +import { Prisma } from "`@dub/prisma/client`";Update the catch block:
- } catch (error) { - if (error.code === "P2025") { + } catch (error) { + if ( + error instanceof Prisma.PrismaClientKnownRequestError && + error.code === "P2025" + ) { throw new DubApiError({ code: "bad_request", message: "You already have a payout method connected.", }); } throw error; }🤖 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/embed/referrals/tremendous/verify-otp/route.ts around lines 188 - 197, The catch block in route.ts reads error.code without narrowing the error; import Prisma from '`@prisma/client`' (near the other Prisma imports) and change the handler so it first checks error instanceof Prisma.PrismaClientKnownRequestError before testing error.code === "P2025", and only then throw the DubApiError (same payload), otherwise rethrow the original error.apps/web/app/(ee)/api/cron/disposable-emails/route.ts (1)
30-41: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winValidate HTTP response status before parsing body.
Promise.allSettledmarks a fetch as "fulfilled" even when the HTTP response is 4xx/5xx (fetch only rejects on network failures). The current code parsesres.value.text()without checkingres.value.ok, which could sync error page content into Redis if the remote server returns an error status.🛡️ Proposed fix
const disposableDomains = - disposableRes.status === "fulfilled" + disposableRes.status === "fulfilled" && disposableRes.value.ok ? (await disposableRes.value.text()).split("\n").filter(Boolean) : []; const tremendousDomains = - tremendousRes.status === "fulfilled" + tremendousRes.status === "fulfilled" && tremendousRes.value.ok ? (await tremendousRes.value.text()) .split("\n") .map((d) => d.trim().toLowerCase()) .filter(Boolean) : [];Optionally, add warnings for non-ok responses:
+ if (disposableRes.status === "fulfilled" && !disposableRes.value.ok) { + console.warn( + `Disposable domains fetch returned HTTP ${disposableRes.value.status}: ${disposableRes.value.statusText}`, + ); + } + + if (tremendousRes.status === "fulfilled" && !tremendousRes.value.ok) { + console.warn( + `Tremendous prohibited domains fetch returned HTTP ${tremendousRes.value.status}: ${tremendousRes.value.statusText}`, + ); + }🤖 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/disposable-emails/route.ts around lines 30 - 41, The code parses response bodies from the Promise.allSettled results (disposableRes and tremendousRes) without checking HTTP status, so replace direct awaits of res.value.text() with a guard that verifies res.value.ok first (for both disposableRes and tremendousRes), skip or return an empty array when !res.value.ok, and optionally log a warning including the status and URL; ensure you still call .text() only after confirming res.value.ok to avoid storing error pages in Redis.apps/web/app/(ee)/app.dub.co/embed/referrals/settings.tsx (1)
40-55: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winMake the disabled-tooltip trigger keyboard focusable.
At Line 46, the tooltip child is a plain
div, so keyboard users can’t focus it to discover why the action is unavailable.🤖 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)/app.dub.co/embed/referrals/settings.tsx around lines 40 - 55, The tooltip content wrapper is a plain div so keyboard users can’t focus it; update the Tooltip child (the element rendering {text} inside the disabledTooltip branch) to be keyboard-focusable by replacing the div or adding attributes: give it tabIndex={0}, role="button" and aria-disabled="true" (keep existing className via cn and the same visual styles), so keyboard users can tab to the disabled control and discover the tooltip; ensure you still render {text} and preserve className, cn, and the surrounding Tooltip props.apps/web/lib/tremendous/send-tremendous-payouts.ts (1)
171-187: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winAdd runtime guard for
data.orderbefore destructuring.The cast
data as CreateOrder200Responserelies on an unchecked assertion. If the API returns an unexpected response, accessingorder.statusororder.rewardswill throw aTypeError. Add a guard to fail gracefully with a clear error message.This echoes the prior review comment on lines 171-187 which remains unaddressed.
🤖 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/tremendous/send-tremendous-payouts.ts` around lines 171 - 187, Add a runtime guard that verifies data.order exists before destructuring: don't directly do const { order } = data as CreateOrder200Response; instead check if data && (data as CreateOrder200Response).order (or use 'if (!data || !("order" in data) || !(data as any).order)') and if missing call console.error with a clear message and await markPayoutsAsProcessed(currentInvoicePayouts) then return; keep the existing flow (redeemUrl, status checks) intact and update references to use the guarded order variable (order, reward, redeemUrl) to avoid TypeError when the API returns an unexpected shape.
🧹 Nitpick comments (3)
packages/prisma/schema/partner.prisma (1)
73-73: 🚀 Performance & Scalability | ⚡ Quick winRemove the redundant index on
tremendousEmail.Line 73 already defines
tremendousEmailas@unique, which creates an index. Keeping Line 122 (@@index(tremendousEmail)) duplicates indexing and adds unnecessary write/index maintenance overhead.Also applies to: 122-122
🤖 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 `@packages/prisma/schema/partner.prisma` at line 73, Remove the redundant explicit index for tremendousEmail: since the tremendousEmail field is already declared with `@unique` in the model (tremendousEmail String? `@unique`), delete the separate model-level index declaration @@index(tremendousEmail) so the unique constraint remains the sole index and avoids duplicate indexing overhead.apps/web/lib/payouts/recompute-partner-payout-state.ts (1)
15-18: 📐 Maintainability & Code Quality | ⚡ Quick winUpdate the priority-order doc comment to include Tremendous.
The comment still says fallback order is
stablecoin > connect > paypal, but runtime priority now also includestremendous. Keeping this stale makes future debugging/reviews harder.🤖 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/payouts/recompute-partner-payout-state.ts` around lines 15 - 18, Update the stale doc comment in recompute-partner-payout-state (the function/module that "Computes payoutsEnabledAt and defaultPayoutMethod") to reflect the current runtime fallback priority by adding "tremendous" to the list; change the fallback order text from "stablecoin > connect > paypal" to "stablecoin > connect > tremendous > paypal" so the comment matches actual behavior. Ensure the updated comment sits above the recomputePartnerPayoutState implementation and preserves the existing phrasing about preserving the partner's existing default when still active.apps/web/lib/tremendous/send-tremendous-payouts.ts (1)
251-269: 🩺 Stability & Availability | 💤 Low valueConsider logging
Promise.allSettledrejections.
waitUntil(Promise.allSettled([...]))silently discards any rejected results fromtrackCommissionStatusUpdatesByProgramorenqueueBatchJobs. WhileenqueueBatchJobslogs internally before throwing, rejected promises won't surface to callers. Consider inspecting the settled results and logging failures.Note: This echoes the prior review comment about
waitUntilswallowing rejections.🤖 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/tremendous/send-tremendous-payouts.ts` around lines 251 - 269, The call to waitUntil(Promise.allSettled([...])) swallows rejected outcomes from trackCommissionStatusUpdatesByProgram and enqueueBatchJobs; update the code to await Promise.allSettled, inspect the returned results array, log any failures (including the reason and which task failed) for traceability, and if any settled result is a rejection rethrow or return a rejected error so waitUntil sees the failure; specifically modify the block using Promise.allSettled with trackCommissionStatusUpdatesByProgram and enqueueBatchJobs to iterate results, log details for result.status === "rejected", and throw a combined Error (or the first rejection) to surface the error.
🤖 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/app/`(ee)/app.dub.co/embed/referrals/page-client.tsx:
- Around line 201-210: The current country eligibility check (computed by
isTremendousCountrySupported and used by showSettingsTab) incorrectly treats
partner.country === null as unsupported; change the predicate from
Boolean(partner.country &&
TREMENDOUS_SUPPORTED_COUNTRIES.includes(partner.country)) to treat null as
allowed by using (!partner.country ||
TREMENDOUS_SUPPORTED_COUNTRIES.includes(partner.country)). Update the same logic
in both page-client.tsx (isTremendousCountrySupported / showSettingsTab) and
quickstart.tsx so the OTP-permitted null-country cases are allowed while still
excluding explicitly unsupported countries.
In `@apps/web/lib/tremendous/create-tremendous-campaign.ts`:
- Line 27: The code is unsafely casting Tremendous responses (e.g., data as
CreateCampaign200Response in create-tremendous-campaign.ts and data as
CreateOrder200Response in send-tremendous-payouts.ts) and then accessing nested
props like campaign.id, order.status, and order.rewards; add runtime validation
before accessing these fields: check that data is an object, that data.campaign
or data.order exists and is an object, and that required fields (id, status,
rewards) have the expected types/structures; if validation fails, return or
throw a clear error (or handle the failure path) instead of proceeding. Use
lightweight inline guards (typeof/Array.isArray/null checks) or a schema
validator (zod/io-ts) to validate the response shape and replace the unsafe
casts in the functions that process the Tremendous API response.
- Around line 27-36: Before updating the DB ensure the API response actually
contains a campaign id: validate that data (the CreateCampaign200Response) has a
non-null campaign object with a defined id before calling prisma.program.update;
if missing, throw or handle the error path (e.g., log and abort) instead of
writing null/undefined to tremendousCampaignId. Locate the use of data as
CreateCampaign200Response and the prisma.program.update call (referenced by
program.id and tremendousCampaignId) and add a runtime guard that checks
data?.campaign?.id (or equivalent) and only calls prisma.program.update when it
is present, otherwise handle the failure case.
In `@apps/web/lib/tremendous/send-tremendous-payouts.ts`:
- Around line 240-248: The error log inside the catch of sendTremendousPayouts
incorrectly references `[createStripeTransfer]`; update the log message in the
catch block that calls log(...) to reference `[sendTremendousPayouts]` (or a
similarly accurate tag) and keep the existing context including
payoutIds.join(", ") and error.message so the commission-update failure is
correctly attributed to sendTremendousPayouts.
---
Duplicate comments:
In `@apps/web/app/`(ee)/api/cron/disposable-emails/route.ts:
- Around line 30-41: The code parses response bodies from the Promise.allSettled
results (disposableRes and tremendousRes) without checking HTTP status, so
replace direct awaits of res.value.text() with a guard that verifies
res.value.ok first (for both disposableRes and tremendousRes), skip or return an
empty array when !res.value.ok, and optionally log a warning including the
status and URL; ensure you still call .text() only after confirming res.value.ok
to avoid storing error pages in Redis.
In `@apps/web/app/`(ee)/api/embed/referrals/tremendous/verify-otp/route.ts:
- Around line 188-197: The catch block in route.ts reads error.code without
narrowing the error; import Prisma from '`@prisma/client`' (near the other Prisma
imports) and change the handler so it first checks error instanceof
Prisma.PrismaClientKnownRequestError before testing error.code === "P2025", and
only then throw the DubApiError (same payload), otherwise rethrow the original
error.
In `@apps/web/app/`(ee)/app.dub.co/embed/referrals/settings.tsx:
- Around line 40-55: The tooltip content wrapper is a plain div so keyboard
users can’t focus it; update the Tooltip child (the element rendering {text}
inside the disabledTooltip branch) to be keyboard-focusable by replacing the div
or adding attributes: give it tabIndex={0}, role="button" and
aria-disabled="true" (keep existing className via cn and the same visual
styles), so keyboard users can tab to the disabled control and discover the
tooltip; ensure you still render {text} and preserve className, cn, and the
surrounding Tooltip props.
In `@apps/web/lib/tremendous/send-tremendous-payouts.ts`:
- Around line 171-187: Add a runtime guard that verifies data.order exists
before destructuring: don't directly do const { order } = data as
CreateOrder200Response; instead check if data && (data as
CreateOrder200Response).order (or use 'if (!data || !("order" in data) || !(data
as any).order)') and if missing call console.error with a clear message and
await markPayoutsAsProcessed(currentInvoicePayouts) then return; keep the
existing flow (redeemUrl, status checks) intact and update references to use the
guarded order variable (order, reward, redeemUrl) to avoid TypeError when the
API returns an unexpected shape.
---
Nitpick comments:
In `@apps/web/lib/payouts/recompute-partner-payout-state.ts`:
- Around line 15-18: Update the stale doc comment in
recompute-partner-payout-state (the function/module that "Computes
payoutsEnabledAt and defaultPayoutMethod") to reflect the current runtime
fallback priority by adding "tremendous" to the list; change the fallback order
text from "stablecoin > connect > paypal" to "stablecoin > connect > tremendous
> paypal" so the comment matches actual behavior. Ensure the updated comment
sits above the recomputePartnerPayoutState implementation and preserves the
existing phrasing about preserving the partner's existing default when still
active.
In `@apps/web/lib/tremendous/send-tremendous-payouts.ts`:
- Around line 251-269: The call to waitUntil(Promise.allSettled([...])) swallows
rejected outcomes from trackCommissionStatusUpdatesByProgram and
enqueueBatchJobs; update the code to await Promise.allSettled, inspect the
returned results array, log any failures (including the reason and which task
failed) for traceability, and if any settled result is a rejection rethrow or
return a rejected error so waitUntil sees the failure; specifically modify the
block using Promise.allSettled with trackCommissionStatusUpdatesByProgram and
enqueueBatchJobs to iterate results, log details for result.status ===
"rejected", and throw a combined Error (or the first rejection) to surface the
error.
In `@packages/prisma/schema/partner.prisma`:
- Line 73: Remove the redundant explicit index for tremendousEmail: since the
tremendousEmail field is already declared with `@unique` in the model
(tremendousEmail String? `@unique`), delete the separate model-level index
declaration @@index(tremendousEmail) so the unique constraint remains the sole
index and avoids duplicate indexing overhead.
🪄 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: 11074f23-4789-45af-a1d5-a17c5510699a
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (45)
apps/web/.env.exampleapps/web/app/(ee)/api/cron/disposable-emails/route.tsapps/web/app/(ee)/api/cron/payouts/charge-succeeded/queue-tremendous-payouts.tsapps/web/app/(ee)/api/cron/payouts/charge-succeeded/route.tsapps/web/app/(ee)/api/cron/payouts/send-tremendous-payout/route.tsapps/web/app/(ee)/api/embed/referrals/tremendous/send-otp/route.tsapps/web/app/(ee)/api/embed/referrals/tremendous/verify-otp/route.tsapps/web/app/(ee)/api/stripe/connect/v2/webhook/recipient-account-closed.tsapps/web/app/(ee)/api/stripe/connect/v2/webhook/recipient-configuration-updated.tsapps/web/app/(ee)/api/stripe/connect/webhook/account-application-deauthorized.tsapps/web/app/(ee)/api/stripe/connect/webhook/account-updated.tsapps/web/app/(ee)/app.dub.co/embed/referrals/get-referrals-embed-data.tsapps/web/app/(ee)/app.dub.co/embed/referrals/page-client.tsxapps/web/app/(ee)/app.dub.co/embed/referrals/quickstart.tsxapps/web/app/(ee)/app.dub.co/embed/referrals/settings.tsxapps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-details-sheet.tsxapps/web/app/(ee)/partners.dub.co/(onboarding)/onboarding/payouts/page.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/webhooks/[webhookId]/page-client.tsxapps/web/lib/actions/partners/force-withdrawal.tsapps/web/lib/api/payouts/payout-eligibility-filter.tsapps/web/lib/constants/payouts-supported-countries.tsapps/web/lib/partners/create-stripe-transfer.tsapps/web/lib/payouts/get-partner-payout-methods.tsapps/web/lib/payouts/recompute-partner-payout-state.tsapps/web/lib/tremendous/configuration.tsapps/web/lib/tremendous/constants.tsapps/web/lib/tremendous/create-tremendous-campaign.tsapps/web/lib/tremendous/send-tremendous-payouts.tsapps/web/lib/types.tsapps/web/lib/zod/schemas/auth.tsapps/web/package.jsonapps/web/playwright/workspaces/billing-trial.spec.tsapps/web/ui/partners/payout-status-descriptions.tsapps/web/ui/partners/payouts/payout-method-config.tsapps/web/ui/partners/payouts/payout-method-dropdown.tsxpackages/email/src/templates/partner-payout-confirmed.tsxpackages/email/src/templates/partner-tremendous-payout.tsxpackages/email/src/templates/partner-tremendous-verify-email.tsxpackages/email/src/types.tspackages/prisma/schema/partner.prismapackages/prisma/schema/payout.prismapackages/prisma/schema/program.prismapackages/utils/src/constants/index.tspackages/utils/src/constants/tremendous-supported-countries.ts
Summary by CodeRabbit
Release Notes