| new file mode 100644 |
| index 0000000..6b9b036 |
| --- /dev/null |
| +++ b/docs/managed-payments.md |
| @@ -0,0 +1,160 @@ |
| +# 🤖 Managed payments — the payout model for paid access levels |
| + |
| +Design doc, 2026-07-22. Status: **designed, not built** (umbrella issue links |
| +the phases). Companion docs: `docs/community-and-tiers.md` (layer 3 — the tier |
| +machinery money assigns into), `docs/tier-economics.md` (the hosting price |
| +book; a different product, same Stripe account), `docs/pricing-bands.md`. |
| +Decisions here are Paul's (2026-07-22); the VAT/OSS findings are research |
| +summaries, not legal advice — a human review gates the go-live phase. |
| + |
| +## The design in one sentence |
| + |
| +> **Members pay the platform and the admin's share transfers structurally — |
| +> no Stripe account required, commission pay-what-you-want between 5.5% and |
| +> 11% all-in — while "bring your own Stripe" stays open to every space, with |
| +> a pay-what-you-want commission (suggested 5.5%) on top of the admin's own |
| +> fees.** |
| + |
| +## Two providers, priced by who bears the risk |
| + |
| +| | Managed (default) | BYO Stripe (open to every space) | |
| +|---|---|---| |
| +| Merchant of record | the platform | the space admin | |
| +| Stripe account | ours; admin gets an **Express** account (payouts + KYC only) | admin's own (OAuth-linked Standard, today's live path) | |
| +| Commission | **pay-what-you-want, 5.5–11%, suggested 11 — all-in** (card processing fees come out of our points, whatever the choice) | **pay-what-you-want, suggested 5.5%** + the admin's own Stripe fees | |
| +| VAT on member subscriptions | ours (OSS from Ireland; Stripe Tax) | admin's | |
| +| Disputes/refunds | ours (payout holds; transfer reversals) | admin's | |
| +| Admin onboarding | minutes (Express account links: name, bank, KYC) | needs a real Stripe account | |
| + |
| +Both commissions are the honor mechanism from `pricing-bands.md` applied to |
| +our own cut: a suggestion, a one-tap choice, no eligibility test. The two |
| +rails bound it differently because the costs differ: |
| + |
| +- **Managed clamps to [5.5, 11]** because we bear real per-charge costs |
| + (processing, VAT handling, disputes). The 5.5 floor is roughly our cost |
| + line — on the €5.50 minimum ticket with an international card it dips a |
| + couple of points under, and that known loss is priced in rather than |
| + guarded against. |
| +- **BYO is unbounded down to zero** because our marginal cost there is nil — |
| + the admin is the merchant, pays their own fees, runs their own books. Any |
| + commission they choose is honest margin; zero is a legitimate choice. |
| + |
| +Why this split, in order of weight: |
| + |
| +1. **Activation.** BYO filters monetization to admins who already run a |
| + business. A Family or Community admin has no Stripe account and never |
| + will; Express onboarding is a bank account and a KYC form. Managed is the |
| + 95% case. |
| +2. **"All-in, including payment fees" becomes structural.** On a |
| + destination charge we own the charge and pay Stripe's processing fee from |
| + our own take; the admin's share is set by `transfer_data` from their |
| + chosen commission, not reconciled after the fact. The |
| + refund-reconciliation and fee-prediction designs this supersedes are |
| + recorded below (§ Superseded) so they aren't reinvented. |
| +3. **The cuts price the risk transfer — and both are the admin's call.** |
| + Managed bears processing + VAT + disputes, so its pay-what-you-want range |
| + floors at 5.5 (≈ our cost line); BYO bears all three itself, keeps its |
| + own books and negotiated rates, so its commission is open all the way to |
| + zero, suggested 5.5 (market-honest: Memberful charges 4.9% flat for the |
| + same shape; Substack takes 10 *plus* processing). |
| + |
| +## Money mechanics (managed) |
| + |
| +- Checkout Sessions on the **platform account** (no `Stripe-Account` header), |
| + `mode=subscription`, `subscription_data[transfer_data][destination]` = the |
| + space's Express account, `[amount_percent]` = 100 − the chosen commission |
| + (89–94.5). Stripe splits every invoice; processing fees land on the |
| + platform side by construction. |
| +- **Level price floors: €5.50/month, €11/year** (enforced at level pricing |
| + time, both providers). Stripe's fixed €0.25/charge makes all-in economics |
| + negative below ~€2.60/charge and thin below €5; the floors keep the |
| + worst-case (international card) take ≥ ~3 points. Same fee logic that made |
| + Business and Family annual-only (`tier-economics.md`). |
| +- Payouts ride Stripe Express schedules; a rolling payout delay (Stripe's |
| + default) is the fraud/dispute buffer. Disputes: transfer reversal claws |
| + back the transferred share; the €15 dispute fee is ours — priced into the |
| + commission. |
| +- VAT: the platform is seller of record for an electronically supplied |
| + service. Consumer stickers are VAT-inclusive (the pricing-bands posture), |
| + so our net per subscription varies by buyer country; OSS (one Irish |
| + registration) covers the EU, Stripe Tax computes per-invoice. Non-EU |
| + registrations (UK first) ratchet on thresholds. **A human tax review gates |
| + the live flip** — same bar as pricing-bands Phase 1. |
| + |
| +## Trust model (the two honest costs) |
| + |
| +- **Privacy centralization.** Today each admin's Stripe sees only their own |
| + members' payment identity; managed payments concentrates every paying |
| + member of every space into one Stripe dashboard. Our own stores still hold |
| + only opaque ids (the billing.go rule), but the dashboard exposure is real |
| + and grows with the fleet. Named here deliberately; BYO remains the |
| + decentralized escape hatch, and that is part of why it stays. |
| +- **Merchant of record for content we cannot see.** E2EE means we underwrite |
| + payments for communities whose content is structurally opaque to us. |
| + Mitigations: the existing report-to-admins/moderation surface, payout |
| + holds, and the standing right to off-board a space from *payments* |
| + (payouts stop; the space itself, being E2EE, was never ours to read). |
| + This asymmetry — we can stop paying out, we cannot inspect — is the |
| + design, not a gap in it. |
| + |
| +## Implementation shape |
| + |
| +- `subscriptions.go` already has the seam: `PayProvider` is an interface and |
| + "adding a provider never touches the state machine." `stripePlatform` |
| + (managed) lands beside `stripeConnect` (BYO). Normalized `SubEvent`s feed |
| + the same `applySubEvent`. |
| +- Provider selection per space: managed is the default wherever platform |
| + creds exist; `stripe_connect_account` (BYO) stays available to every |
| + space — no tier gate. A space that links its own Stripe chooses the BYO |
| + rail by that act. |
| +- Commission choices live in the space config: `managed_commission_pct` |
| + (clamped [5.5, 11], default 11) and, phase 3, the BYO percent (suggested |
| + 5.5, floor 0). Changing a choice applies to new checkouts; existing |
| + subscriptions keep the split they were created with. |
| +- Express account id lives in the space config (`stripe_express_account`), |
| + set by the onboarding flow (Account Links API; `account.updated` webhooks |
| + flip the payouts-ready bit). Until payouts-ready, levels can be drafted |
| + but not sold — never sell against an account that can't be paid. |
| +- Level prices: managed levels create their Stripe Price on the platform |
| + account (BYO keeps creating them on the admin's). The floors apply in the |
| + level-pricing path, before any Stripe call. |
| +- Existing BYO spaces (paulca) are grandfathered: linked accounts keep |
| + working; the 10% → pay-what-you-want change (suggested 5.5) applies from |
| + its deploy — a cut *reduction* at the suggestion, so no notice period. |
| + |
| +## Lifetime access (single payment) |
| + |
| +A level can price as **`interval: "once"`** — pay once, in for as long as the |
| +space exists (PR #1290; both rails). The copy says "as long as the space |
| +exists" deliberately: an admin can close a space, and we don't sell promises |
| +admins can't keep. Mechanics: `mode=payment` checkouts with the commission as |
| +a fixed amount (payment mode has no percent splits) — BYO takes its flat fee, |
| +managed applies the chosen PWYW commission to the transfer. The grant has no |
| +lifecycle — exempt from every renewal-shaped sweep — and the only exits are a |
| +refund/chargeback (`charge.refunded` / `charge.dispute.created` revoke |
| +immediately, with a payment-intent tombstone so redelivered grants can't |
| +re-buy a refunded level) or an admin removal. **Floor: 11.00 one-time** — |
| +the elevens' third coin (€5.50/month, €11/year, €11 once). |
| + |
| +## Rollout phases (mirrored in the umbrella issue) |
| + |
| +1. **Provider core (dark):** `stripePlatform` + destination-charge checkout |
| + + webhook parse + tests; config-gated off. |
| +2. **Express onboarding:** account links flow, payouts-ready gating, admin |
| + UI (web + native surfaces per parity rule). |
| +3. **Floors + BYO repricing:** €5.50/€11 level-price floors both |
| + providers; BYO's application fee becomes the pay-what-you-want config |
| + (suggested 5.5, floor 0) with its admin picker; changelog. |
| +4. **Tax + go-live:** Stripe Tax on, OSS registration confirmed, human tax |
| + review, live platform creds, docs/status updates. |
| + |
| +## Superseded designs (recorded so they stay dead) |
| + |
| +- **Fee-refund reconciliation** (charge 11%, refund the actual |
| + `balance_transaction.fee` after `invoice.paid`): correct but only needed |
| + when the admin is MoR and we promise all-in — i.e. never, under this |
| + split. BYO makes no all-in promise; managed doesn't need reconciliation. |
| +- **Fee prediction from the payment intent**: impossible before the first |
| + charge (no card yet), asymmetric on renewals (application fees refund |
| + down, never up). Reconciliation was prediction with the error bar |
| + collapsed; destination charges remove the need for either. |