Data Model (starter)
User { id, role[rider|driver|admin], name, email, phone, rating, createdAt }
DriverProfile { userId(FK), vehicleMake, vehicleModel, plate, licenseDocUrl, kycStatus }
PaymentMethod { id, userId(FK), provider[stripe], token, last4 }
Trip {
id, riderId(FK), driverId(FK|null), status[req|acc|pick|inprog|done|cancel],
pickupLat, pickupLng, dropLat, dropLng,
estimateFare, finalFare, distanceKm, durationMin,
requestedAt, acceptedAt, startedAt, completedAt
}
Payout { id, driverId(FK), amount, periodStart, periodEnd, status[pending|paid], providerRef }
Flag { id, userId(FK), tripId(FK|null), reason, note, createdAt }
API (v1 outline)
POST /auth/otp/request → { phone/email }
POST /auth/otp/verify → session
GET /drivers/me → driver profile
POST /drivers/me → update profile, docs, availability
POST /rider/quote → { pickup, dropoff } → { estimateFare, etaMin }
POST /rider/request → create Trip(req)
WS /rider/live → trip status stream
GET /driver/queue → next request (poll/WS)
POST /driver/accept → Trip(acc)
POST /trip/start → Trip(inprog)
POST /trip/complete → Trip(done) → charge rider → enqueue payout
GET /admin/overview → basic stats (read-only)
Notes: WebSocket (or SSE) channels for live status are ideal, but polling works for MVP. Payments integrate Stripe Checkout/Payment Intents for rider and Connect for driver payouts.