Skip to content

Case studyNo. 03

Independent product

GuestTrim

A wedding guest list trimming app with a one-time paid unlock.

Plate 03. What is free, and what the one-time Pro unlock adds. Not a screenshot.

Problem

GuestTrim is a wedding guest list trimming app with a one-time paid unlock. The calculator and its results are free; a Pro purchase adds a planning pack.

Constraints & decisions

  • No server secrets in the browser. The Stripe secret key and the Supabase service-role key are never used in frontend code. Only the Supabase URL, the public anon key and the site URL are exposed to the browser; everything else is read by Netlify Functions alone.
  • The paid content is not in the bundle. The Pro planning pack is not included in the frontend bundle. A protected function, get-pro-pack, returns it only after it verifies the user's token and Pro status.
  • Unlock state lives on the profile. The frontend reads unlock state from profiles.is_pro. Payment fields are updated by the Stripe webhook and the protected entitlement functions.

Solution

The app is built with Vite, React, TypeScript and Tailwind CSS, with Supabase for the database and magic-link sign-in, Stripe Checkout for the one-time purchase, and Netlify Functions for the server side.

As built, it includes:

  • A landing page with the calculator, free results and a Pro upgrade card on /.
  • Supabase magic-link sign-in on /login.
  • A Stripe webhook that unlocks Pro purchases.
  • Account details and sign-out on /account.
  • The one-time Pro unlock through Stripe Checkout, recorded as email entitlements and the profile's is_pro flag.
  • The Pro planning pack, generated by a protected Netlify Function after sign-in.

A SQL migration creates the purchases and pro_entitlements tables and adds an email_normalized column to profiles.

Outcome

As built, GuestTrim covers the free calculator and results, magic-link sign-in, and the one-time Pro unlock listed above.