is_bidirectional || !$receiver->callback_url) { return false; } try { // Create callback log entry $callbackLog = ApiCallbackLog::create([ 'api_receiver_id' => $receiver->id, 'api_receive_log_id' => $receiveLog->id, 'api_client_id' => $receiver->api_client_id, 'callback_url' => $receiver->callback_url, 'method' => $receiver->callback_method ?? 'POST', 'max_retries' => $receiver->retry_attempts ?? 3, 'status' => 'pending' ]); // Prepare payload $payload = $this->prepareCallbackPayload($receiver, $receiveLog, $data); $headers = $this->prepareCallbackHeaders($receiver); $callbackLog->update([ 'payload_sent' => json_encode($payload), 'payload_size' => strlen(json_encode($payload)), 'headers_sent' => $headers, 'sent_at' => now() ]); // Send HTTP request $startTime = microtime(true); $response = Http::withHeaders($headers) ->timeout(30) ->retry(1, 1000) ->{strtolower($receiver->callback_method ?? 'POST')}($receiver->callback_url, $payload); $responseTime = (microtime(true) - $startTime) * 1000; // Handle response if ($response->successful()) { $callbackLog->markAsSuccess( $response->status(), $response->body(), $response->headers(), $responseTime ); // Update receiver statistics $receiver->increment('total_callbacks_sent'); $receiver->update(['last_callback_at' => now()]); Log::info('Callback sent successfully', [ 'receiver_id' => $receiver->id, 'callback_log_id' => $callbackLog->id, 'response_code' => $response->status() ]); return true; } else { $this->handleCallbackFailure($callbackLog, $receiver, $response->status(), $response->body()); return false; } } catch (Exception $e) { $this->handleCallbackException($callbackLog ?? null, $receiver, $e); return false; } } /** * Generate response for incoming request */ public function generateResponse(ApiReceiver $receiver, ApiReceiveLog $receiveLog, array $data = [], bool $success = true, string $error = null) { if (!$receiver->is_bidirectional) { return response()->json(['status' => 'received'], 200); } $template = $success ? $receiver->success_response_template : $receiver->error_response_template; if (!$template) { // Default templates if ($success) { $responseData = [ 'status' => 'success', 'message' => 'Data received successfully', 'id' => $receiveLog->id, 'timestamp' => now()->toISOString() ]; } else { $responseData = [ 'status' => 'error', 'message' => $error ?? 'Failed to process data', 'code' => 'PROCESSING_ERROR', 'timestamp' => now()->toISOString() ]; } } else { // Use template with variable replacement $responseData = $this->processTemplate($template, $receiveLog, $data, $error); } // Format response based on receiver configuration switch ($receiver->response_format) { case 'xml': return response($this->arrayToXml($responseData), $success ? 200 : 400) ->header('Content-Type', 'application/xml'); case 'plain': $message = is_array($responseData) ? ($responseData['message'] ?? 'OK') : $responseData; return response($message, $success ? 200 : 400) ->header('Content-Type', 'text/plain'); default: // json return response()->json($responseData, $success ? 200 : 400); } } /** * Prepare callback payload */ protected function prepareCallbackPayload(ApiReceiver $receiver, ApiReceiveLog $receiveLog, array $data = []) { $payload = [ 'receiver_id' => $receiver->id, 'receiver_name' => $receiver->name, 'log_id' => $receiveLog->id, 'timestamp' => $receiveLog->created_at->toISOString(), 'method' => $receiveLog->method, 'ip_address' => $receiveLog->ip_address, 'data' => $data ?: json_decode($receiveLog->body, true) ]; // Add client information if available if ($receiver->apiClient) { $payload['client'] = [ 'id' => $receiver->apiClient->id, 'name' => $receiver->apiClient->name ]; } return $payload; } /** * Prepare callback headers */ protected function prepareCallbackHeaders(ApiReceiver $receiver) { $headers = [ 'Content-Type' => 'application/json', 'User-Agent' => 'API8-Bidirectional/1.0', 'X-API8-Receiver-ID' => $receiver->id, 'X-API8-Timestamp' => now()->toISOString() ]; // Add custom headers from receiver configuration if ($receiver->callback_headers) { $headers = array_merge($headers, $receiver->callback_headers); } // Add client authentication if available if ($receiver->apiClient) { $headers['X-API8-Client-ID'] = $receiver->apiClient->client_id; } return $headers; } /** * Process template with variable replacement */ protected function processTemplate(string $template, ApiReceiveLog $receiveLog, array $data = [], string $error = null) { $variables = [ '{{id}}' => $receiveLog->id, '{{timestamp}}' => $receiveLog->created_at->toISOString(), '{{method}}' => $receiveLog->method, '{{ip}}' => $receiveLog->ip_address, '{{error}}' => $error, '{{code}}' => $receiveLog->status_code ]; // Add data variables if ($data) { foreach ($data as $key => $value) { $variables["{{data.{$key}}}"] = is_array($value) ? json_encode($value) : $value; } } $processedTemplate = str_replace(array_keys($variables), array_values($variables), $template); return json_decode($processedTemplate, true) ?: ['message' => $processedTemplate]; } /** * Convert array to XML */ protected function arrayToXml(array $data, string $rootElement = 'response') { $xml = new \SimpleXMLElement("<{$rootElement}>"); $this->arrayToXmlRecursive($data, $xml); return $xml->asXML(); } /** * Recursive array to XML conversion */ protected function arrayToXmlRecursive(array $data, \SimpleXMLElement $xml) { foreach ($data as $key => $value) { if (is_array($value)) { $child = $xml->addChild($key); $this->arrayToXmlRecursive($value, $child); } else { $xml->addChild($key, htmlspecialchars($value)); } } } /** * Handle callback failure */ protected function handleCallbackFailure(ApiCallbackLog $callbackLog, ApiReceiver $receiver, int $statusCode, string $responseBody) { $callbackLog->markAsFailed( "HTTP {$statusCode} response from callback URL", $responseBody, $statusCode ); if ($callbackLog->canRetry()) { $callbackLog->scheduleRetry(); } else { $receiver->increment('total_callbacks_failed'); } Log::warning('Callback failed', [ 'receiver_id' => $receiver->id, 'callback_log_id' => $callbackLog->id, 'status_code' => $statusCode, 'can_retry' => $callbackLog->canRetry() ]); } /** * Handle callback exception */ protected function handleCallbackException(?ApiCallbackLog $callbackLog, ApiReceiver $receiver, Exception $e) { if ($callbackLog) { $callbackLog->markAsFailed($e->getMessage(), $e->getTraceAsString()); if ($callbackLog->canRetry()) { $callbackLog->scheduleRetry(); } else { $receiver->increment('total_callbacks_failed'); } } Log::error('Callback exception', [ 'receiver_id' => $receiver->id, 'callback_log_id' => $callbackLog?->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } }