Gado Imkerei
Website and shop for an apiary in Däniken, Switzerland: from the cart to the paid order.
- Role
- Sole developer, commissioned by an external client
- Period
- July to September 2026
- React 19
- TypeScript
- Tailwind CSS v4
- Supabase
- Postgres
- Row Level Security
- Deno
- Stripe
- Resend
- ASPSMS
- Vercel
What it does
Beyond the first paragraph this page is still in German. A full translation is on its way.
An apiary that had sold at the farm gate and by word of mouth needed a presence people can also order from, without turning into an operation somebody has to run every day. What came out of it is the complete purchase path: cart, checkout with pickup or shipping, Stripe Checkout with card and TWINT, customer accounts, order notifications by e-mail and SMS, and a protected report for the client. Halfway through, the scope changed: shipping and online payment arrived after the pickup checkout already stood, which is the part a side project cannot simulate.
Eine Imkerei verkaufte bis dahin ab Hof und über Zurufe. Gesucht war ein Auftritt, über den auch bestellt werden kann, ohne dass daraus ein Betrieb wird, den jemand täglich bedienen muss.
Entstanden ist der vollständige Kaufweg: Warenkorb, Kasse mit Abholung oder Versand, Bestellung per Formular oder WhatsApp, Stripe Checkout mit Karte und TWINT, Kundenkonto, Bestellmeldung per E-Mail und SMS und eine geschützte Auswertung für die Kundin. Dazu die Schweizer Rechtstexte, die ein Verkauf an Konsumenten verlangt.
Mitten im Projekt änderte sich der Umfang: Versand und Online-Zahlung kamen dazu, nachdem die Kasse für Abholung bereits stand. Aus der One-Page-Site wurde ein Shop mit Datenbank, Zahlungsanbieter und Meldewegen. Das ist der Teil, der sich in einem Eigenprojekt nicht simulieren lässt: Der Entwurf muss eine Änderung aushalten, die man sich nicht ausgesucht hat.
Numbers
- lines of code
- ~8000 lines of code
- payment methods: card, TWINT, cash
- 3 payment methods: card, TWINT, cash
- external client
- 1 external client
Screenshots




Eine Lücke in der eigenen Insert-Policy, selbst gefunden
Bei einem Sicherheitsdurchgang gegen die eigene Datenbank liess sich über den öffentlichen anon-Key eine Bestellung mit dem Status "bezahlt" und beliebigem Betrag anlegen, und die Datenbank antwortete mit 201. Row Level Security war aktiv, aber die Insert-Policy prüfte nur, wer schreiben darf, nicht was geschrieben wird. Die Policy verlangt seither einen definierten Anfangszustand; "bezahlt" kann ausschliesslich der signierte Stripe-Webhook setzen. Die eigentliche Lehre steckt nicht im Fix, sondern in der Frage, die ihn gefunden hat: Nicht "ist RLS an?", sondern "was kann jemand mit dem Schlüssel tun, der ohnehin im Browser liegt?".
Stack
- Frontend
- React 19
- TypeScript (strict)
- Vite 8
- Tailwind CSS v4
- shadcn auf Base UI
- lucide-react
- Fontsource
- Backend
- Supabase
- Postgres mit Row Level Security
- Auth mit eigenen Mail-Vorlagen
- vier Edge Functions in Deno
- SQL-Migrationen über die Supabase CLI
- Region Zürich
- Payment and messaging
- Stripe Checkout mit Karte und TWINT
- signierte Webhooks
- Erstattung
- Resend auf eigener Domain
- ASPSMS
- WhatsApp-Deeplinks
- Quality and operations
- ESLint
- tsc als Build-Gate
- eigene Prüfskripte vor jedem Build
- Vercel mit SPA-Rewrites
- WebP-Bildpipeline
Status and reading
Delivered, live since September 2026 at gadoimkerei.ch. Repository private, access on request.