Fallstudie
Bluumo: En marknadsplats för välbefinnande på båda sidor
Skala
181,000+
Rader i TypeScript
482
Filer
120+
Skärmar / sidor
123
Återanvändbara komponenter
80
Kantfunktioner (Deno)
112+
Service-/verktygsmoduler
2,199+
Git-commits
2
Aktiva betalningsleverantörer
3 / 7
Språk (live / infrastruktur)
3,937
Översättningsnycklar
13
i18n-namnrymder
Problemet
Grundaren drev ett nystartat företag inom hälso- och friskvård med en WordPress-webbplats i broschyrformat och utan någon som helst operativ kapacitet. Varje telefonsamtal, varje bokning och varje faktura sköttes manuellt. De behövde snabbt få till stånd en fullt fungerande marknadsplats där båda parter kan interagera - där kunderna kan hitta och boka hälso- och friskvårdsleverantörer och leverantörerna kan hantera sina kalendrar och intäkter - både på webben och i dedikerade mobilappar.
Vad som byggdes
Kundgränssnitt (11+ skärmbilder)
- ·Sökning bland tjänster med filter för kategori, pris och varaktighet
- ·Sökning efter leverantörer med betyg och jämförelseverktyg
- ·Platsbaserad sökning med interaktiva kartor
- ·Tillgänglighet i realtid och omedelbar bokningsbekräftelse
- ·Finska banköverföringar, kortbetalningar, MobilePay, Edenreds friskvårdskort, stöd för ersättning från KELA
- ·Hantering av återkommande bokningar
- ·Köp av presentkort med generering av PDF-fil
- ·Referensprogram med automatiska belöningar
- ·Prenumerationsnivån Bluumo Plus
- ·Röstsamtal i appen via Agora
- ·"Helmi" - en AI-concierge baserad på Claude som hjälper till med bokningar
Leverantörsportalen (13+ skärmbilder)
- ·Dokumentbaserad verifiering vid registrering
- ·Hantering av flerspråkiga profiler
- ·Visuell tillgänglighetskalender med synkronisering med Google Kalender
- ·Dynamisk prissättning (rusningstider, rabatter, tidsbaserade justeringar)
- ·Meddelanden om bokningar i realtid
- ·Resultatuppföljning och utbetalningshistorik (Holvis utbetalningsinfrastruktur är uppbyggd, men ännu inte aktiv)
- ·Bokningsstatus framskrider genom delsteg med visuella slutförandeindikatorer
- ·'Markera som klar' - åtgärd för att slutföra leverantörens bokningsarbetsflöde
- ·Granska hanteringsverktyg och konfiguration av tjänsteområden
Företagsportal för B2B
- ·Företagsregistrering med godkännandeprocesser
- ·Personalhantering med massåtgärder
- ·Bokning av välbefinnandedag (enskilda och gruppbokningar)
- ·Uppföljning av kostnadsställen, budgethantering, godkännandeköer för chefer
- ·Fakturaanalys, användningsrapportering, automatisk fakturagenerering
- ·System för offertförfrågningar
Administratörspanelen (12 skärmbilder)
- ·Plattformsövergripande nyckeltal och analyser
- ·Hantering av leverantörsverifiering
- ·Överblick över bokningar och supportsystem
- ·Bloggpublicering med SEO-metadata
- ·Kampanj- och marknadsföringshantering
- ·Återbetalningar och fakturahantering
Skärmdumpar
Teknisk arkitektur
Säkerhetsgranskning (juni 2026)
En oberoende säkerhetsgranskning i juni 2026 identifierade och åtgärdade en rad sårbarheter innan de hann nå produktionsmiljön.
- Verifiering av betalningar på serversidan - belopp härleds från databasen, inga klientbetrodda priser
- Bokningsprisgolv blockerar utmålsförsök under gränsvärdet
- JWT-verifiering har lagts till i alla kantfunktioner
- Rabattkoder, presentkort och socialfondskrediter hanteras på serversidan
- AI-chatt begränsad till sessionsägare via RLS
- Begränsning av begärandetal för API-ändpunkter
- Rensning av XSS i blogginnehåll
- GDPR: fördröjda raderingar utförda, fullständig dataexport levererad
- Varningar från Supabases säkerhetsrådgivare: 105 till ~36
Prestanda
- ->Interaction Next Paint (INP) förbättrades från 1 280 ms till under 200 ms genom memoisering av StyleSheet
- ->Tunga arbetsmoment skjuts upp för att möjliggöra omedelbar visuell återkoppling
- ->Stöd för OTA-uppdateringar via EAS för omedelbara produktionsuppdateringar utan granskning i appbutiken
Vad jag lärde mig
En marknadsplats hanterar andras pengar, vilket förstärker konsekvenserna av att ha fel på ett subtilt sätt. De som drabbades hårdast var alla tysta.
Ett standardvärde som betyder ”ej angivet” måste vara NULL
Varje bokning medförde en fast provision på 20 %. Prissättningen berodde på om variabeln commission_rate angavs: om värdet inte var null innebar det en uttrycklig B2B-överskrivning, vilket innebar att både GMV-nivåerna (24/22/20/18) och Bluumo Pro-rabatten hoppades över. Kolumnen hade ett standardvärde som inte var null, så värdet saknades aldrig och överskrivningsgrenen vann alltid. Inga fel uppstod, beräkningarna var korrekta, men varje rad i tabellen var felaktig. Genom att ta bort standardvärdet kunde en utelämnad sats registreras som NULL, vilket gjorde att triggern kunde beräkna korrekt. Om en förgrening baseras på närvaro, är det närvaron - inte värdet - som schemat måste kunna uttrycka.
En tillåtande fallback döljer en funktion som aldrig har fungerat
Avståndsfiltret fungerade inte. Leverantörer inkluderades även om de saknade koordinater, vilket är en rimlig standardinställning, och ingen kodväg skrev någonsin ut leverantörens latitud eller longitud, vilket innebar att alla leverantörer använde fallback-lösningen och visades inom vilken radie som helst. Funktionen såg ut att vara implementerad, returnerade rimliga resultat och var inaktiv. En fallback som ignorerar det tomma fallet gör att det tomma fallet aldrig uppmärksammas.
Att återkalla en behörighet ändrar innebörden av select('*')
Att låsa finansiella kolumner bakom behörigheter på kolumnnivå gjorde att läsförfrågningar som begärde all information inte fungerade. PostgREST kan inte i tysthet begränsa ett select('*') till de kolumner som en roll faktiskt har behörighet att se; utan SELECT på tabellnivå genererar det istället ett fel. Lösningen är att namnge kolumnerna explicit vid varje anrop, vilket ändå är bättre praxis, men lärdomen är att skärpta behörigheter också är en förändring på klientsidan, inte bara i databasen.
Låt de valfria delarna fallera, inte transaktionen
Push-meddelanden på den här plattformen returnerar ett rent 500-fel. Istället för att låta detta framstå som ett misslyckande övergår flödena som använder dem till e-post och meddelanden i appen, och en kontaktadress för leverantören som kan vara tom faller tillbaka till kontots e-postadress istället för att meddelandet tas bort. Att i förväg bestämma vilka delar av ett flöde som får misslyckas i det tysta, och vilka som aldrig får göra det, är det som främst gör att en betalningsväg känns pålitlig.