← All work

Loyalty / points engine

Making the points ledger impossible to update wrong

Balance is a sum of history, not a column, so double-awarding points is architecturally impossible.

97row-level security policies
31tables
2apps served by one backend

Problem

A loyalty platform where small businesses award points to customers needed to serve two mobile apps (a white-label business app and a customer marketplace) from one backend. In systems like this, balances drift from history and the same sale gets double-counted.

Constraint

The third and most expensive risk: touching the payment flow means entering PCI scope. The system had to work by reading transactions, never holding money.

Approach

The points ledger is append-only and trigger-protected; the balance is a view, so it can never drift from its history by definition. Sales land through a unique idempotency key, so the same webhook arriving three times still awards points once. 97 row-level security policies across 31 tables isolate everything by business. POS credentials are stored as Vault secret names, never values; phone numbers are hashed.

Result

Balance drift and double-awarding became architecturally impossible rather than a matter of code discipline, and both apps can be demonstrated end to end without a live backend.

POS sale event → idempotency_key UNIQUE → Append-only ledger → Trigger-protected balance → Double-award impossible
PostgreSQLSupabaseFlutterRow Level SecurityGDPR/KVKK

Need something like this?

Send two or three lines: what you have, what is broken or missing, and when you need it. I reply within 12 hours, Monday to Saturday.