sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } // Validierung $required = ['email', 'password', 'first_name', 'last_name']; foreach ($required as $field) { if (empty($input[$field])) { $this->sendError("Missing required field: $field", 400); } } if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) { $this->sendError('Invalid email address', 400); } if (strlen($input['password']) < 8) { $this->sendError('Password must be at least 8 characters long', 400); } try { // Prüfen ob E-Mail bereits existiert $stmt = $this->conn->prepare('SELECT id FROM ws_customer WHERE email = ?'); $stmt->execute([$input['email']]); $existing = $stmt->fetchAssociative(); if ($existing) { $this->sendError('Email already exists', 409); } // Passwort hashen $hashedPassword = password_hash($input['password'], PASSWORD_DEFAULT); // Kunde erstellen $stmt = $this->conn->prepare(' INSERT INTO ws_customer (email, password, first_name, last_name, phone, newsletter, active, created_at) VALUES (?, ?, ?, ?, ?, ?, 1, NOW()) '); $stmt->execute([ $input['email'], $hashedPassword, $input['first_name'], $input['last_name'], $input['phone'] ?? '', isset($input['newsletter']) ? 1 : 0 ]); $customerId = $this->conn->lastInsertId(); // JWT-Token generieren $token = $this->generateJWT($customerId, $input['email']); $this->sendResponse([ 'success' => true, 'data' => [ 'customer_id' => $customerId, 'email' => $input['email'], 'first_name' => $input['first_name'], 'last_name' => $input['last_name'], 'token' => $token ], 'message' => 'Customer registered successfully' ]); } catch (Exception $e) { $this->sendError('Failed to register customer: ' . $e->getMessage(), 500); } } /** * Kunde anmelden */ public function login() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } if (empty($input['email']) || empty($input['password'])) { $this->sendError('Email and password are required', 400); } try { $stmt = $this->conn->prepare(' SELECT id, email, password, first_name, last_name, active FROM ws_customer WHERE email = ? '); $stmt->execute([$input['email']]); $customer = $stmt->fetchAssociative(); if (!$customer) { $this->sendError('Invalid credentials', 401); } if (!$customer['active']) { $this->sendError('Account is deactivated', 401); } if (!password_verify($input['password'], $customer['password'])) { $this->sendError('Invalid credentials', 401); } // JWT-Token generieren $token = $this->generateJWT($customer['id'], $customer['email']); // Login-Zeit aktualisieren $stmt = $this->conn->prepare(' UPDATE ws_customer SET last_login = NOW() WHERE id = ? '); $stmt->execute([$customer['id']]); $this->sendResponse([ 'success' => true, 'data' => [ 'customer_id' => $customer['id'], 'email' => $customer['email'], 'first_name' => $customer['first_name'], 'last_name' => $customer['last_name'], 'token' => $token ], 'message' => 'Login successful' ]); } catch (Exception $e) { $this->sendError('Failed to login: ' . $e->getMessage(), 500); } } /** * Kundenprofil abrufen */ public function getProfile($customerId) { $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } try { $stmt = $this->conn->prepare(' SELECT id, email, first_name, last_name, phone, newsletter, created_at, last_login FROM ws_customer WHERE id = ? AND active = 1 '); $stmt->execute([$customerId]); $customer = $stmt->fetchAssociative(); if (!$customer) { $this->sendError('Customer not found', 404); } // Adressen laden $stmt = $this->conn->prepare(' SELECT * FROM ws_customer_address WHERE customer_id = ? '); $stmt->execute([$customerId]); $addresses = $stmt->fetchAllAssociative(); $customer['addresses'] = $addresses; $this->sendResponse([ 'success' => true, 'data' => $customer ]); } catch (Exception $e) { $this->sendError('Failed to get profile: ' . $e->getMessage(), 500); } } /** * Kundenprofil aktualisieren */ public function updateProfile($customerId) { if ($_SERVER['REQUEST_METHOD'] !== 'PUT') { $this->sendError('Method not allowed', 405); } $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } try { $updateFields = []; $params = []; if (isset($input['first_name'])) { $updateFields[] = 'first_name = ?'; $params[] = $input['first_name']; } if (isset($input['last_name'])) { $updateFields[] = 'last_name = ?'; $params[] = $input['last_name']; } if (isset($input['phone'])) { $updateFields[] = 'phone = ?'; $params[] = $input['phone']; } if (isset($input['newsletter'])) { $updateFields[] = 'newsletter = ?'; $params[] = $input['newsletter'] ? 1 : 0; } if (empty($updateFields)) { $this->sendError('No fields to update', 400); } $updateFields[] = 'updated_at = NOW()'; $params[] = $customerId; $sql = 'UPDATE ws_customer SET ' . implode(', ', $updateFields) . ' WHERE id = ?'; $stmt = $this->conn->prepare($sql); $stmt->execute($params); if ($stmt->rowCount() === 0) { $this->sendError('Customer not found', 404); } $this->sendResponse([ 'success' => true, 'message' => 'Profile updated successfully' ]); } catch (Exception $e) { $this->sendError('Failed to update profile: ' . $e->getMessage(), 500); } } /** * Passwort ändern */ public function changePassword($customerId) { if ($_SERVER['REQUEST_METHOD'] !== 'PUT') { $this->sendError('Method not allowed', 405); } $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } if (empty($input['current_password']) || empty($input['new_password'])) { $this->sendError('Current password and new password are required', 400); } if (strlen($input['new_password']) < 8) { $this->sendError('New password must be at least 8 characters long', 400); } try { // Aktuelles Passwort prüfen $stmt = $this->conn->prepare('SELECT password FROM ws_customer WHERE id = ?'); $stmt->execute([$customerId]); $customer = $stmt->fetchAssociative(); if (!$customer) { $this->sendError('Customer not found', 404); } if (!password_verify($input['current_password'], $customer['password'])) { $this->sendError('Current password is incorrect', 400); } // Neues Passwort hashen und speichern $hashedPassword = password_hash($input['new_password'], PASSWORD_DEFAULT); $stmt = $this->conn->prepare(' UPDATE ws_customer SET password = ?, updated_at = NOW() WHERE id = ? '); $stmt->execute([$hashedPassword, $customerId]); $this->sendResponse([ 'success' => true, 'message' => 'Password changed successfully' ]); } catch (Exception $e) { $this->sendError('Failed to change password: ' . $e->getMessage(), 500); } } /** * Adresse hinzufügen */ public function addAddress($customerId) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError('Method not allowed', 405); } $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } $required = ['first_name', 'last_name', 'address', 'city', 'postal_code', 'country']; foreach ($required as $field) { if (empty($input[$field])) { $this->sendError("Missing required field: $field", 400); } } try { $stmt = $this->conn->prepare(' INSERT INTO ws_customer_address (customer_id, first_name, last_name, company, address, address2, city, postal_code, country, phone, is_default) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '); $stmt->execute([ $customerId, $input['first_name'], $input['last_name'], $input['company'] ?? '', $input['address'], $input['address2'] ?? '', $input['city'], $input['postal_code'], $input['country'], $input['phone'] ?? '', isset($input['is_default']) ? 1 : 0 ]); $addressId = $this->conn->lastInsertId(); // Wenn Standard-Adresse, andere Adressen auf nicht-Standard setzen if (isset($input['is_default']) && $input['is_default']) { $stmt = $this->conn->prepare(' UPDATE ws_customer_address SET is_default = 0 WHERE customer_id = ? AND id != ? '); $stmt->execute([$customerId, $addressId]); } $this->sendResponse([ 'success' => true, 'data' => [ 'address_id' => $addressId ], 'message' => 'Address added successfully' ]); } catch (Exception $e) { $this->sendError('Failed to add address: ' . $e->getMessage(), 500); } } /** * Adresse aktualisieren */ public function updateAddress($customerId, $addressId) { if ($_SERVER['REQUEST_METHOD'] !== 'PUT') { $this->sendError('Method not allowed', 405); } $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } try { $updateFields = []; $params = []; $fields = ['first_name', 'last_name', 'company', 'address', 'address2', 'city', 'postal_code', 'country', 'phone', 'is_default']; foreach ($fields as $field) { if (isset($input[$field])) { $updateFields[] = "$field = ?"; $params[] = $input[$field]; } } if (empty($updateFields)) { $this->sendError('No fields to update', 400); } $params[] = $addressId; $params[] = $customerId; $sql = 'UPDATE ws_customer_address SET ' . implode(', ', $updateFields) . ' WHERE id = ? AND customer_id = ?'; $stmt = $this->conn->prepare($sql); $stmt->execute($params); if ($stmt->rowCount() === 0) { $this->sendError('Address not found', 404); } // Wenn Standard-Adresse, andere Adressen auf nicht-Standard setzen if (isset($input['is_default']) && $input['is_default']) { $stmt = $this->conn->prepare(' UPDATE ws_customer_address SET is_default = 0 WHERE customer_id = ? AND id != ? '); $stmt->execute([$customerId, $addressId]); } $this->sendResponse([ 'success' => true, 'message' => 'Address updated successfully' ]); } catch (Exception $e) { $this->sendError('Failed to update address: ' . $e->getMessage(), 500); } } /** * Adresse löschen */ public function deleteAddress($customerId, $addressId) { if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') { $this->sendError('Method not allowed', 405); } $token = $this->getBearerToken(); $decodedToken = $this->validateJWT($token); if (!$decodedToken || $decodedToken['customer_id'] != $customerId) { $this->sendError('Unauthorized', 401); } try { $stmt = $this->conn->prepare(' DELETE FROM ws_customer_address WHERE id = ? AND customer_id = ? '); $stmt->execute([$addressId, $customerId]); if ($stmt->rowCount() === 0) { $this->sendError('Address not found', 404); } $this->sendResponse([ 'success' => true, 'message' => 'Address deleted successfully' ]); } catch (Exception $e) { $this->sendError('Failed to delete address: ' . $e->getMessage(), 500); } } /** * JWT-Token generieren */ private function generateJWT($customerId, $email) { $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']); $payload = json_encode([ 'customer_id' => $customerId, 'email' => $email, 'iat' => time(), 'exp' => time() + (60 * 60 * 24 * 7) // 7 Tage ]); $base64Header = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64Payload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); $signature = hash_hmac('sha256', $base64Header . "." . $base64Payload, getenv('JWT_SECRET') ?: 'webshop_secret_key', true); $base64Signature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); return $base64Header . "." . $base64Payload . "." . $base64Signature; } /** * JWT-Token validieren */ private function validateJWT($token) { if (!$token) { return false; } $parts = explode('.', $token); if (count($parts) !== 3) { return false; } list($header, $payload, $signature) = $parts; $validSignature = hash_hmac('sha256', $header . "." . $payload, getenv('JWT_SECRET') ?: 'webshop_secret_key', true); $validSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($validSignature)); if ($signature !== $validSignature) { return false; } $payload = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $payload)), true); if (!$payload || $payload['exp'] < time()) { return false; } return $payload; } /** * Bearer-Token aus Header extrahieren */ private function getBearerToken() { $headers = getallheaders(); $authHeader = $headers['Authorization'] ?? ''; if (preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) { return $matches[1]; } return null; } }