Checkout‑Registrierung validiert jetzt die E‑Mail‑Länge, und die Checkout‑Flows sind Paddle‑only: Stripe‑Endpoints/

Services/Helpers sind entfernt, API/Frontend angepasst, Tests auf Paddle umgestellt. Außerdem wurde die CSP gestrafft
  und Stripe‑Texte in den Abandoned‑Checkout‑Mails ersetzt.
This commit is contained in:
Codex Agent
2025-12-18 11:14:42 +01:00
parent 7213aef108
commit 2e4226a838
33 changed files with 314 additions and 1219 deletions

View File

@@ -25,63 +25,6 @@ class CheckoutWebhookService
private readonly GiftVoucherService $giftVouchers,
) {}
public function handleStripeEvent(array $event): bool
{
$eventType = $event['type'] ?? null;
$intent = $event['data']['object'] ?? null;
if (! $eventType || ! is_array($intent)) {
return false;
}
if (! str_starts_with($eventType, 'payment_intent.')) {
return false;
}
$intentId = $intent['id'] ?? null;
if (! $intentId) {
return false;
}
$session = $this->locateStripeSession($intent);
if (! $session) {
return false;
}
$lock = Cache::lock("checkout:webhook:stripe:{$intentId}", 30);
if (! $lock->get()) {
Log::info('[CheckoutWebhook] Stripe intent lock busy', [
'intent_id' => $intentId,
'session_id' => $session->id,
]);
return true;
}
try {
$session->forceFill([
'stripe_payment_intent_id' => $session->stripe_payment_intent_id ?: $intentId,
'provider' => CheckoutSession::PROVIDER_STRIPE,
])->save();
$metadata = [
'stripe_last_event' => $eventType,
'stripe_last_event_id' => $event['id'] ?? null,
'stripe_intent_status' => $intent['status'] ?? null,
'stripe_last_update_at' => now()->toIso8601String(),
];
$this->mergeProviderMetadata($session, $metadata);
return $this->applyStripeIntent($session, $eventType, $intent);
} finally {
$lock->release();
}
}
public function handlePaddleEvent(array $event): bool
{
$eventType = $event['event_type'] ?? null;
@@ -158,51 +101,6 @@ class CheckoutWebhookService
}
}
protected function applyStripeIntent(CheckoutSession $session, string $eventType, array $intent): bool
{
switch ($eventType) {
case 'payment_intent.processing':
case 'payment_intent.amount_capturable_updated':
$this->sessions->markProcessing($session, [
'stripe_intent_status' => $intent['status'] ?? null,
]);
return true;
case 'payment_intent.requires_action':
$reason = $intent['next_action']['type'] ?? 'requires_action';
$this->sessions->markRequiresCustomerAction($session, $reason);
return true;
case 'payment_intent.payment_failed':
$failure = $intent['last_payment_error']['message'] ?? 'payment_failed';
$this->sessions->markFailed($session, $failure);
return true;
case 'payment_intent.succeeded':
if ($session->status !== CheckoutSession::STATUS_COMPLETED) {
$this->sessions->markProcessing($session, [
'stripe_intent_status' => $intent['status'] ?? null,
]);
$this->assignment->finalise($session, [
'source' => 'stripe_webhook',
'stripe_payment_intent_id' => $intent['id'] ?? null,
'stripe_charge_id' => $this->extractStripeChargeId($intent),
]);
$this->sessions->markCompleted($session, now());
}
return true;
default:
return false;
}
}
protected function applyPaddleEvent(CheckoutSession $session, string $eventType, array $data): bool
{
$status = strtolower((string) ($data['status'] ?? ''));
@@ -417,30 +315,6 @@ class CheckoutWebhookService
$session->save();
}
protected function locateStripeSession(array $intent): ?CheckoutSession
{
$intentId = $intent['id'] ?? null;
if ($intentId) {
$session = CheckoutSession::query()
->where('stripe_payment_intent_id', $intentId)
->first();
if ($session) {
return $session;
}
}
$metadata = $intent['metadata'] ?? [];
$sessionId = $metadata['checkout_session_id'] ?? null;
if ($sessionId) {
return CheckoutSession::find($sessionId);
}
return null;
}
protected function isGiftVoucherEvent(array $data): bool
{
$metadata = $data['metadata'] ?? [];
@@ -498,14 +372,4 @@ class CheckoutWebhookService
return null;
}
protected function extractStripeChargeId(array $intent): ?string
{
$charges = $intent['charges']['data'] ?? null;
if (is_array($charges) && count($charges) > 0) {
return $charges[0]['id'] ?? null;
}
return null;
}
}