Add integrations health monitoring
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\EventPurchase;
|
||||
use App\Models\IntegrationWebhookEvent;
|
||||
use App\Models\Tenant;
|
||||
use App\Services\Integrations\IntegrationWebhookRecorder;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
@@ -25,24 +27,27 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
|
||||
private ?string $eventId;
|
||||
|
||||
private ?int $webhookEventId;
|
||||
|
||||
public int $tries = 5;
|
||||
|
||||
public int $backoff = 60;
|
||||
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
public function __construct(array $payload, ?string $eventId = null)
|
||||
public function __construct(array $payload, ?string $eventId = null, ?int $webhookEventId = null)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
$this->eventId = $eventId !== '' ? $eventId : null;
|
||||
$this->webhookEventId = $webhookEventId;
|
||||
$this->queue = config('services.revenuecat.queue', 'webhooks');
|
||||
$this->onQueue($this->queue);
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$webhookEvent = $this->resolveWebhookEvent();
|
||||
$appUserId = $this->value('event.app_user_id')
|
||||
?? $this->value('subscriber.app_user_id');
|
||||
|
||||
@@ -50,6 +55,8 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
Log::warning('RevenueCat webhook missing app_user_id', [
|
||||
'event_id' => $this->eventId,
|
||||
]);
|
||||
$this->markFailed($webhookEvent, 'Missing app_user_id');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,6 +66,8 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
'event_id' => $this->eventId,
|
||||
'app_user_id' => $appUserId,
|
||||
]);
|
||||
$this->markFailed($webhookEvent, 'Tenant not found');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,13 +83,15 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
if (EventPurchase::where('provider', 'revenuecat')
|
||||
->where('external_receipt_id', $transactionId)
|
||||
->exists()) {
|
||||
$this->markIgnored($webhookEvent, 'Duplicate transaction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$amount = (float) ($this->value('event.price') ?? 0);
|
||||
$currency = strtoupper((string) ($this->value('event.currency') ?? 'EUR'));
|
||||
|
||||
DB::transaction(function () use ($tenant, $credits, $transactionId, $productId, $amount, $currency) {
|
||||
DB::transaction(function () use ($tenant, $credits, $transactionId, $amount, $currency) {
|
||||
$tenant->refresh();
|
||||
|
||||
$purchase = EventPurchase::create([
|
||||
@@ -104,6 +115,14 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
'product_id' => $productId,
|
||||
'credits' => $credits,
|
||||
]);
|
||||
|
||||
$this->markProcessed($webhookEvent);
|
||||
}
|
||||
|
||||
public function failed(\Throwable $exception): void
|
||||
{
|
||||
$webhookEvent = $this->resolveWebhookEvent();
|
||||
$this->markFailed($webhookEvent, $exception->getMessage());
|
||||
}
|
||||
|
||||
private function updateSubscriptionStatus(Tenant $tenant): void
|
||||
@@ -149,7 +168,7 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
}
|
||||
|
||||
foreach ([':', '-', '_'] as $delimiter) {
|
||||
$needle = strtolower($prefix) . $delimiter;
|
||||
$needle = strtolower($prefix).$delimiter;
|
||||
if (str_starts_with($lower, $needle)) {
|
||||
$candidate = substr($appUserId, strlen($needle));
|
||||
if (is_numeric($candidate)) {
|
||||
@@ -226,7 +245,7 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
private function value(string $path, $default = null)
|
||||
private function value(string $path, $default = null): mixed
|
||||
{
|
||||
$segments = explode('.', $path);
|
||||
$value = $this->payload;
|
||||
@@ -241,4 +260,40 @@ class ProcessRevenueCatWebhook implements ShouldQueue
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function resolveWebhookEvent(): ?IntegrationWebhookEvent
|
||||
{
|
||||
if (! $this->webhookEventId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return IntegrationWebhookEvent::find($this->webhookEventId);
|
||||
}
|
||||
|
||||
private function markProcessed(?IntegrationWebhookEvent $event): void
|
||||
{
|
||||
if (! $event) {
|
||||
return;
|
||||
}
|
||||
|
||||
app(IntegrationWebhookRecorder::class)->markProcessed($event);
|
||||
}
|
||||
|
||||
private function markIgnored(?IntegrationWebhookEvent $event, string $reason): void
|
||||
{
|
||||
if (! $event) {
|
||||
return;
|
||||
}
|
||||
|
||||
app(IntegrationWebhookRecorder::class)->markIgnored($event, ['reason' => $reason]);
|
||||
}
|
||||
|
||||
private function markFailed(?IntegrationWebhookEvent $event, string $reason): void
|
||||
{
|
||||
if (! $event) {
|
||||
return;
|
||||
}
|
||||
|
||||
app(IntegrationWebhookRecorder::class)->markFailed($event, $reason);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user