getContent(); $verifier = match ($provider) { 'paddle' => app(PaddleWebhookVerifier::class),'lemon-squeezy' => app(LemonSqueezyWebhookVerifier::class),default => null }; if (! $verifier) { return response()->json(['message' => 'Unknown webhook provider.'], 404); } try { $verifier->verify($request, $raw); } catch (\Throwable $e) { report($e); return response()->json(['message' => 'Invalid webhook signature.'], 400); } try { $payload = json_decode($raw, true, flags: JSON_THROW_ON_ERROR); } catch (\Throwable) { return response()->json(['message' => 'Invalid JSON.'], 400); } $externalID = (string) ($provider === 'paddle' ? ($payload['event_id'] ?? '') : ($payload['meta']['event_name'] ?? '').':'.($payload['data']['id'] ?? '')); $eventType = (string) ($provider === 'paddle' ? ($payload['event_type'] ?? 'unknown') : ($payload['meta']['event_name'] ?? 'unknown')); if ($externalID === '') { return response()->json(['message' => 'Webhook event has no stable identifier.'], 400); } try { DB::transaction(function () use ($provider, $externalID, $eventType, $payload, $raw, $provisioner) { $event = LicenseWebhookEvent::firstOrCreate(['provider' => $provider, 'external_event_id' => $externalID], ['event_type' => $eventType, 'payload_sha256' => hash('sha256', $raw), 'payload' => $payload]); if ($event->processed_at) { return; } if (! hash_equals($event->payload_sha256, hash('sha256', $raw))) { throw new \RuntimeException('Webhook ID was reused with a different payload.'); } try { $provisioner->provision($provider, $payload); $event->update(['processed_at' => now(), 'processing_error' => null]); } catch (\Throwable $e) { $event->update(['processing_error' => mb_substr($e->getMessage(), 0, 4000)]); throw $e; } }); } catch (\Throwable $e) { report($e); return response()->json(['message' => 'Webhook could not be processed.'], 500); } // Delivery is intentionally outside the webhook transaction. A temporary mail // outage must not roll back an otherwise valid Paddle event. Pending outbox // rows remain available for portal resend or a later webhook delivery attempt. try { $delivery->deliverPending(10); } catch (\Throwable $e) { report($e); } return response()->json(['ok' => true]); } }