70 lines
2.2 KiB
PHP
70 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Jobs\ProcessRevenueCatWebhook;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class RevenueCatWebhookController extends Controller
|
|
{
|
|
public function handle(Request $request): JsonResponse
|
|
{
|
|
$secret = (string) config('services.revenuecat.webhook', '');
|
|
|
|
if ($secret === '') {
|
|
Log::error('RevenueCat webhook secret not configured');
|
|
return response()->json(['error' => 'Webhook not configured'], 500);
|
|
}
|
|
|
|
$signature = trim((string) $request->header('X-Signature', ''));
|
|
if ($signature === '') {
|
|
return response()->json(['error' => 'Signature missing'], 400);
|
|
}
|
|
|
|
$payload = $request->getContent();
|
|
if (! $this->signatureMatches($payload, $signature, $secret)) {
|
|
return response()->json(['error' => 'Invalid signature'], 400);
|
|
}
|
|
|
|
$decoded = json_decode($payload, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($decoded)) {
|
|
Log::warning('RevenueCat webhook received invalid JSON', [
|
|
'error' => json_last_error_msg(),
|
|
]);
|
|
return response()->json(['error' => 'Invalid payload'], 400);
|
|
}
|
|
|
|
ProcessRevenueCatWebhook::dispatch(
|
|
$decoded,
|
|
(string) $request->header('X-Event-Id', '')
|
|
);
|
|
|
|
return response()->json(['status' => 'accepted'], 202);
|
|
}
|
|
|
|
private function signatureMatches(string $payload, string $providedSignature, string $secret): bool
|
|
{
|
|
$normalized = preg_replace('/\s+/', '', $providedSignature);
|
|
if (! is_string($normalized)) {
|
|
return false;
|
|
}
|
|
|
|
$candidates = [
|
|
base64_encode(hash_hmac('sha1', $payload, $secret, true)),
|
|
base64_encode(hash_hmac('sha256', $payload, $secret, true)),
|
|
hash_hmac('sha1', $payload, $secret),
|
|
hash_hmac('sha256', $payload, $secret),
|
|
];
|
|
|
|
foreach ($candidates as $expected) {
|
|
if (hash_equals($expected, $normalized)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|