SYSNOMINAL|BUILDv2026.01.01|REGIONSELF_HOST

Why Your Billing System Should Not Block Your Login Logic

Signal-only primitives. Self-hosted. Deterministic. No magic.

This note describes a failure pattern. It is not a tutorial, not best practice, and not integration guidance.

← Back to Notes

Claim

Billing truth (Stripe, invoices, subscriptions) is state. Authentication and access checks are enforcement.

If billing truth directly blocks login, you have coupled state ingestion to a user-facing enforcement path.

The common failure mode

Teams do this:

  • Stripe webhook arrives
  • DB updates a “paid” boolean
  • Login middleware checks “paid”
  • If false → block

This looks clean. It is not clean. It converts “stale billing state” into “customer cannot log in.”

Why this breaks in production

  • Webhook delays (Stripe outage, retries, queue lag)
  • Clock skew and ordering issues
  • Transient DB failures
  • Partial deploys / migration windows
  • Data mismatch between Stripe truth and your local cache

These are normal events. When billing is welded to login enforcement, normal events become user-facing lockouts.

Signal-only framing

A safer posture is to treat billing as a signal your system reads, then decide how your system behaves when the signal is missing, stale, or contradictory.

  • “Plan is Standard” is a fact.
  • “User may access feature X” is enforcement.
  • “User may log in” is enforcement.

Your system defines fail-open vs fail-closed per surface area. (Login is not the same as premium feature gating.)

What this does NOT mean

  • It does not mean “allow unpaid users forever.”
  • It does not mean “ignore billing truth.”
  • It does not mean “SimpleStates enforces anything.”

It means: keep state reading separate from enforcement decisions, and be explicit about degraded modes.

Links