Projectoverzicht
Onze klant draait een cashbackplatform waarmee fysieke winkels — cafés, salons, boetieks, buurtwinkels — hun klanten kunnen belonen en, minstens zo belangrijk, eindelijk kunnen leren kennen. Winkels met een pand hebben zelden de klantgegevens die webwinkels vanzelfsprekend vinden: wie hun vaste klanten zijn, wanneer die voor het laatst langskwamen, wat ze terugbrengt. Het platform dicht dat gat door elke deelnemende winkel een cashback- en loyaliteitsprogramma in handen te geven, en elke klant een merkapp in de zak.
In plaats van één app uit te brengen vraagt het businessmodel om een familie applicaties: een vlaggenschipapp voor consumenten waar klanten deelnemende winkels ontdekken en hun cashback volgen, een aparte app voor winkeliers om aan te sluiten en hun eigen beloningen te draaien, en een groeiende catalogus white-label-apps — elk een volledig gemerkte, zelfstandig gepubliceerde cashbackapp voor een partnermerk of winkelketen, allemaal aangedreven door hetzelfde product.
Tegen de tijd dat het project volwassen werd, besloeg het ecosysteem 15+ live mobiele applicaties. De technische uitdaging was nooit één app goed bouwen — het was één codebase bouwen die een willekeurig aantal merk-cashbackapps kon worden zonder dat de onderhoudskosten met elke nieuwe partner meegroeiden.
We waren van begin tot eind verantwoordelijk voor het hele systeem: de gedeelde consumentenapp, de winkeliersapp, de white-label vloot, de cashbackregelmotor en backendservices, en het interne beheergereedschap dat van "een nieuwe merkapp lanceren" een selfserviceformulier maakte in plaats van een meerdaagse engineeringklus.
Onder NDA
Dit project valt onder een geheimhoudingsovereenkomst. De klant, de partnermerken achter de white-label apps en de gepubliceerde winkelvermeldingen worden niet genoemd — de schermafbeeldingen hieronder tonen het product op testgegevens.

Startscherm van de consumentenapp met cashbacksaldo en bonus voor vrienden uitnodigen

Winkeldetails met cashbackpercentage per aankoop

Vouchers en nieuwsoverzicht van de winkel

Instellen van één white-label applicatie

Lijst met white-label applicaties in de beheer-UI
Eén codebase, veel apps
De vlaggenschipapp voor consumenten en elke white-label variant draaien op één codebase. Er zijn geen forks, geen branches per merk, en geen gekopieerde projecten die synchroon gehouden moeten worden. Wat tussen apps verschilt — merkidentiteit, thema, functieschakelaars, beloningsinstellingen en metadata voor de winkelvermelding — is volledig uitgedrukt als configuratie die buiten de code leeft.
We bouwden de client rond een systeem van flavours en configuratie: één binaire blauwdruk die zijn merkidentiteit tijdens het bouwen uit een configuratiebundel per app oplost. Kleuren, typografie, logo's, splashschermen, app-iconen, bundle-identifiers en ingeschakelde functiesets worden per flavour ingespoten. Een bug die één keer wordt opgelost, of een functie die één keer wordt opgeleverd, landt bij de volgende release in elke app in de vloot — er is geen afwijking per merk om te onderhouden.
Cruciaal is dat de knoppen die elk merk bepalen bediend worden door managers zonder programmeerkennis. Via de beheer-UI stelt een niet-technische medewerker de iconen, kleuren, helderheid (licht of donker thema) en bepaalde labels van een nieuwe app in — en die keuzes vloeien rechtstreeks de build in zonder tussenkomst van een developer. De mensen die de uitstraling van een merk beheren kunnen die zelf wijzigen, in plaats van een ticket in te dienen en op engineering te wachten.
Dit is de economische hefboom van het project: de marginale kosten van de zestiende app liggen dicht bij nul, omdat het dezelfde code is als de eerste, met een andere configuratie.
Native buildconfiguratie in Dart
Een white-label vloot staat of valt bij de native buildconfiguratie — de Android- en iOS-instellingen waar de winkels werkelijk om geven. Applicatie-ID's en bundle-identifiers, weergavenamen, versie- en buildnummers, ondertekening, icoon- en splashassets, en regels per flavour in build.gradle en het Xcode-project moeten voor elke app en elke build kloppen.
In plaats van dat met de hand te onderhouden schreven we eigen Dart-tooling die de Android- en iOS-gerelateerde buildconfiguratie programmatisch beheert. Op basis van de configuratiebundel van een merk herschrijft het gereedschap de betreffende Gradle-, manifest-, Info.plist- en assetcatalogusregels naar de doelflavour voordat de build draait. Die logica in Dart houden betekent dat ze dezelfde bron van waarheid deelt als de Flutter-client en als volwaardige stap in ons buildproces draait — zodat de native configuratie van een merk deterministisch uit zijn configuratie wordt gegenereerd en nooit met de hand wordt bewerkt.
De winkeliersapp
Fysieke winkels hadden hun eigen gereedschap nodig — geen uitgeklede consumentenweergave, maar een app die speciaal is gebouwd om een beloningsprogramma te draaien en klanten te begrijpen. Vanuit de winkeliersapp sluit een eigenaar in enkele minuten aan op het platform, legt cashback- en kortingsregels vast, bevestigt aankopen om cashback toe te kennen, en ziet eindelijk wie zijn klanten zijn: nieuw tegenover terugkerend, hoe vaak ze langskomen, en hoe elke actie presteert. Voor veel van deze ondernemers is het het eerste gestructureerde beeld van hun klantenbestand dat ze ooit hebben gehad. We bouwden hem op hetzelfde Flutter-fundament en dezelfde gedeelde componentbibliotheek als de consumentenapps, zodat updates aan het designsysteem en platformfixes bij beide doelgroepen doorwerken, terwijl de werkstromen die specifiek zijn voor winkeliers in eigen modules leven.
Instelbare cashback- en kortingsregels
Het hart van het platform is een flexibele regelmotor waarmee elke winkel zonder onze hulp eigen beloningen ontwerpt. Via de winkeliersapp stelt een winkel regels samen als:
- Cashback per aankoop — een percentage of vast bedrag dat bij elke in aanmerking komende aankoop op het saldo van de klant terugkomt.
- Verjaardagsbonus — een beloning die rond de verjaardag van een klant automatisch wordt bijgeschreven om hem weer over de drempel te krijgen.
- Uitnodigingsbonus — een verwijsbeloning die uitbetaalt wanneer een bestaande klant een vriend uitnodigt die zich aansluit en zijn eerste aankoop doet.
Regels zijn data, geen code: elke winkel kiest zijn eigen combinatie, stelt bedragen, plafonds en geldigheidsperiodes in, en de motor beoordeelt ze op het moment van aankoop. Doordat de regels in configuratie leven, kan een winkel zelf en direct een verjaardagsactie starten of een cashbackpercentage wijzigen, in de hele app — en brengen wij daar nooit een release voor uit.
De beheer-UI en de geautomatiseerde buildpijplijn
Het meest opvallende onderdeel van het systeem is de interne beheer-UI. Een nieuw white-label merk aansluiten was vroeger een engineeringboodschap — de configuratie aanmaken, de bundle-identifiers registreren, ondertekeningsassets genereren, de winkelvermeldingen inrichten en het nieuwe target aan CI toevoegen. Dat hebben we samengevouwen tot één webapplicatie die managers zonder code bedienen.
Een manager vult een formulier in — merknaam, thema, assets, winkelmetadata, functiekeuze — en het systeem doet de rest:
Configuratie genereren: de beheer-UI bewaart de configuratie en assets van het nieuwe merk en produceert de configuratiebundel per flavour die de Flutter-client verbruikt.
Registratie in de pijplijn: het nieuwe apptarget wordt automatisch in de CI/CD-pijplijn geregistreerd. Het buildsysteem pikt de nieuwe flavour op, draait onze eigen Dart-tooling om de native Android- en iOS-configuratie toe te passen, en produceert ondertekende artefacten voor beide platforms.
Lanceren als selfservice: wat eerder bij elk nieuw merk een developer in de lus vereiste, werd een formuliergedreven, herhaalbaar proces. Het bedrijf kan het aantal gepubliceerde apps opschalen zonder het engineeringteam op te schalen.
Build- en uitrolautomatisering met Fastlane
Het hele build- en releaseproces is van begin tot eind geautomatiseerd met Fastlane — de opensource toolchain om mobiele builds en uitrol te automatiseren. We gebruiken het om elke stap te standaardiseren die anders over 15+ apps handwerk en foutgevoelig zou zijn: ondertekening en provisioning beheren, de iOS- en Android-binaries bouwen en ondertekenen, en elke app met bijbehorende metadata en schermafbeeldingen indienen bij de App Store en Google Play.
Doordat elke app één Fastlane-configuratie deelt die per flavour geparametriseerd is, is de hele vloot uitbrengen één herhaalbare handeling. Een nieuw white-label merk erft precies hetzelfde beproefde uitrolpad als de vlaggenschipapp — zonder eigen releasescripts.
De beheer-UI is een Vue-applicatie die op onze PHP-services en een PostgreSQL-datastore rust, met Ruby- en Fastlane-tooling die de build- en releaseautomatisering aanstuurt.
Backend en services
Onder elke app ligt een gedeelde backend die het cashbackgrootboek, de regelmotor, klantprofielen en de configuratie per merk draait. Eén set services bedient de hele vloot, met merkcontext die per verzoek wordt bepaald — zodat een klant in welke white-label app dan ook, in de vlaggenschipapp, of een winkel in de winkeliersapp met hetzelfde beproefde API-oppervlak praat, afgebakend tot zijn merk. Het grootboek legt elke bijschrijving en inwisseling vast zodat saldi altijd controleerbaar zijn, en klantprofielen geven elke winkel het inzicht in zijn publiek dat fysieke detailhandel doorgaans mist.
Uitgestelde deep linking en de uitnodigingsbonus
De functie die dit ontsloot was de uitnodigingsbonus. Verwijzingen zijn hoe het platform groeit: een tevreden klant nodigt een vriend uit, die op de uitnodigingslink tikt — maar vrijwel nooit de app al heeft. Met een gewone deep link ging die uitnodigingscontext verloren zodra de vriend in de winkel belandde: hij installeerde, opende de app, kwam op een generiek beginscherm terecht, en het verband tussen uitnodiger en genodigde was weg — en daarmee de uitnodigingsbonus die beiden was beloofd. Het meest virale moment in het product was stuk op de belangrijkste stap. We losten het op met uitgestelde deep linking, zodat de uitnodiging de installatie overleeft.
Omdat de hele vloot één codebase deelt, is de functie één keer gebouwd en erfde elke merkapp — vlaggenschip, winkeliers en alle white-label varianten — hem automatisch. We bouwden hem met eigen mechanismen in plaats van een externe dienst: een lichte iOS App Clip vangt de uitnodigingslink op voordat de volledige app bestaat en geeft hem door via een gedeelde App Group-container, terwijl de Google Play Install Referrer API de lading op Android meedraagt. Een wachtrij met openstaande links houdt de uitnodiging vast tot de app klaar is en de nieuwe klant zich heeft aangemeld en ingelogd — pas dan wordt de verwijzing toegekend en de uitnodigingsbonus zowel bij de uitnodiger als bij de nieuwe klant bijgeschreven, zodat de beloning correct oplost ook al overspant ze een installatie en een verse login bij een koude start.
De volledige technische aanpak — inclusief waarom de gangbare oplossing ophield te werken — beschreven we in Uitgestelde deep linking na Firebase Dynamic Links: App Clips + Play Install Referrer.
Resultaat
De klant kan nu in een fractie van de tijd die het ooit kostte een volledig gemerkte, winkelklare cashbackapp voor een nieuwe partner lanceren — zonder bij elke lancering engineers van productwerk te halen. De architectuur met de gedeelde codebase houdt 15+ apps in de pas: één fix, één functie, één release bereikt ze allemaal. De beheer-UI maakte van een terugkerend engineeringknelpunt een selfservicemogelijkheid, en de geautomatiseerde pijplijn maakte van elke nieuwe lancering een voorspelbare, herhaalbare gebeurtenis in plaats van een project op maat. En de fysieke winkels die ooit blind voeren, draaien nu hun eigen cashback- en kortingsacties en weten voor het eerst wie hun klanten zijn.
Dit is het soort platform dat we bouwen als opdracht voor white-label appontwikkeling — één Flutter-codebase die een hele vloot merkapps aandrijft.
