booking) { return; } $agb = LegalDocument::current(LegalDocument::TYPE_AGB); BookingConfirmation::query()->create([ 'user_id' => auth()->id(), 'booking_type' => $this->booking['type'], 'booking_ref' => $this->booking['ref'], 'interval' => $this->booking['interval'], 'legal_document_id' => $agb?->id, 'agb_version' => $agb?->version ?? (string) config('legal.agb_version'), 'ip' => request()->ip(), 'confirmed_at' => now(), ]); } /** * Öffnet das Bestätigungs-Modal für eine Buchung (Tarif-Abo, Einzel-PM * oder Credit-Paket) mit USt.-Aufschlüsselung anhand der Rechnungsadresse. * Bestätigt wird per Weiterleitung auf die jeweilige Checkout-Route. */ public function selectBooking(string $type, ?string $ref = null, ?string $interval = null): void { $user = auth()->user(); [$title, $subtitle, $netCents, $url] = match ($type) { 'subscription' => $this->subscriptionBooking($ref, $interval), 'single_pm' => [ __('Einzel-Pressemitteilung'), __('Genau eine Veröffentlichung inkl. Prüfung — eingelöst erst bei Veröffentlichung.'), (int) config('billing.single_pm_price_cents'), route('me.checkout.single-pm'), ], 'credit_pack' => $this->creditPackBooking($ref), default => [null, null, 0, null], }; if (! $url) { return; } $resolver = app(VatResolver::class); $treatment = $resolver->resolve($user->billingAddress?->country_code, $user->billingAddress?->vat_id); $vatCents = $resolver->taxCentsFor($netCents, $treatment); // Tarif-Auswahl mit bestehendem Abo = Tarifwechsel (Swap statt // neuem Checkout) — das Modal erklärt die anteilige Verrechnung. $subscription = $user->subscription(); $isSwap = $type === 'subscription' && (bool) $subscription?->valid(); // Downgrade-Erkennung: Günstigere Tarife werden erst zum Ende der // bezahlten Periode wirksam (keine Gutschrift, kein negativer // Betrag) — das Modal erklärt das, der Controller entscheidet // verbindlich über dieselbe Service-Logik. $isDowngrade = false; if ($isSwap && ($targetPlan = Plan::query()->active()->where('slug', $ref)->first())) { $isDowngrade = app(StripeCheckoutService::class) ->isDowngrade($user, $targetPlan, $interval === 'yearly' ? 'yearly' : 'monthly'); } $this->booking = [ 'is_swap' => $isSwap, 'is_downgrade' => $isDowngrade, 'resumes_cancellation' => $isSwap && $subscription->onGracePeriod(), 'type' => $type, 'ref' => $ref, 'title' => $title, 'subtitle' => $subtitle, 'interval' => $interval, 'net' => $this->formatEuro($netCents), 'vat_percent' => (int) round($resolver->rateFor($treatment) * 100), 'vat' => $this->formatEuro($vatCents), 'gross' => $this->formatEuro($netCents + $vatCents), 'reverse_charge' => $treatment === VatTreatment::ReverseCharge, 'tax_exempt' => $treatment->isTaxExempt(), 'url' => $url, 'needs_address' => ! $user->hasCompleteBillingAddress(), ]; // Jede Buchung verlangt eine frische Unternehmer-Bestätigung. $this->entrepreneurConfirmed = false; Flux::modal('confirm-booking')->show(); } /** * @return array{0: ?string, 1: ?string, 2: int, 3: ?string} */ private function subscriptionBooking(?string $slug, ?string $interval): array { $plan = Plan::query()->active()->where('slug', $slug)->first(); if (! $plan) { return [null, null, 0, null]; } $isYearly = $interval === 'yearly'; $routeName = auth()->user()->subscription()?->valid() ? 'me.checkout.swap' : 'me.checkout.subscription'; return [ $plan->name . ' — ' . ($isYearly ? __('Jährlich') : __('Monatlich')), __(':quota Pressemitteilungen pro Monat, Prüfung und Veröffentlichung inklusive.', ['quota' => $plan->press_release_quota]), $isYearly ? (int) $plan->yearly_price_cents : (int) $plan->monthly_price_cents, route($routeName, ['planSlug' => $plan->slug, 'interval' => $isYearly ? 'yearly' : 'monthly']), ]; } /** * @return array{0: ?string, 1: ?string, 2: int, 3: ?string} */ private function creditPackBooking(?string $key): array { $pack = app(CreditTopupService::class)->findPack((string) $key); if (! $pack) { return [null, null, 0, null]; } $bonus = $pack['credits'] - intdiv($pack['price_cents'], 100); return [ __(':credits Credits Guthaben', ['credits' => $pack['credits']]), $bonus > 0 ? __('Enthält :bonus Bonus-Credits. 1 Credit = 1 € beim Einlösen.', ['bonus' => $bonus]) : __('1 Credit = 1 € beim Einlösen.'), (int) $pack['price_cents'], route('me.checkout.credit-topup', ['pack' => $pack['key']]), ]; } /** * Extra-PM aus der Wallet nachkaufen (tier-gestaffelter Preis). Reicht das * Guthaben nicht, zeigt der kontextuelle Mini-Checkout den Fehlbetrag. */ public function buyExtraPm(ExtraPmPurchaseService $service): void { $this->walletNotice = null; $this->walletError = null; try { $service->purchase(auth()->user()); $this->walletNotice = __('Extra-Pressemitteilung gebucht — Ihr PM-Kontingent wurde um eine Veröffentlichung erhöht.'); } catch (InsufficientCreditsException $e) { $this->walletError = __('Die Extra-PM kostet :need Credits, Ihr Guthaben beträgt :have. Bitte laden Sie mindestens :short Credits nach.', [ 'need' => $e->required, 'have' => $e->available, 'short' => $e->shortfall(), ]); } } public function planIcon(Plan $plan): string { return match ($plan->slug) { 'starter' => 'rocket-launch', 'business' => 'briefcase', 'pro' => 'chart-bar', 'agency' => 'building-office-2', default => 'megaphone', }; } /** * Lesbares Portal-Label für einen Bestandsschutz-Posten (aus den * Legacy-Konditionen). Fällt auf „Portal unbekannt" zurück. */ public function legacyPortalLabel(UserPaymentOption $option): string { $value = data_get($option->legacy_conditions, 'legacy_portal'); return Portal::tryFrom((string) $value)?->label() ?? __('Portal unbekannt'); } /** * Abrechnungsintervall eines Bestandsschutz-Postens als Label. */ public function legacyIntervalLabel(UserPaymentOption $option): string { return match (data_get($option->legacy_conditions, 'interval')) { 'monthly' => __('Monat'), default => __('Jahr'), }; } public function with(): array { $user = auth()->user(); $subscription = $user->subscription(); $currentPlan = $user->currentPlan(); $currentTier = $user->currentTier(); $pricing = app(CreditPricingService::class); $currentInterval = null; if ($currentPlan && $subscription) { $currentInterval = $subscription->stripe_price === $currentPlan->stripe_price_id_yearly ? 'yearly' : 'monthly'; } // Buchungs- und Verlängerungsdatum des laufenden Abos (Stripe spiegelt // die Periode nicht lokal, daher gecachter Abruf; null bei Fehler). $subscriptionPeriod = $subscription ? app(SubscriptionPeriodResolver::class)->resolve($subscription) : null; // Vorgemerkter Tarifwechsel (Downgrade zum Periodenende): lokale // Spiegelung der Stripe-Schedule-Vormerkung für die Anzeige. $scheduledChangePlan = null; if ($subscription?->scheduled_stripe_price) { $scheduledChangePlan = Plan::query() ->where('stripe_price_id_monthly', $subscription->scheduled_stripe_price) ->orWhere('stripe_price_id_yearly', $subscription->scheduled_stripe_price) ->first(); } return [ // Rückkehr aus dem Stripe-Checkout (?checkout=erfolg|abbruch) // bzw. Hinweis aus den Checkout-Guards (Session-Flash). 'checkoutResult' => request()->query('checkout'), 'checkoutNotice' => session('checkout-notice'), 'plans' => Plan::query()->active()->get(), 'currentPlan' => $currentPlan, 'currentInterval' => $currentInterval, 'subscription' => $subscription, 'subscriptionPeriod' => $subscriptionPeriod, 'scheduledChangePlan' => $scheduledChangePlan, 'scheduledChangeAt' => $subscription?->scheduled_change_at ? \Illuminate\Support\Carbon::parse($subscription->scheduled_change_at) : null, // Bestandstarife: laufende Legacy-Vereinbarungen (MAN-Kreis, // unbegrenzte PMs — Entscheidung 12.06.2026). 'legacyOptions' => $user ->userPaymentOptions() ->whereIn('status', [UserPaymentOptionStatus::Active->value, UserPaymentOptionStatus::Grandfathered->value]) ->orderBy('current_period_end') ->get(), 'openPurchases' => $user->singlePurchases()->grantingSubmission()->orderBy('paid_at')->get(), 'consumedPurchases' => $user->singlePurchases()->where('status', SinglePurchaseStatus::Consumed->value)->with('pressRelease')->latest('consumed_at')->limit(10)->get(), 'quotaRemaining' => $user->pressReleaseQuotaRemaining(), 'quotaTotal' => $user->pressReleaseQuotaTotal(), 'singlePmPrice' => $this->formatEuro((int) config('billing.single_pm_price_cents')), 'singlePmAvailable' => (bool) config('billing.single_pm_stripe_price_id'), // Credit-Wallet & Add-ons (Decision-Update Rev. 4). 'hasCompleteBillingAddress' => $user->hasCompleteBillingAddress(), 'creditBalance' => $user->creditBalance(), 'creditPacks' => app(CreditTopupService::class)->packs(), 'extraPmPrice' => $pricing->extraPmCreditsFor($user), // Extra-PM nur anbieten, wenn man Guthaben hat oder im Abo ist — // sonst ergibt sich der Preis aus der Preisliste (Decision-Update). 'showExtraPm' => $user->creditBalance() > 0 || (bool) $subscription, 'recentTransactions' => $user->creditTransactions()->latest()->limit(6)->get(), // Preisliste (Add-on-Credits). 'extraPmStaffel' => collect(Tier::cases())->map(fn (Tier $t): array => [ 'label' => $t->label(), 'credits' => $pricing->extraPmCredits($t), 'isCurrent' => $t === $currentTier, ])->all(), 'boostStaffel' => $pricing->boostOptions(), 'boostCategoryStaffel' => $pricing->boostOptions(BoostPlacement::Category), 'proofPrice' => $pricing->proofPdfCredits(), ]; } }; ?>
{{-- ============== CHECKOUT-RÜCKMELDUNG ============== --}} @if ($checkoutResult === 'erfolg')
{{ __('Vielen Dank für Ihre Buchung!') }} {{ __('Die Zahlung wird von Stripe bestätigt — die Buchung erscheint hier in wenigen Augenblicken. Die Rechnung finden Sie anschließend unter Rechnungen.') }}
@elseif ($checkoutResult === 'wechsel')
{{ __('Ihr Tarifwechsel wurde durchgeführt!') }} {{ __('Der neue Tarif gilt ab sofort. Bereits gezahlte Beträge wurden anteilig verrechnet — die Rechnung finden Sie unter Rechnungen.') }}
@elseif ($checkoutResult === 'wechsel-geplant')
{{ __('Ihr Tarifwechsel ist vorgemerkt!') }} @if ($scheduledChangeAt) {{ __('Er wird am :date wirksam — bis dahin gilt Ihr aktueller Tarif unverändert, es entsteht keine weitere Zahlung.', ['date' => $scheduledChangeAt->format('d.m.Y')]) }} @else {{ __('Er wird zum Ende der laufenden Abrechnungsperiode wirksam — bis dahin gilt Ihr aktueller Tarif unverändert.') }} @endif
@elseif ($checkoutResult === 'abbruch')
{{ __('Der Bezahlvorgang wurde abgebrochen. Es wurde nichts gebucht — Sie können den Checkout jederzeit erneut starten.') }}
@endif @if ($checkoutNotice)
{{ $checkoutNotice }}
@endif {{-- ============== RECHNUNGSADRESSE FEHLT ============== --}} {{-- Proaktiver Hinweis: jede Buchung setzt eine vollständige Rechnungsadresse voraus (sonst leitet der Checkout-Guard ins Profil). --}} @if (! $hasCompleteBillingAddress)
{{ __('Rechnungsadresse fehlt.') }} {{ __('Für jede Buchung ist eine vollständige Rechnungsadresse nötig — sie wird an Stripe übergeben.') }} {{ __('Jetzt hinterlegen') }}
@endif {{-- ============== PAGE HEADER ============== --}} {{-- ============== BESTANDSSCHUTZ-PAKETE ============== --}} {{-- Migrierte Legacy-Vereinbarungen als echte, gebuchte Pakete dargestellt: eigener Block, Rahmen, Icon, Portal-Badge, klare Typo. --}} @if ($legacyOptions->isNotEmpty())
{{ __('Ihr Bestandsschutz') }}

{{ trans_choice('Ihr gebuchtes Paket|Ihre gebuchten Pakete', $legacyOptions->count()) }}

{{ __('Ihre bisherigen Konditionen gelten unverändert weiter — unbegrenzte Pressemitteilungen, Abrechnung wie gewohnt per Rechnung.') }}

$legacyOptions->count() > 1])> @foreach ($legacyOptions as $option) @php $netCents = (int) data_get($option->legacy_conditions, 'net_cents', 0); $legacyName = data_get($option->legacy_conditions, 'name') ?? ($option->paymentOption?->article_number ?? __('Bestehende Vereinbarung')); @endphp
{{-- Akzent-Leiste --}}
{{ __('Bestandsschutz') }} {{ $this->legacyPortalLabel($option) }}

{{ $legacyName }}

{{ $this->formatEuro($netCents) }}
{{ __('netto / :interval', ['interval' => $this->legacyIntervalLabel($option)]) }}
  • {{ __('Unbegrenzte') }} {{ __('Pressemitteilungen') }}
  • {{ __('Konditionen gelten unverändert weiter') }}
  • @if ($option->current_period_end)
  • {{ __('Nächste Rechnung am :date', ['date' => $option->current_period_end->format('d.m.Y')]) }}
  • @endif
@endforeach
@if ($legacyOptions->count() > 1)

{{ __('Ihre Pakete werden gemeinsam geführt und sind nur zusammen kündbar (keine Einzelkündigung).') }}

@endif
@endif {{-- ============== AKTUELLER TARIF ============== --}} {{-- Erscheint erst, wenn eine Buchung existiert — vorher würde hier nur „kein Tarif" stehen und das Kontingent wäre irreführend. --}} @if ($subscription || $openPurchases->isNotEmpty())
{{ __('Aktueller Tarif') }} @if ($currentPlan) {{ $currentPlan->name }} @else {{ __('Einzel-PM') }} @endif
@if ($currentPlan)
{{ $currentInterval === 'yearly' ? $this->formatEuro($currentPlan->yearly_price_cents) . ' / ' . __('Jahr') : $this->formatEuro($currentPlan->monthly_price_cents) . ' / ' . __('Monat') }} {{ __('netto') }}

{{ __(':quota Pressemitteilungen pro Monat', ['quota' => $currentPlan->press_release_quota]) }} @if ($currentPlan->daily_limit) · {{ __('max. :limit Veröffentlichungen pro Tag', ['limit' => $currentPlan->daily_limit]) }} @endif

@if ($subscriptionPeriod)
{{ __('Gebucht am :date', ['date' => $subscriptionPeriod['booked_at']->format('d.m.Y')]) }} @unless ($subscription?->onGracePeriod()) {{ __('Verlängerung am :date', ['date' => $subscriptionPeriod['renews_at']->format('d.m.Y')]) }} @endunless
@endif @if ($subscription?->onGracePeriod())

{{ __('Gekündigt — läuft am :date aus.', ['date' => $subscription->ends_at?->format('d.m.Y')]) }}

@endif @if ($scheduledChangePlan && $scheduledChangeAt)

{{ __('Tarifwechsel zu :plan am :date vorgemerkt — bis dahin gilt Ihr aktueller Tarif.', [ 'plan' => $scheduledChangePlan->name, 'date' => $scheduledChangeAt->format('d.m.Y'), ]) }}

@endif @elseif ($openPurchases->isNotEmpty())
{{ trans_choice(':count Einzel-Pressemitteilung verfügbar|:count Einzel-Pressemitteilungen verfügbar', $openPurchases->count(), ['count' => $openPurchases->count()]) }}

{{ __('Jeder Kauf berechtigt zu genau einer Veröffentlichung — eingelöst wird er erst, wenn die Pressemitteilung live geht.') }}

@endif
{{-- Kontingent nur als echte Zahl — „unbegrenzt" wäre vor dem Launch-Schalter inhaltlich falsch. --}} @if (!is_null($quotaRemaining))
{{ __('PM-Kontingent diesen Monat') }}
{{ $quotaRemaining }} / {{ $quotaTotal }}
{{ __('Wird erst bei Veröffentlichung verbraucht.') }}
@endif @if ($subscription) {{ __('Abo verwalten') }}

{{ __('Zahlungsmethode, Rechnungen und Kündigung — sicher über das Stripe-Kundenportal.') }}

@endif
@endif {{-- ============== TARIF-RASTER ============== --}}
{{ __('Tarife') }}

{{ __('Den passenden Tarif wählen') }}

{{ __('Monatstarife sind monatlich kündbar. Im Jahrestarif sind 2 Monate gratis — Sie zahlen 10 von 12 Monaten, das Abo verlängert sich um jeweils zwölf Monate und ist zum Ende der Laufzeit kündbar.') }}

@foreach ($plans as $plan) @php($isCurrent = $currentPlan && $plan->is($currentPlan))
$isCurrent, ]) wire:key="plan-{{ $plan->slug }}">

{{ $plan->name }}

@if ($isCurrent) {{ __('Aktuell') }} @endif
{{ $this->formatEuro($plan->monthly_price_cents) }} / {{ __('Monat') }}
{{ $this->formatEuro($plan->yearly_price_cents) }} / {{ __('Jahr') }}
{{ __('netto zzgl. USt.') }}
  • {{ $plan->press_release_quota }} {{ __('Pressemitteilungen pro Monat') }}
  • @if ($plan->daily_limit) {{ __('max. :limit Veröffentlichungen pro Tag', ['limit' => $plan->daily_limit]) }} @else {{ __('Ohne Tageslimit') }} @endif
  • @forelse ($plan->benefitsList() as $benefit)
  • {{ $benefit }}
  • @empty
  • {{ __('Prüfung und Veröffentlichung inklusive') }}
  • @endforelse
@if ($subscription) {{-- Tarifwechsel: Swap des bestehenden Abos statt neuem Checkout — pro Intervall, der aktuell gebuchte Tarif ist deaktiviert. --}}
@if ($isCurrent && $currentInterval === 'monthly') {{ __('Ihr aktueller Tarif') }} @else {{ __('Zu diesem Tarif wechseln') }} @endif
@if ($isCurrent && $currentInterval === 'yearly') {{ __('Ihr aktueller Tarif') }} @else {{ __('Zu diesem Tarif wechseln') }} @endif
@else
{{ __('Monatlich buchen') }}
{{ __('Jährlich buchen') }}
@endif
@endforeach

{{ __('Mehr als 60 Pressemitteilungen pro Monat, mehrere Teams oder Sonderkonditionen? Enterprise-Konditionen erhalten Sie auf Anfrage unter info@pressekonto.com — inklusive Inhaltsprüfung und Veröffentlichung.') }}

{{-- ============== EINZEL-PM (OHNE ABO) ============== --}}

{{ __('Einzel-Pressemitteilung — ohne Abo') }}

{{ __('Genau eine Veröffentlichung inklusive Prüfung und Veröffentlichung. Eingelöst wird der Kauf erst, wenn die Pressemitteilung live geht — Ablehnungen kosten nichts.') }} @if ($openPurchases->isNotEmpty()) {{ trans_choice('Aktuell :count offener Kauf.|Aktuell :count offene Käufe.', $openPurchases->count(), ['count' => $openPurchases->count()]) }} @endif

{{ $singlePmPrice }}
{{ __('netto zzgl. USt.') }}
{{ __('Jetzt buchen') }}
{{-- ============== CREDIT-WALLET & ADD-ONS ============== --}}
{{ __('Credit-Wallet & Add-ons') }}

{{ __('Guthaben & Credit-Add-ons') }}

{{ __('1 Credit = 1 €. Mit Guthaben kaufen Sie Extra-Pressemitteilungen, Boosts und Veröffentlichungsnachweise — größere Pakete enthalten Bonus-Credits.') }}

{{-- Guthaben + Aufladen --}}
{{ __('Ihr Guthaben') }}
{{ $creditBalance }} {{ __('Credits') }}
{{ __('Guthaben aufladen') }}
@foreach ($creditPacks as $pack) @php($bonus = $pack['credits'] - intdiv($pack['price_cents'], 100)) @endforeach
{{-- Extra-PM: nur bei vorhandenem Guthaben oder aktivem Abo (sonst Preisliste) --}} @if ($showExtraPm) @if ($walletNotice)
{{ $walletNotice }}
@endif @if ($walletError)
{{ $walletError }}
@endif

{{ __('Extra-Pressemitteilung') }}

{{ __('Eine weitere PM, wenn Ihr Monatskontingent voll ist — ohne Tarifwechsel.') }}

{{ $extraPmPrice }} {{ __('Credits') }}
{{ __('Aus Guthaben buchen') }}
@endif {{-- Wallet-Verlauf --}} @if ($recentTransactions->isNotEmpty())
{{ __('Letzte Wallet-Buchungen') }}
@foreach ($recentTransactions as $tx)
{{ $tx->description ?? $tx->type->label() }}
{{ $tx->created_at?->format('d.m.Y H:i') }}
$tx->amount_credits > 0, 'text-[color:var(--color-ink-2)]' => $tx->amount_credits < 0, ])> {{ $tx->amount_credits > 0 ? '+' : '' }}{{ $tx->amount_credits }}
@endforeach
@endif
{{-- ============== PREISLISTE (Add-on-Credits) ============== --}}
{{ __('Preisliste') }}

{{ __('Was kostet wie viel') }}

{{ __('Add-ons werden mit Credits bezahlt (1 Credit = 1 €). Der Extra-PM-Preis richtet sich nach Ihrem Tarif — höhere Tarife zahlen weniger.') }}

{{-- Extra-PM nach Tarif --}}

{{ __('Extra-Pressemitteilung') }}

@foreach ($extraPmStaffel as $row) $row['isCurrent']])> @endforeach
{{ $row['label'] }} @if ($row['isCurrent']) {{ __('Ihr Tarif') }} @endif {{ $row['credits'] }} {{ __('Credits') }}
{{-- Boost nach Laufzeit (zwei Stufen — 13.07.2026) --}}

{{ __('Boost — Platzierung') }}

{{ __('Startseiten-Boost') }}
@foreach ($boostStaffel as $days => $credits) @endforeach
{{ __(':days Tage', ['days' => $days]) }} {{ $credits }} {{ __('Credits') }}
{{ __('Kategorie-Boost (nur Branchenseite)') }}
@foreach ($boostCategoryStaffel as $days => $credits) @endforeach
{{ __(':days Tage', ['days' => $days]) }} {{ $credits }} {{ __('Credits') }}
{{-- Veröffentlichungsnachweis --}}

{{ __('Veröffentlichungsnachweis') }}

{{ __('PDF pro PM') }} {{ $proofPrice }} {{ __('Credits') }}

{{ __('Boost und Nachweis buchen Sie direkt an der jeweiligen veröffentlichten Pressemitteilung.') }}

{{-- ============== VERLAUF ============== --}} {{-- Aktive Buchungen stehen oben im Tarif-Panel — hier nur die Historie, durch Trennlinie und Zwischenüberschrift klar abgesetzt. --}}
{{ __('Verlauf') }}

{{ __('Eingelöste Käufe') }}

{{ __('Jeder eingelöste Einzelkauf mit der zugehörigen Pressemitteilung — die Rechnungen dazu finden Sie unter Rechnungen.') }}

@if ($consumedPurchases->isNotEmpty())
@foreach ($consumedPurchases as $purchase)
{{ $purchase->type->shortLabel() }}{{ $purchase->pressRelease ? ': ' . $purchase->pressRelease->title : '' }}
{{ __('eingelöst am :date', ['date' => $purchase->consumed_at?->format('d.m.Y')]) }}
{{ __('eingelöst') }}
@endforeach
@else
{{ __('Noch kein Buchungsverlauf') }}

{{ __('Eingelöste Einzelkäufe erscheinen hier mit der zugehörigen Pressemitteilung.') }}

@endif
{{-- ============== BUCHUNGS-BESTÄTIGUNG (Modal) ============== --}} @if ($booking)
{{ ($booking['is_swap'] ?? false) ? __('Tarifwechsel bestätigen') : __('Buchung bestätigen') }} {{ $booking['title'] }} @if ($booking['subtitle'])

{{ $booking['subtitle'] }}

@endif
{{-- Preistabelle: Rahmen/Zeilen mit Alpha-Farben, damit sie auf jeder Modal-Fläche lesbar bleiben (Theme-Tokens wie --color-bg-rule gehen im Dark Mode gegen den Modal-Bg unter). --}}
{{ __('Netto') }} {{ $booking['net'] }}
@if ($booking['reverse_charge'])
{{ __('USt.') }} {{ __('Reverse Charge (0 %)') }}
@elseif ($booking['tax_exempt'])
{{ __('USt.') }} {{ __('steuerbefreit') }}
@else
{{ __('zzgl. :p % USt.', ['p' => $booking['vat_percent']]) }} {{ $booking['vat'] }}
@endif
{{ __('Gesamt') }} {{ $booking['gross'] }}

{{ __('Die endgültige Steuer berechnet Stripe anhand Ihrer Rechnungsadresse. Die Abrechnung erfolgt sicher über Stripe.') }}

@if ($booking['is_swap'] ?? false)

@if ($booking['is_downgrade'] ?? false) {{ __('Der Wechsel wird zum Ende der bereits bezahlten Abrechnungsperiode vorgemerkt. Bis dahin behalten Sie Ihren aktuellen Tarif — heute entsteht keine Zahlung, der neue Preis gilt ab der nächsten Abrechnung.') }} @else {{ __('Der Wechsel gilt sofort: Bereits gezahlte Beträge werden anteilig angerechnet, nur die Differenz wird umgehend in Rechnung gestellt.') }} @endif @if ($booking['resumes_cancellation'] ?? false) {{ __('Ihre vorgemerkte Kündigung wird durch den Wechsel aufgehoben.') }} @endif

@endif
@if ($booking['needs_address'])
{{ __('Bitte hinterlegen Sie zuerst eine vollständige Rechnungsadresse — sie ist Voraussetzung für jede Buchung.') }}
{{ __('Abbrechen') }} {{ __('Rechnungsadresse hinterlegen') }}
@else {{-- Unternehmer-Pflicht-Checkbox (T1neu, AGB § 1 Abs. 4): Bestätigung wird serverseitig protokolliert (Timestamp, IP, AGB-Version); ohne sie kein Checkout-Link — und der Checkout-Controller verlangt sie zusätzlich. --}}

{{ __('Das Angebot richtet sich ausschließlich an Unternehmen, Selbstständige und Agenturen.') }} {{ __('AGB (Version :version)', ['version' => \App\Models\LegalDocument::currentAgbVersion()]) }}

{{ __('Abbrechen') }} @if ($booking['is_swap'] ?? false) {{-- Tarifwechsel mutiert das Abo direkt (kein Stripe-Redirect) — darum POST statt Link. --}} @if ($entrepreneurConfirmed)
@csrf {{ ($booking['is_downgrade'] ?? false) ? __('Tarifwechsel vormerken') : __('Kostenpflichtig wechseln') }}
@else {{ ($booking['is_downgrade'] ?? false) ? __('Tarifwechsel vormerken') : __('Kostenpflichtig wechseln') }} @endif @elseif ($entrepreneurConfirmed) {{ __('Kostenpflichtig buchen') }} @else {{ __('Kostenpflichtig buchen') }} @endif
@endif
@endif