-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Reduce the payoutsUsage by the invoice amount when the charge failed
#2633
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughA new database update operation has been introduced in the Stripe charge failure webhook handler. After marking an invoice as failed and resetting payouts, the code now decrements the Changes
Sequence Diagram(s)sequenceDiagram
participant Stripe
participant WebhookHandler
participant Database
Stripe->>WebhookHandler: Stripe charge.failed event
WebhookHandler->>Database: Mark invoice as failed
WebhookHandler->>Database: Reset payouts
WebhookHandler->>Database: Decrement project.payoutsUsage by invoice amount
Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: steven-tey
PR: dubinc/dub#0
File: :0-0
Timestamp: 2025-06-25T21:20:59.837Z
Learning: In the Dub codebase, payout limit validation uses a two-stage pattern: server actions perform quick sanity checks (payoutsUsage > payoutsLimit) for immediate user feedback, while the cron job (/cron/payouts) performs authoritative validation (payoutsUsage + payoutAmount > payoutsLimit) with actual calculated amounts before processing. This design provides fast user feedback while ensuring accurate limit enforcement at transaction time.
⏰ 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)
apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts (1)
56-66: Good implementation of payoutsUsage reduction on charge failure.The logic correctly decrements the
payoutsUsagewhen a charge fails, which aligns with the PR objectives. This ensures accurate usage tracking for payout limits.
| // Reduce the payoutsUsage by the invoice amount since the charge failed | ||
| await prisma.project.update({ | ||
| where: { | ||
| id: invoice.workspaceId, | ||
| }, | ||
| data: { | ||
| payoutsUsage: { | ||
| decrement: invoice.amount, | ||
| }, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider wrapping database operations in a transaction for atomicity.
The current implementation performs three separate database operations sequentially. If the payoutsUsage decrement fails, the invoice and payouts would already be updated, leading to an inconsistent state.
Consider wrapping these operations in a transaction:
+ await prisma.$transaction(async (tx) => {
+ // Mark the invoice as failed
+ const invoice = await tx.invoice.update({
+ where: {
+ id: invoiceId,
+ },
+ data: {
+ status: "failed",
+ failedReason,
+ },
+ });
+
+ // Mark the payouts as pending again
+ await tx.payout.updateMany({
+ where: {
+ invoiceId,
+ },
+ data: {
+ status: "pending",
+ userId: null,
+ invoiceId: null,
+ },
+ });
+
+ // Reduce the payoutsUsage by the invoice amount since the charge failed
+ await tx.project.update({
+ where: {
+ id: invoice.workspaceId,
+ },
+ data: {
+ payoutsUsage: {
+ decrement: invoice.amount,
+ },
+ },
+ });
+ });Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts between lines 56 and
66, the database update for decrementing payoutsUsage should be wrapped in a
transaction along with the other related database operations to ensure
atomicity. Refactor the code to use a Prisma transaction that includes all the
database updates so that if any operation fails, none of the changes are
committed, preventing inconsistent state.
💡 Verification agent
🧩 Analysis chain
Verify that payoutsUsage cannot go negative and validate usage tracking consistency.
The decrement operation could potentially make payoutsUsage negative if there's a data inconsistency (e.g., if the original payout wasn't properly tracked in usage). This could affect payout limit validation.
Please verify the usage tracking flow and ensure proper safeguards:
🏁 Script executed:
#!/bin/bash
# Description: Search for payoutsUsage increment operations to understand the usage tracking flow
# Expected: Find where payoutsUsage is incremented to ensure consistent tracking
echo "=== Searching for payoutsUsage increment operations ==="
rg -A 5 -B 5 "payoutsUsage.*increment"
echo -e "\n=== Searching for payoutsUsage update operations ==="
rg -A 5 -B 5 "payoutsUsage.*\{"
echo -e "\n=== Searching for payout processing logic ==="
rg -A 10 -B 5 "status.*completed.*payout"Length of output: 3958
Prevent Negative payoutsUsage in charge-failed webhook
Guard against underflow when decrementing payoutsUsage—ensure it never drops below zero by first reading the current value and clamping the result.
Locations to update:
- apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts
Suggested change:
--- a/apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts
+++ b/apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts
@@ /* before decrement */
- // Reduce the payoutsUsage by the invoice amount since the charge failed
- await prisma.project.update({
- where: { id: invoice.workspaceId },
- data: {
- payoutsUsage: { decrement: invoice.amount },
- },
- });
+ // Safely reduce payoutsUsage, clamping at zero to avoid negative values
+ const project = await prisma.project.findUnique({
+ where: { id: invoice.workspaceId },
+ select: { payoutsUsage: true },
+ });
+ if (project) {
+ const newUsage = Math.max(project.payoutsUsage - invoice.amount, 0);
+ await prisma.project.update({
+ where: { id: invoice.workspaceId },
+ data: { payoutsUsage: newUsage },
+ });
+ }This preserves the two-stage design (immediate sanity vs. authoritative cron check) and prevents negative balances.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Reduce the payoutsUsage by the invoice amount since the charge failed | |
| await prisma.project.update({ | |
| where: { | |
| id: invoice.workspaceId, | |
| }, | |
| data: { | |
| payoutsUsage: { | |
| decrement: invoice.amount, | |
| }, | |
| }, | |
| }); | |
| // Safely reduce payoutsUsage, clamping at zero to avoid negative values | |
| const project = await prisma.project.findUnique({ | |
| where: { id: invoice.workspaceId }, | |
| select: { payoutsUsage: true }, | |
| }); | |
| if (project) { | |
| const newUsage = Math.max(project.payoutsUsage - invoice.amount, 0); | |
| await prisma.project.update({ | |
| where: { id: invoice.workspaceId }, | |
| data: { payoutsUsage: newUsage }, | |
| }); | |
| } |
🤖 Prompt for AI Agents
In apps/web/app/(ee)/api/stripe/webhook/charge-failed.ts around lines 56 to 66,
the code decrements payoutsUsage by invoice.amount without checking if it will
go below zero. To fix this, first query the current payoutsUsage value for the
project, then calculate the new value by subtracting invoice.amount but clamp it
to a minimum of zero. Finally, update payoutsUsage with this clamped value to
prevent negative balances.
Summary by CodeRabbit