// Fare model tuned to the Lebanese market: // - Distance-based with a base fare (like CTaxi/local taxis), not time-only. // - Typical Beirut rides land in the $3–8 range riders expect. // - Prices are quoted in USD (the de facto ride-hailing currency) with an // L.B.P. equivalent shown for cash settlement. import { DEFAULT_SERVICE, SERVICES, type ServiceId } from "@/constants/services"; import { tr } from "@/lib/i18n"; export const FARE = { base: 1.5, // USD, flag drop perKm: 0.55, // USD per kilometer of the trip perMin: 0.15, // USD per minute of the trip minimum: 3.0, // USD minimum fare } as const; // Parallel market rate used for the L.B.P. cash equivalent shown in-app. export const LBP_RATE = 89500; /** * The platform's cut of each completed fare. * * Until this existed, the driver's earnings and the rider's fare were the same * number: the dashboard summed `fare_price` and called it "Today's earnings", * so a rider reading their receipt was reading the driver's revenue, and the * company's own books had no line of its own. Splitting the fare is what makes * "what the driver keeps" and "what the company earns" separate, answerable * questions. */ export const COMMISSION_RATE = 0.2; export type FareSplit = { /** What the rider pays. */ fareCents: number; /** What the platform keeps. */ platformFeeCents: number; /** What the driver is owed. */ driverPayoutCents: number; /** The rate this split was computed at. */ rate: number; }; /** * Split a fare into the driver's payout and the platform's fee. * * The fee is rounded and the payout is the remainder, so the two always add * back up to exactly the fare — computing both by multiplication would let a * rounding cent go missing or be paid twice, which is the kind of discrepancy * that surfaces months later as an unreconcilable ledger. */ export const splitFare = ( fareCents: number, rate: number = COMMISSION_RATE, ): FareSplit => { const fare = Math.max(0, Math.round(fareCents)); const platformFeeCents = Math.round(fare * rate); return { fareCents: fare, platformFeeCents, driverPayoutCents: fare - platformFeeCents, rate, }; }; export const calculateFare = ( { distanceMeters, durationSeconds, }: { distanceMeters: number; durationSeconds: number; }, service: ServiceId = DEFAULT_SERVICE, ): string => { const km = distanceMeters / 1000; const minutes = durationSeconds / 60; const multiplier = SERVICES.find((s) => s.id === service)?.fareMultiplier ?? 1; const fare = (FARE.base + km * FARE.perKm + minutes * FARE.perMin) * multiplier; // The minimum fare is a floor on the final amount. return Math.max(fare, FARE.minimum).toFixed(2); }; // Rounds to the nearest 1,000 L.B.P. — the smallest practical cash note. // The currency suffix is localized (" L.B.P." / " ل.ل." / " LBP"). export const formatLBP = (usd: number): string => `${(Math.round((usd * LBP_RATE) / 1000) * 1000).toLocaleString("en-US")}${tr("units.lbpSuffix")}`;