GuestTrim
A wedding guest list trimming app with a one-time paid unlock.
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_proflag. - 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.