-
Notifications
You must be signed in to change notification settings - Fork 2.8k
External payouts #3033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
External payouts #3033
Changes from all commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
6301979
Refactor payout settings modal to use a sheet component
devkiran df10cc7
reuse the webhook validation logic
devkiran c4a8358
store the payout mode
devkiran ee44a7f
display the payout methods in the settings sheet
devkiran 056d5ae
Update program-payout-methods.tsx
devkiran 5d04f9f
Add ProgramPayoutRouting component for managing payout modes in settings
devkiran e39e190
add empty state
devkiran e13b17e
Update partner-row-item.tsx
devkiran b2186e4
Merge branch 'main' into external-payouts
devkiran 2dfb621
Enhance payout calculations and UI for external payouts
devkiran 2a3fa3d
Refactor payout logic and improve webhook validation messages
devkiran 928c148
Implement external payout processing and webhook integration
devkiran a07cd5e
Update validate-webhook.ts
devkiran d5c525e
Update is-external-payout.ts
devkiran c69bc68
Update confirm-payouts-sheet.tsx
devkiran baa075f
Refactor payout processing and enhance partner data handling
devkiran 5fec019
Update process-payouts.ts
devkiran 030dafe
Update payout-table.tsx
devkiran ac6b743
Merge branch 'main' into external-payouts
devkiran fa34a9e
Merge branch 'main' into external-payouts
devkiran 6eef27f
Refactor partner payout settings to include external payout enrollmen…
devkiran 33b1445
Add partner payout details sheet component and update imports
devkiran 90ff124
Implement payout eligibility filter and add webhook handling for exte…
devkiran 446418f
Update process-payouts.ts
devkiran bfe21fa
Update index.test.ts
devkiran 1843837
fix the icons
devkiran dccd7c8
Update route.ts
devkiran cd58f0d
Update process-payouts.ts
devkiran 2ab3743
add slack template for the new trigger
devkiran 75110c3
Add maxDuration to webhook route, refactor payout variable names, and…
devkiran 3ae5e89
Update confirm-payouts.ts
devkiran 637358c
Merge branch 'main' into external-payouts
devkiran 13a9553
Add payoutMode to program and update email template
marcusljf 1d6c54b
Enhance external payout handling by integrating webhook checks and up…
devkiran 6287804
Removed hybrid
marcusljf 4d45bd0
Merge branch 'external-payouts' of https://github.com/dubinc/dub into…
devkiran a24530a
Refactor payout processing to utilize invoice payoutMode
devkiran 8d5ae29
Refactor payout externality check method
devkiran 532e2fd
Merge branch 'main' into external-payouts
devkiran b9d3ebb
Refactor payout processing to use 'mode' instead of 'payoutMode' for …
devkiran 471ccfd
improve payout mode logic across various components.
devkiran 4687fcb
fix email sending for external payouts
devkiran 057cf81
Refactor payout externality checks to use getEffectivePayoutMode for …
devkiran 6ad5626
Refactor payout transformation logic to consistently use 'mode' prope…
devkiran ba80d55
Enhance invoice visibility logic by restricting invoice links to inte…
devkiran 7a76a9f
Update partner-payout-settings-sheet.tsx
devkiran de04463
Update program-payout-methods.tsx
devkiran 1689c2e
Merge branch 'main' into external-payouts
devkiran 971d53d
Create update-payout-mode-to-internal.ts
devkiran 18adda1
Refactor payout eligibility logic by introducing getEligiblePayouts f…
devkiran 4f53dec
Update invoice.prisma
devkiran c611e30
Update program-payout-methods.tsx
devkiran 038432f
Update confirm-payouts-sheet.tsx
devkiran e6ade74
Update confirm-payouts.ts
devkiran 13de9a3
Update get-webhooks.ts
devkiran e01e537
Refactor payout mode references to use 'payoutMode' instead of 'mode'
devkiran b2aca15
Merge branch 'main' into external-payouts
steven-tey 45bf31f
Update program-payout-settings-sheet.tsx
steven-tey b84aa1a
Merge branch 'main' into external-payouts
steven-tey af6910e
update a bunch of tooltip/UI copywriting
steven-tey b61851f
fix tests
steven-tey 7f74e59
simplify: remove externalPayoutsEnabledAt, rely on payoutMode
steven-tey b6b44ac
Update program-payout-mode-section.tsx
steven-tey 8f63f9d
Improve processExternalPayouts (#3065)
steven-tey 349d2cc
refactor Payout method display
devkiran b047881
Add payoutMode checks in queueExternalPayouts and queueStripePayouts …
devkiran 81100ba
Update partner-payout-settings-sheet.tsx
devkiran 0540ed2
Update queue-external-payouts.ts
devkiran 1e352a5
Update process-payouts.ts
devkiran 7db7816
address coderabbit feedback
steven-tey de0e16a
simplify prisma call
steven-tey b601d74
address coderabbit feedback, finalize copy
steven-tey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
148 changes: 148 additions & 0 deletions
148
apps/web/app/(ee)/api/cron/payouts/charge-succeeded/queue-external-payouts.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| import { queueBatchEmail } from "@/lib/email/queue-batch-email"; | ||
| import { sendWorkspaceWebhook } from "@/lib/webhook/publish"; | ||
| import { payoutWebhookEventSchema } from "@/lib/zod/schemas/payouts"; | ||
| import type PartnerPayoutConfirmed from "@dub/email/templates/partner-payout-confirmed"; | ||
| import { prisma } from "@dub/prisma"; | ||
| import { Invoice } from "@dub/prisma/client"; | ||
|
|
||
| export async function queueExternalPayouts( | ||
| invoice: Pick< | ||
| Invoice, | ||
| "id" | "paymentMethod" | "programId" | "workspaceId" | "payoutMode" | ||
| >, | ||
| ) { | ||
| // All payouts are processed internally, hence no need to queue external payouts | ||
| if (invoice.payoutMode === "internal") { | ||
| console.log(`Invoice ${invoice.id} is paid internally. Skipping...`); | ||
| return; | ||
| } | ||
|
|
||
| // should never happen, but just in case | ||
| if (!invoice.programId) { | ||
| console.log(`Invoice ${invoice.id} has no program ID. Skipping...`); | ||
| return; | ||
| } | ||
|
|
||
| const program = await prisma.program.findUnique({ | ||
| where: { | ||
| id: invoice.programId, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| name: true, | ||
| logo: true, | ||
| supportEmail: true, | ||
| }, | ||
| }); | ||
|
|
||
| // should never happen, but just in case | ||
| if (!program) { | ||
| console.log(`Program not found for invoice ${invoice.id}. Skipping...`); | ||
| return; | ||
| } | ||
|
|
||
| const externalPayouts = await prisma.payout.findMany({ | ||
| where: { | ||
| invoiceId: invoice.id, | ||
| status: "processing", | ||
| mode: "external", | ||
| }, | ||
| include: { | ||
| partner: { | ||
| include: { | ||
| programs: { | ||
| where: { | ||
| programId: program.id, | ||
| }, | ||
| select: { | ||
| tenantId: true, | ||
| status: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (externalPayouts.length === 0) { | ||
| console.log("No external payouts found for invoice", invoice.id); | ||
| return; | ||
| } | ||
|
|
||
| const webhooks = await prisma.webhook.findMany({ | ||
| where: { | ||
| projectId: invoice.workspaceId, | ||
| disabledAt: null, | ||
| triggers: { | ||
| array_contains: ["payout.confirmed"], | ||
| }, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| url: true, | ||
| secret: true, | ||
| }, | ||
| }); | ||
|
|
||
| if (webhooks.length === 0) { | ||
| console.log( | ||
| `No webhooks found for workspace ${invoice.workspaceId} for invoice ${invoice.id}. Skipping...`, | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| for (const payout of externalPayouts) { | ||
| try { | ||
| const data = payoutWebhookEventSchema.parse({ | ||
| ...payout, | ||
| partner: { | ||
| ...payout.partner, | ||
| ...payout.partner.programs[0], | ||
| }, | ||
| }); | ||
|
|
||
| await sendWorkspaceWebhook({ | ||
| workspace: { | ||
| id: invoice.workspaceId, | ||
| webhookEnabled: true, | ||
| }, | ||
| webhooks, | ||
| data, | ||
| trigger: "payout.confirmed", | ||
| }); | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| } | ||
| } | ||
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
devkiran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| await queueBatchEmail<typeof PartnerPayoutConfirmed>( | ||
| externalPayouts | ||
| .filter((payout) => payout.partner.email) | ||
| .map((payout) => ({ | ||
| to: payout.partner.email!, | ||
| subject: "You've got money coming your way!", | ||
| variant: "notifications", | ||
| replyTo: program.supportEmail || "noreply", | ||
| templateName: "PartnerPayoutConfirmed", | ||
| templateProps: { | ||
| email: payout.partner.email!, | ||
| program: { | ||
| id: program.id, | ||
| name: program.name, | ||
| logo: program.logo, | ||
| }, | ||
| payout: { | ||
| id: payout.id, | ||
| amount: payout.amount, | ||
| startDate: payout.periodStart, | ||
| endDate: payout.periodEnd, | ||
| mode: "external", | ||
| paymentMethod: invoice.paymentMethod ?? "ach", | ||
| }, | ||
| }, | ||
| })), | ||
| { | ||
| idempotencyKey: `payout-confirmed-external/${invoice.id}`, | ||
| }, | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.