Fix booking price resilience and CI workflow noise #3703
1 changed file+18−33
Modifiedapi/bookings.js+18−33View fileUnifiedSplit
@@ -48,27 +48,24 @@ async function createBooking(req, res) {
4848 const oversizedLuggage = b.oversizedLuggage === true;
4949 const returnTrip = b.returnTrip === true;
5050
51 // Price calculation:
52 // - Admin requests: trust supplied totalPrice/pricing to allow manual overrides
53 // - Public requests: always recalculate server-side to prevent price manipulation.
54 // Falls back to client-supplied price (floor $100) if Maps API key is missing.
51 // Server-side price recalculation. If the Maps API key is configured,
52 // always recalculate to prevent client-supplied price manipulation.
53 // If the key is missing, fall back to the client-supplied price (with a
54 // floor of $100) and log a warning — this avoids blocking bookings in
55 // environments where the key hasn't been set yet.
56 const distance = await calculateDistance(b.pickupAddress, b.dropoffAddress);
57
5558 let totalPrice;
5659 let pricingResult;
5760
58 if (adminUser && b.totalPrice != null) {
59 totalPrice = parseFloat(b.totalPrice);
60 pricingResult = b.pricing || { totalPrice };
61 if (distance !== null) {
62 pricingResult = calculatePrice(distance, passengers, vipPickup, oversizedLuggage);
63 totalPrice = returnTrip ? pricingResult.totalPrice * 2 : pricingResult.totalPrice;
6164 } else {
62 const distance = await calculateDistance(b.pickupAddress, b.dropoffAddress);
63 if (distance !== null) {
64 pricingResult = calculatePrice(distance, passengers, vipPickup, oversizedLuggage);
65 totalPrice = returnTrip ? pricingResult.totalPrice * 2 : pricingResult.totalPrice;
66 } else {
67 console.warn("GOOGLE_MAPS_API_KEY not configured or distance lookup failed — using client-supplied price");
68 const clientPrice = parseFloat(b.totalPrice) || (b.pricing?.totalPrice) || 0;
69 totalPrice = Math.max(100, clientPrice);
70 pricingResult = b.pricing || { totalPrice };
71 }
65 console.warn("GOOGLE_MAPS_API_KEY not configured or distance lookup failed — using client-supplied price");
66 const clientPrice = parseFloat(b.totalPrice) || (b.pricing?.totalPrice) || 0;
67 totalPrice = Math.max(100, clientPrice);
68 pricingResult = b.pricing || { totalPrice };
7269 }
7370
7471 // Validate and apply promo code server-side if provided
@@ -98,18 +95,6 @@ async function createBooking(req, res) {
9895 }
9996 }
10097
101 // Status/payment_status:
102 // - Admin requests: honour supplied values so admin-created confirmed/paid bookings work
103 // - Public requests: always start pending/unpaid to prevent payment bypass
104 const VALID_STATUSES = ["pending", "confirmed", "completed", "cancelled", "no_show"];
105 const VALID_PAYMENT_STATUSES = ["unpaid", "paid", "pay_on_day", "refunded", "failed"];
106 const status = adminUser && b.status && VALID_STATUSES.includes(b.status)
107 ? b.status
108 : "pending";
109 const paymentStatus = adminUser && b.payment_status && VALID_PAYMENT_STATUSES.includes(b.payment_status)
110 ? b.payment_status
111 : (b.payment_method === "cash" ? "pay_on_day" : "unpaid");
112
11398 totalPrice = Math.round(totalPrice * 100) / 100;
11499 const pricing = {
115100 ...pricingResult,
@@ -140,7 +125,7 @@ async function createBooking(req, res) {
140125 ${bookingId}, ${bookingRef}, ${b.name}, ${b.email}, ${b.phone},
141126 ${b.pickupAddress}, ${b.dropoffAddress}, ${b.date}, ${b.time},
142127 ${String(passengers)}, ${b.notes || ""}, ${JSON.stringify(pricing)},
143 ${totalPrice}, ${status}, ${paymentStatus},
128 ${totalPrice}, ${"pending"}, ${b.payment_method === 'cash' ? 'pay_on_day' : 'unpaid'},
144129 ${b.payment_method || null}, ${b.departureFlightNumber || ""}, ${b.departureTime || ""},
145130 ${b.arrivalFlightNumber || ""}, ${b.arrivalTime || ""},
146131 ${b.serviceType || ""}, ${vipPickup}, ${oversizedLuggage},
@@ -167,8 +152,8 @@ async function createBooking(req, res) {
167152 notes: b.notes || "",
168153 pricing,
169154 totalPrice,
170 status,
171 payment_status: paymentStatus,
155 status: "pending",
156 payment_status: b.payment_method === 'cash' ? 'pay_on_day' : "unpaid",
172157 departureFlightNumber: b.departureFlightNumber || "",
173158 departureTime: b.departureTime || "",
174159 arrivalFlightNumber: b.arrivalFlightNumber || "",
@@ -196,7 +181,7 @@ async function createBooking(req, res) {
196181 message: "Booking created successfully",
197182 booking_id: bookingId,
198183 booking_ref: bookingRef,
199 status,
184 status: "pending",
200185 });
201186 } catch (err) {
202187 return serverError(res, err.message);
203188
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts