diff --git a/STRIPE_PLAN.md b/STRIPE_PLAN.md index 5248109..828f9e6 100644 --- a/STRIPE_PLAN.md +++ b/STRIPE_PLAN.md @@ -29,7 +29,7 @@ decisions before code starts (marked **DECISION NEEDED**). These aren't engineering choices — they change what gets built. Flagging rather than guessing: -- **DECISION NEEDED — Pricing amounts.** What does Pro cost/month? Family? Annual discount? Stripe Prices are created once you know this. +- **Pricing amounts — recommended, see §1c.** Pro €4.99/mo (€39/yr), Family €8.99/mo (€69/yr). Still a decision to confirm, not locked — §1c has the full reasoning. - **Family tier is multi-user, confirmed.** One subscription, shared by several app accounts (Netflix/Spotify-Family model) — not one-tier-per-payer like Pro. This needs a new schema concept (§1a) and changes how a member's effective tier gets resolved. See below. - **DECISION NEEDED — Family member cap.** How many accounts per family subscription? (Netflix Family = 5, Spotify Family = 6.) A flat number, enforced app-side — recommend **5** as a default, easy to change later since it's just a constant, not a schema value. - **DECISION NEEDED — Downgrade/cancellation behavior.** Cancel immediately (lose Pro/Family features now) or at period end (keep access until the paid period runs out, matching what Stripe bills)? Recommend **at period end** — standard SaaS expectation, and Stripe's Customer Portal defaults to this. For Family specifically, also decide: if the owner cancels, do members lose access immediately at that point, or also ride out the period end? Recommend the latter for consistency. @@ -107,6 +107,29 @@ Checkout — everything else here is optional polish. --- +## 1c. Recommended pricing (resolves the §1 "pricing amounts" decision) + +A starting recommendation, not a locked-in number — see the "adjust after +launch" note at the end. + +| Tier | Monthly | Annual | Annual discount | +|---|---|---|---| +| Free | €0 | — | — | +| Pro | **€4.99** | **€39** | ~35% off (39 vs 59.88) | +| Family | **€8.99** | **€69** | ~36% off (69 vs 107.88) | + +**Why these numbers, not round-trip guesses:** + +- **Stripe's fee floor makes very cheap pricing inefficient.** Domestic EEA rate is 1.5% + €0.25 (§10). At €0.99/month that flat €0.25 alone is >25% of revenue before the percentage even applies — pricing has to clear roughly €3–4 before the fixed component stops dominating. €4.99 clears it comfortably. +- **Priced against the AI cost the tier actually permits**, not against competitors. `tierDefinitions` already caps Pro at 500 AI calls/month, Family at 2000 (`packages/db/src/schema/tiers.ts`). Those calls cost real money once they leave OpenRouter's free tier — €4.99 needs to cover the *realistic* per-user AI spend plus Stripe's cut plus margin, not just "feel competitive." The actual per-call cost depends on which model users end up on (admin-configured default, or BYOK bypasses this entirely per `aiConfig.isByok` skip-quota logic already in `withAiQuota`) — **this is the part most likely to be wrong before you have real usage data**, not the psychology of the price itself. +- **Annual discount (~35%) is the standard SaaS anchor** — enough to visibly reward committing, not so steep it cannibalizes monthly revenue or makes monthly pricing look like a rip-off by comparison. Also improves cash flow (a year of revenue upfront) and reduces monthly churn-management overhead (11 fewer renewal events per converted customer per year). +- **Family priced below 2× Pro on purpose.** €8.99 vs €9.98 (2 × €4.99) — the whole pitch of a household plan is "cheaper than everyone buying Pro separately." If Family cost the same or more than 2 individual Pro subscriptions, nobody rational picks it over just having each person subscribe individually (or one person subscribing and not sharing at all, which the member cap in §1a doesn't prevent — sharing outside the app isn't something billing can stop, only the in-app group feature needs pricing that makes *joining the group* the obviously better option over each member paying separately). +- **Family's 4× AI-call allowance (2000 vs Pro's 500) for less than 2× the price** is intentional generosity, not an oversight — it's the concrete value-per-euro reason to pick Family over "everyone just buys their own Pro," beyond the flat convenience of one payment. + +**Adjust after launch, and adjust the price — not the AI-call limits.** Limits are what users plan around; changing them after people are used to a number reads as a downgrade even if the price stays flat. Price is the variable meant to absorb reality once you have actual OpenRouter spend-per-active-user data — if real costs run higher than assumed here, raise Pro/Family prices (existing subscribers keep their price until they actively resubscribe/change plans, per Stripe's normal behavior — see §8's note on the admin manual-override path for edge cases), don't quietly shrink what €4.99 already promised someone. + +--- + ## 2. Package & client setup - Add `stripe` (server SDK) to `apps/web/package.json`. Do **not** add `@stripe/stripe-js`/`@stripe/react-stripe-js` — Checkout/Portal are hosted redirects, no Stripe Elements needed for v1 (see §7 for why hosted over custom).