OneShot

All packsSaaS Billing + Tax CoreSolve

stripe seat based billing prorate seats add remove

Adding seats now, removing them at renewal

Seat increases prorate immediately; seat decreases must wait for period end -- refunding mid-cycle desyncs billing from the entitlements it's supposed to gate (D11).

3 of 3 runs passedOne decision inside SaaS Billing + Tax Core (nextjs-prisma-postgres).

What goes wrong

Seat count changes need the same asymmetric handling as plan changes: adding a seat mid-cycle should bill the prorated difference now, but removing a seat mid-cycle should not refund immediately -- the team already used those seats. Code that treats seat add and seat remove symmetrically either fails to charge for the seats actually used, or issues refunds the business didn't intend to give.

Why agents get it wrong

Seats look like a simple quantity field on a subscription item, so it's tempting to write one updateSeats() function that always calls the same Stripe update with the new quantity -- the asymmetry (immediate for increases, scheduled for decreases) isn't visible from the Stripe API shape itself, only from the architecture decision that says which direction gets which treatment.

What SaaS Billing + Tax Core pre-decides

D11 pins seat increases to the same immediate-update, always_invoice path as an upgrade, and seat decreases to the same scheduled-at-period-end path as a downgrade, reusing D10's existing-schedule rule so a decrease never creates a second schedule. Decreases below the currently occupied seat count, and increases above a plan's maxSeats, are rejected before any Stripe call runs -- billing must not desync from entitlements.

Source: ARCHITECTURE.md D11

Coming soon — $149Full receipt and details for SaaS Billing + Tax Core

Related problems