fix(billing): add idempotency to billing#4157
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Extends the idempotency service with a Reviewed by Cursor Bugbot for commit 93311f2. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR adds an optional
Confidence Score: 3/5Not safe to merge as-is — the unconditional claim release in the error handler can cause the double-billing this PR is designed to prevent. One P1 logic bug: apps/sim/app/api/billing/update-cost/route.ts — error handler release logic (lines 176–184) Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Copilot
participant R as /api/billing/update-cost
participant I as IdempotencyService
participant DB as recordUsage / DB
participant S as Stripe (checkAndBillOverage)
C->>R: POST idempotencyKey abc
R->>I: atomicallyClaim(update-cost, abc)
I-->>R: claimed true
R->>DB: recordUsage(...)
DB-->>R: ok
R->>S: checkAndBillOverageThreshold(userId)
alt Success
S-->>R: ok
R-->>C: 200 OK
end
alt Stripe throws error
S-->>R: throws
R->>I: release(key) key deleted even though recordUsage succeeded
R-->>C: 500 Error
C->>R: POST retry same idempotencyKey
R->>I: atomicallyClaim(update-cost, abc)
I-->>R: claimed true key was deleted
R->>DB: recordUsage(...) double-billing
end
alt Duplicate within TTL
C->>R: POST retry same idempotencyKey
R->>I: atomicallyClaim(update-cost, abc)
I-->>R: claimed false
R-->>C: 409 Conflict
end
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Releasing idempotency claim after billing committed risks double-billing
- Added a
billingCommittedguard so idempotency claims are released only when billing failed beforerecordUsagecommitted.
- Added a
Or push these changes by commenting:
@cursor push 31f2524bbc
Preview (31f2524bbc)
diff --git a/apps/sim/app/api/billing/update-cost/route.ts b/apps/sim/app/api/billing/update-cost/route.ts
--- a/apps/sim/app/api/billing/update-cost/route.ts
+++ b/apps/sim/app/api/billing/update-cost/route.ts
@@ -31,6 +31,7 @@
const requestId = generateRequestId()
const startTime = Date.now()
let claim: AtomicClaimResult | null = null
+ let billingCommitted = false
try {
logger.info(`[${requestId}] Update cost request started`)
@@ -137,6 +138,7 @@
],
additionalStats,
})
+ billingCommitted = true
logger.info(`[${requestId}] Recorded usage`, {
userId,
@@ -173,7 +175,7 @@
duration,
})
- if (claim?.claimed) {
+ if (claim?.claimed && !billingCommitted) {
await billingIdempotency
.release(claim.normalizedKey, claim.storageMethod)
.catch((releaseErr) => {This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 93311f2. Configure here.


Summary
Our usage log has external dependencies to stripe. This can hang, causing copilot to retry and double-bill.
Added a optional idempotency key to skip billing if a request is duplicated.
Type of Change
Testing
Checklist
Screenshots/Videos