Skip to content

fix(billing): keep billing a request whose period start moved forward before its first charge - #8480

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix/billing-anchor-reset-first-callback
Oct 1, 2026
Merged

waleedlatif1 merged 2 commits into
stagingfrom
fix/billing-anchor-reset-first-callback

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Problem. If a Stripe billing-anchor reset moved a subscription's period start forward between a request's admission and its first cost callback, every cost callback after the first one for that request was refused with a billing-context mismatch. That spend was never billed.

Root cause. The first charge stamps the ledger row with the period that is current at that moment, which is the reset period. Later callbacks still carry the period frozen at admission. assertCumulativeUsageLedgerBinding accepted a later stamped period only when it started at or after the admitted period's end. That covers a request that outlives its period, but not an anchor reset, where the new start falls inside the admitted period.

Fix. The binding now uses the same forward-only rule as the roll into a new period row: it accepts any stamped period whose start is later than the admitted start. A stamped period that starts earlier than the admitted one is still refused.

The upgrade-card subscription read in update-cost now runs under the same 1 s standing deadline as the verdict read. Before, a slow read there could hold the callback past its budget.

Behaviour changes

  • A request whose period start moved forward before its first charge keeps billing into the stamped period and is no longer refused.
  • A request admitted after the period its first charge was stamped with is still refused with billing period as the mismatched field.
  • update-cost still answers usageExceeded: false when the verdict read outlasts the standing budget. When the verdict is exceeded but the upgrade-card read runs past the same budget, it keeps usageExceeded: true and falls back to the upgrade_plan card. Before, the upgrade-card read was unbounded.

Test plan

  • usage-log.integration.ts against disposable Postgres (bun run test:integration): new case keeps billing a request whose period start moved forward before its first charge fails on the old >= end rule and passes with the fix; refuses a request admitted after the period its first charge was stamped with pins the backward direction. Suite: 19/19 passed.
  • update-cost/route.test.ts: new case keeps the exceeded verdict with the plan-upgrade card when the card read outlasts the callback budget fails before the deadline covers the upgrade read and passes after. Suite: 52 passed, 1 skipped.
  • bun run type-check (apps/sim)
  • Biome check on the changed files
  • bun run check:audits

… before its first charge

A Stripe anchor reset between admission and a request's first cost callback
stamps row 0 with the reset period. The ledger binding only accepted a later
period starting at or after the admitted period's end, so every later callback
was refused with a billing-context mismatch and its spend went unbilled. The
binding now accepts the same forward-only rule the roll uses: a start later
than the admitted one. An earlier period is still refused.

Also bound the upgrade-card subscription read in update-cost under the same
1 s standing deadline as the verdict read.
@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Sep 30, 2026 10:34pm UTC

Request Review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Critical risk] Changes billing period validation and charge timing logic.

The PR appears safe to merge; no outstanding blocking finding remains.

Summary

The PR accepts a forward-shifted billing period stamped on a request’s first charge. The follow-up change preserves an exceeded usage verdict when the upgrade-card lookup exceeds the shared callback deadline, falling back to a plan-upgrade card.

Reviews (2) · Last reviewed commit: "fix(billing): keep an exceeded verdict w..."

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

Comment thread apps/sim/app/api/billing/update-cost/route.ts Outdated
Comment thread apps/sim/lib/billing/core/usage-log.ts
…slow

The shared deadline discarded an exceeded verdict when the card lookup ran
past the budget. The verdict read keeps the deadline; the card lookup now
falls back to the plan-upgrade card past the same deadline.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit f89b168 into staging Oct 1, 2026
33 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/billing-anchor-reset-first-callback branch October 1, 2026 01:24

This branch was previously deployed

1 inactive deployment
Preview — ac744d7f Deployed Sep 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant