Repository navigation
fix(billing): settle a free payer's run after they subscribe mid-run - #9498
waleedlatif1 wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
1 issue found across 3 files
Confidence score: 3/5
- In
apps/sim/lib/billing/threshold-billing.ts, the guard treats an open period as no subscription, so a paid or entitled subscription with missing billing dates can be serialized with the default period while the callback is acknowledged. Distinguish missing billing dates from having no subscription.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/lib/billing/threshold-billing.ts">
<violation number="1" location="apps/sim/lib/billing/threshold-billing.ts:222">
P1: This guard conflates an open period with having no subscription. A paid or entitled subscription with missing billing dates is serialized with the default period, so this line acknowledges the callback without resolving or settling its overage instead of raising the existing retryable `required_state_missing` error; only bypass the check after confirming that the payer has no subscription.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
| ): Promise<ThresholdSettlementOutcome | undefined> { | ||
| // Spend admitted without a subscription stays in the open period, which no subscription the | ||
| // payer gains later ever spans, so it has no threshold to settle. | ||
| if (options.expectedBillingPeriod && isDefaultBillingPeriod(options.expectedBillingPeriod)) { |
There was a problem hiding this comment.
P1: This guard conflates an open period with having no subscription. A paid or entitled subscription with missing billing dates is serialized with the default period, so this line acknowledges the callback without resolving or settling its overage instead of raising the existing retryable required_state_missing error; only bypass the check after confirming that the payer has no subscription.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/billing/threshold-billing.ts, line 222:
<comment>This guard conflates an open period with having no subscription. A paid or entitled subscription with missing billing dates is serialized with the default period, so this line acknowledges the callback without resolving or settling its overage instead of raising the existing retryable `required_state_missing` error; only bypass the check after confirming that the payer has no subscription.</comment>
<file context>
@@ -216,6 +217,11 @@ export async function checkAndBillPayerOverageThreshold(
): Promise<ThresholdSettlementOutcome | undefined> {
+ // Spend admitted without a subscription stays in the open period, which no subscription the
+ // payer gains later ever spans, so it has no threshold to settle.
+ if (options.expectedBillingPeriod && isDefaultBillingPeriod(options.expectedBillingPeriod)) {
+ return noOp(options, 'no-subscription')
+ }
</file context>
[Medium impact] The change reaches cost callbacks in a bounded billing workflow. The billing fix appears sound, but the exported helper must meet the repository’s documentation requirement before merging. Findings
SummaryFixes cost callbacks for runs admitted while the payer was free, then continued after the payer subscribed.
|
| } | ||
| } | ||
|
|
||
| export function isDefaultBillingPeriod(period: { start: Date; end: Date }): boolean { |
There was a problem hiding this comment.
The exported helper lacks documentation
isDefaultBillingPeriod is a new exported API without TSDoc. The repository requires TSDoc for exported APIs, so this must be addressed before merging. Add a short comment explaining that it recognizes the open fallback period by its dates, without requiring a source field.
| export function isDefaultBillingPeriod(period: { start: Date; end: Date }): boolean { | |
| /** Recognizes the open fallback billing period by its dates, without requiring a source field. */ | |
| export function isDefaultBillingPeriod(period: { start: Date; end: Date }): boolean { |
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
billing_period_mismatch, and answers a retryable 503 on every attempt. The usage is already recorded, but the callback is never acknowledged, so the worker spends its whole retry budget and leaves the charge unconfirmedcheckAndBillPayerOverageThresholdnow treats spend in the default period as having no threshold to settle (no subscription period ever spans it), which is what a free payer's callback already resolved to before the upgradeisDefaultBillingPeriodnext todefaultBillingPeriod, comparing dates so envelopes without a periodsourceare covered tooType of Change
Testing
update-costintegration cases (direct-v1 and attribution-v1): free payer's first callback, payer subscribes, next callback settles (200), replay is a duplicate (409), spend stays in the default period. Both fail with 503 on the pre-fix codebun run test:integration update-cost/route.integration,vitest run lib/billing app/api/billing,apps/simtype-checkChecklist
🤖 Generated with Claude Code