Add real order persistence, customer accounts, and cart sync
Checkout now persists orders server-side (Payload orders collection, re-priced from live product data, discount codes redeemed exactly once) instead of writing a client-only sessionStorage snapshot. Buying requires an account (registration inline in checkout, no separate step) — accounts get order history with delivery status, profile/address editing, password change, and a cart that syncs across devices while logged in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import type { CartItem } from "../../../lib/cart";
|
||||
import { getServerCart, getSessionCustomer, saveServerCart } from "../../../lib/customerAuth";
|
||||
import { fetchProductsBySlug } from "../../../lib/productsServer";
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ cart: [] }, { status: 401 });
|
||||
const cart = await getServerCart(session.token);
|
||||
return NextResponse.json({ cart });
|
||||
}
|
||||
|
||||
// Called by CartSync.tsx (debounced) on every local cart change while a
|
||||
// session is active — keeps the server-side mirror current so the cart
|
||||
// follows the customer across devices. Silently no-ops when logged out;
|
||||
// the caller doesn't care either way.
|
||||
export async function POST(request: Request) {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ ok: false }, { status: 401 });
|
||||
|
||||
const body = await request.json().catch(() => null);
|
||||
const cart: CartItem[] = Array.isArray(body?.cart) ? body.cart : [];
|
||||
|
||||
const productsBySlug = await fetchProductsBySlug();
|
||||
const lines = cart
|
||||
.map((item) => {
|
||||
const product = productsBySlug.get(item.id);
|
||||
return product ? { productId: product.id, productSlug: product.slug, quantity: item.qty } : null;
|
||||
})
|
||||
.filter((line): line is { productId: number; productSlug: string; quantity: number } => line !== null);
|
||||
|
||||
const ok = await saveServerCart(session.token, session.customer.id, lines);
|
||||
return NextResponse.json({ ok });
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { loginCustomer, setSessionCookie } from "../../../lib/customerAuth";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json().catch(() => null);
|
||||
const email = typeof body?.email === "string" ? body.email : "";
|
||||
const password = typeof body?.password === "string" ? body.password : "";
|
||||
if (!email || !password) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte E-Mail und Passwort angeben." }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await loginCustomer({ email, password });
|
||||
if (!result.ok) return NextResponse.json(result, { status: 401 });
|
||||
|
||||
await setSessionCookie(result.token);
|
||||
return NextResponse.json({ ok: true, customer: result.customer });
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { clearSessionCookie } from "../../../lib/customerAuth";
|
||||
|
||||
export async function POST() {
|
||||
await clearSessionCookie();
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getSessionCustomer } from "../../../lib/customerAuth";
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ customer: null }, { status: 401 });
|
||||
return NextResponse.json({ customer: session.customer });
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getCustomerOrders, getSessionCustomer } from "../../../lib/customerAuth";
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ orders: [] }, { status: 401 });
|
||||
|
||||
const orders = await getCustomerOrders(session.token, session.customer.id);
|
||||
return NextResponse.json({ orders });
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { changeCustomerPassword, getSessionCustomer } from "../../../lib/customerAuth";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ ok: false, reason: "Bitte zuerst einloggen." }, { status: 401 });
|
||||
|
||||
const body = await request.json().catch(() => null);
|
||||
const currentPassword = typeof body?.currentPassword === "string" ? body.currentPassword : "";
|
||||
const newPassword = typeof body?.newPassword === "string" ? body.newPassword : "";
|
||||
if (!currentPassword || !newPassword || newPassword.length < 8) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte aktuelles und ein neues Passwort (mind. 8 Zeichen) angeben." }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await changeCustomerPassword(session.customer.email, currentPassword, newPassword);
|
||||
return NextResponse.json(result, { status: result.ok ? 200 : 400 });
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getSessionCustomer, updateCustomerProfile } from "../../../lib/customerAuth";
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ profile: null }, { status: 401 });
|
||||
return NextResponse.json({ profile: session.customer });
|
||||
}
|
||||
|
||||
export async function PATCH(request: Request) {
|
||||
const session = await getSessionCustomer();
|
||||
if (!session) return NextResponse.json({ ok: false, reason: "Bitte zuerst einloggen." }, { status: 401 });
|
||||
|
||||
const body = await request.json().catch(() => null);
|
||||
const { firstName, lastName, deliveryMethod, street, packstationNumber, postNumber, zip, city, country } = body ?? {};
|
||||
if (
|
||||
typeof firstName !== "string" ||
|
||||
!firstName ||
|
||||
typeof lastName !== "string" ||
|
||||
!lastName ||
|
||||
(deliveryMethod !== "address" && deliveryMethod !== "packstation") ||
|
||||
typeof zip !== "string" ||
|
||||
!zip ||
|
||||
typeof city !== "string" ||
|
||||
!city ||
|
||||
typeof country !== "string" ||
|
||||
!country
|
||||
) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte alle Pflichtfelder ausfüllen." }, { status: 400 });
|
||||
}
|
||||
if (deliveryMethod === "address" && !street) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte Straße und Hausnummer angeben." }, { status: 400 });
|
||||
}
|
||||
if (deliveryMethod === "packstation" && (!packstationNumber || !postNumber)) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte Packstation- und Postnummer angeben." }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await updateCustomerProfile(session.token, session.customer.id, {
|
||||
firstName,
|
||||
lastName,
|
||||
deliveryMethod,
|
||||
street,
|
||||
packstationNumber,
|
||||
postNumber,
|
||||
zip,
|
||||
city,
|
||||
country,
|
||||
});
|
||||
return NextResponse.json(result, { status: result.ok ? 200 : 400 });
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { registerCustomer, setSessionCookie } from "../../../lib/customerAuth";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json().catch(() => null);
|
||||
const { firstName, lastName, email, password } = body ?? {};
|
||||
if (
|
||||
typeof firstName !== "string" ||
|
||||
typeof lastName !== "string" ||
|
||||
typeof email !== "string" ||
|
||||
typeof password !== "string" ||
|
||||
!firstName ||
|
||||
!lastName ||
|
||||
!email ||
|
||||
!password
|
||||
) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte alle Felder ausfüllen." }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await registerCustomer({ firstName, lastName, email, password });
|
||||
if (!result.ok) return NextResponse.json(result, { status: 400 });
|
||||
|
||||
await setSessionCookie(result.token);
|
||||
return NextResponse.json({ ok: true, customer: result.customer });
|
||||
}
|
||||
Reference in New Issue
Block a user