Auf ausdrücklichen Wunsch, mit Fokus auf rechtliche Absicherung: - Klarna komplett entfernt (hätte eigene Händlerprüfung + Bonitätsprüfungs- Pflichten nach der neuen EU-Verbraucherkreditrichtlinie vorausgesetzt). - Eigene Kreditkarten-Eingabemaske komplett entfernt (wäre PCI-DSS-pflichtig gewesen — für einen kleinen Shop praktisch nicht stemmbar). Kartenzahlung bleibt möglich: PayPals eigener, PCI-zertifizierter Gast-Checkout bietet Kredit-/Debitkarte an, ohne dass Kartendaten je unsere Seite berühren. - PayPal ist jetzt ECHT server-seitig verifiziert statt dem Browser blind zu vertrauen: eigener Cloudflare-Function-Flow (functions/_shared/paypal.js + functions/api/paypal/) legt die PayPal-Bestellung server-seitig an, zieht die Zahlung nach Bestätigung server-seitig ein und prüft den eingezogenen Betrag gegen die Bestellsumme — die Bestellung wird ausschließlich bei bestätigter, betragsgleicher Zahlung angelegt. /api/orders lehnt direkte PayPal-Bestellungen jetzt ausdrücklich ab (verhindert vorgetäuschte "bezahlte" Bestellungen ohne echte Zahlung). - Neuer, ehrlicherer Bestellstatus "zahlungOffen": Überweisungs-Bestellungen starten jetzt so (Geld noch nicht da) statt fälschlich sofort "bezahlt" zu heißen — Schutz vor Warenversand, bevor das Geld wirklich angekommen ist. Eigene Kachel/Filter/Badge-Farbe in der Verwaltung, Umsatz-/Auswertungs- Zahlen zählen "zahlungOffen" bewusst nicht mit. - Rechtstexte (AGB, Datenschutzerklärung, FAQ, Versand & Zahlung) auf allen 4 Sprachen aktualisiert: nur noch PayPal + Überweisung erwähnt, inkl. DSGVO- Hinweis zur internationalen Datenübertragung an PayPal (Data Privacy Framework-Zertifizierung). - D1-Migration 0003: neue Spalte paypal_order_id (Zahlungsbeleg) + erweiterter Status-Wertebereich, auf Live-Datenbank angewendet, bestehende Daten intakt. Mit echten Testbestellungen lokal verifiziert: Überweisung legt korrekt "zahlungOffen" an, direkter PayPal-Bypass-Versuch an /api/orders wird abgelehnt, PayPal-Route meldet sauber "noch nicht eingerichtet" ohne Zugangsdaten. Checkout-UI zeigt nur noch 2 Zahlungsarten, Bestellknopf ist bei PayPal ausgeblendet (Zahlung läuft exklusiv über den echten PayPal-Button). Für den echten Zahlungseingang fehlt noch VanVans eigenes PayPal-Business- Konto (Client-ID + Secret) — Details in der Vault-Dokumentation. Co-Authored-By: Claude Sonnet 5 <[email protected]>
69 lines
2.3 KiB
JavaScript
69 lines
2.3 KiB
JavaScript
/* =====================================================================
|
|
PATCH /api/verwaltung/orders/:id — Status (und/oder Tracking-Nummer) einer Bestellung
|
|
ändern, direkt aus der Verwaltungsseite heraus (siehe src/pages/verwaltung/index.astro).
|
|
|
|
Genau wie bei GET /api/verwaltung/orders: zusätzlich zur Middleware-Sperre wird die
|
|
Sitzung hier noch einmal eigenständig geprüft. ===================================================================== */
|
|
|
|
import { getGateRole, unauthorizedJson } from "../../../_shared/auth.js";
|
|
import { json } from "../../../_shared/http.js";
|
|
|
|
const ERLAUBTE_STATUS = ["zahlungOffen", "bezahlt", "bearbeitung", "versandVorbereitet", "versendet", "abgeschlossen", "storniert"];
|
|
|
|
export async function onRequestPatch(context) {
|
|
const { request, env, params } = context;
|
|
|
|
const role = await getGateRole(request, env);
|
|
if (!role) return unauthorizedJson();
|
|
|
|
if (!env.DB) {
|
|
return json(500, { ok: false, error: "Datenbank nicht verbunden." });
|
|
}
|
|
|
|
const id = Number(params.id);
|
|
if (!Number.isInteger(id) || id <= 0) {
|
|
return json(400, { ok: false, error: "Ungültige Bestell-ID." });
|
|
}
|
|
|
|
let body;
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
return json(400, { ok: false, error: "Ungültige Anfrage." });
|
|
}
|
|
|
|
const updates = [];
|
|
const values = [];
|
|
|
|
if (body.status !== undefined) {
|
|
if (!ERLAUBTE_STATUS.includes(body.status)) {
|
|
return json(400, { ok: false, error: "Ungültiger Status." });
|
|
}
|
|
updates.push("status = ?");
|
|
values.push(body.status);
|
|
}
|
|
if (body.tracking !== undefined) {
|
|
updates.push("tracking = ?");
|
|
values.push(String(body.tracking || ""));
|
|
}
|
|
if (updates.length === 0) {
|
|
return json(400, { ok: false, error: "Nichts zu ändern übergeben." });
|
|
}
|
|
|
|
updates.push("updated_at = ?");
|
|
values.push(new Date().toISOString());
|
|
values.push(id);
|
|
|
|
try {
|
|
const result = await env.DB.prepare(`UPDATE orders SET ${updates.join(", ")} WHERE id = ?`)
|
|
.bind(...values)
|
|
.run();
|
|
if (result.meta.changes === 0) {
|
|
return json(404, { ok: false, error: "Bestellung nicht gefunden." });
|
|
}
|
|
return json(200, { ok: true });
|
|
} catch (err) {
|
|
return json(500, { ok: false, error: "Bestellung konnte nicht aktualisiert werden." });
|
|
}
|
|
}
|