Add PayPal, Stripe, CSV-Export; fix .env.production issue
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 7s
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 7s
- PayPal: Checkout-Integration via srmklive/paypal, Weiterleitung zu PayPal, Bestellung wird nach Capture automatisch auf payment_received gesetzt - Stripe: Payment Elements im Checkout, Intent-API, Zahlung direkt bestätigt - CSV-Export: Admin → Bestellungen → CSV exportieren (UTF-8 BOM für Excel) - PayPal/Stripe nur sichtbar wenn Keys in .env gesetzt - .env.example um PAYPAL_* und STRIPE_* Keys erweitert Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -76,4 +76,39 @@ class OrderController extends Controller
|
||||
{
|
||||
return $this->invoiceService->generate($order)->download($this->invoiceService->filename($order));
|
||||
}
|
||||
|
||||
public function exportCsv()
|
||||
{
|
||||
$orders = Order::with(['items','billingAddress'])->orderByDesc('created_at')->get();
|
||||
|
||||
$rows = [];
|
||||
$rows[] = ['Bestellnr.','Datum','Status','Zahlungsart','Kunde','E-Mail','Zwischensumme','Rabatt','Versand','Gesamt','MwSt.'];
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$addr = $order->billingAddress();
|
||||
$rows[] = [
|
||||
$order->order_number,
|
||||
$order->created_at->format('d.m.Y H:i'),
|
||||
Order::$statusLabels[$order->status] ?? $order->status,
|
||||
Order::$paymentLabels[$order->payment_method] ?? $order->payment_method,
|
||||
$addr ? $addr->full_name : ($order->guest_email ?? ''),
|
||||
$order->guest_email ?? $order->user?->email ?? '',
|
||||
number_format($order->subtotal, 2, ',', '.'),
|
||||
number_format($order->discount_amount ?? 0, 2, ',', '.'),
|
||||
number_format($order->shipping_cost, 2, ',', '.'),
|
||||
number_format($order->total, 2, ',', '.'),
|
||||
number_format($order->tax_total, 2, ',', '.'),
|
||||
];
|
||||
}
|
||||
|
||||
$csv = "\xEF\xBB\xBF"; // UTF-8 BOM für Excel
|
||||
foreach ($rows as $row) {
|
||||
$csv .= implode(';', array_map(fn($v) => '"' . str_replace('"', '""', $v) . '"', $row)) . "\n";
|
||||
}
|
||||
|
||||
return response($csv, 200, [
|
||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||
'Content-Disposition' => 'attachment; filename="bestellungen-' . date('Y-m-d') . '.csv"',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,10 @@ class CheckoutController extends Controller
|
||||
|
||||
session(['checkout_data' => $data]);
|
||||
|
||||
if ($data['payment_method'] === 'paypal') {
|
||||
return redirect()->route('paypal.redirect');
|
||||
}
|
||||
|
||||
return redirect()->route('checkout.confirm');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Shop;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\CartService;
|
||||
use App\Services\OrderService;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\Order;
|
||||
use App\Models\ShippingRate;
|
||||
use Illuminate\Http\Request;
|
||||
use Srmklive\PayPal\Services\PayPal as PayPalClient;
|
||||
|
||||
class PayPalController extends Controller
|
||||
{
|
||||
public function __construct(private CartService $cart, private OrderService $orderService) {}
|
||||
|
||||
public function redirect(Request $request)
|
||||
{
|
||||
$checkoutData = session('checkout_data');
|
||||
if (!$checkoutData || $this->cart->count() === 0) {
|
||||
return redirect()->route('checkout.index');
|
||||
}
|
||||
|
||||
$subtotal = $this->cart->subtotal();
|
||||
$country = $checkoutData['shipping']['country'] ?? $checkoutData['billing']['country'] ?? 'DE';
|
||||
$shippingRate = ShippingRate::forCart($subtotal, $country);
|
||||
$shippingCost = $shippingRate ? $shippingRate->getCostForTotal($subtotal) : 0;
|
||||
$discount = 0;
|
||||
if ($code = session('coupon_code')) {
|
||||
$coupon = Coupon::findValid($code);
|
||||
if ($coupon) $discount = $coupon->calcDiscount($subtotal);
|
||||
}
|
||||
$total = max(0, $subtotal - $discount) + $shippingCost;
|
||||
|
||||
$provider = new PayPalClient;
|
||||
$provider->setApiCredentials(config('paypal'));
|
||||
$provider->getAccessToken();
|
||||
|
||||
$order = $provider->createOrder([
|
||||
'intent' => 'CAPTURE',
|
||||
'purchase_units' => [[
|
||||
'amount' => [
|
||||
'currency_code' => 'EUR',
|
||||
'value' => number_format($total, 2, '.', ''),
|
||||
],
|
||||
'description' => config('app.name') . ' Bestellung',
|
||||
]],
|
||||
'application_context' => [
|
||||
'return_url' => route('paypal.success'),
|
||||
'cancel_url' => route('checkout.confirm'),
|
||||
],
|
||||
]);
|
||||
|
||||
if (isset($order['id'])) {
|
||||
session(['paypal_order_id' => $order['id']]);
|
||||
foreach ($order['links'] as $link) {
|
||||
if ($link['rel'] === 'approve') {
|
||||
return redirect($link['href']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('checkout.confirm')->with('error', 'PayPal-Verbindung fehlgeschlagen. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
|
||||
public function success(Request $request)
|
||||
{
|
||||
$checkoutData = session('checkout_data');
|
||||
$paypalOrderId = session('paypal_order_id');
|
||||
|
||||
if (!$checkoutData || !$paypalOrderId) {
|
||||
return redirect()->route('checkout.index');
|
||||
}
|
||||
|
||||
$provider = new PayPalClient;
|
||||
$provider->setApiCredentials(config('paypal'));
|
||||
$provider->getAccessToken();
|
||||
|
||||
$result = $provider->capturePaymentOrder($paypalOrderId);
|
||||
|
||||
if (($result['status'] ?? '') !== 'COMPLETED') {
|
||||
return redirect()->route('checkout.confirm')->with('error', 'PayPal-Zahlung nicht abgeschlossen.');
|
||||
}
|
||||
|
||||
$checkoutData['payment_method'] = 'paypal';
|
||||
$order = $this->orderService->createFromSession($checkoutData, 'paypal');
|
||||
$order->update([
|
||||
'payment_id' => $paypalOrderId,
|
||||
'status' => 'payment_received',
|
||||
'paid_at' => now(),
|
||||
]);
|
||||
|
||||
session()->forget(['checkout_data', 'checkout_country', 'paypal_order_id']);
|
||||
|
||||
return redirect()->route('checkout.thankyou', $order->order_number);
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
session()->forget('paypal_order_id');
|
||||
return redirect()->route('checkout.confirm')->with('error', 'PayPal-Zahlung abgebrochen.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Shop;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Coupon;
|
||||
use App\Models\Order;
|
||||
use App\Models\ShippingRate;
|
||||
use App\Services\CartService;
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Http\Request;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
class StripeController extends Controller
|
||||
{
|
||||
public function __construct(private CartService $cart, private OrderService $orderService) {}
|
||||
|
||||
public function createIntent(Request $request)
|
||||
{
|
||||
$subtotal = $this->cart->subtotal();
|
||||
$country = session('checkout_country', 'DE');
|
||||
$shippingRate = ShippingRate::forCart($subtotal, $country);
|
||||
$shippingCost = $shippingRate ? $shippingRate->getCostForTotal($subtotal) : 0;
|
||||
$discount = 0;
|
||||
if ($code = session('coupon_code')) {
|
||||
$coupon = Coupon::findValid($code);
|
||||
if ($coupon) $discount = $coupon->calcDiscount($subtotal);
|
||||
}
|
||||
$total = max(0, $subtotal - $discount) + $shippingCost;
|
||||
|
||||
$stripe = new StripeClient(config('services.stripe.secret'));
|
||||
$intent = $stripe->paymentIntents->create([
|
||||
'amount' => (int) round($total * 100),
|
||||
'currency' => 'eur',
|
||||
'automatic_payment_methods' => ['enabled' => true],
|
||||
]);
|
||||
|
||||
return response()->json(['client_secret' => $intent->client_secret]);
|
||||
}
|
||||
|
||||
public function success(Request $request)
|
||||
{
|
||||
$checkoutData = session('checkout_data');
|
||||
$paymentIntent = $request->input('payment_intent');
|
||||
|
||||
if (!$checkoutData || !$paymentIntent) {
|
||||
return redirect()->route('checkout.index');
|
||||
}
|
||||
|
||||
$stripe = new StripeClient(config('services.stripe.secret'));
|
||||
$intent = $stripe->paymentIntents->retrieve($paymentIntent);
|
||||
|
||||
if ($intent->status !== 'succeeded') {
|
||||
return redirect()->route('checkout.confirm')->with('error', 'Zahlung nicht abgeschlossen.');
|
||||
}
|
||||
|
||||
$checkoutData['payment_method'] = 'stripe';
|
||||
$order = $this->orderService->createFromSession($checkoutData, 'stripe');
|
||||
$order->update([
|
||||
'payment_id' => $paymentIntent,
|
||||
'status' => 'payment_received',
|
||||
'paid_at' => now(),
|
||||
]);
|
||||
|
||||
session()->forget(['checkout_data', 'checkout_country']);
|
||||
|
||||
return redirect()->route('checkout.thankyou', $order->order_number);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user