new funcionality
This commit is contained in:
89
app/Jobs/SendWebhookJob.php
Normal file
89
app/Jobs/SendWebhookJob.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\WebhookDelivery;
|
||||
use App\Support\WebhookEndpoints;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SendWebhookJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int $tries = 3;
|
||||
|
||||
/**
|
||||
* Backoff creciente entre reintentos (segundos) — le da tiempo al
|
||||
* endpoint del cliente a recuperarse si está caído momentáneamente.
|
||||
*/
|
||||
public array $backoff = [10, 60, 300];
|
||||
|
||||
public function __construct(public WebhookDelivery $delivery)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$endpoint = $this->delivery->endpoint;
|
||||
|
||||
// El endpoint pudo haberse borrado o desactivado entre que se
|
||||
// encoló el job y que se procesó — no tiene sentido reintentar.
|
||||
if (! $endpoint || ! $endpoint->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-chequeo anti-SSRF: la URL ya se validó al crear/editar el
|
||||
// webhook, pero un dominio público en ese momento puede resolver a
|
||||
// una IP interna más tarde (DNS rebinding). No reintentamos esto —
|
||||
// si sigue sin ser seguro en el próximo intento, va a fallar igual.
|
||||
if (! WebhookEndpoints::isSafeUrl($endpoint->url)) {
|
||||
$this->delivery->update([
|
||||
'response_body' => 'Bloqueado: la URL resuelve a una dirección privada/reservada.',
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$body = json_encode([
|
||||
'event' => $this->delivery->event,
|
||||
'data' => $this->delivery->payload,
|
||||
]);
|
||||
|
||||
$signature = hash_hmac('sha256', $body, $endpoint->secret);
|
||||
|
||||
$this->delivery->increment('attempts');
|
||||
|
||||
try {
|
||||
$response = Http::timeout(5)
|
||||
->withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Webhook-Event' => $this->delivery->event,
|
||||
'X-Webhook-Signature' => 'sha256='.$signature,
|
||||
])
|
||||
->withBody($body, 'application/json')
|
||||
->post($endpoint->url);
|
||||
|
||||
$this->delivery->update([
|
||||
'response_status' => $response->status(),
|
||||
'response_body' => Str::limit($response->body(), 1000),
|
||||
'delivered_at' => $response->successful() ? now() : null,
|
||||
]);
|
||||
|
||||
if (! $response->successful()) {
|
||||
throw new \RuntimeException("El endpoint del webhook respondió {$response->status()}.");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->delivery->update([
|
||||
'response_body' => Str::limit($e->getMessage(), 1000),
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user