← Projects
In Development 2024 – Present

Qifa

Solo-built beauty services marketplace — client app, merchant app, back-office, and API — from first line of code to production-ready platform

Qifa Shop — Welcome / landing screen Landing screen — role selection before login
Select account type — Shop Owner or Worker Shop owner vs worker role selection
Phone input with terms acceptance prompt OTP auth — phone input + terms consent
OTP verification screen OTP verification
Shop home screen after successful login Merchant home — post login
Post-login screen After login — dashboard entry
Location permission prompt Location permission — required for order routing

What it is

Qifa is a two-sided marketplace for beauty and personal care services in China. Think Didi, but for salon bookings. Customers use one app to find nearby salons, browse available services, and submit a request. Salons use a separate merchant app to receive those requests, respond with a custom offer (services, price, assigned worker), and manage their queue.

The platform is backed by real business stakeholders, with one holding approximately 33% company share. I joined as the sole technical person and have built the entire product from scratch — from an empty Flutter project to a multi-app ecosystem with a shared backend API named QuadroPod.

The platform scope

This is not a single app. It is an ecosystem of four connected surfaces:

  • Qifa Client — the customer-facing Flutter app (iOS + Android)
  • Qifa Shop — the merchant Flutter app for salons
  • Qifa Office — internal back-office management suite
  • QuadroPod API — the Django REST backend powering all three apps, deployed via Docker with aaPanel

Each app is distinct in architecture, user role, and business logic, but all share the same backend under different authentication contexts.

How an order works

  1. A customer opens Qifa Client, enables location, and sets a search radius (e.g. 5 km)
  2. They browse nearby salons on a map and submit a service request
  3. Every matched salon in range receives the request via the Qifa Shop app
  4. Each shop has a time window to respond — if they miss it, the request expires for them
  5. The shop’s response includes: which services they’ll provide, the total price, which worker they’ll assign, and whether their shop-side insurance applies
  6. The customer sees a list of all offers that came in and picks the best one
  7. Further details are confirmed — appointment time, coupon redemption, final worker selection — and the order is placed

Insurance as a product feature

One of the more interesting business decisions was a two-tier insurance system:

  • Platform insurance (Qifa) — a guarantee backed by the platform itself, giving users compensation if a service falls below standard
  • Shop insurance — an optional add-on controlled by each individual merchant, toggled per-service-response

Implementing this required careful backend modelling — insurance eligibility, compensation ratios, and disable-per-shop flags all needed to be tracked at the order level, not just the account level.

Technical decisions and what made them hard

Maps — the long way around

The first major challenge was maps integration. The initial plan was Gaode (AMap), the dominant map SDK in China. Flutter support for AMap was poor — limited wrappers, inconsistent documentation, and no reliable path to getting the API keys working inside Flutter’s platform channel architecture.

After weeks of fighting with AMap and then trying Baidu Maps (also blocked: couldn’t obtain the required API key), I eventually built a solution using Tencent Maps and a direct WebView fallback, giving users the choice between available Chinese map providers depending on their device and region.

That experience was expensive in time but valuable. It forced me to understand Flutter’s native platform channel layer at a level I wouldn’t have reached otherwise.

State management evolution

The client app started with BLoC throughout. After bringing GetX into the shop app to handle instance order flows, the performance improvement was significant — less lag in the confirmation flow, cleaner routing. The client app is being evaluated for a partial GetX migration based on those results.

The architecture follows Clean Architecture across both apps:

  • Domain layer: entities, use cases, repository contracts
  • Data layer: remote data sources, model mapping, repository implementations
  • Presentation layer: BLoC/GetX, views, widgets

Rating system

The QuadroPod API has a harmonic-weighted rating calculation:

  • Service quality: 40%
  • Environment: 20%
  • Worker quality: 40%

Shop ratings update automatically when reviews are posted via Django signals. Management commands exist to recalculate ratings on demand and monitor drift. Getting this right required fixing corruption bugs in the initial naive implementation.

Operational visibility

Early in development, ops couldn’t tell what was happening inside active orders. I built a custom log dashboard — a separate web interface giving the team real-time visibility into order pipeline states. This was not a requested feature. I added it because without it, debugging production issues required reading raw database queries.

Payment integration (upcoming)

WeChat Pay and Alipay integration is the final major technical milestone. Payment SDKs in China require business license verification and platform approval, which is a non-trivial process. This is the current blocker for launch, not the app itself.

What’s been built

  • OTP-based phone authentication, multi-language support (English / Mandarin)
  • Full address management with map-based selection and three-tier province → city → district region selector
  • Product catalog, cart, wishlist, coupon system (platform + shop-level)
  • Dual insurance system with per-order eligibility tracking
  • Appointment booking and order lifecycle management across multiple states
  • Push notifications, integrated ChatWoot for customer service chat
  • Shop membership module, promo banners, review voting with upvote/downvote
  • Pagination, filtering, and distance-based discovery in the API
  • Docker deployment via aaPanel on a self-hosted server

What I learned

Building this solo has been the most complete engineering education I’ve had. Every problem was mine to solve — architecture, API design, mobile, DevOps, business logic, and operational tooling.

The hardest part was not any individual feature. It was maintaining coherent architecture across four separate codebases while product requirements shifted based on stakeholder discussions. Keeping Clean Architecture disciplined under that pressure is something I would do differently from the start on the next project.

The product ships when WeChat Pay is live.