feat: revenue cat
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user