← ClaudeAtlas

billing-entitlementslisted

Design plans, entitlements, and metered billing for a developer tool — plans as data, a single getEntitlements() resolver, per-account overrides, usage snapshots, and Stripe-as-ledger sync. Use when the user wants to add pricing tiers, gate features by plan, meter usage, pick a billing metric, or wire up Stripe subscriptions. Also use when they mention "free tier limits", "grandfathering", "overage", "seat pricing", or ask "what should we charge for", even if they never say "billing" or "entitlements".
alexpate/devtool-skills · ★ 0 · AI & Automation · score 67
Install: claude install-skill alexpate/devtool-skills
# Billing & Entitlements Billing is where developer tools quietly lose years: plan logic smeared across the codebase, a metric customers can't predict, and a Stripe integration that *is* the entitlements system until the first webhook drops. The bar is Stripe-grade bookkeeping with PostHog-grade generosity. Get the architecture right once and every future pricing change is a data edit, not a migration. ## Before you start Check for `.agents/devtool-context.md` and read it if present (pricing model, metered unit, and stage change everything below). If absent, ask: 1. What does one unit of value look like: an email sent, an API call, an active user, a GB stored? 2. What plans exist or are planned, and is anyone already paying under terms you'd have to grandfather? 3. Where does subscription state live today: Stripe only, your DB, or both? ## The design, in order of consequence Positions below are defaults; deviate only with a reason. ### 1. Plans are data, not code The failure mode has a name, plan constants: `if (plan === 'pro')` scattered through the app. Every pricing change becomes a deploy, every grandfathered customer becomes a special case in code, and within a year nobody can answer "what does the Pro plan actually include?" without grepping. Instead, plans live in a `plans` table (or a versioned config file loaded into one). Shipping a new plan, changing a limit, or grandfathering old customers on old terms is an `INSERT`, not a release: ```sql create table p