Files
fotospiel-app/app/Http/Controllers/PaddleWebhookController.php

69 lines
1.9 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Services\Checkout\CheckoutWebhookService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
class PaddleWebhookController extends Controller
{
public function __construct(private readonly CheckoutWebhookService $webhooks) {}
public function handle(Request $request): JsonResponse
{
if (! $this->verify($request)) {
Log::warning('Paddle webhook signature verification failed');
return response()->json(['status' => 'invalid'], Response::HTTP_BAD_REQUEST);
}
$payload = $request->json()->all();
if (! is_array($payload)) {
return response()->json(['status' => 'ignored'], Response::HTTP_ACCEPTED);
}
$eventType = $payload['event_type'] ?? null;
$handled = false;
if ($eventType) {
$handled = $this->webhooks->handlePaddleEvent($payload);
}
Log::info('Paddle webhook processed', [
'event_type' => $eventType,
'handled' => $handled,
]);
$statusCode = $handled ? Response::HTTP_OK : Response::HTTP_ACCEPTED;
return response()->json([
'status' => $handled ? 'processed' : 'ignored',
], $statusCode);
}
protected function verify(Request $request): bool
{
$secret = config('paddle.webhook_secret');
if (! $secret) {
// Allow processing in sandbox or when secret not configured
return true;
}
$signature = (string) $request->headers->get('Paddle-Webhook-Signature', '');
if ($signature === '') {
return false;
}
$payload = $request->getContent();
$expected = hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}
}