Depublikation: Kauf als eingelöst markieren, Rechnung & Bestätigungs-Mail

Drei Lücken im bezahlten Depublikations-Flow (T12) geschlossen:

- Webhook setzt den Kauf jetzt auf status=consumed (vorher nur
  consumed_at) — erst damit erscheint er unter „Eingelöste Käufe".
- Einmalkäufe (Depublikation, Einzel-PM, Credit-Topup) aktivieren
  invoice_creation im Stripe-Checkout: Ohne das Flag erzeugt Stripe im
  Payment-Mode keine Rechnung und die STR-Spiegelung lief ins Leere.
  Abo-Checkouts bleiben unverändert (Parameter dort unzulässig).
- Neue Bestätigungs-Mail PressReleaseDepublished an den Käufer
  (nicht zwingend der PM-Autor) nach vollzogener Depublikation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-08 11:25:12 +00:00
parent c1382cea78
commit 723d51c21e
7 changed files with 180 additions and 9 deletions

View file

@ -1,8 +1,10 @@
<?php
use App\Enums\Portal;
use App\Mail\ContactRequestSubmitted;
use App\Mail\GoLivePasswordReset;
use App\Mail\MagicLoginLink;
use App\Mail\PressReleaseDepublished;
use App\Mail\PressReleasePublished;
use App\Mail\PressReleaseRejected;
use App\Mail\TerminationRequestReceived;
@ -20,7 +22,9 @@ use Illuminate\Auth\Notifications\VerifyEmail;
* Alle System-Mails müssen im pressekonto-CI erscheinen: Wortmarke,
* „Publisher · Hub"-Tagline und die Zugehörigkeits-Erklärung zur
* Brand-Familie (presseecho + businessportal24) aus dem zentralen
* E-Mail-Layout (components/emails/layout).
* E-Mail-Layout (components/emails/layout). Zusätzlich: Hub verlinkt,
* Rechts-Links (AGB/Impressum/Datenschutz) im Footer und keine direkte
* Service-Mailadresse im Fließtext.
*/
function assertHubEmailBranding(string $html): void
{
@ -30,7 +34,15 @@ function assertHubEmailBranding(string $html): void
->toContain('businessportal24')
->toContain('ist der gemeinsame Publisher')
->toContain('#1A2540')
->toContain('#B07A3A');
->toContain('#B07A3A')
// Hub ist verlinkt (Wortmarke im Header + Brand-Familien-Zeile).
->toContain('href="'.config('domains.domain_portal_url').'"')
// Rechtliche Links im Footer (alle auf dem Hub).
->toContain(config('domains.domain_portal_url').'/de/agb')
->toContain(config('domains.domain_portal_url').'/de/impressum')
->toContain(config('domains.domain_portal_url').'/de/datenschutz')
// Keine direkte Service-Mailadresse in der Mail.
->not->toContain(config('mail.from.address'));
}
test('the magic login link mail uses the hub ci layout', function () {
@ -53,6 +65,42 @@ test('the press release published and rejected mails use the hub ci layout', fun
assertHubEmailBranding((new PressReleaseRejected($user, $pressRelease, 'Bitte Quellen ergänzen.'))->render());
});
test('the press release depublished mail uses the hub ci layout and explains the tombstone', function () {
$pressRelease = PressRelease::factory()->published()->create(['depublished_at' => now()]);
$user = $pressRelease->user;
$html = (new PressReleaseDepublished($user, $pressRelease))->render();
assertHubEmailBranding($html);
expect($html)
->toContain($pressRelease->title)
->toContain('Depubliziert am')
->toContain('auf Wunsch des Absenders entfernt');
});
test('the published mail names the portal with a link to the public detail page', function () {
$pressRelease = PressRelease::factory()->published()->forPortal(Portal::Presseecho)->create();
$html = (new PressReleasePublished($pressRelease->user, $pressRelease))->render();
expect($html)
->toContain('Veröffentlicht auf')
->toContain('Presseecho')
->toContain(config('domains.domain_presseecho_url').'/de/press-release/'.$pressRelease->slug);
});
test('the published mail links both portals for a cross-portal press release', function () {
$pressRelease = PressRelease::factory()->published()->forPortal(Portal::Both)->create();
$html = (new PressReleasePublished($pressRelease->user, $pressRelease))->render();
expect($html)
->toContain('Presseecho')
->toContain('Businessportal24')
->toContain(config('domains.domain_presseecho_url').'/de/press-release/'.$pressRelease->slug)
->toContain(config('domains.domain_businessportal_url').'/de/press-release/'.$pressRelease->slug);
});
test('the termination mails use the hub ci layout', function () {
$terminationRequest = TerminationRequest::factory()->create();