/* ===================================================================== POST /api/orders — legt eine echte Bestellung in D1 an (orders + order_items). Wird vom Checkout aufgerufen (siehe src/pages/checkout/index.astro), sobald jemand die Bestellung abschickt. BEWUSST nicht dauerhaft hinter dem Zugangscode-Gate (siehe functions/_middleware.js) — sobald der Shop öffentlich ist (SITE_PUBLIC=true), müssen auch echte Kund:innen hier bestellen können, genau wie sie den Checkout selbst erreichen können. Vor dem Live-Gang ist ohnehin die ganze Seite inkl. Checkout hinter dem Gate, d.h. nur qciga/VanVan können bis hierhin überhaupt kommen. In Phase 1 gibt es kein echtes Zahlungs-Backend — jede vollständig abgeschickte Bestellung gilt sofort als "bezahlt" (siehe scripts/account.ts, derselbe Grundsatz). ===================================================================== */ import { json } from "../_shared/http.js"; const ERLAUBTE_LAENDER = ["de", "at", "ch", "lu"]; export async function onRequestPost(context) { const { request, env } = context; if (!env.DB) { return json(500, { ok: false, error: "Datenbank nicht verbunden." }); } let body; try { body = await request.json(); } catch { return json(400, { ok: false, error: "Ungültige Anfrage." }); } // ── Validierung: lieber eine klare Fehlermeldung im Checkout als eine kaputte/leere // Bestellung in der Datenbank. ── const land = String(body.land || "").toLowerCase(); if (!ERLAUBTE_LAENDER.includes(land)) { return json(400, { ok: false, error: "Ungültiges Land." }); } const kunde = body.kunde && typeof body.kunde === "object" ? body.kunde : {}; const name = String(kunde.name || "").trim(); const email = String(kunde.email || "").trim(); if (!name || !email || !email.includes("@")) { return json(400, { ok: false, error: "Ungültige Kundendaten." }); } const artikel = Array.isArray(body.artikel) ? body.artikel : []; if (artikel.length === 0) { return json(400, { ok: false, error: "Die Bestellung enthält keine Artikel." }); } for (const a of artikel) { if (!a || typeof a.slug !== "string" || !a.slug.trim() || typeof a.name !== "string" || !a.name.trim()) { return json(400, { ok: false, error: "Ungültige Artikeldaten." }); } if (!Number.isFinite(a.menge) || a.menge <= 0) { return json(400, { ok: false, error: "Ungültige Artikelmenge." }); } if (!Number.isFinite(a.preis) || a.preis < 0) { return json(400, { ok: false, error: "Ungültiger Artikelpreis." }); } } const summe = Number(body.summe); if (!Number.isFinite(summe) || summe < 0) { return json(400, { ok: false, error: "Ungültige Bestellsumme." }); } const zwischensumme = Number.isFinite(Number(body.zwischensumme)) ? Number(body.zwischensumme) : 0; const versandkosten = Number.isFinite(Number(body.versandkosten)) ? Number(body.versandkosten) : 0; const rabattGesamt = Number.isFinite(Number(body.rabattGesamt)) ? Number(body.rabattGesamt) : 0; const now = new Date().toISOString(); try { const insertOrder = await env.DB.prepare( `INSERT INTO orders (created_at, updated_at, status, kunde_name, kunde_email, land, strasse, plz, ort, zahlungsart, zwischensumme, versandkosten, rabatt_gesamt, summe, treuebonus_verwendet, abo_rabatt_verwendet) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)` ) .bind( now, now, "bezahlt", name, email, land, String(kunde.strasse || ""), String(kunde.plz || ""), String(kunde.ort || ""), String(body.zahlungsart || ""), zwischensumme, versandkosten, rabattGesamt, summe, body.treuebonusVerwendet ? 1 : 0, body.aboRabattVerwendet ? 1 : 0 ) .run(); const orderId = insertOrder.meta.last_row_id; // Menschenlesbare Bestellnummer wird ERST NACH dem Insert aus der autoinkrementierten, // garantiert eindeutigen ID abgeleitet — keine Race Condition bei zwei gleichzeitigen // Bestellungen (anders als z.B. "COUNT(*) + 1" vorher zu berechnen). const jahr = new Date().getFullYear(); const bestellnummer = `VDB-${jahr}-${String(orderId).padStart(4, "0")}`; await env.DB.prepare(`UPDATE orders SET bestellnummer = ? WHERE id = ?`).bind(bestellnummer, orderId).run(); const itemStmts = artikel.map((a) => env.DB.prepare( `INSERT INTO order_items (order_id, produkt_slug, name, kategorie_slug, menge, preis, gratis) VALUES (?,?,?,?,?,?,?)` ).bind(orderId, a.slug, a.name, a.kategorie || null, Math.round(a.menge), a.preis, a.gratis ? 1 : 0) ); await env.DB.batch(itemStmts); return json(201, { ok: true, id: orderId, bestellnummer }); } catch (err) { return json(500, { ok: false, error: "Bestellung konnte nicht gespeichert werden." }); } }