admin: Verlauf protokolliert den Gang zur Stripe-Zahlung

Neuer ActivityType "Zur Zahlung": Der StripeCheckoutService vermerkt
zentral in allen vier Checkout-Erzeugern (Abo, Einzel-PM, Depublikation,
Credit-Topup), wenn ein User zu Stripe weitergeleitet wird — mit Art
und Referenz in den Metadaten. Der Abschluss war bereits abgedeckt
(Webhook: "Zahlung eingegangen" mit Rechnungsnummer/Betrag, Abo-Events
als "Abo", Wallet-Aufladungen als "Credit-Aufladung").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-21 13:11:38 +00:00
parent 37e543e69a
commit 5bf9f846a6
3 changed files with 72 additions and 4 deletions

View file

@ -2,10 +2,12 @@
namespace App\Services\Billing;
use App\Enums\ActivityType;
use App\Models\CreditTopup;
use App\Models\Plan;
use App\Models\SinglePurchase;
use App\Models\User;
use App\Services\ActivityLog;
use Carbon\CarbonInterface;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
@ -28,8 +30,27 @@ class StripeCheckoutService
public function __construct(
private readonly VatResolver $vatResolver,
private readonly VatIdValidationService $vatIdValidation,
private readonly ActivityLog $activityLog,
) {}
/**
* Verlaufs-Eintrag „zur Zahlung gegangen": wird von allen
* Checkout-Erzeugern aufgerufen, sobald die Stripe-Session steht und
* der User zu Stripe weitergeleitet wird. Der Abschluss (oder Abbruch)
* folgt separat über den Webhook („Zahlung"/„Abo").
*
* @param array<string, mixed> $meta
*/
public function recordCheckoutStarted(User $user, string $what, array $meta = []): void
{
$this->activityLog->record(
ActivityType::CheckoutStarted,
sprintf('%s (%s) ist zur Zahlung weitergeleitet — %s.', $user->name, $user->email, $what),
user: $user,
meta: $meta,
);
}
/**
* Stripe-Checkout für ein Tarif-Abo (monatlich/jährlich). Die Steuer
* ergänzt Stripe Tax automatisch (Cashier::calculateTaxes, Netto-Preise).
@ -42,9 +63,17 @@ class StripeCheckoutService
$this->syncCustomerFromBillingAddress($user);
return $user
$checkout = $user
->newSubscription('default', $priceId)
->checkout($this->sessionOptions($user));
$this->recordCheckoutStarted($user, sprintf(
'Abo „%s" (%s)',
$plan->name,
$interval === 'yearly' ? 'jährlich' : 'monatlich',
), ['kind' => 'subscription', 'plan_id' => $plan->id, 'interval' => $interval]);
return $checkout;
}
/**
@ -402,10 +431,17 @@ class StripeCheckoutService
{
$this->syncCustomerFromBillingAddress($user);
return $user->checkout([config('billing.single_pm_stripe_price_id') => 1], [
$checkout = $user->checkout([config('billing.single_pm_stripe_price_id') => 1], [
...$this->oneTimeSessionOptions($user),
'metadata' => ['single_purchase_id' => (string) $purchase->id],
]);
$this->recordCheckoutStarted($user, 'Einzel-Pressemitteilung', [
'kind' => 'single_pm',
'single_purchase_id' => $purchase->id,
]);
return $checkout;
}
/**
@ -417,13 +453,20 @@ class StripeCheckoutService
{
$this->syncCustomerFromBillingAddress($user);
return $user->checkout(
$checkout = $user->checkout(
[$this->adHocLineItem($purchase->price_cents, 'Depublikation einer Pressemitteilung')],
[
...$this->oneTimeSessionOptions($user),
'metadata' => ['single_purchase_id' => (string) $purchase->id],
],
);
$this->recordCheckoutStarted($user, 'Depublikation einer Pressemitteilung', [
'kind' => 'depublication',
'single_purchase_id' => $purchase->id,
]);
return $checkout;
}
/**
@ -435,13 +478,20 @@ class StripeCheckoutService
{
$this->syncCustomerFromBillingAddress($user);
return $user->checkout(
$checkout = $user->checkout(
[$this->adHocLineItem($topup->price_cents, "Credit-Guthaben ({$topup->credits} Credits)")],
[
...$this->oneTimeSessionOptions($user),
'metadata' => ['credit_topup_id' => (string) $topup->id],
],
);
$this->recordCheckoutStarted($user, "Credit-Paket ({$topup->credits} Credits)", [
'kind' => 'credit_topup',
'credit_topup_id' => $topup->id,
]);
return $checkout;
}
/**