Newwebshop/app/API/controllers/CustomerApiController.php

604 lines
20 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* Customer API Controller für das Webshop-System
*
* @author Webshop System
* @license GPL v3
*/
namespace App\API\Controllers;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
class CustomerApiController extends ApiController
{
/**
* Kunde registrieren
*/
public function register()
{
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);
}
// 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;
}
}