v5.49.0 6 June 2026 Feature Fix

Billing Lifecycle Receipts, Stripe Envelope Fix & Cancellation Banner Live

Why This Matters

SpeyBooks’ first real subscription charge happens on 3 September, and the platform’s promise is that billing state is provably mirrored from Stripe, not assumed. This release ships the transition receipt system that makes every billing webhook accountable for what it did, and the system proved its worth within hours: on its first night of operation it detected that two Stripe fields had moved under a newer API version, leaving pending-cancellation state invisible to the platform. The defect was found by the transition ledger, not by a user. Both affected read paths were fixed, re-verified live, and a cancellation banner that had shipped weeks earlier switched itself on the moment its data source started telling the truth.


Billing Lifecycle Receipts

Every subscription webhook now leaves exactly one receipt recording what it did, alongside the existing duplicate-delivery gate.

  • Four honest outcomes An event is recorded as applied (it changed local billing state), no-op (it was byte-identical to what was already recorded), stale-discarded (it arrived after a newer event for the same subscription and was not applied), or unsupported (its status falls outside the supported vocabulary and never reaches the record).

  • Byte-identity semantics “Applied” means any change, not just a status change: scheduling a cancellation flips a flag while the status stays the same, and that records as applied. “No-op” is reserved for events that change nothing at all, which is precisely the property that made the detection below possible.

  • Out-of-order delivery handled Webhook ordering is not guaranteed by Stripe. Events strictly older than the last applied event for a subscription are discarded with a receipt, never applied. Same-moment ties defer to the database state machine, which remains the arbiter of legal transitions (Class M; migration 100). Event identity is unique by construction (Class M; migration 099). The ordering and receipt behaviours are application-level safeguards today; the behavioural property tests that promote them are the next scheduled work.

  • Resolution hardening Subscription events are matched to an organisation through verified identifiers. A reference that points at an organisation belonging to a different Stripe customer is never used: it is logged and resolution falls through to the authoritative lookup.


Stripe API Envelope Fix

The webhook endpoint is pinned to a current Stripe API version, and two fields are no longer where older integrations read them: Stripe’s 2025 Basil release moved the billing period end onto the subscription item, and a scheduled cancellation is now expressed as a cancellation timestamp while the older boolean stays false.

  • What was blind Pending cancellations were invisible: cancelling at period end never reached the local record, the billing status API reported the cancellation flag as false during a live scheduled cancellation, and the billing period end had never been recorded. After the trial period, the status API would also have reported a frozen period end indefinitely.

  • How it was caught A routine cancel-and-resume check produced two no-op receipts where applied was expected. The receipts were honest: the handler compared what it could see, and what it could see had not changed. The discrepancy between the receipt and the action taken was the entire detection.

  • The fix Both read paths (the webhook handler and the billing status endpoint, GET /billing/status) now read the period end from the subscription item and derive the pending-cancellation state from either spelling Stripe uses. Re-verified live the same night: cancel recorded as applied with the flag set and the period end recorded for the first time, an intervening cosmetic event correctly recorded as no-op, and resume recorded as applied with the flag cleared.


Cancellation Banner Live

  • A shipped feature, activated The pending-cancellation banner (amber notice, access-until date, one-click Reactivate) shipped in 5.45.0 and had been waiting on a field that was permanently false. With the envelope fix it now appears the moment a cancellation is scheduled and clears the moment the plan is resumed. No frontend change was required.

  • The cancellation promise, intact Cancel from the billing page in two clicks, resume in one, access until the period end, data retained throughout. The banner shows the actual date, never “soon”.


Operational Impact

  • Pending cancellations are now visible end to end (Stripe, local record, status API, billing page) within seconds of the action
  • The billing period end is recorded and remains correct after the trial converts, where the previous reading would have frozen
  • Every subscription webhook since this release carries a receipt; the first applied receipt in the platform’s history is on record
  • A defect class described in the wider ecosystem as a silent “200 OK with a missing field” failure was caught internally before any customer beyond the founder account could be affected
  • The pre-fix no-op receipts remain in the ledger permanently as the detection evidence: receipts are history, not state

Files Changed

Backend:

  • Webhook subscription processing: receipt outcomes, ordering check, byte-identity comparison, verified organisation resolution, envelope reading corrected to current Stripe field locations
  • Billing status endpoint: period end and cancellation flag corrected to the same envelope reading

Frontend / Portal:

  • None. The cancellation banner activated without code change.

Docs site:

  • API reference updated: receipt outcome vocabulary, envelope of record, supported status vocabulary, and corrected plan pricing in examples

Known Issues

  • The pending-cancellation banner copy is written for the trial period; the wording for a cancellation scheduled mid-cycle after the trial converts is under review before 3 September.

Next: the behavioural property tests that promote the ordering and transition guarantees from application-level safeguards to mechanically verified invariants, ahead of the first live charge.