Glitz: the anatomy

A three-sided commerce marketplace. Brands list products, creators mint tracking links, buyers purchase through them. Commissions are attributed automatically, held thirty days, and paid out, with teams of creators splitting the commission by rule.

Live on iOS, Android and web. Built for a client; live payments and live Shopify confirmed May 2026.

Stack Flutter, Firebase, Cloud Functions, Next.js, Stripe Connect, Shopify

787
commits across mobile, backend and web
83k
lines of Dart, 94 screens
59
Cloud Functions
89
web pages against the same database
58
mobile builds shipped
34
Maestro flows, screenshot helpers included

Open the interactive anatomy · See it live

The signal path reads top to bottom. Every stage is a decision that was made in the code, with the problem it answered and what it cost.

01 Catalogue: A brand connects a store. Products import and stay in sync.

The problem

A brand will not maintain a second catalogue by hand. If the store and the marketplace drift, the link a creator shared last week sells a product that no longer exists at that price.

The decision

Shopify OAuth, import, sync and reconcile, with HMAC-verified webhooks for changes and draft orders on the way back. The brand does nothing after connecting.

What it cost

A whole integration layer for one storefront platform: 3,493 lines in one directory, 11 of the 59 functions. It is a seam by folder, not by interface. The storefront's name is in the collection names and the security rules, so a second storefront would have to earn that interface first.

03 Attribution: A buyer taps the link, often inside another app's browser.

The problem

Instagram's in-app browser, then the App Store, then a fresh install. The referrer does not survive that trip, and an unattributed sale is a creator who did the work and got nothing.

The decision

A 48-hour bridge: the tap is recorded server-side by IP with the user agent alongside, the app asks once on first launch whether anyone at its address tapped a link, and the newest unclaimed match wins. The recording endpoint is rate-limited and only accepts links that exist, so the pool cannot be flooded or poisoned.

What it cost

It is probabilistic. Two people behind one address installing within the window would be matched to the most recent tap, and the window and the rate limit are the whole defence. The note says so rather than pretending otherwise.

04 Charge: The buyer pays. A commission is minted against the link.

The problem

A colluding brand and creator could round-trip a purchase of product X and mint a commission against a link for product Y. And the first version checked for an existing commission and then wrote one, so two calls for the same sale a few milliseconds apart would both pass the check and mint twice.

The decision

Every commission must prove the payment bought this product. Commission documents get deterministic ids, keyed on the transaction rather than the payment intent because a multi-item cart shares one intent, so a duplicate create throws instead of paying.

What it cost

Exactly-once assembled from a document store's create-or-fail, with the mint called from the checkout rather than a webhook, so a failed mint never blocks a buyer and can leave a creator's entry missing. It is the same shape as the payments platform's webhook fix, solved the same way, which is the point of showing both.

05 Hold: Money sits on the platform for thirty days before transfer.

The problem

Splitting at charge time is simpler and wrong: a refund after the creator has been paid is a clawback nobody enjoys. Holding creates its own problems: float, stuck balances, transfers that fail.

The decision

Take the full charge, transfer after the hold. Around that: a retry ladder from one hour to sixteen over five attempts, then a platform alert; a balance monitor; and a reconciler for payouts that got stuck between states.

What it cost

A great deal more machinery than a split at charge. Every piece of it exists because a real case needed it.

06 Team: A creator's team splits the commission between leader and members.

The problem

Three ways to cheat it: mint a solo link to dodge the leader's cut, send a tampered split from the client, or carry a split over 100% so the leader's share goes negative.

The decision

A member's link is routed through their team automatically, earliest membership winning. The split is read from the team's own membership record, never the client. The split is clamped to 0..100.

What it cost

Creators who genuinely want a solo deal alongside a team cannot have one. That trade was made on purpose and is written down.

// Validate team member is on the team, and derive the split from the
// authoritative team_members doc — never trust the client's teamSplitRate.
if (teamMemberId && teamLeaderId) {
  const memberCheck = await db
    .collection("team_members")
    .where("leaderId", "==", teamLeaderId)
    .where("memberId", "==", teamMemberId)
    .where("status", "==", "active")
    .limit(1)
    .get();
  if (memberCheck.empty) {
    throw new HttpsError("invalid-argument", …);
  }
  const rawSplit = memberCheck.docs[0].data().splitRate ?? 50;
  resolvedTeamSplitRate = Math.max(0, Math.min(100, rawSplit));
}
Glitz, the function that mints a tracking link, as last committed 11 Aug 2026 and read 4 Sep 2026. Nothing renamed; one declaration line dropped and one error message shortened to an ellipsis.

07 Payout: Held money transfers to creators through connected accounts.

The problem

Payout minimums are per brand, not per creator. Grouping by creator alone leaves money stranded under the minimum forever, and the claim on it has already been taken.

The decision

Group by creator and brand, so each brand's minimum gates only its own money; groups under the minimum are skipped before anything is claimed and keep accumulating. Each entry is claimed in a transaction before its transfer, transfers carry deterministic idempotency keys, and marking paid is a separate step so a bookkeeping failure cannot be mistaken for a transfer failure.

What it cost

Found by auditing a money path that was already live, not by design up front. Two of the seven findings in that pass were open ways to lose money. Amounts under the processor's transfer floor still just sit, and the code says so.

08 Rules: Seven hundred lines of security rules, treated as code.

The problem

Parent rules do not cascade to subcollections. Without repeating the participant check, one collection-group query would return every private message on the platform.

The decision

Privilege-escalation defence on role fields, a guest tier excluded from every write surface, immutable financial records, and the participant check repeated where the platform makes it easy to forget. Rules tested like code.

What it cost

Repetition, on purpose, in the one file where a clever abstraction is more dangerous than a copy.

Notes on this platform
  • The sale that arrives with no referrerA creator shares a link on Instagram, the buyer taps it, installs the app and purchases. Nothing in that chain carries the creator's name across. Here is what does.
  • The minimum that belongs to someone elseHeld commissions are released to creators once a brand's payout minimum is met. Group the money by creator, the obvious way, and one brand's minimum ends up holding another brand's money.
  • The commission that must not mint twiceA buyer pays and the platform owes a creator a commission. Between those two facts sit a colluding pair, a call that arrives twice, and a cart with three items on one payment.
  • The rule that has to be written twiceFirestore security rules do not cascade to subcollections. Forget that in one place and a single query returns every private message on the platform.
  • Thirty days in the ledgerSplitting a payment at charge time is simpler and wrong: a refund after the creator is paid is a clawback nobody enjoys. Holding the money creates a different set of problems, and each piece of machinery around the hold answers one of them.
The other platforms
  • GoldVault: the anatomyPayment infrastructure for high-risk merchants. Identity, risk screening, a ledger and fee collection, sitting between a merchant's customers and the merchant's own card processing. It never holds anyone's money.
  • MicroGym: the anatomyA live-operations tool for small-group training, built with a Toronto gym. Trainers assemble workouts, schedule sessions with a roster, then run the session live on a tablet while an assessment engine adapts every exercise to every member in the room.