FPT University · Thesis 2025
SWallet
Reward-based student engagement platform
- Role
- Backend developer
- Stack
- 80+REST endpoints across five domains
- 95%Less detected check-in fraud
- 40%Fewer abandoned vouchers
Overview
A campus app where students check in at real locations, finish missions, earn points and redeem vouchers at partner merchants. Flutter on the phone, ASP.NET Core behind it.
What I built
- 80+ REST endpoints for campaigns, wallets, merchants, mini-game state and profiles, with one error and pagination contract.
- GPS check-in validated server-side with the Haversine formula, plus speed and cooldown heuristics against spoofing.
- A gamification engine for streaks, missions, badges and a lucky spin, with all state kept on the server.
System design
GPS anti-fraud check-in
Coordinates are checked against authorised zones with the Haversine formula and a configurable radius, then cross-checked with timestamps and travel speed.
Gamification engine
Rules for daily streaks, mission progress, badges and the lucky spin live server-side to prevent tampering, synced to the app over REST.
Voucher lifecycle
Earn, browse, reserve, redeem and expire, with inventory tracking and every transaction logged for audit and reconciliation.
One API contract
Endpoints are grouped by domain and share the same error shape, pagination format and status codes.
Problems solved
ProblemStudents used GPS spoofing apps to check in without being on campus.
SolutionLayered detection: server-side Haversine distance, timestamp and speed heuristics to reject teleporting, and cooldowns between locations. Detected fraud fell by 95%.
ProblemSimultaneous lucky-spin requests could hand out rewards without deducting spin tokens.
SolutionOptimistic concurrency with EF Core row versioning, so token deduction and reward assignment commit atomically or retry.
ProblemRedeemed-but-unused vouchers blocked inventory for everyone else.
SolutionA two-phase lifecycle, reserved then redeemed, with an hourly job that releases expired reservations and push reminders before expiry.
Gallery

Lessons
- Client-side validation is never enough for location features. Every GPS check has to be re-verified on the server.
- Optimistic concurrency with row versioning is simpler and faster than distributed locks for single-resource contention.
- A shared API contract saved weeks of frontend-backend integration debugging.