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:
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\EventPurchase;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ValidateStripeWebhookJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $payload;
|
||||
public $sig;
|
||||
public $tries = 3;
|
||||
public $backoff = [60, 300, 600]; // Retry delays
|
||||
|
||||
public function __construct($payload, $sig)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
$this->sig = $sig;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$secret = config('services.stripe.webhook');
|
||||
|
||||
if (!$secret) {
|
||||
Log::error('No Stripe webhook secret configured');
|
||||
return;
|
||||
}
|
||||
|
||||
$expectedSig = 'v1=' . hash_hmac('sha256', $this->payload, $secret);
|
||||
|
||||
if (!hash_equals($expectedSig, $this->sig)) {
|
||||
Log::error('Invalid signature in Stripe webhook job');
|
||||
return;
|
||||
}
|
||||
|
||||
$event = json_decode($this->payload, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
Log::error('Invalid JSON in Stripe webhook job: ' . json_last_error_msg());
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event['type'] === 'checkout.session.completed') {
|
||||
$session = $event['data']['object'];
|
||||
$receiptId = $session['id'];
|
||||
|
||||
if (EventPurchase::where('external_receipt_id', $receiptId)->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tenantId = $session['metadata']['tenant_id'] ?? null;
|
||||
|
||||
if (!$tenantId) {
|
||||
Log::warning('No tenant_id in Stripe metadata job', ['receipt_id' => $receiptId]);
|
||||
return;
|
||||
}
|
||||
|
||||
$tenant = Tenant::find($tenantId);
|
||||
|
||||
if (!$tenant) {
|
||||
Log::error('Tenant not found in Stripe webhook job', ['tenant_id' => $tenantId]);
|
||||
return;
|
||||
}
|
||||
|
||||
$amount = $session['amount_total'] / 100;
|
||||
$currency = $session['currency'];
|
||||
$eventsPurchased = (int) ($session['metadata']['events_purchased'] ?? 1);
|
||||
|
||||
DB::transaction(function () use ($tenant, $amount, $currency, $eventsPurchased, $receiptId) {
|
||||
$purchase = EventPurchase::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'events_purchased' => $eventsPurchased,
|
||||
'amount' => $amount,
|
||||
'currency' => $currency,
|
||||
'provider' => 'stripe',
|
||||
'external_receipt_id' => $receiptId,
|
||||
'status' => 'completed',
|
||||
'purchased_at' => now(),
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
Log::info('Processed Stripe purchase via job', ['receipt_id' => $receiptId, 'tenant_id' => $tenantId]);
|
||||
} else {
|
||||
Log::info('Unhandled Stripe event in job', ['type' => $event['type']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user