Build & Deploy / deploy (push) Waiting to run
- Absturzsicherheit: Express 4 faengt Fehler aus async-Handlern nicht ab, eine einzige fehlerhafte Anfrage konnte den ganzen Shop beenden - besonders heikel bei capture-order, wo das direkt nach der PayPal-Abbuchung passieren wuerde. Neu: lib/async-wrap.js um alle Handler, zentraler Fehler-Handler in index.js, .catch(next) in der Zugangsschranke, unhandledRejection/uncaughtException-Netz. - Fehlerhaftes JSON lieferte bisher Express' HTML-Fehlerseite mit komplettem Stacktrace und Serverpfaden (weil NODE_ENV nicht gesetzt war). Jetzt saubere JSON-Antwort, NODE_ENV=production in .env.example ergaenzt. - Sicherheits-Header und x-powered-by wie bei der DogFather-Seite. - Falsche Domain .de statt .com korrigiert: astro.config.mjs (betraf alle Canonical-URLs und die komplette Sitemap), robots.txt, Layout.astro sowie den Verwaltungs-Link in jeder Bestellbenachrichtigung (war ein toter Link). - Fehlende Uebersetzung nav.cart in EN/CH/FR ergaenzt: das Warenkorb-Symbol hatte in drei von vier Sprachen keinen Namen fuer Screenreader.
77 lines
3.2 KiB
JavaScript
77 lines
3.2 KiB
JavaScript
/* =====================================================================
|
|
routes/paypal.js — POST /api/paypal/create-order + /api/paypal/capture-order. 1:1 portiert
|
|
aus functions/api/paypal/create-order.js + capture-order.js. Der entscheidende Sicherheits-
|
|
Schritt bleibt unverändert: die Bestellung wird erst NACH server-seitig bei PayPal bestätigter
|
|
Zahlung angelegt, mit Betragsabgleich (Toleranz 0,02 €).
|
|
===================================================================== */
|
|
|
|
import { Router } from "express";
|
|
import { wrap } from "../lib/async-wrap.js";
|
|
import { paypalKonfiguriert, erstellePaypalBestellung, erfassePaypalZahlung } from "../lib/paypal.js";
|
|
import { erstelleBestellung } from "../lib/bestellung-erstellen.js";
|
|
|
|
export const paypalRouter = Router();
|
|
|
|
const BETRAG_TOLERANZ_EURO = 0.02;
|
|
|
|
paypalRouter.post("/api/paypal/create-order", wrap(async (req, res) => {
|
|
if (!paypalKonfiguriert()) {
|
|
return res.status(503).json({
|
|
ok: false,
|
|
error: "PayPal ist auf dieser Seite noch nicht eingerichtet (fehlende Zugangsdaten). Bitte eine andere Zahlungsart wählen.",
|
|
});
|
|
}
|
|
|
|
const body = req.body || {};
|
|
const summe = Number(body.summe);
|
|
if (!Number.isFinite(summe) || summe <= 0) {
|
|
return res.status(400).json({ ok: false, error: "Ungültiger Betrag." });
|
|
}
|
|
|
|
try {
|
|
const paypalOrderId = await erstellePaypalBestellung(summe);
|
|
return res.json({ ok: true, id: paypalOrderId });
|
|
} catch (err) {
|
|
return res.status(502).json({ ok: false, error: err instanceof Error ? err.message : "PayPal-Bestellung fehlgeschlagen." });
|
|
}
|
|
}));
|
|
|
|
paypalRouter.post("/api/paypal/capture-order", wrap(async (req, res) => {
|
|
if (!paypalKonfiguriert()) {
|
|
return res.status(503).json({ ok: false, error: "PayPal ist auf dieser Seite noch nicht eingerichtet." });
|
|
}
|
|
|
|
const body = req.body || {};
|
|
const paypalOrderId = typeof body.paypalOrderId === "string" ? body.paypalOrderId.trim() : "";
|
|
if (!paypalOrderId) {
|
|
return res.status(400).json({ ok: false, error: "Fehlende PayPal-Bestell-ID." });
|
|
}
|
|
|
|
const summe = Number(body.summe);
|
|
if (!Number.isFinite(summe) || summe <= 0) {
|
|
return res.status(400).json({ ok: false, error: "Ungültige Bestellsumme." });
|
|
}
|
|
|
|
const zahlung = await erfassePaypalZahlung(paypalOrderId);
|
|
if (!zahlung.ok) {
|
|
return res.status(402).json({ ok: false, error: `Zahlung konnte nicht bestätigt werden: ${zahlung.error}` });
|
|
}
|
|
|
|
if (Math.abs(zahlung.betragEuro - summe) > BETRAG_TOLERANZ_EURO) {
|
|
return res.status(402).json({
|
|
ok: false,
|
|
error: `Der bei PayPal gezahlte Betrag (${zahlung.betragEuro.toFixed(2)} €) stimmt nicht mit der Bestellsumme (${summe.toFixed(2)} €) überein.`,
|
|
});
|
|
}
|
|
|
|
const ergebnis = await erstelleBestellung({ ...body, zahlungsart: "paypal" }, { status: "bezahlt", paypalOrderId });
|
|
if (!ergebnis.ok) {
|
|
return res.status(500).json({
|
|
ok: false,
|
|
error: `Deine Zahlung wurde erfolgreich bei PayPal abgebucht (Beleg-ID: ${zahlung.captureId}), aber die Bestellung konnte nicht gespeichert werden. Bitte kontaktiere uns mit dieser Beleg-ID, damit wir das von Hand nachtragen — bitte NICHT erneut bezahlen.`,
|
|
});
|
|
}
|
|
|
|
return res.status(201).json({ ok: true, id: ergebnis.id, bestellnummer: ergebnis.bestellnummer });
|
|
}));
|