owner_id === $request->user()->id, 404); return response()->json( WebhookEndpoint::where('client_id', $client->id) ->latest('created_at') ->get() ->map(fn (WebhookEndpoint $endpoint) => WebhookEndpoints::present($endpoint)) ); } /** * Register a new webhook endpoint for one of the user's own clients. */ public function store(Request $request, Client $client) { abort_unless($client->owner_id === $request->user()->id, 404); $data = $request->validate([ 'url' => ['required', 'url', 'max:2048', new SafeWebhookUrl()], 'events' => ['required', 'array', 'min:1'], 'events.*' => [Rule::in(WebhookEndpoints::EVENTS)], ]); $secret = Str::random(40); $endpoint = WebhookEndpoint::create([ 'client_id' => $client->id, 'url' => $data['url'], 'secret' => $secret, 'events' => $data['events'], 'active' => true, ]); AuditLog::record($request, 'client.webhook_created', 'client', $client->id, $client->name, [ 'url' => $data['url'], 'events' => $data['events'], ]); return response()->json([ ...WebhookEndpoints::present($endpoint), 'secret' => $secret, ], 201); } public function update(Request $request, Client $client, WebhookEndpoint $webhook) { abort_unless($client->owner_id === $request->user()->id, 404); abort_unless($webhook->client_id === $client->id, 404); $data = $request->validate([ 'url' => ['sometimes', 'url', 'max:2048', new SafeWebhookUrl()], 'events' => ['sometimes', 'array', 'min:1'], 'events.*' => [Rule::in(WebhookEndpoints::EVENTS)], 'active' => ['sometimes', 'boolean'], ]); $webhook->update($data); return response()->json(WebhookEndpoints::present($webhook->fresh())); } public function destroy(Request $request, Client $client, WebhookEndpoint $webhook) { abort_unless($client->owner_id === $request->user()->id, 404); abort_unless($webhook->client_id === $client->id, 404); AuditLog::record($request, 'client.webhook_deleted', 'client', $client->id, $client->name, [ 'url' => $webhook->url, ]); $webhook->delete(); return response()->noContent(); } /** * Historial paginado de intentos de entrega de un webhook. */ public function deliveries(Request $request, Client $client, WebhookEndpoint $webhook) { abort_unless($client->owner_id === $request->user()->id, 404); abort_unless($webhook->client_id === $client->id, 404); return response()->json(WebhookDelivery::paginatedFor($webhook, $request)); } /** * Reintento manual de una entrega puntual — vuelve a encolar el mismo * registro (acumula el intento en su historial, no crea uno nuevo). */ public function retryDelivery(Request $request, Client $client, WebhookEndpoint $webhook, WebhookDelivery $delivery) { abort_unless($client->owner_id === $request->user()->id, 404); abort_unless($webhook->client_id === $client->id, 404); abort_unless($delivery->webhook_endpoint_id === $webhook->id, 404); SendWebhookJob::dispatch($delivery); return response()->json(['queued' => true]); } }