Changelog

User-visible changes, newest first. Technical details live in the commit history.

Cards per page picker, per-class donate URL, honest dashboard counts, Top classes panel
  • newCards per page picker (1up / 2up / 4up). Every place you download a printable QR card PDF now has an inline picker next to the link. 1up is one full card per letter page, 2up stacks two landscape cards on a portrait letter page, 4up lays out four card-stock-sized cards in a 2x2 grid. The picker remembers your choice across pages and across campaigns, and the renderer cleanly blanks any unused slots when your last page is partial (no more template artifacts on Greenwood's single fundraiser printed in 4up).
  • newPer-class donate URL inline in the dashboard. Expand any class and every fundraiser now shows a copyable scanraise.com/d/<short_code> URL pill plus Copy link and Open ↗ buttons. Used to require clicking through to the class detail screen to find a URL; now it's one click from the campaign view.
  • fixHonest counts when no roster is uploaded. A class created via the skip-roster flow gets one auto-default fundraiser representing the class itself. The dashboard used to count those as "students" everywhere ("STUDENTS: 2", "2 classes · 3 students", "1 student"). Now every count surface uses real roster only: the per-class meta reads "no students yet", the per-grade and per-campaign meta read "no students yet", the FUNDRAISERS card collapses into a 3-card stat grid (Total raised / Donations / Avg donation), and the per-fundraiser row inside an empty class is labeled "Class link" instead of repeating the class name. Same wording adapts per org type ("no players yet" / "no scouts yet" / etc).
  • newTop classes leaderboard with "Still getting started" empty state. When the org has 0 rostered students, the Top fundraisers panel pivots to "Top classes" (or "Top dens", "Top squads", etc) ranked by class raised, useful for any campaign with 20+ classes. When no class has any donations yet, the panel reads "Still getting started. Classes will appear here once donations come in." instead of a list of $0 entries.
  • fixSkip-roster path on the new-class flow now creates a class-level auto-fundraiser when the admin doesn't have a roster CSV ready, so the class is donateable from minute one and the QR card prints with the class name as the top line.
  • fixEdit class modal now closes on click-on-backdrop, ×, Cancel, and Esc (was trapping admins until submit-or-reload).
  • fixDelete class endpoint refuses 409 with a friendly message if any donations exist on the class's fundraisers; cascades empty fundraisers explicitly so SQLite (test) and Postgres (prod) behave identically.
  • fix4up card layout: the URL line and "Raised by Name" line no longer double-print on the new template (slot transforms re-derived against the redesigned 4up template Mike shipped this morning).
League battle: orgs racing each other across a district / association
  • newMulti-org battle leaderboards. Group two or more organizations into a "league" with crown / flame badges showing which one is in the lead. The same primitive that powers class-vs-class racing inside a campaign now works at the org-vs-org level: school districts, sports leagues, scout councils, fire-department mutual-aid groups, all run the same race. Three nested layers: classes battle inside an org, orgs battle inside a league.
  • newPublic projector page at /league/<slug>. Dark-theme, auto-refreshing every 30 seconds, shows the league-wide goal bar plus per-org standings with rank, raised, goal, and badge. Press F to fullscreen for events. Each org row gets a per-org progress bar when goals are set, so a small school keeping pace with a big school can still win the league by progress %.
  • newLeague admin at admin.scanraise.com/leagues. Superadmins create new leagues; league admins set the league-wide goal, tagline, competitive flag, and public-visibility toggle, plus per-org goals and member roster. Add an org by pasting its UUID. Idempotent re-adds are no-ops.
  • newPer-class labels now adapt to org type. The Edit Class modal title, name field, goal field, and hint all read from the org's ORG_LABELS system, so a sports campaign opens "Edit squad / Squad goal", a scout campaign opens "Edit den", a church campaign opens "Edit class" (for Sunday school groups), etc. Same battle bars, same crown / flame badges, vocabulary that matches the org. The data layer (groups.goal_cents) was always generic; only the UI copy was hardcoded.
Class battle bars, donate URL on every card, and Clever Library import
  • newPer-class goals + head-to-head class battle bars. Set a goal for each class (Mrs. Anderson's 6th: $2,500). The class head row now shows an animated progress bar, plus a crown on the leading class and a flame on the one catching up ("$607 behind"). Two homerooms can race head to head, and the bar flips green with a trophy on goal-hit. Edit class names, leader, and goal in one place from the new Edit Class modal.
  • newDonate URL printed on every QR card. The new card template prints scanraise.com/d/<short_code> directly under the QR. Families who don't scan can text the URL to a friend or type it in. The QR shrank a touch (still scans cleanly) to make room for the URL line.
  • newLogo file upload on Org Settings. Drop in a PNG / JPG / GIF / WebP up to 1 MB and it lands on every QR card and donation page automatically. Old "paste a URL" workflow still works, just hidden behind a disclosure now.
  • newClever Library auto-roster import. Teachers who sign in with Clever can one-tap import their sections as ScanRaise classes, with students rostered automatically by Clever ID, first name, and last initial. Replaces the three-step CSV wizard for any teacher whose school is on Clever. The import is opt-in per section, so teachers control which classes sync.
  • newClever Library certification submitted. Marketing collateral, Universal Data Sharing Agreement, Integration Guide all complete. Awaiting Partner Engineering review (1 to 2 week SLA). Once approved, ScanRaise will appear in the Clever Library catalog where teachers discover and one-click install integrated apps.
  • newFirst live PTA on Stripe Connect. Greenwood Elementary PTA finished onboarding with a real bank account, and donors have already processed real donations through the platform. The full loop (donor scans QR, pays through Stripe Checkout, 2.5% platform fee, payout to the org's bank) is now functionally proven against a real merchant.
  • newCleaner login screen branding. The "Log in with Clever" button now uses Clever's official C mark and brand blue (#1464FF) per their integrator guidelines.
  • fixRoster-upload form now highlights the offending field in red when validation fails (instead of just showing a banner above the card). Auto-clears on the next keystroke.
  • fixFresh PTA admins on auto-created campaigns can now see "+ Add class" on the campaign head row (was hidden by an older single-organizer gate that no longer applied).
  • fixMobile hamburger menu backdrop no longer dims the panel itself; only the area behind it.
  • fixLucide icons across the for-PTA / for-school pages now render in the brand blue instead of near-black.
Strava brand attribution + Help Center
  • newHelp Center. A real /help page with a table of contents, search, and how-to mini-articles for every feature (SSO, auctions, P2P, athons, scavenger hunts, text-to-give, Stripe Connect, parental consent, and more).
  • newStrava brand attribution shipped. Required for Strava developer program approval. The Connect with Strava button now uses Strava's exact wording. Imported activities show a View on Strava link per row. Admin athon view source badges are clickable links back to strava.com. Powered by Strava attribution wherever Strava data is displayed.
  • newPost-OAuth Strava activity picker. After connecting Strava, participants now see their last 7 days of activities and can import any one as a tracked log. Previously the OAuth completed but the import UI was missing.
  • newContextual help icons on admin form fields where the meaning isn't obvious (auction reserve price, athon verification mode, district scope, Stripe Connect status).
  • newWhat's New callout on the dashboard so existing users can find features that shipped after their last visit.
SSO portfolio expansion + commerce hardening
  • newMicrosoft 365 / Entra SSO live. Multitenant. Cert-based client_assertion JWT (10-year cert, no client secret to rotate). Schools and districts on Microsoft can sign in with their work account.
  • newClassLink admin + parent SSO. Admin OAuth and parent-side SSO both live. Uses full scope (cascade /v2/my/info, /v2/my/profileinfo, id_token) so we can match parents to their kid's fundraiser.
  • newDistrict SSO playbook covering 5 providers (Microsoft, Google for Education, Clever, ClassLink, generic OIDC). Single SSO login gates the district dashboard.
  • newReserve price on auctions. Set a private minimum bid on any auction item. The auction won't auto-award if bids stay below reserve, you decide whether to award anyway, re-list, or cancel.
  • newText-to-give keyword admin UI. Set a per-org keyword from your Org Settings page. Donors text it to (515) 461-8728 and get a payment link back.
  • newMulti-property SMS architecture. A single A2P 10DLC brand and campaign covers every StanHattie property. ScanRaise rides on the verified registration so messages don't get filtered as spam.
  • newCommerce hardening: 4 confirmation emails (placed, shipped, delivered, refunded), atomic inventory (no overselling under load), abandon cleanup that releases held inventory after 15 minutes.
  • fixStripe Connect onboarding empty-email bug fixed. Eliminates the redundant pre-fill call that was breaking new merchant verification.
  • fixParental-consent grant now uses XFF first-hop+clamp (45 chars, IPv6-safe) so the audit log can't get hosed by a spoofed XFF header.
  • fixTwilio webhook signature validation rebuilds URL from forwarded headers (Cloudflare/Railway proxy chain doesn't forward original Host header to Flask). Real Twilio inbound was 403'ing because the URL didn't match the HMAC input.
  • fixLive page: switched QR library to qrcode-generator (the qrcode npm package has no UMD build); shows error immediately on first-load failure instead of waiting 60s; fixed overlapping overlays.
  • fixTicket QR security: check-in rejects raw ticket IDs, accepts valid signed QR tokens, accepts ID + email fallback, rejects ID + wrong email and forged tokens. Server-side HMAC verified on every check-in.
  • fixOIDC multitenant validation correctly detects {tenantid} placeholder in discovery issuer for Microsoft /common/, /organizations/, and /consumers/ tenants.
  • fix/start wizard auth panel now shows a "Sign in here" link for first-timers with an existing account. Hero CTA meta updated.
  • newCron-driven demo seed (/api/cron/seed-demo) so the public demo always has fresh, realistic data.
Smartwatch fundraising scaffolding
  • newGarmin Connect IQ submission. Direct integration scaffolded; awaiting Garmin's developer program reopening (currently paused on their side, no ETA).
  • newCOROS submission in flight.
  • newMulti-size favicon.ico (16, 24, 32, 48, 64 px) for legacy-browser fallback.
  • newFavicon link added to 103 HTML heads. /es/contact mirror page added. Sitemap entries refreshed.
  • fixAudit log: clamp X-Forwarded-For to first hop and 45 chars (the same VARCHAR overflow class that hit parental-consent).
  • fixDrop the "Submit your match request" employer-match CTA (was misleading, we don't actually broker employer matches yet).
Spanish site is real now
  • i18nLocale-aware nav, footer, hero rotator. One shared script detects <html lang="es"> and serves the right copy on every page.
  • i18n/es/start wizard fully translated - step indicators, field labels, placeholders, error messages, resume-draft banner, submit button.
  • i18n/es/donate and /es/d/{code} donation flow fully translated - amount buttons, donor fields, cover-fees checkbox, success states, athon pledge tabs.
  • i18n/es/find search results UI translated; dynamic rows now render in Spanish.
  • i18n/es/homepage hero gained the split layout with QR collage image, four trust chips, and proper CTA buttons to match the English version.
  • i18nAccent sweep across every /es/*.html file - campaña, recaudación, términos, política, código, cómo, más, configuración, nadatón, caminatón, pruébalo, fácil, and more.
  • newHomepage product line under the rotating hero headline: a clear one-sentence description of what ScanRaise is, for cold visitors who land before the rotator cycles.
  • newChangelog (this page).
  • fixLegal pages (terms, privacy, disclaimer, DMCA) now redirect /es/* → English; no more partial Spanish legal docs.
Bug sweep - friction removed from the primary funnel
  • fix/start no longer hides behind a login wall. Anyone can fill out the wizard; auth only kicks in at submit, and your draft auto-restores after sign-in.
  • fix/pricing now 301-redirects to the pricing anchor on the homepage instead of silently serving duplicate content.
  • fixFundraiser-not-found page now has two clear next actions - "Find My QR Code" and "Back to home" - instead of being a dead end.
  • fixCustom donation amount now shows a visible "Up to $2,500 per transaction" hint instead of silently rejecting larger values.
  • fixDuplicate footer rendering mid-page (footer.js was loading twice on some routes) - footer is now idempotent and waits for DOMContentLoaded.
  • fixCompetitive comparison row renamed from "QR codes" to "Per-person QR cards" - the new label is a defensible differentiator, the old one overstated the gap.
Recurring donations + Stripe Connect onboarding
  • newRecurring donation management on the org dashboard - list active recurring gifts, cancel from one place.
  • newOrg settings page (/orgs/<id>/settings) with Stripe Connect onboarding CTA, bank-account status, and return URL fixes.
  • new12 org-type-specific roster CSV templates - schools, sports (rec + travel), boosters, dance, band, scouts, church, fire, robotics, veterans, default.
  • fixMobile hero now scales correctly at 480px and 860px breakpoints.
Full pentest pass
  • fixSession management, rate limiting, strict CSP, admin moved to the admin.scanraise.com subdomain.
  • fix468 pytest tests passing. Zero console errors on key user flows.
Enterprise hardening - schools edition
  • newSDPC registered (Student Data Privacy Consortium).
  • newClever SSO + ClassLink SSO live for K-12 districts.
  • newDistrict-level management + /for/district landing page.
  • newCOPPA/FERPA compliance and Next Insurance (GL + E&O) coverage.