RideKit
Changelog

Everything that shipped — bugs included

Rendered at build time from docs/CHANGELOG.md in the product repo. The "Fixed" sections stay on the record — a changelog with only features is marketing.

[Unreleased] — Platform completion phases 1–4 (2026-08)

Added

  • Bidding/negotiation end-to-end — inDrive-style offer/counter-offer UI on both apps over the existing PricingMode.BIDDING backend, with a one-shot 60-second bid grace on dispatch.
  • Refunds — operator refund console (payment.refund permission): wallet debit first, then gateway refund with compensation on failure; refuses insufficient balance stating the max refundable. Corporate rides get credit notes instead (no gateway leg to reverse).
  • Women-only rides — market-gated (womenOnlyEnabled), fails closed at booking before pricing; driver side filtered in dispatch SQL.
  • Live trip sharing — tokenized public tracking page (identity, phone and money structurally excluded from the payload); unknown/revoked/expired tokens all 404 identically.
  • Safety centre — trusted contacts (cap 5), SOS fan-out SMS carrying a live-trip link, automatic ride check-in (long-trip and stationary signals, escalation to a HIGH incident), emergency numbers for all 37 markets in market config. Audio recording deliberately deferred — see docs/safety-audio-recording.md for the reasoning and the full design.
  • Driver retention — destination mode (2/day, dropoff must close ≥25% of remaining distance), trip preferences (pickup distance / min fare / long-trip / tier opt-outs), auto-accept through the normal accept path, payout limits endpoint with honest settlement estimates.
  • Corporate rides — book on a company account with credit-limit enforcement at booking, org settlement as a first-class ledger account (negative balance = the month-end invoice), org-owner portal in the rider app, admin billing console with credit notes and company payments.
  • Parcel delivery — full vertical: OTP-at-both-ends + photo proof of delivery (codes redacted from the courier's app), priced failure paths (free retry → return-to-sender at 60%, courier paid both legs), chain of custody in admin, per-market size/weight tier pricing, market gating.
  • Rentals — hourly packages (1h/10km … 12h/120km) seeded from each market's own economy fare, live overage meter mirrored in both apps, mid-trip destination edits, package-expiry sweeper, rider-ends-the-trip semantics.
  • Intercity/outstation — one-way (1.3×) and round-trip (0.85× with a 250 km/day floor), driver daily/night allowances and toll/permit line items, driver opt-in as a dispatch predicate, 30-minute dispatch lead on scheduled departures.
  • Local payment rails — M-Pesa STK push, Paystack, Flutterwave, OXXO vouchers (top-up only) and GCash behind a new AsyncTopUpProvider interface (instruction + later-outcome semantics); Pix retrofitted onto the same path. Each rail declares an honest integration status the admin shows as "Keys set · unverified" until proven against the live provider.
  • Languages — Thai, Vietnamese and Indonesian across rider, driver and admin (catalogs now 14/14/15), parity-test enforced.
  • Public banners endpointGET /content/banners?audience= serving only live, windowed banners with an app-safe payload.

Fixed

  • Ledger-corrupting minor-unit bugs (formatMoney/toMinorSafe hardcoded /100; JPY and KWD amounts rendered and stored wrong).
  • Tax invoices claimed issued without any authority response; local fallback is now local_only.
  • Re-inviting a revoked org member silently did nothing (stale soft-delete timestamp written back over the restore).
  • Unaccepted parcels were uncloseable (no_drivers terminal-state gap).
  • Payout reconciliation: confirmed failures now return driver funds; uncertainty never moves money.
  • Rail errors no longer surface to riders as HTTP 500.
  • backend/storage/ untracked from git — dev uploads (including driver KYC test documents) no longer ship in the product archive.

[Unreleased] — Gap Closure (P0 → P3)

Closing every item in Claude_Code_Gap_Closure_Spec.md, sequence admin → rider → driver. Each item verified end-to-end (UI → API → DB → response), all four codebases (backend / admin / rider / driver) tsc-clean.

Added

  • Multi-stop rides (up to 3) — multi-leg fare pricing (distanceKmMultiPoint), rider "+ Add stop" flow + multi-leg polyline, driver per-stop arrival progression, POST /driver/rides/:id/stops/:index/arrive (idempotent, ownership-checked). Verified: fare rises monotonically with total leg distance; 4th stop rejected (400).
  • Driver incentives / quests — operator-defined target campaigns (rides or earnings goal → wallet reward), admin CRUD (/admin/incentives), driver quests screen with live progress, idempotent ledger-backed award (posts as adjustment → shows in earnings). Verified: reaching target pays once, never double-pays.
  • Country Preset Pack — canonical 37-country data (currency/locale/timezone/dial/distance-unit/gateways/tax-engine/top-up presets), seed migration, installer applies + activates a market on onboarding, both apps' COUNTRY_DIALS regenerated from the same source.
  • Distance units (km/mi) — per-market distanceUnit on /branding, formatDistance() helper (km→mi at render), i18n unit labels.
  • Market-driven dates & timezone — locale-aware client date helpers; server-side formatInMarketTz() (Intl, zero deps); daily report buckets + tax-invoice issue date rendered in the market IANA timezone (storage stays UTC).
  • Call masking — provider abstraction (Twilio Proxy-style) with direct-dial default; GET /rides/:id/call-number returns a masked or real number, ownership-checked; both apps dial via the endpoint.
  • Government e-invoicing live wiring — all 4 tax engines (India GST / ZATCA / CFDI / NFS-e) credential-gated: real authority/GSP filing when configured, deterministic local invoice as dev/B2C fallback; never blocks a ride. See docs/E-INVOICING.md.
  • Referral programme, broadcast (push/email/SMS), CMS FAQ + Blog, provider credential store (AES-256-GCM), SMS providers (Twilio/MSG91), S3/R2 storage, RazorpayX payouts, social login (Apple + Google) — full slices, all verified.
  • DocsE-INVOICING.md (per-country credential requirements), BRANDING-ASSETS.md (white-label icon/splash spec).

Changed

  • White-label theming — welcome-screen gradients, Razorpay checkout name/theme, push LED colour, and wallet top-up presets all now derive from the market/branding config instead of hardcoded values; app icon/splash paths are env-overridable.
  • Status colours tokenized (rider + driver) — hardcoded success/danger/warn hex replaced with theme tokens that adapt to dark mode.
  • Money formatting — one shared formatMoney + useInstallation across the admin app (removed 10 local money() helpers).

Fixed

  • Promo money bug — a shown discount is now actually charged (promo persisted at booking, settled on the discounted fare).
  • Saudi gateway routing — SA now resolves to Moyasar (was falling through to Stripe); removed gateway keys with no provider code.
  • Socket event mismatch — offer-expiry now dismisses correctly (ride:offer_expired shared constant).
  • Silent error swallows — stale-tracking / fake-zero-balance / invisible-reject-failure surfaced to users; backend config-load failures now logged before defaulting (never silently price on defaults).
  • GeoJSON coords — pickup/drop-off markers now render from the raw ride entity's PostGIS GeoJSON in both apps.

[Unreleased] — Phase 3: Multi-Entity Panels

Added

  • Admin panel (Next.js, /admin) — OTP login, live dashboard (stats + trips, auto-refresh, sparklines, status pills, skeletons), drivers, riders, reports, dispatch. Premium UI pass (dark sidebar + light content); a deeper redesign is still planned.
  • Dispatcher — manual booking on a rider's behalf, force-assign + change-driver (ride.assign gated). Live-verified.
  • Geofencing / zones — PostGIS polygon zones (service / restricted / airport / surge), point-in-zone (ST_Covers, GIST-indexed), serviceability check. Live-verified with real Bangalore polygons.
  • Fleet / Corporate / Franchise — unified organizations model + memberships; create, add/remove members, scoped reports (fleet earnings / corporate billing vs credit limit / franchise city), owner-only isolation (403 otherwise). Live-verified all three types.
  • Docs — Admin Guide (panels, dispatch, zones).

Fixed

  • Added admin panel origin (:3200) to backend CORS (was blocking the panel login).

[Unreleased] — Phase 2: Money & Operations

Added

  • Wallet on the double-entry ledger — top-ups, balances, idempotent.
  • Ride settlement + commission — completing a ride splits the fare (rider → driver + platform commission) in one balanced entry; commission per vehicle type, floored.
  • Pricing engine extras — configurable surge/night multipliers (pricing_rules, compounding, midnight-wrap windows), waiting charge, flat toll.
  • Cancellation policy engine — admin-configurable free window, late-cancel fee, no-show fee, driver/rider fault; rider may go into debt for mandatory fees.
  • Promo codes (flat/percent + cap, limits, validity) and referrals (refer-a-rider + refer-a-driver, separate rewards) — all credited via the ledger.
  • Payout engine — driver wallet → bank, reserve→process→paid/failed-refund, pluggable rails (manual + RazorpayX stub), payout.approve gated.
  • Payment integration — gateway charge → wallet top-up bridge; real Stripe (PaymentIntents, India 3-D Secure handled via webhook capture), signed webhook verification; manual/cash provider; refunds.
  • Reports — revenue, drivers, customers, trips, tax (taxable base) — RBAC report.view, date-ranged, reconciled from the ledger.
  • Docs — Admin Guide (money & operations), User Manual (wallet & payments).

Fixed (found via live e2e testing)

  • Cancellation fee on a rider with no balance was blocked — rider wallet now goes into debt for mandatory fees.
  • Assigned driver was not released back to online on cancel/no-show.
  • Discovered (via real Stripe keys) the account is India-registered → server-side auto-capture isn't possible; reworked to webhook-driven capture.

[Unreleased] — Phase 1: Core Ride Loop

Added

  • Ride schema — vehicle types, vehicles, driver profiles (PostGIS location), rides, immutable ride-event audit, ride bids. GIST spatial indexes.
  • Driver presence — online/offline, location updates (PostGIS geography).
  • Dispatch engine — PostGIS nearest-driver query (ST_DWithin + ST_Distance), distance + rating weighted, GIST-indexed. Live-verified ranking.
  • Fare estimate — base + per-km + per-minute, min-fare floor, per vehicle type; integer minor-unit money math.
  • Ride loop — book → accept → arrive → start → complete, enforced by a state machine; every transition audited and pushed to rider + driver.
  • Bidding — inDriver-style negotiation toggle (rider bids, driver counters, rider accepts at agreed fare).
  • Realtime — Socket.IO ride:status / driver:location events to per-user rooms (JWT-authed, Redis-adapter fan-out). Live-verified.
  • Basic admin — RBAC-gated live trips, dashboard stats, driver/rider lists.
  • Docs — User Manual (booking), Driver Guide (going online + trip flow).

Fixed (found via live e2e testing)

  • rides.pricing_mode column mapping missing on the entity (every ride fetch 500'd).
  • Bidding accept DTO missing a validation decorator (rejected by global whitelist).

[Unreleased] — Phase 0: Foundation

Added

  • Project scaffold — NestJS + TypeScript, ESLint/Prettier, Node 20 pinned (.nvmrc, engines).
  • Config + env validation — typed config; boot fails fast on missing/weak vars (JWT secret ≥32 chars; prod rejects placeholder secrets and empty CORS).
  • Database — PostgreSQL + PostGIS via TypeORM; UUID PKs, soft deletes, dormant tenant_id, migration tooling. Extensions auto-enabled by first migration.
  • Wallet ledger — double-entry, immutable. DB-enforced: per-currency balance, ≥2 postings, positive amounts, immutability triggers, one system account per (owner_type, currency).
  • RBAC — roles + permissions + @RequirePermissions guard; 8 system roles seeded.
  • Auth — phone OTP (Redis, hashed), JWT access + rotating refresh tokens (theft detection), password login, social-login structure, SMS provider plugin (dev log provider).
  • Payments — gateway plugin layer (manager pattern); manual/cash provider + Stripe stub with HMAC webhook-signature verification.
  • Storage — local + S3/R2 driver abstraction, config-selected; path-traversal guarded.
  • Realtime + queues — Socket.IO with Redis adapter (multi-instance), BullMQ background jobs.
  • Observability — pino structured logging, Sentry capture filter, /api/health readiness (DB+Redis, returns 503 on failure with bounded Redis ping).
  • Ops — Docker multi-stage image (non-root), docker-compose (Postgres+PostGIS, Redis), GitHub Actions CI (lint→build→migrate→test→docker build).
  • Docs — Installation + Deployment drafted; section tree scaffolded.

Fixed (found via live testing during Phase 0 audit)

  • Ledger: cross-currency false-balance, duplicate system accounts, zero-posting entries, mixed-case currency split wallets, soft-delete vs immutability crash, idempotency race returning 409/500 instead of the existing entry.
  • Runtime: health check hanging on dead Redis; 5xx errors silent without Sentry DSN.
  • Auth: refresh replaced_by typed uuid vs token hash (every refresh 500'd).
  • Build: broken package-lock.json failing npm ci in Docker/CI.