Skip to content

fix(billing): filter mixed subscription seat items#3037

Merged
jeanduplessis merged 3 commits intomainfrom
fix/restore-seat-filter-regression
May 5, 2026
Merged

fix(billing): filter mixed subscription seat items#3037
jeanduplessis merged 3 commits intomainfrom
fix/restore-seat-filter-regression

Conversation

@kilo-code-bot
Copy link
Copy Markdown
Contributor

@kilo-code-bot kilo-code-bot Bot commented May 4, 2026

Summary

Fix mixed Stripe subscription handling so only Teams/Enterprise seat product line items contribute to organization seat records and seat-count updates.

The bug is real for subscriptions that contain seat products alongside Kilo Pass or other non-seat add-ons: unfiltered subscription items can inflate organizations.seat_count, purchase amount_usd, and downstream paid-seat update calculations. This branch reapplies the previously proposed seat-product filtering from closed PR #1060 on top of current main; the earlier PR text's claim that PR #1609 reverted a merged fix has been corrected here.

Key changes:

  • Filter subscription event line items to Teams/Enterprise seat products before computing seat_count, amount_usd, billing period, and billing cycle.
  • Preserve free seat items during seat-count updates without treating non-seat add-ons as free seats.
  • Add regression coverage for non-seat items appearing first in Stripe's item list and for seat updates with paid seats, free seats, and non-seat add-ons.
  • Update the Team/Enterprise seat billing spec to define seat line items by seat product and require non-seat add-ons to be excluded from seat counts and recorded seat subscription amount.

Verification

Manual/code verification only:

  • Reviewed the mixed-subscription webhook path: Kilo Pass/add-on line items are excluded before purchase records and organization seat counts are derived.
  • Reviewed the seat-count update path: only non-paid items under seat products reduce the paid seat quantity, while non-seat add-ons are ignored.
  • Confirmed this is server-side billing logic with no user-facing UI path to manually exercise.

Visual Changes

N/A — server-side billing logic and tests only.

Reviewer Notes

Non-seat products (Kilo Pass, KiloClaw add-ons, etc.) are currently summed
into the organization seat count because PR #1609 was squash-merged from a
branch based on pre-fix code, silently reverting commit 55b8424 and its
follow-up 5d21daa. This causes double-counting for organizations with
mixed product subscriptions — e.g., FSKAB (060090ad-e0ec-4612-a2d2-935b1c74a60f)
shows 54 seats (27 seats + 27 Kilo Pass line items) when it should show 27.

Re-apply the product-ID-based filter in handleSubscriptionEventInternal:
only subscription items whose price.product matches the Teams or Enterprise
seat product contribute to seat_count and amount_usd. Derive the billing
period (starts_at / expires_at) from the first filtered seat line item so a
non-seat add-on appearing first in the list cannot corrupt the period.

Also restore the ~154 lines of tests that were removed, updating the base
mock subscription to use STRIPE_TEAMS_SUBSCRIPTION_PRODUCT_ID so existing
tests continue to exercise the seat path, and update the two no-line-item
error assertions to match the new, more specific error message.
@kilo-code-bot
Copy link
Copy Markdown
Contributor Author

kilo-code-bot Bot commented May 4, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .specs/team-enterprise-seat-billing.md
  • apps/web/src/lib/organizations/organization-seats.ts
  • apps/web/src/lib/organizations/organization-subscription-event.test.ts
  • apps/web/src/lib/organizations/stripe-seat-line-items.ts
  • apps/web/src/lib/stripe-3ds.test.ts
  • apps/web/src/lib/stripe.ts

Reviewed by gpt-5.5-20260423 · 701,273 tokens

@jeanduplessis jeanduplessis changed the title fix(billing): restore seat-filter regression overwritten by PR #1609 fix(billing): filter mixed subscription seat items May 5, 2026
Comment thread apps/web/src/lib/stripe.ts Outdated
@jeanduplessis jeanduplessis merged commit a1139f7 into main May 5, 2026
13 checks passed
@jeanduplessis jeanduplessis deleted the fix/restore-seat-filter-regression branch May 5, 2026 17:45
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.

2 participants