Service Marketplace Platform
Solo-architected, solo-built two-sided service marketplace for Malaysia — Go backend, Flutter mobile, Next.js web, PostGIS spatial matching, and a full escrow payment engine.
Overview
SolveMY is Malaysia's answer to Kaodim — a platform where homeowners post jobs, vetted providers quote and win bookings, and payment is held in escrow until the work is done. Built everything from scratch: a Go 1.24 Clean Architecture backend with 90+ REST API endpoints, a Flutter 3.41 mobile app with 30+ screens in 3 languages, and a Next.js 14 web portal for seekers, providers, and admins. PostGIS handles geolocation radius matching. iPay88 handles Malaysian payments (TNG, DuitNow, FPX, card). A double-entry ledger tracks every financial state transition. Contact masking in real-time chat enforces on-platform communication.
What I Built
- •Architected a Go 1.24 Clean Architecture backend (domain/service/repository/handler layers) with 90+ REST API endpoints across 15 service domains and 19 test files with mock-based coverage
- •Designed a 13-table PostgreSQL 16 schema with PostGIS — spatial auto-sync triggers kept lat/lng columns and PostGIS geometry in sync, enabling real radius queries on job posts and provider locations
- •Built a complete escrow and double-entry ledger system: payment held on booking, released after seeker confirms completion, with 8 ledger entry types tracking balance before/after every transition
- •Integrated iPay88 (Malaysian payment gateway) with SHA256 HMAC signature generation, webhook handler responding "RECEIVEOK", and support for Touch 'n Go (523), DuitNow (538), FPX, and card
- •Shipped a Flutter 3.41 mobile app with 30+ screens across 7 feature modules (auth, jobs, services, bookings, chat, payments, profile), Riverpod state management, GoRouter navigation, and i18n in 3 languages
- •Built a Next.js 14 web portal (seeker + provider + admin dashboards) with role-based routing, Zod-validated forms, and TanStack Query for server state
- •Designed dual-mode auth: Clerk JWT with JWKS verification and webhook-based user sync (migration 000007 makes clerk_id nullable), with native bcrypt password auth fallback
- •Implemented contact masking in real-time messaging — flagged and blocked messages containing contact details are replaced with [Message blocked: Contains contact information]
- •Caught a production-impacting contract mismatch during integration testing: Dart's DateTime.toIso8601String() emits no timezone suffix, Go's RFC3339 unmarshaling requires Z — fixed with toUtc().toIso8601String()
- •Produced a minimal Docker image using multi-stage build (FROM scratch runtime) with statically linked binary, embedded CA certs, and timezone data
Key Achievements
- 90+ REST API endpoints, 13 PostgreSQL tables, 30+ Flutter screens, 20+ Next.js pages — all solo
- PostGIS spatial triggers auto-sync lat/lng → geometry columns on every insert/update, enabling native radius queries
- Escrow + ledger architecture: financial correctness enforced at the database level, not just the application layer
- Caught a silent DateTime RFC3339 contract bug between Dart and Go before production through real integration testing
- FROM scratch Docker runtime — minimal attack surface, no shell, no package manager, embedded certs
- Multilingual at the schema level — title_en/title_ms/title_zh columns on all content entities, not just at the app layer