import * as WebBrowser from "expo-web-browser"; import { ApiError, fetchAPI } from "@/lib/fetch"; import type { ServiceId } from "@/constants/services"; import type { Ride } from "@/types/type"; // The rider's request flow, used by the confirm-ride screen. This is the card // (Areeba hosted checkout -> server verify -> consume order -> create ride) // and cash (create ride directly) paths, now unified behind one entry point so // the screen doesn't re-implement the gateway dance. // // The ride is always created with status='requested' and driver_id=null; the // server's auto-match engine assigns a driver asynchronously. Returns the // created ride so the caller can navigate to the status screen. export type RequestInput = { method: "cash" | "card"; service: ServiceId; user: { name: string; email: string }; // Location snapshot at request time. origin: { address: string; latitude: number; longitude: number }; destination: { address: string; latitude: number; longitude: number }; rideTimeSeconds: number; fareCents: number; }; export type RequestResult = { ride: Ride }; const recordRide = async ( input: RequestInput, method: "cash" | "card", orderId?: string, ): Promise => { const res = await fetchAPI("/(api)/ride/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ origin_address: input.origin.address, destination_address: input.destination.address, origin_latitude: input.origin.latitude, origin_longitude: input.origin.longitude, destination_latitude: input.destination.latitude, destination_longitude: input.destination.longitude, ride_time: Math.round(input.rideTimeSeconds), fare_price: input.fareCents, payment_method: method, service: input.service, ...(orderId ? { payment_order_id: orderId } : {}), }), }); return res.data as Ride; }; export const requestRide = async (input: RequestInput): Promise => { if (input.method === "cash") { const ride = await recordRide(input, "cash"); return { ride }; } // Card: create an Areeba checkout session on our server. const { orderId, checkoutUrl, error } = await fetchAPI( "/(api)/(areeba)/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: input.user.name || input.user.email, email: input.user.email, fare_cents: input.fareCents, origin_address: input.origin.address, destination_address: input.destination.address, origin_latitude: input.origin.latitude, origin_longitude: input.origin.longitude, destination_latitude: input.destination.latitude, destination_longitude: input.destination.longitude, ride_time: Math.round(input.rideTimeSeconds), }), }, ); if (error || !checkoutUrl) throw new Error(error || "No checkout URL"); // Open Areeba's hosted payment page. After payment the gateway redirects // back to the app (waseel://book-ride). const browserResult = await WebBrowser.openAuthSessionAsync( checkoutUrl, "waseel://book-ride", ); let resultIndicator: string | undefined; if (browserResult.type === "success" && browserResult.url) { resultIndicator = new URL(browserResult.url).searchParams.get( "resultIndicator", ) ?? undefined; } // Verify the payment server-side. const verification = await fetchAPI("/(api)/(areeba)/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ orderId, resultIndicator }), }); if (!verification.success) { throw new ApiError( 400, "Your payment was cancelled or could not be verified. Please try again.", ); } const ride = await recordRide(input, "card", orderId); return { ride }; };