[ 'name' => 'PayPal', 'enabled' => false, 'sandbox' => true, 'client_id' => '', 'client_secret' => '', 'webhook_id' => '' ], 'stripe' => [ 'name' => 'Stripe', 'enabled' => false, 'test_mode' => true, 'publishable_key' => '', 'secret_key' => '', 'webhook_secret' => '' ], 'sepa' => [ 'name' => 'SEPA-Lastschrift', 'enabled' => false, 'test_mode' => true, 'merchant_id' => '', 'iban' => '', 'bic' => '' ] ]; public function __construct() { $this->conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $this->config = new Configuration(); $this->multiShop = new MultiShop(); $this->loadPaymentConfig(); } /** * Payment-Konfiguration laden */ private function loadPaymentConfig() { $shopId = $this->multiShop->getCurrentShopId(); foreach ($this->providers as $provider => &$config) { $config['enabled'] = $this->multiShop->getShopConfig("PAYMENT_{$provider}_ENABLED", false); switch ($provider) { case 'paypal': $config['sandbox'] = $this->multiShop->getShopConfig('PAYMENT_PAYPAL_SANDBOX', true); $config['client_id'] = $this->multiShop->getShopConfig('PAYMENT_PAYPAL_CLIENT_ID', ''); $config['client_secret'] = $this->multiShop->getShopConfig('PAYMENT_PAYPAL_CLIENT_SECRET', ''); $config['webhook_id'] = $this->multiShop->getShopConfig('PAYMENT_PAYPAL_WEBHOOK_ID', ''); break; case 'stripe': $config['test_mode'] = $this->multiShop->getShopConfig('PAYMENT_STRIPE_TEST_MODE', true); $config['publishable_key'] = $this->multiShop->getShopConfig('PAYMENT_STRIPE_PUBLISHABLE_KEY', ''); $config['secret_key'] = $this->multiShop->getShopConfig('PAYMENT_STRIPE_SECRET_KEY', ''); $config['webhook_secret'] = $this->multiShop->getShopConfig('PAYMENT_STRIPE_WEBHOOK_SECRET', ''); break; case 'sepa': $config['test_mode'] = $this->multiShop->getShopConfig('PAYMENT_SEPA_TEST_MODE', true); $config['merchant_id'] = $this->multiShop->getShopConfig('PAYMENT_SEPA_MERCHANT_ID', ''); $config['iban'] = $this->multiShop->getShopConfig('PAYMENT_SEPA_IBAN', ''); $config['bic'] = $this->multiShop->getShopConfig('PAYMENT_SEPA_BIC', ''); break; } } } /** * Verfügbare Zahlungsmethoden abrufen */ public function getAvailablePaymentMethods() { $methods = []; foreach ($this->providers as $provider => $config) { if ($config['enabled']) { $methods[$provider] = [ 'name' => $config['name'], 'provider' => $provider, 'enabled' => true, 'test_mode' => $config['sandbox'] ?? $config['test_mode'] ?? false ]; } } return $methods; } /** * PayPal Payment erstellen */ public function createPayPalPayment($orderData) { if (!$this->providers['paypal']['enabled']) { throw new \Exception('PayPal ist nicht aktiviert'); } $paypalConfig = $this->providers['paypal']; $apiUrl = $paypalConfig['sandbox'] ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; // PayPal Access Token abrufen $accessToken = $this->getPayPalAccessToken($apiUrl, $paypalConfig); // PayPal Order erstellen $paypalOrder = $this->createPayPalOrder($apiUrl, $accessToken, $orderData); // Payment Transaction speichern $transactionId = $this->savePaymentTransaction([ 'order_id' => $orderData['order_id'], 'provider' => 'paypal', 'transaction_id' => $paypalOrder['id'], 'amount' => $orderData['total_amount'], 'currency' => $orderData['currency'], 'status' => 'pending', 'payment_data' => json_encode($paypalOrder) ]); return [ 'transaction_id' => $transactionId, 'paypal_order_id' => $paypalOrder['id'], 'approval_url' => $paypalOrder['links'][1]['href'] ?? null ]; } /** * PayPal Access Token abrufen */ private function getPayPalAccessToken($apiUrl, $config) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiUrl . '/v1/oauth2/token'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials'); curl_setopt($ch, CURLOPT_USERPWD, $config['client_id'] . ':' . $config['client_secret']); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/x-www-form-urlencoded' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { throw new \Exception('PayPal Access Token Fehler: ' . $response); } $data = json_decode($response, true); return $data['access_token']; } /** * PayPal Order erstellen */ private function createPayPalOrder($apiUrl, $accessToken, $orderData) { $payload = [ 'intent' => 'CAPTURE', 'purchase_units' => [ [ 'reference_id' => $orderData['order_id'], 'amount' => [ 'currency_code' => $orderData['currency'], 'value' => number_format($orderData['total_amount'], 2, '.', '') ], 'description' => 'Bestellung #' . $orderData['order_id'] ] ], 'application_context' => [ 'return_url' => getenv('BASE_URL') . '/payment/paypal/return', 'cancel_url' => getenv('BASE_URL') . '/payment/paypal/cancel' ] ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiUrl . '/v2/checkout/orders'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $accessToken ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { throw new \Exception('PayPal Order Fehler: ' . $response); } return json_decode($response, true); } /** * PayPal Payment verarbeiten */ public function processPayPalPayment($paypalOrderId) { $paypalConfig = $this->providers['paypal']; $apiUrl = $paypalConfig['sandbox'] ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; $accessToken = $this->getPayPalAccessToken($apiUrl, $paypalConfig); // PayPal Order erfassen $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiUrl . "/v2/checkout/orders/{$paypalOrderId}/capture"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $accessToken ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { throw new \Exception('PayPal Capture Fehler: ' . $response); } $captureData = json_decode($response, true); // Payment Transaction aktualisieren $this->updatePaymentTransaction($paypalOrderId, [ 'status' => 'completed', 'capture_id' => $captureData['purchase_units'][0]['payments']['captures'][0]['id'], 'payment_data' => json_encode($captureData) ]); return $captureData; } /** * Stripe Payment Intent erstellen */ public function createStripePaymentIntent($orderData) { if (!$this->providers['stripe']['enabled']) { throw new \Exception('Stripe ist nicht aktiviert'); } $stripeConfig = $this->providers['stripe']; $apiUrl = $stripeConfig['test_mode'] ? 'https://api.stripe.com' : 'https://api.stripe.com'; $payload = [ 'amount' => intval($orderData['total_amount'] * 100), // Stripe erwartet Cents 'currency' => strtolower($orderData['currency']), 'metadata' => [ 'order_id' => $orderData['order_id'] ], 'automatic_payment_methods' => [ 'enabled' => true ] ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiUrl . '/v1/payment_intents'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/x-www-form-urlencoded', 'Authorization: Bearer ' . $stripeConfig['secret_key'] ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { throw new \Exception('Stripe Payment Intent Fehler: ' . $response); } $paymentIntent = json_decode($response, true); // Payment Transaction speichern $transactionId = $this->savePaymentTransaction([ 'order_id' => $orderData['order_id'], 'provider' => 'stripe', 'transaction_id' => $paymentIntent['id'], 'amount' => $orderData['total_amount'], 'currency' => $orderData['currency'], 'status' => 'pending', 'payment_data' => json_encode($paymentIntent) ]); return [ 'transaction_id' => $transactionId, 'client_secret' => $paymentIntent['client_secret'], 'payment_intent_id' => $paymentIntent['id'] ]; } /** * Stripe Webhook verarbeiten */ public function processStripeWebhook($payload, $signature) { $stripeConfig = $this->providers['stripe']; // Webhook-Signatur verifizieren $expectedSignature = hash_hmac('sha256', $payload, $stripeConfig['webhook_secret']); if (!hash_equals($expectedSignature, $signature)) { throw new \Exception('Stripe Webhook Signatur ungültig'); } $event = json_decode($payload, true); switch ($event['type']) { case 'payment_intent.succeeded': $this->handleStripePaymentSuccess($event['data']['object']); break; case 'payment_intent.payment_failed': $this->handleStripePaymentFailure($event['data']['object']); break; } return $event; } /** * Stripe Payment Success Handler */ private function handleStripePaymentSuccess($paymentIntent) { $this->updatePaymentTransaction($paymentIntent['id'], [ 'status' => 'completed', 'payment_data' => json_encode($paymentIntent) ]); // Order Status aktualisieren $orderId = $paymentIntent['metadata']['order_id'] ?? null; if ($orderId) { $this->updateOrderStatus($orderId, 'paid'); } } /** * Stripe Payment Failure Handler */ private function handleStripePaymentFailure($paymentIntent) { $this->updatePaymentTransaction($paymentIntent['id'], [ 'status' => 'failed', 'payment_data' => json_encode($paymentIntent) ]); // Order Status aktualisieren $orderId = $paymentIntent['metadata']['order_id'] ?? null; if ($orderId) { $this->updateOrderStatus($orderId, 'payment_failed'); } } /** * SEPA-Lastschrift erstellen */ public function createSEPAPayment($orderData, $customerData) { if (!$this->providers['sepa']['enabled']) { throw new \Exception('SEPA-Lastschrift ist nicht aktiviert'); } $sepaConfig = $this->providers['sepa']; // SEPA-Mandat erstellen $mandateId = $this->createSEPAMandate($orderData['order_id'], $customerData); // SEPA-Transaction erstellen $sepaTransaction = [ 'mandate_id' => $mandateId, 'amount' => $orderData['total_amount'], 'currency' => $orderData['currency'], 'debtor_iban' => $customerData['iban'], 'debtor_bic' => $customerData['bic'], 'debtor_name' => $customerData['name'], 'creditor_iban' => $sepaConfig['iban'], 'creditor_bic' => $sepaConfig['bic'], 'creditor_name' => $sepaConfig['merchant_id'], 'purpose' => 'Bestellung #' . $orderData['order_id'] ]; // Payment Transaction speichern $transactionId = $this->savePaymentTransaction([ 'order_id' => $orderData['order_id'], 'provider' => 'sepa', 'transaction_id' => $mandateId, 'amount' => $orderData['total_amount'], 'currency' => $orderData['currency'], 'status' => 'pending', 'payment_data' => json_encode($sepaTransaction) ]); return [ 'transaction_id' => $transactionId, 'mandate_id' => $mandateId, 'status' => 'pending' ]; } /** * SEPA-Mandat erstellen */ private function createSEPAMandate($orderId, $customerData) { $mandateId = 'MNDT' . date('Ymd') . strtoupper(substr(md5($orderId), 0, 8)); try { $stmt = $this->conn->prepare(' INSERT INTO ws_sepa_mandate (mandate_id, order_id, customer_id, iban, bic, name, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW()) '); $stmt->execute([ $mandateId, $orderId, $customerData['customer_id'], $customerData['iban'], $customerData['bic'], $customerData['name'] ]); return $mandateId; } catch (Exception $e) { throw new \Exception('SEPA-Mandat Fehler: ' . $e->getMessage()); } } /** * Payment Transaction speichern */ private function savePaymentTransaction($data) { try { $stmt = $this->conn->prepare(' INSERT INTO ws_payment_transaction ( order_id, provider, transaction_id, amount, currency, status, payment_data, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW()) '); $stmt->execute([ $data['order_id'], $data['provider'], $data['transaction_id'], $data['amount'], $data['currency'], $data['status'], $data['payment_data'] ]); return $this->conn->lastInsertId(); } catch (Exception $e) { throw new \Exception('Payment Transaction Fehler: ' . $e->getMessage()); } } /** * Payment Transaction aktualisieren */ private function updatePaymentTransaction($transactionId, $data) { try { $stmt = $this->conn->prepare(' UPDATE ws_payment_transaction SET status = ?, payment_data = ?, updated_at = NOW() WHERE transaction_id = ? '); $stmt->execute([ $data['status'], $data['payment_data'], $transactionId ]); } catch (Exception $e) { throw new \Exception('Payment Transaction Update Fehler: ' . $e->getMessage()); } } /** * Order Status aktualisieren */ private function updateOrderStatus($orderId, $status) { try { $stmt = $this->conn->prepare(' UPDATE ws_order SET status = ?, updated_at = NOW() WHERE id = ? '); $stmt->execute([$status, $orderId]); } catch (Exception $e) { error_log('Order Status Update Fehler: ' . $e->getMessage()); } } /** * Payment-Statistiken abrufen */ public function getPaymentStatistics($shopId = null) { $shopId = $shopId ?: $this->multiShop->getCurrentShopId(); try { $stmt = $this->conn->prepare(' SELECT provider, COUNT(*) as total_transactions, SUM(amount) as total_amount, COUNT(CASE WHEN status = "completed" THEN 1 END) as successful_transactions, COUNT(CASE WHEN status = "failed" THEN 1 END) as failed_transactions, AVG(amount) as average_amount FROM ws_payment_transaction pt JOIN ws_order o ON pt.order_id = o.id WHERE o.shop_id = ? GROUP BY provider '); $stmt->execute([$shopId]); return $stmt->fetchAllAssociative(); } catch (Exception $e) { error_log('Payment Statistics Fehler: ' . $e->getMessage()); return []; } } /** * Payment-Konfiguration aktualisieren */ public function updatePaymentConfig($provider, $config) { $shopId = $this->multiShop->getCurrentShopId(); foreach ($config as $key => $value) { $configKey = "PAYMENT_{$provider}_" . strtoupper($key); $this->multiShop->setShopConfig($configKey, $value); } // Konfiguration neu laden $this->loadPaymentConfig(); } /** * Payment-Provider Status prüfen */ public function checkPaymentProviderStatus($provider) { if (!isset($this->providers[$provider])) { return false; } $config = $this->providers[$provider]; if (!$config['enabled']) { return false; } // Provider-spezifische Tests switch ($provider) { case 'paypal': return $this->testPayPalConnection(); case 'stripe': return $this->testStripeConnection(); case 'sepa': return $this->testSEPAConfiguration(); default: return false; } } /** * PayPal-Verbindung testen */ private function testPayPalConnection() { try { $config = $this->providers['paypal']; $apiUrl = $config['sandbox'] ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; $accessToken = $this->getPayPalAccessToken($apiUrl, $config); return !empty($accessToken); } catch (\Exception $e) { return false; } } /** * Stripe-Verbindung testen */ private function testStripeConnection() { try { $config = $this->providers['stripe']; $apiUrl = 'https://api.stripe.com'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiUrl . '/v1/account'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $config['secret_key'] ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode === 200; } catch (\Exception $e) { return false; } } /** * SEPA-Konfiguration testen */ private function testSEPAConfiguration() { $config = $this->providers['sepa']; return !empty($config['merchant_id']) && !empty($config['iban']) && !empty($config['bic']); } }