paywall · architecture
The One-Boolean Paywall
hasAccess plus planType covers lifetime and monthly without a pricing engine.

Boring Team
September 8, 2026 · 1 min read
Pricing engines grow tentacles: tiers, seats, trials, meters, coupons. An MVP needs exactly one question — did this user pay? Boring answers it with one boolean and one string.
user.hasAccess is the gate. user.billingProvider plus order history derives planType: lifetime for one-time buys, monthly for recurring, free otherwise. requirePaidUser() returns all of it in one call, so pages branch and APIs map to 401 (no session) versus 403 (free user) without thinking.
Marketplace orders reuse the same shape. Each completed payment is one Order row keyed by (productId, buyerId/email); ownership is hasBought(). A product flagged as a template or membership additionally flips global hasAccess, so repo claims and vault access ride the same boolean.
Revocation is symmetric: mark the order cancelled, and clear hasAccess only when no other completed orders remain. Lifetime buyers survive subscription churn untouched.
When you genuinely need seats or metering, add columns then — not before the first paying customer.