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:
parent
d57f6b5c66
commit
f6882b621a
7 changed files with 180 additions and 9 deletions
|
|
@ -6,6 +6,7 @@ use App\Enums\InvoiceStatus;
|
|||
use App\Enums\PressReleaseStatus;
|
||||
use App\Enums\SinglePurchaseStatus;
|
||||
use App\Enums\SinglePurchaseType;
|
||||
use App\Mail\PressReleaseDepublished;
|
||||
use App\Models\CreditTopup;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceBillingAddress;
|
||||
|
|
@ -15,6 +16,7 @@ use App\Services\Billing\CreditTopupService;
|
|||
use App\Services\Billing\InvoiceNumberGenerator;
|
||||
use App\Services\PressRelease\PressReleaseService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Laravel\Cashier\Cashier;
|
||||
use Laravel\Cashier\Events\WebhookReceived;
|
||||
use Laravel\Cashier\Subscription;
|
||||
|
|
@ -268,7 +270,18 @@ class ProcessStripeWebhook
|
|||
}
|
||||
|
||||
app(PressReleaseService::class)->depublish($pressRelease);
|
||||
$purchase->update(['consumed_at' => now()]);
|
||||
$purchase->update([
|
||||
'status' => SinglePurchaseStatus::Consumed->value,
|
||||
'consumed_at' => now(),
|
||||
]);
|
||||
|
||||
// Bestätigung an den Käufer (nicht zwingend der PM-Autor — der
|
||||
// Kauf kann von einem Firmen-Kollegen mit Zugriff stammen).
|
||||
$buyer = $purchase->user;
|
||||
|
||||
if ($buyer && $buyer->email) {
|
||||
Mail::to($buyer->email)->queue(new PressReleaseDepublished($buyer, $pressRelease));
|
||||
}
|
||||
|
||||
Log::info('Pressemitteilung depubliziert.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
|
|
|
|||
44
app/Mail/PressReleaseDepublished.php
Normal file
44
app/Mail/PressReleaseDepublished.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\PressRelease;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Bestätigung der kostenpflichtigen Depublikation (T12) an den Käufer:
|
||||
* Die PM ist aus der öffentlichen Ansicht entfernt, unter der bisherigen
|
||||
* URL bleibt nur der Hinweis auf die Entfernung (Tombstone) sichtbar.
|
||||
*/
|
||||
class PressReleaseDepublished extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public string $showUrl;
|
||||
|
||||
public function __construct(
|
||||
public readonly User $user,
|
||||
public readonly PressRelease $pressRelease,
|
||||
) {
|
||||
$this->showUrl = $user->canAccessAdmin()
|
||||
? route('admin.press-releases.show', $pressRelease->id)
|
||||
: route('me.press-releases.show', $pressRelease->id);
|
||||
}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Ihre Pressemitteilung wurde depubliziert: '.$this->pressRelease->title,
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(view: 'emails.press-release.depublished');
|
||||
}
|
||||
}
|
||||
|
|
@ -341,7 +341,7 @@ class StripeCheckoutService
|
|||
$this->syncCustomerFromBillingAddress($user);
|
||||
|
||||
return $user->checkout([config('billing.single_pm_stripe_price_id') => 1], [
|
||||
...$this->sessionOptions($user),
|
||||
...$this->oneTimeSessionOptions($user),
|
||||
'metadata' => ['single_purchase_id' => (string) $purchase->id],
|
||||
]);
|
||||
}
|
||||
|
|
@ -358,7 +358,7 @@ class StripeCheckoutService
|
|||
return $user->checkout(
|
||||
[$this->adHocLineItem($purchase->price_cents, 'Depublikation einer Pressemitteilung')],
|
||||
[
|
||||
...$this->sessionOptions($user),
|
||||
...$this->oneTimeSessionOptions($user),
|
||||
'metadata' => ['single_purchase_id' => (string) $purchase->id],
|
||||
],
|
||||
);
|
||||
|
|
@ -376,9 +376,27 @@ class StripeCheckoutService
|
|||
return $user->checkout(
|
||||
[$this->adHocLineItem($topup->price_cents, "Credit-Guthaben ({$topup->credits} Credits)")],
|
||||
[
|
||||
...$this->sessionOptions($user),
|
||||
...$this->oneTimeSessionOptions($user),
|
||||
'metadata' => ['credit_topup_id' => (string) $topup->id],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session-Optionen für Einmalkäufe (Payment-Mode: Einzel-PM, Depublikation,
|
||||
* Credit-Topup). Anders als im Subscription-Mode erzeugt Stripe hier ohne
|
||||
* `invoice_creation` keine Rechnung — und damit auch kein
|
||||
* `invoice.payment_succeeded`, das die STR-Spiegelung anstoßen könnte.
|
||||
* Im Subscription-Mode ist der Parameter unzulässig, daher nicht in
|
||||
* sessionOptions() selbst.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function oneTimeSessionOptions(User $user): array
|
||||
{
|
||||
return [
|
||||
...$this->sessionOptions($user),
|
||||
'invoice_creation' => ['enabled' => true],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue