Files
leonm 29e85589b0
CI / 🧪 Tests Laravel (push) Successful in 2m24s
CI / 🐳 Build & Push Image (push) Successful in 1m13s
feat: revenue cat
2026-08-12 16:00:11 +02:00

104 lines
3.3 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Resources\UserResource;
use App\Models\RevenueCatWebhookEvent;
use App\Models\User;
use App\Notifications\PaymentFailedNotification;
use App\Services\RevenueCatService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class RevenueCatController extends Controller
{
public function sync(Request $request, RevenueCatService $revenueCat): UserResource
{
return new UserResource($revenueCat->sync($request->user()));
}
public function webhook(Request $request, RevenueCatService $revenueCat): JsonResponse
{
$expectedAuthorization = trim((string) config('billing.revenuecat.webhook_authorization'));
$providedAuthorization = (string) $request->header('Authorization');
abort_if(
$expectedAuthorization === '' || ! hash_equals($expectedAuthorization, $providedAuthorization),
401,
);
$event = $request->input('event');
abort_unless(is_array($event), 422);
$eventId = $this->stringValue($event['id'] ?? null);
$eventType = $this->stringValue($event['type'] ?? null);
abort_unless($eventId && $eventType, 422);
$webhookEvent = RevenueCatWebhookEvent::query()->firstOrCreate([
'event_id' => $eventId,
], [
'type' => $eventType,
'app_user_id' => $this->stringValue($event['app_user_id'] ?? null),
'product_id' => $this->stringValue($event['product_id'] ?? null),
'entitlement_ids' => $this->stringList($event['entitlement_ids'] ?? []),
'environment' => $this->stringValue($event['environment'] ?? null),
'payload' => $event,
]);
if ($webhookEvent->processed_at !== null) {
return response()->json(['received' => true]);
}
$user = $this->userFromEvent($event);
if ($user) {
$revenueCat->sync($user);
if ($eventType === 'BILLING_ISSUE') {
$user->notify(
(new PaymentFailedNotification)->locale($user->preferredLocale()),
);
}
}
$webhookEvent->forceFill(['processed_at' => now()])->save();
return response()->json(['received' => true]);
}
/**
* @param array<string, mixed> $event
*/
private function userFromEvent(array $event): ?User
{
$identifiers = collect([
$event['app_user_id'] ?? null,
$event['original_app_user_id'] ?? null,
])
->merge(Arr::wrap($event['aliases'] ?? []))
->merge(Arr::wrap($event['transferred_to'] ?? []))
->filter(fn (mixed $identifier): bool => is_string($identifier) && $identifier !== '')
->unique()
->values();
return User::query()->whereIn('id', $identifiers)->first();
}
/**
* @return list<string>
*/
private function stringList(mixed $value): array
{
return collect(Arr::wrap($value))
->filter(fn (mixed $item): bool => is_string($item) && $item !== '')
->values()
->all();
}
private function stringValue(mixed $value): ?string
{
return is_string($value) && $value !== '' ? $value : null;
}
}