added marketing page and moved events&general landing page
This commit is contained in:
207
app/Http/Controllers/MarketingController.php
Normal file
207
app/Http/Controllers/MarketingController.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\Checkout\Session;
|
||||
use Stripe\StripeClient;
|
||||
use Exception;
|
||||
use PayPal\Api\Amount;
|
||||
use PayPal\Api\Payer;
|
||||
use PayPal\Api\Payment;
|
||||
use PayPal\Api\RedirectUrls;
|
||||
use PayPal\Api\Transaction;
|
||||
use PayPal\Rest\ApiContext;
|
||||
use PayPal\Auth\OAuthTokenCredential;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\EventPurchase;
|
||||
|
||||
class MarketingController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
\Stripe\Stripe::setApiKey(config('services.stripe.key'));
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$packages = [
|
||||
['id' => 'basic', 'name' => 'Basic', 'events' => 1, 'price' => 0, 'description' => '1 Event, 100 Fotos, Grundfunktionen'],
|
||||
['id' => 'standard', 'name' => 'Standard', 'events' => 10, 'price' => 99, 'description' => '10 Events, Unbegrenzt Fotos, Erweiterte Features'],
|
||||
['id' => 'premium', 'name' => 'Premium', 'events' => 50, 'price' => 199, 'description' => '50 Events, Support & Custom, Alle Features'],
|
||||
];
|
||||
|
||||
return view('marketing', compact('packages'));
|
||||
}
|
||||
|
||||
public function contact(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255',
|
||||
'message' => 'required|string|max:1000',
|
||||
]);
|
||||
|
||||
Mail::raw("Kontakt-Anfrage von {$request->name} ({$request->email}): {$request->message}", function ($message) use ($request) {
|
||||
$message->to('admin@fotospiel.de')
|
||||
->subject('Neue Kontakt-Anfrage');
|
||||
});
|
||||
|
||||
return redirect()->back()->with('success', 'Nachricht gesendet!');
|
||||
}
|
||||
|
||||
public function checkout(Request $request, $package)
|
||||
{
|
||||
$packages = [
|
||||
'basic' => ['name' => 'Basic', 'price' => 0, 'events' => 1],
|
||||
'standard' => ['name' => 'Standard', 'price' => 9900, 'events' => 10], // cents
|
||||
'premium' => ['name' => 'Premium', 'price' => 19900, 'events' => 50],
|
||||
];
|
||||
|
||||
if (!isset($packages[$package])) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$pkg = $packages[$package];
|
||||
|
||||
if ($pkg['price'] == 0) {
|
||||
// Free package: create tenant and event
|
||||
$tenant = Tenant::create([
|
||||
'name' => $request->input('tenant_name', 'New Tenant'),
|
||||
'slug' => Str::slug('new-' . now()),
|
||||
'email' => $request->input('email'),
|
||||
'events_remaining' => $pkg['events'],
|
||||
]);
|
||||
|
||||
// Create initial event
|
||||
$event = $tenant->events()->create([
|
||||
'name' => $request->input('event_name', 'My Event'),
|
||||
'slug' => Str::slug($request->input('event_name', 'my-event')),
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$purchase = EventPurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'events_purchased' => $pkg['events'],
|
||||
'amount' => 0,
|
||||
'currency' => 'EUR',
|
||||
'provider' => 'free',
|
||||
'status' => 'completed',
|
||||
'purchased_at' => now(),
|
||||
]);
|
||||
|
||||
return redirect("/admin/tenants/{$tenant->id}/edit")->with('success', 'Konto erstellt! Willkommen bei Fotospiel.');
|
||||
}
|
||||
|
||||
$stripe = new \Stripe\StripeClient(config('services.stripe.secret'));
|
||||
$session = $stripe->checkout->sessions->create([
|
||||
'payment_method_types' => ['card'],
|
||||
'line_items' => [[
|
||||
'price_data' => [
|
||||
'currency' => 'eur',
|
||||
'product_data' => [
|
||||
'name' => $pkg['name'] . ' Package',
|
||||
],
|
||||
'unit_amount' => $pkg['price'],
|
||||
],
|
||||
'quantity' => 1,
|
||||
]],
|
||||
'mode' => 'payment',
|
||||
'success_url' => route('marketing.success', $package),
|
||||
'cancel_url' => route('marketing'),
|
||||
'metadata' => [
|
||||
'package' => $package,
|
||||
'events' => $pkg['events'],
|
||||
],
|
||||
]);
|
||||
|
||||
return redirect($session->url, 303);
|
||||
}
|
||||
|
||||
public function stripeCheckout($sessionId)
|
||||
{
|
||||
// Handle Stripe success
|
||||
return view('marketing.success', ['provider' => 'Stripe']);
|
||||
}
|
||||
|
||||
public function paypalCheckout(Request $request, $package)
|
||||
{
|
||||
$packages = [
|
||||
'basic' => ['name' => 'Basic', 'price' => 0, 'events' => 1],
|
||||
'standard' => ['name' => 'Standard', 'price' => 99, 'events' => 10],
|
||||
'premium' => ['name' => 'Premium', 'price' => 199, 'events' => 50],
|
||||
];
|
||||
|
||||
if (!isset($packages[$package])) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$pkg = $packages[$package];
|
||||
|
||||
if ($pkg['price'] == 0) {
|
||||
// Free package: create tenant and event
|
||||
$tenant = Tenant::create([
|
||||
'name' => $request->input('tenant_name', 'New Tenant'),
|
||||
'slug' => Str::slug('new-' . now()),
|
||||
'email' => $request->input('email'),
|
||||
'events_remaining' => $pkg['events'],
|
||||
]);
|
||||
|
||||
// Create initial event
|
||||
$event = $tenant->events()->create([
|
||||
'name' => $request->input('event_name', 'My Event'),
|
||||
'slug' => Str::slug($request->input('event_name', 'my-event')),
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$purchase = EventPurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'events_purchased' => $pkg['events'],
|
||||
'amount' => 0,
|
||||
'currency' => 'EUR',
|
||||
'provider' => 'free',
|
||||
'status' => 'completed',
|
||||
'purchased_at' => now(),
|
||||
]);
|
||||
|
||||
return redirect("/admin/tenants/{$tenant->id}/edit")->with('success', 'Konto erstellt! Willkommen bei Fotospiel.');
|
||||
}
|
||||
|
||||
$apiContext = new ApiContext(
|
||||
new OAuthTokenCredential(
|
||||
config('services.paypal.client_id'),
|
||||
config('services.paypal.secret')
|
||||
)
|
||||
);
|
||||
|
||||
$payment = new Payment();
|
||||
$payer = new Payer();
|
||||
$payer->setPaymentMethod('paypal');
|
||||
|
||||
$amountObj = new Amount();
|
||||
$amountObj->setCurrency('EUR');
|
||||
$amountObj->setTotal($pkg['price']);
|
||||
|
||||
$transaction = new Transaction();
|
||||
$transaction->setAmount($amountObj);
|
||||
|
||||
$redirectUrls = new RedirectUrls();
|
||||
$redirectUrls->setReturnUrl(route('marketing.success', $package));
|
||||
$redirectUrls->setCancelUrl(route('marketing'));
|
||||
|
||||
$payment->setIntent('sale')
|
||||
->setPayer($payer)
|
||||
->setTransactions([$transaction])
|
||||
->setRedirectUrls($redirectUrls);
|
||||
|
||||
try {
|
||||
$payment->create($apiContext);
|
||||
return redirect($payment->getApprovalLink());
|
||||
} catch (Exception $e) {
|
||||
return back()->with('error', 'Zahlung fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Http/Controllers/PayPalWebhookController.php
Normal file
56
app/Http/Controllers/PayPalWebhookController.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\EventPurchase;
|
||||
use App\Models\Tenant;
|
||||
|
||||
class PayPalWebhookController extends Controller
|
||||
{
|
||||
public function handle(Request $request)
|
||||
{
|
||||
$input = $request->all();
|
||||
$ipnMessage = $input['ipn_track_id'] ?? null;
|
||||
$payerEmail = $input['payer_email'] ?? null;
|
||||
$paymentStatus = $input['payment_status'] ?? null;
|
||||
$mcGross = $input['mc_gross'] ?? 0;
|
||||
$packageId = $input['custom'] ?? null;
|
||||
|
||||
if ($paymentStatus === 'Completed' && $mcGross > 0) {
|
||||
// Verify IPN with PayPal (simplified; use SDK for full verification)
|
||||
// $verified = $this->verifyIPN($input);
|
||||
|
||||
// Find or create tenant (for public checkout, perhaps create new or use session)
|
||||
// For now, assume tenant_id from custom or session
|
||||
$tenantId = $packageId ? Tenant::where('slug', $packageId)->first()->id ?? 1 : 1;
|
||||
|
||||
// Create purchase and increment credits
|
||||
$purchase = EventPurchase::create([
|
||||
'tenant_id' => $tenantId, // Implement tenant resolution
|
||||
'events_purchased' => $mcGross / 49, // Example: 49€ per event credit
|
||||
'amount' => $mcGross,
|
||||
'currency' => $input['mc_currency'] ?? 'EUR',
|
||||
'provider' => 'paypal',
|
||||
'external_receipt_id' => $ipnMessage,
|
||||
'status' => 'completed',
|
||||
'purchased_at' => now(),
|
||||
]);
|
||||
|
||||
$tenant = Tenant::find($tenantId);
|
||||
$tenant->incrementCredits($purchase->events_purchased, 'paypal_purchase', 'PayPal IPN', $purchase->id);
|
||||
|
||||
Log::info('PayPal IPN processed', $input);
|
||||
}
|
||||
|
||||
return response('OK', 200);
|
||||
}
|
||||
|
||||
private function verifyIPN($input)
|
||||
{
|
||||
// Use PayPal SDK to verify
|
||||
// Return true/false
|
||||
return true; // Placeholder
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"laravel/sanctum": "^4.2",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"laravel/wayfinder": "^0.1.9",
|
||||
"paypal/rest-api-sdk-php": "^1.6",
|
||||
"simplesoftwareio/simple-qrcode": "^4.2",
|
||||
"stripe/stripe-php": "^17.6"
|
||||
},
|
||||
|
||||
55
composer.lock
generated
55
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "855707cf018e10451bf627dbd6593f0a",
|
||||
"content-hash": "b0eebcaa1d38fb0a00e546fad7bdc991",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -4407,6 +4407,59 @@
|
||||
},
|
||||
"time": "2024-05-08T12:36:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paypal/rest-api-sdk-php",
|
||||
"version": "v1.6.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paypal/PayPal-PHP-SDK.git",
|
||||
"reference": "06837d290c4906578cfd92786412dff330a1429c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paypal/PayPal-PHP-SDK/zipball/06837d290c4906578cfd92786412dff330a1429c",
|
||||
"reference": "06837d290c4906578cfd92786412dff330a1429c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "3.7.*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PayPal": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache2"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PayPal",
|
||||
"homepage": "https://github.com/paypal/rest-api-sdk-php/contributors"
|
||||
}
|
||||
],
|
||||
"description": "PayPal's PHP SDK for REST APIs",
|
||||
"homepage": "http://paypal.github.io/PayPal-PHP-SDK/",
|
||||
"keywords": [
|
||||
"payments",
|
||||
"paypal",
|
||||
"rest",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paypal/PayPal-PHP-SDK/issues",
|
||||
"source": "https://github.com/paypal/PayPal-PHP-SDK/tree/stable"
|
||||
},
|
||||
"abandoned": "paypal/paypal-server-sdk",
|
||||
"time": "2016-01-20T17:45:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.4",
|
||||
|
||||
@@ -50,7 +50,7 @@ function HomeLayout() {
|
||||
}
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{ path: '/', element: <SimpleLayout title="Fotospiel"><LandingPage /></SimpleLayout> },
|
||||
{ path: '/event', element: <SimpleLayout title="Event"><LandingPage /></SimpleLayout> },
|
||||
{
|
||||
path: '/setup/:slug',
|
||||
element: <SetupLayout />,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
import { home } from '@/routes';
|
||||
import { marketing } from '@/routes';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function AuthSimpleLayout({ children, title, description }: Props
|
||||
<div className="w-full max-w-sm">
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Link href={home()} className="flex flex-col items-center gap-2 font-medium">
|
||||
<Link href={marketing()} className="flex flex-col items-center gap-2 font-medium">
|
||||
<div className="mb-1 flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<AppLogoIcon className="size-9 fill-current text-[var(--foreground)] dark:text-white" />
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>{{ config('app.name', 'Fotospiel') }}</title>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@viteReactRefresh
|
||||
@vite('resources/js/guest/main.tsx')
|
||||
@vite(['resources/css/app.css', 'resources/js/guest/main.tsx'])
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
16
resources/views/legal/datenschutz.blade.php
Normal file
16
resources/views/legal/datenschutz.blade.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Datenschutzerklärung - Fotospiel</title>
|
||||
</head>
|
||||
<body class="container mx-auto px-4 py-8">
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p>Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst und halten uns strikt an die Regeln der Datenschutzgesetze.</p>
|
||||
<p>Verantwortlich: Fotospiel GmbH, Musterstraße 1, 12345 Musterstadt</p>
|
||||
<p>Datenerfassung: Keine PII-Speicherung, anonyme Sessions für Gäste. E-Mails werden nur für Kontaktzwecke verarbeitet.</p>
|
||||
<p>Ihre Rechte: Auskunft, Löschung, Widerspruch. Kontaktieren Sie uns unter <a href="/kontakt">Kontakt</a>.</p>
|
||||
<p>Cookies: Nur funktionale Cookies für die PWA.</p>
|
||||
</body>
|
||||
</html>
|
||||
21
resources/views/legal/impressum.blade.php
Normal file
21
resources/views/legal/impressum.blade.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Impressum - Fotospiel</title>
|
||||
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
|
||||
</head>
|
||||
<body class="container mx-auto px-4 py-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Impressum</h1>
|
||||
<p class="mb-4">Angaben gemäß § 5 TMG</p>
|
||||
<p class="mb-4">Fotospiel GmbH<br>
|
||||
Musterstraße 1<br>
|
||||
12345 Musterstadt<br>
|
||||
Vertreten durch: Max Mustermann<br>
|
||||
Kontakt: <a href="/kontakt">Kontakt</a></p>
|
||||
<p class="mb-4">Umsatzsteuer-ID: DE123456789</p>
|
||||
<p>Registergericht: Amtsgericht Musterstadt</p>
|
||||
<p>Handelsregister: HRB 12345</p>
|
||||
</body>
|
||||
</html>
|
||||
205
resources/views/marketing.blade.php
Normal file
205
resources/views/marketing.blade.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fotospiel - Event-Fotos einfach und sicher</title>
|
||||
<meta name="description" content="Erstellen Sie unvergessliche Event-Fotos mit unserer PWA-Plattform. Für Hochzeiten, Firmenevents und mehr. Kostenloser Einstieg.">
|
||||
<link rel="icon" href="{{ asset('logo.svg') }}" type="image/svg+xml">
|
||||
@vite(['resources/css/app.css'])
|
||||
</head>
|
||||
<body class="bg-gray-50 text-gray-900">
|
||||
<!-- Hero Section -->
|
||||
<section class="bg-gradient-to-r from-[#FFB6C1] via-[#FFD700] to-[#87CEEB] text-white py-20 px-4">
|
||||
<div class="container mx-auto text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold mb-4">Fotospiel</h1>
|
||||
<p class="text-xl md:text-2xl mb-8 max-w-3xl mx-auto">Erleben Sie Events durch professionelle Fotos. Unsere sichere PWA-Plattform für Gäste und Organisatoren. Einfach, mobil und datenschutzkonform.</p>
|
||||
<a href="/buy-credits/basic" class="bg-white text-[#FFB6C1] px-8 py-4 rounded-full font-semibold text-lg hover:bg-gray-100 transition">Jetzt starten – Kostenlos</a>
|
||||
<img src="https://images.unsplash.com/photo-1511285560929-80b456fea0bc?w=800&h=400&fit=crop" alt="Event-Fotos" class="mt-8 mx-auto rounded-lg shadow-lg max-w-4xl">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section class="py-20 px-4 bg-white">
|
||||
<div class="container mx-auto">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">Warum Fotospiel?</h2>
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<div class="text-center p-6">
|
||||
<div class="w-16 h-16 bg-[#FFB6C1] rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Sichere Uploads</h3>
|
||||
<p>GDPR-konform, anonyme Sessions, keine PII-Speicherung.</p>
|
||||
</div>
|
||||
<div class="text-center p-6">
|
||||
<div class="w-16 h-16 bg-[#FFD700] rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Mobile PWA</h3>
|
||||
<p>Offline-fähig, App-ähnlich, für iOS und Android.</p>
|
||||
</div>
|
||||
<div class="text-center p-6">
|
||||
<div class="w-16 h-16 bg-[#87CEEB] rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2">Schnell & Einfach</h3>
|
||||
<p>Automatische Thumbnails, Echtzeit-Updates, einfache Bedienung.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4-Schritte Section -->
|
||||
<section class="py-20 px-4 bg-gray-50">
|
||||
<div class="container mx-auto">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">So funktioniert es – in 4 einfachen Schritten</h2>
|
||||
<div class="grid md:grid-cols-4 gap-8">
|
||||
<div class="text-center">
|
||||
<div class="w-12 h-12 bg-[#FFB6C1] rounded-full flex items-center justify-center mx-auto mb-4 text-white font-bold">1</div>
|
||||
<h3 class="font-semibold mb-2">Event erstellen</h3>
|
||||
<p>Als Organisator: Registrieren, Event anlegen, Gäste einladen.</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-12 h-12 bg-[#FFD700] rounded-full flex items-center justify-center mx-auto mb-4 text-white font-bold">2</div>
|
||||
<h3 class="font-semibold mb-2">Fotos hochladen</h3>
|
||||
<p>Gäste: PWA öffnen, Fotos via Kamera oder Galerie teilen.</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-12 h-12 bg-[#87CEEB] rounded-full flex items-center justify-center mx-auto mb-4 text-white font-bold">3</div>
|
||||
<h3 class="font-semibold mb-2">Freigaben & Likes</h3>
|
||||
<p>Emotions auswählen, Fotos liken, Galerie browsen.</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="w-12 h-12 bg-[#FFB6C1] rounded-full flex items-center justify-center mx-auto mb-4 text-white font-bold">4</div>
|
||||
<h3 class="font-semibold mb-2">Download & Teilen</h3>
|
||||
<p>Freigegebene Fotos herunterladen, Event abschließen.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Preise Section -->
|
||||
<section class="py-20 px-4 bg-white">
|
||||
<div class="container mx-auto">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">Tarife</h2>
|
||||
<div class="grid md:grid-cols-3 gap-8 max-w-4xl mx-auto">
|
||||
<div class="bg-gray-50 p-8 rounded-lg text-center border-2 border-gray-200">
|
||||
<h3 class="text-2xl font-bold mb-4">Basic</h3>
|
||||
<p class="text-4xl font-bold text-[#FFB6C1] mb-4">0 €</p>
|
||||
<ul class="mb-6 space-y-2">
|
||||
<li>1 Event</li>
|
||||
<li>100 Fotos</li>
|
||||
<li>Grundfunktionen</li>
|
||||
</ul>
|
||||
<a href="/buy-credits/basic" class="bg-[#FFB6C1] text-white px-6 py-3 rounded-full font-semibold">Kostenlos starten</a>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-8 rounded-lg text-center border-2 border-[#FFD700]">
|
||||
<h3 class="text-2xl font-bold mb-4">Standard</h3>
|
||||
<p class="text-4xl font-bold text-[#FFD700] mb-4">99 €</p>
|
||||
<ul class="mb-6 space-y-2">
|
||||
<li>10 Events</li>
|
||||
<li>Unbegrenzt Fotos</li>
|
||||
<li>Erweiterte Features</li>
|
||||
</ul>
|
||||
<a href="/buy-credits/standard" class="bg-[#FFD700] text-white px-6 py-3 rounded-full font-semibold">Kaufen</a>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-8 rounded-lg text-center border-2 border-gray-200">
|
||||
<h3 class="text-2xl font-bold mb-4">Premium</h3>
|
||||
<p class="text-4xl font-bold text-[#87CEEB] mb-4">199 €</p>
|
||||
<ul class="mb-6 space-y-2">
|
||||
<li>50 Events</li>
|
||||
<li>Support & Custom</li>
|
||||
<li>Alle Features</li>
|
||||
</ul>
|
||||
<a href="/buy-credits/premium" class="bg-[#87CEEB] text-white px-6 py-3 rounded-full font-semibold">Kaufen</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Kontakt Section -->
|
||||
<section class="py-20 px-4 bg-gray-50">
|
||||
<div class="container mx-auto max-w-2xl">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">Kontakt</h2>
|
||||
<form method="POST" action="/kontakt" class="space-y-4">
|
||||
@csrf
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium mb-2">Name</label>
|
||||
<input type="text" id="name" name="name" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#FFB6C1]">
|
||||
</div>
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium mb-2">E-Mail</label>
|
||||
<input type="email" id="email" name="email" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#FFB6C1]">
|
||||
</div>
|
||||
<div>
|
||||
<label for="message" class="block text-sm font-medium mb-2">Nachricht</label>
|
||||
<textarea id="message" name="message" rows="4" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#FFB6C1]"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="w-full bg-[#FFB6C1] text-white py-3 rounded-md font-semibold hover:bg-[#FF69B4] transition">Senden</button>
|
||||
</form>
|
||||
@if (session('success'))
|
||||
<p class="mt-4 text-green-600 text-center">{{ session('success') }}</p>
|
||||
@endif
|
||||
@if ($errors->any())
|
||||
<div class="mt-4 p-4 bg-red-100 border border-red-400 rounded-md">
|
||||
<ul class="list-disc list-inside">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Testimonials Section -->
|
||||
<section class="py-20 px-4 bg-white">
|
||||
<div class="container mx-auto">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">Was unsere Kunden sagen</h2>
|
||||
<div class="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto">
|
||||
<div class="bg-gray-50 p-6 rounded-lg">
|
||||
<p class="mb-4">"Perfekt für unsere Hochzeit! Einfach und sicher."</p>
|
||||
<p class="font-semibold">- Anna & Max</p>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-6 rounded-lg">
|
||||
<p class="mb-4">"Großes Firmenevent – alle Fotos zentral und mobil."</p>
|
||||
<p class="font-semibold">- Team XYZ GmbH</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FAQ Section -->
|
||||
<section class="py-20 px-4 bg-gray-50">
|
||||
<div class="container mx-auto max-w-3xl">
|
||||
<h2 class="text-3xl font-bold text-center mb-12">Häufige Fragen</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<h3 class="font-semibold">Ist es kostenlos?</h3>
|
||||
<p>Ja, der Basic-Tarif ist kostenlos für 1 Event. Upgrades ab 99€.</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<h3 class="font-semibold">Datenschutz?</h3>
|
||||
<p>100% GDPR-konform. Keine personenbezogenen Daten gespeichert. Siehe <a href="/datenschutz" class="text-[#FFB6C1]">Datenschutzerklärung</a>.</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<h3 class="font-semibold">Wie lade ich Fotos hoch?</h3>
|
||||
<p>Über die PWA-App: Kamera oder Galerie, Emotions zuweisen, teilen.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray-800 text-white py-8 px-4">
|
||||
<div class="container mx-auto text-center">
|
||||
<p>© 2025 Fotospiel GmbH. Alle Rechte vorbehalten.</p>
|
||||
<div class="mt-4 space-x-4">
|
||||
<a href="/impressum" class="hover:text-[#FFB6C1]">Impressum</a>
|
||||
<a href="/datenschutz" class="hover:text-[#FFB6C1]">Datenschutz</a>
|
||||
<a href="/kontakt" class="hover:text-[#FFB6C1]">Kontakt</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
13
resources/views/marketing/success.blade.php
Normal file
13
resources/views/marketing/success.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Zahlung erfolgreich - Fotospiel</title>
|
||||
</head>
|
||||
<body class="container mx-auto px-4 py-8 text-center">
|
||||
<h1>Zahlung erfolgreich!</h1>
|
||||
<p>Vielen Dank für Ihren Kauf. Ihr Konto wurde aktualisiert.</p>
|
||||
<a href="/admin" class="bg-green-600 text-white px-4 py-2 rounded">Zum Admin-Dashboard</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,8 @@
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
|
||||
// Public landing: serve Guest PWA shell at root
|
||||
Route::view('/', 'guest')->name('home');
|
||||
// Marketing-Seite unter Root
|
||||
Route::view('/', 'marketing')->name('marketing');
|
||||
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('dashboard', function () {
|
||||
@@ -15,19 +15,32 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
require __DIR__.'/settings.php';
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
// Guest PWA shell (served for event and /pwa paths; React handles routing)
|
||||
// Guest PWA shell for /event and sub-routes
|
||||
Route::view('/event/{any?}', 'guest')->where('any', '.*');
|
||||
Route::view('/e/{any?}', 'guest')->where('any', '.*');
|
||||
Route::view('/pwa/{any?}', 'guest')->where('any', '.*');
|
||||
Route::view('/legal/{any?}', 'guest')->where('any', '.*');
|
||||
|
||||
// Minimal public API for Guest PWA (stateless; no CSRF)
|
||||
Route::prefix('api/v1')->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class])->group(function () {
|
||||
// Public legal pages
|
||||
Route::get('/legal/{slug}', [\App\Http\Controllers\Api\LegalController::class, 'show']);
|
||||
// Public legal pages (for marketing)
|
||||
Route::get('/impressum', function () {
|
||||
return view('legal.impressum');
|
||||
})->name('impressum');
|
||||
Route::get('/datenschutz', function () {
|
||||
return view('legal.datenschutz');
|
||||
})->name('datenschutz');
|
||||
Route::get('/kontakt', function () {
|
||||
return view('legal.kontakt');
|
||||
})->name('kontakt');
|
||||
Route::post('/kontakt', [\App\Http\Controllers\MarketingController::class, 'contact'])->name('kontakt.submit');
|
||||
});
|
||||
|
||||
// Stripe webhooks (no CSRF, no auth)
|
||||
Route::post('/webhooks/stripe', [\App\Http\Controllers\StripeWebhookController::class, 'handle']);
|
||||
// Stripe webhooks (no CSRF, no auth)
|
||||
Route::post('/webhooks/stripe', [\App\Http\Controllers\StripeWebhookController::class, 'handle']);
|
||||
// PayPal IPN webhook
|
||||
Route::post('/webhooks/paypal', [\App\Http\Controllers\PayPalWebhookController::class, 'handle']);
|
||||
// PayPal IPN webhook
|
||||
Route::post('/webhooks/paypal', [\App\Http\Controllers\PayPalWebhookController::class, 'handle']);
|
||||
|
||||
// CSV templates for Super Admin imports
|
||||
Route::get('/super-admin/templates/emotions.csv', function () {
|
||||
@@ -60,3 +73,14 @@ Route::get('/super-admin/templates/tasks.csv', function () {
|
||||
};
|
||||
return response()->stream($callback, 200, $headers);
|
||||
});
|
||||
|
||||
// E-Commerce Routen für Marketing
|
||||
Route::get('/buy-credits/{package}', [\App\Http\Controllers\MarketingController::class, 'checkout'])->name('buy.credits');
|
||||
Route::get('/checkout/{sessionId}', [\App\Http\Controllers\MarketingController::class, 'stripeCheckout']);
|
||||
Route::get('/paypal-checkout/{package}', [\App\Http\Controllers\MarketingController::class, 'paypalCheckout']);
|
||||
Route::get('/marketing/success/{package}', [\App\Http\Controllers\MarketingController::class, 'success'])->name('marketing.success');
|
||||
|
||||
// E-Commerce Routen für Marketing
|
||||
Route::get('/buy-credits/{package}', [\App\Http\Controllers\MarketingController::class, 'checkout'])->name('buy.credits');
|
||||
Route::get('/checkout/{sessionId}', [\App\Http\Controllers\MarketingController::class, 'stripeCheckout']);
|
||||
Route::get('/paypal-checkout/{package}', [\App\Http\Controllers\MarketingController::class, 'paypalCheckout']);
|
||||
|
||||
Reference in New Issue
Block a user