Split out from #19. Rescoped 2026-07-03 — the original pay-to-create gate is superseded: the poll tool is a free lead magnet for metsa.app, and gating creation would kill the viral loop (participants seeing "create your own, free"). Full artifacts live in openspec/changes/pro-poll-upgrade/.
What
Creation stays free and instant. Each poll gets an optional one-time Pro upgrade (10 DKK Stripe Checkout) from the organizer dashboard:
- New nullable
events.pro_at (UTC ISO text; NULL = free). No backfill — existing events are correctly free.
- Free polls' participant pages (
/r, /s) show a discreet "made with — create your own, free" badge in the poll's language; Pro removes it (launch Pro value).
- Further Pro features ship as separate changes flipping on the same flag: email notify when everyone answered, deadline/auto-close + reminders, ICS invite on close. No free-tier limits, ever — "free without limits" is the positioning against Doodle.
- Metsa members will eventually get Pro free (Zitadel OIDC) — deferred, separate issue.
Technical notes that carry over from the original plan
- Stripe webhook is the source of truth, not the browser redirect:
checkout.session.completed → idempotent UPDATE events SET pro_at = ? WHERE id = ? AND pro_at IS NULL, then purge the event's edge-cache entries (badge visibility flips).
- Workers needs the fetch HTTP client +
constructEventAsync with createSubtleCryptoProvider() (no sync Node crypto).
- Tokens stay out of Stripe:
success_url/cancel_url point at a /paid?session_id=… endpoint that resolves the organizer token server-side from metadata.event_id.
- E2E:
STRIPE_STUB flag (e2e wrangler config only) fakes the checkout round trip through real routes; unsigned webhook POSTs must change nothing.
- Fee caveat accepted: Stripe's fixed fee eats ~20% of 10 DKK — the microtransaction is a funnel signal (and a metsa pitch surface), not the revenue model.
Split out from #19. Rescoped 2026-07-03 — the original pay-to-create gate is superseded: the poll tool is a free lead magnet for metsa.app, and gating creation would kill the viral loop (participants seeing "create your own, free"). Full artifacts live in
openspec/changes/pro-poll-upgrade/.What
Creation stays free and instant. Each poll gets an optional one-time Pro upgrade (10 DKK Stripe Checkout) from the organizer dashboard:
events.pro_at(UTC ISO text;NULL= free). No backfill — existing events are correctly free./r,/s) show a discreet "made with — create your own, free" badge in the poll's language; Pro removes it (launch Pro value).Technical notes that carry over from the original plan
checkout.session.completed→ idempotentUPDATE events SET pro_at = ? WHERE id = ? AND pro_at IS NULL, then purge the event's edge-cache entries (badge visibility flips).constructEventAsyncwithcreateSubtleCryptoProvider()(no sync Node crypto).success_url/cancel_urlpoint at a/paid?session_id=…endpoint that resolves the organizer token server-side frommetadata.event_id.STRIPE_STUBflag (e2e wrangler config only) fakes the checkout round trip through real routes; unsigned webhook POSTs must change nothing.