import { requireAuth } from "@/lib/jwt"; import { retrieveOrder } from "@/lib/areeba"; import { getOrder, markPaid } from "@/lib/payment-orders"; export async function POST(req: Request) { const auth = requireAuth(req); if ("error" in auth) return auth.error; const body = await req.json().catch(() => ({})); const { orderId, resultIndicator } = body; if (!orderId || !resultIndicator) return Response.json( { error: "Missing order id or result indicator." }, { status: 400 }, ); try { // Look the order up server-side — never trust a client-supplied // successIndicator value, only the one we persisted at creation time. const order = await getOrder(orderId); if (!order) return Response.json({ error: "Order not found." }, { status: 404 }); if (order.user_id !== auth.userId) return Response.json({ error: "Unauthorized." }, { status: 403 }); // An order can only be verified once. A 'paid' or 'consumed' order has // already settled — rejecting here is the primary double-spend defense: it // stops a client from re-verifying an order it already used for a ride. if (order.status !== "pending") return Response.json( { error: "This payment order is no longer pending." }, { status: 400 }, ); if ( !order.success_indicator || order.success_indicator !== resultIndicator ) return Response.json( { error: "Payment verification failed." }, { status: 400 }, ); const retrieved = await retrieveOrder(orderId); if (!retrieved.paid) return Response.json({ error: "Payment not captured." }, { status: 400 }); // Reconcile the gateway's amount/currency against what we stored, so a // tampered or partial payment cannot mark a full-fare order paid. const gatewayCents = Math.round(Number(retrieved.amount) * 100); const gatewayCurrency = retrieved.currency ?? "USD"; if (gatewayCents !== order.amount_cents || gatewayCurrency !== order.currency) return Response.json( { error: "Payment amount mismatch." }, { status: 400 }, ); await markPaid(orderId); return Response.json({ success: true, orderId }); } catch (err) { console.log("[AREEBA_PAYMENT_VERIFY]: ", err); return Response.json({ error: "Internal Server Error" }, { status: 500 }); } }