104 lines
3.3 KiB
PHP
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;
|
|
}
|
|
}
|