Rebuilding a small town's discount card as an app
A paper card with one flat discount printed on the back kept good merchants off it entirely. We built the phone version, and a fundraiser around it.

Where this stands
As of August 2026 the app and the web portals are built and running against real data, with app store submission, the cutover from Stripe test keys to live mode, and a final legal pass still open before launch.
Lisa Uhrick runs a shop in Cookeville and could not put a flat 10% on everything she sells. Some of what's on her shelves doesn't have 10% in it. So she wasn't on the Star Card, and other merchants were out for the same reason. The program's ceiling showed up as businesses quietly declining, not as customers complaining.
Discovery
Lewis Matheney was the one who said it should be a phone app. His reasoning was practical. Let each merchant write their own offer and the shops that were locked out can come in. Take away the card and there’s nothing to lose or leave at home. Put it on a screen and the program can finally see what happens after someone buys a membership.
Most of the constraints were about the counter. Merchants in a town this size are not going to install software, scan anything, or learn a device to honor a discount they're already paying for out of their own margin. Whatever we built had to work if the only thing the merchant ever does is look at a phone someone hands across the register.
The other constraint was money. Discount cards in Tennessee mostly sell through school and club fundraisers, and the groups doing the selling keep roughly half of what they bring in, after order forms, inventory, and delivery day eat the rest. That comparison reshaped the second half of the product. If a card sells through a link instead of a catalog, most of the cost of running a fundraiser disappears and the group's share can go up.
Key Takeaway
The card's real defect was the single number on the back, not the plastic. Once each merchant writes their own offer, the shops that were locked out can join, and the offer becomes something that can change month to month. The phone was the means. The flexibility was the point.
What We Built
The customer app is Flutter, iOS and Android from one codebase. Members browse participating shops as a grid or on a map, search by name or category, and open a shop for its current offer, hours, phone, and directions. Sign-in runs through Better Auth with Google, Apple, or email and password. A savings tab totals what the member has actually saved across the year, which is the honest argument for renewing.
Redemption has no scanner in it. The member taps Redeem, and the app shows the offer terms, their name, and a live timestamp, valid for a few minutes so a screenshot doesn't travel. The merchant looks at the screen and applies the discount. That tap writes the redemption, and the offer's estimated value gets copied onto the row at the moment it happens, so the savings total stays correct even after the merchant changes the offer.
Nearby alerts run on the device. A member who opts in gets a push when they are close to a participating shop with a live offer, and the shop coordinates never leave the phone. Matching, dwell, and throttling all happen locally; the server only ever hears that something fired. iOS monitors 20 regions at a time, so the app ranks the nearest set and re-ranks when the member walks or drives out of the area those cover. We wrote a field-test checklist for it, because unit tests prove the rules and only a real walk proves the operating system wakes the app.
The web side is a single Next.js deploy carrying several audiences behind one login. StarCard staff manage merchants, offers, memberships, and announcements. Shop owners get their own portal to edit their offer and read their own numbers: profile views through to redemptions, repeat customer rate, a time-of-day heatmap, and how they compare with the median shop in their category. There is a printable counter sign and window cling generated per shop. Summit Studio has an operator view for billing reconciliation, webhook logs, and background job status. Roles are rows in a grants table rather than a column on the user, so one person can hold several of them without a migration.
The fundraiser side wasn't in the original spec at all. An organization runs a season and every kid selling gets their own link. The kids have no accounts: a parent receives a 30-minute email token that trades for a sliding session, because asking children to invent passwords was never going to be the answer. Sales through a link are credited to that kid, deep links survive an app install so a tap before download still attributes correctly, and the organization's share transfers to their bank account through Stripe Connect 14 days after the sale, with the split configurable per campaign.
Looking Forward
The software is close to finished. What's left is mostly the kind of work that doesn't show up in a diff.
- App store submission — the listing copy and the App Store and Play privacy label answers are written and waiting to be entered at submission time
- Live Stripe mode — Connect is wired and verified end to end in sandbox, but production still runs test keys, so the cutover is enabling Connect on the live account and swapping every key, webhook, and secret
- Signing the real merchant network — the seeded shop catalogue is placeholder data transcribed from a competitor's card, so the public shop directory is built and deliberately held behind a feature flag until real merchants sign
- A first fundraiser season — running one with a local organization and fixing whatever a real season breaks, which will be something
- Legal follow-through — counsel reviewed the terms and privacy policy in August, and the sections drafted from their comments still need a second read
None of that is interesting work, and it is exactly where products like this stall. It launches when those are closed and not before.
Have something on paper that belongs on a phone?
We build the app, the admin behind it, and the parts nobody thinks about until launch week.
Schedule a Conversation