/* ===================================================================== lib/paypal.js — echte, server-seitige PayPal-Anbindung (REST API v2). 1:1 portiert aus functions/_shared/paypal.js (reines fetch()-basiertes Modul, keine Cloudflare-Spezifika, deshalb unverändert übernehmbar). Zugangsdaten kommen aus process.env statt env.*. ===================================================================== */ function paypalBasisUrl() { return process.env.PAYPAL_ENV === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"; } export function paypalKonfiguriert() { return !!(process.env.PAYPAL_CLIENT_ID && process.env.PAYPAL_CLIENT_SECRET); } async function holeZugangstoken() { const res = await fetch(`${paypalBasisUrl()}/v1/oauth2/token`, { method: "POST", headers: { Authorization: `Basic ${Buffer.from(`${process.env.PAYPAL_CLIENT_ID}:${process.env.PAYPAL_CLIENT_SECRET}`).toString("base64")}`, "Content-Type": "application/x-www-form-urlencoded", }, body: "grant_type=client_credentials", }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`PayPal-Zugangstoken konnte nicht geholt werden (Status ${res.status}): ${text}`); } const data = await res.json(); return data.access_token; } export async function erstellePaypalBestellung(betragEuro) { const token = await holeZugangstoken(); const res = await fetch(`${paypalBasisUrl()}/v2/checkout/orders`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ intent: "CAPTURE", purchase_units: [{ amount: { currency_code: "EUR", value: Math.max(0.01, betragEuro).toFixed(2) } }], }), }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`PayPal-Bestellung konnte nicht angelegt werden (Status ${res.status}): ${text}`); } const data = await res.json(); return data.id; } export async function erfassePaypalZahlung(paypalOrderId) { try { const token = await holeZugangstoken(); const res = await fetch(`${paypalBasisUrl()}/v2/checkout/orders/${encodeURIComponent(paypalOrderId)}/capture`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, }); const data = await res.json().catch(() => null); if (!res.ok || !data) { return { ok: false, error: `PayPal-Zahlung konnte nicht erfasst werden (Status ${res.status}).` }; } const capture = data?.purchase_units?.[0]?.payments?.captures?.[0]; if (data.status !== "COMPLETED" || !capture || capture.status !== "COMPLETED") { return { ok: false, error: `PayPal-Zahlung ist nicht abgeschlossen (Status: ${data.status}).` }; } return { ok: true, captureId: capture.id, betragEuro: Number(capture.amount?.value ?? 0) }; } catch (err) { return { ok: false, error: err instanceof Error ? err.message : String(err) }; } }