PluginProbe
SuperFrete / 3.3.2
SuperFrete v3.3.2
trunk 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4
superfrete / api / Http / Request.php

Request.php in SuperFrete 3.3.2, at api/Http/Request.php

505 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SuperFrete_API\Http;
4
5 use SuperFrete_API\Helpers\Logger;
6
7 class Request {
8
9 private $api_url;
10 private $api_token;
11
12 /**
13 * Construtor para inicializar as configurações da API.
14 */
15 public function __construct() {
16 // Set API URL based on environment
17 $use_dev_env = get_option('superfrete_sandbox_mode') === 'yes';
18
19 if ($use_dev_env) {
20 $this->api_url = 'https://sandbox.superfrete.com/';
21 $this->api_token = get_option('superfrete_api_token_sandbox');
22 } else {
23 $this->api_url = 'https://api.superfrete.com/';
24 $this->api_token = get_option('superfrete_api_token');
25 }
26
27 // Debug logging
28 error_log('SuperFrete Request: API URL = ' . $this->api_url);
29 error_log('SuperFrete Request: Token present = ' . (!empty($this->api_token) ? 'yes' : 'no'));
30 error_log('SuperFrete Request: Use dev env = ' . ($use_dev_env ? 'yes' : 'no'));
31 }
32
33 /**
34 * Método genérico para chamadas à API do SuperFrete.
35 */
36 public function call_superfrete_api($endpoint, $method = 'GET', $payload = [], $retorno = false) {
37
38 // Enhanced debug logging
39 $environment = (strpos($this->api_url, 'sandbox') !== false || strpos($this->api_url, 'dev') !== false) ? 'SANDBOX/DEV' : 'PRODUCTION';
40 $full_url = $this->api_url . $endpoint;
41 $token_preview = !empty($this->api_token) ? substr($this->api_token, 0, 8) . '...' . substr($this->api_token, -4) : 'EMPTY';
42
43 Logger::log('SuperFrete', "API CALL [{$environment}]: {$method} {$full_url}");
44 Logger::log('SuperFrete', "TOKEN USADO [{$environment}]: {$token_preview}");
45
46 if (empty($this->api_token)) {
47 Logger::log('SuperFrete', 'API token is empty - cannot make API call');
48 return false;
49 }
50
51 // Check if proxy is configured and force proxy mode is enabled
52 $proxy_url = get_option('superfrete_proxy_url');
53
54 // Set default proxy URL if not configured
55 if (empty($proxy_url)) {
56 $proxy_url = 'https://api.dev.superintegrador.superfrete.com/headless/proxy/superfrete';
57 Logger::log('SuperFrete', "Using default proxy URL: {$proxy_url}");
58 }
59
60 $force_proxy = get_option('superfrete_force_proxy', 'no') === 'yes';
61
62 if (!empty($proxy_url) && $force_proxy) {
63 Logger::log('SuperFrete', "Force proxy mode enabled - using proxy directly");
64 return $this->call_via_proxy($proxy_url, $endpoint, $method, $payload);
65 }
66
67 try {
68 $headers = [
69 'Content-Type' => 'application/json',
70 'Accept' => 'application/json',
71 'Authorization' => 'Bearer ' . $this->api_token,
72 'User-Agent' => 'WooCommerce SuperFrete Plugin (github.com/superfrete/woocommerce)',
73 'Platform' => 'Woocommerce SuperFrete',
74 ];
75
76 $params = [
77 'headers' => $headers,
78 'method' => $method,
79 'timeout' => 30, // Increased timeout to 30 seconds
80 'sslverify' => false, // Skip SSL verification for faster connection
81 'redirection' => 5,
82 'httpversion' => '1.1',
83 ];
84
85 // LiteSpeed server detected - add additional timeout parameters
86 if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) {
87 // LiteSpeed often ignores standard timeout - try cURL-specific options
88 $params['timeout'] = 35; // Slightly higher for LiteSpeed
89 $params['user-agent'] = 'WordPress/' . get_bloginfo('version') . '; SuperFrete Plugin';
90
91 // Add stream context options for alternative transport
92 $params['stream_context'] = stream_context_create([
93 'http' => [
94 'timeout' => 35,
95 'user_agent' => 'WordPress/' . get_bloginfo('version') . '; SuperFrete Plugin',
96 ]
97 ]);
98 }
99
100 if ($method === 'POST' && !empty($payload)) {
101 $params['body'] = wp_json_encode($payload);
102 error_log('SuperFrete API Payload: ' . wp_json_encode($payload));
103 }
104
105 // Force timeout to prevent hosting overrides
106 $timeout_filter = function() { return 30; };
107 $args_filter = function($args) {
108 if (isset($args['headers']['Authorization']) && strpos($args['headers']['Authorization'], 'Bearer') === 0) {
109 $args['timeout'] = 30;
110 }
111 return $args;
112 };
113
114 add_filter('http_request_timeout', $timeout_filter);
115 add_filter('http_request_args', $args_filter);
116
117 $max_attempts = 3;
118 $attempt = 1;
119 $response = null;
120
121 while ($attempt <= $max_attempts) {
122 $start_time = microtime(true);
123 $response = ($method === 'POST') ? wp_remote_post($this->api_url . $endpoint, $params) : wp_remote_get($this->api_url . $endpoint, $params);
124 $end_time = microtime(true);
125
126 // If successful or not a timeout, break
127 if (!is_wp_error($response) || strpos($response->get_error_message(), 'timeout') === false) {
128 break;
129 }
130
131 // Log retry attempt
132 $error_msg = $response->get_error_message();
133 Logger::log('SuperFrete', "Attempt {$attempt}/{$max_attempts} failed: {$error_msg}");
134
135 $attempt++;
136 if ($attempt <= $max_attempts) {
137 // Wait before retry (exponential backoff)
138 $wait_seconds = pow(2, $attempt - 1);
139 Logger::log('SuperFrete', "Retrying in {$wait_seconds} seconds...");
140 sleep($wait_seconds);
141 }
142 }
143
144 // Remove filters to not affect other plugins
145 remove_filter('http_request_timeout', $timeout_filter);
146 remove_filter('http_request_args', $args_filter);
147 $request_time = round(($end_time - $start_time) * 1000, 2);
148
149 error_log('SuperFrete API Request Time: ' . $request_time . ' ms');
150
151 // Check for WP errors first (timeout, connection issues, etc.)
152 if (is_wp_error($response)) {
153 $error_code = $response->get_error_code();
154 $error_message = $response->get_error_message();
155
156 // Collect diagnostic information
157 $diagnostics = [
158 'error_code' => $error_code,
159 'error_message' => $error_message,
160 'endpoint' => $endpoint,
161 'method' => $method,
162 'request_time_ms' => round(($end_time - $start_time) * 1000, 2),
163 'configured_timeout' => $params['timeout'] ?? 'unknown',
164 'api_url' => $this->api_url,
165 'environment' => $environment,
166 'wp_version' => get_bloginfo('version'),
167 'php_version' => PHP_VERSION,
168 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'unknown',
169 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
170 'server_ip' => $_SERVER['SERVER_ADDR'] ?? 'unknown',
171 'client_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
172 'host' => $_SERVER['HTTP_HOST'] ?? 'unknown',
173 ];
174
175 // Check if it's a timeout specifically
176 if (strpos($error_message, 'timeout') !== false || strpos($error_message, 'timed out') !== false) {
177 $diagnostics['timeout_type'] = 'connection_timeout';
178
179 // Check for hosting-specific indicators
180 if (strpos($_SERVER['SERVER_SOFTWARE'] ?? '', 'nginx') !== false) {
181 $diagnostics['server_type'] = 'nginx';
182 } elseif (strpos($_SERVER['SERVER_SOFTWARE'] ?? '', 'Apache') !== false) {
183 $diagnostics['server_type'] = 'apache';
184 }
185
186 // Check for common hosting providers
187 $host_indicators = [
188 'hostgator' => strpos($_SERVER['HTTP_HOST'] ?? '', 'hostgator') !== false,
189 'godaddy' => strpos($_SERVER['HTTP_HOST'] ?? '', 'secureserver') !== false,
190 'bluehost' => strpos($_SERVER['HTTP_HOST'] ?? '', 'bluehost') !== false,
191 'siteground' => strpos($_SERVER['HTTP_HOST'] ?? '', 'siteground') !== false,
192 'wpengine' => strpos($_SERVER['HTTP_HOST'] ?? '', 'wpengine') !== false,
193 ];
194 $diagnostics['hosting_indicators'] = array_filter($host_indicators);
195
196 // Check PHP configurations that might affect timeouts
197 $diagnostics['php_config'] = [
198 'max_execution_time' => ini_get('max_execution_time'),
199 'default_socket_timeout' => ini_get('default_socket_timeout'),
200 'curl_available' => function_exists('curl_init'),
201 'openssl_version' => OPENSSL_VERSION_TEXT ?? 'unknown',
202 ];
203
204 // Test basic connectivity
205 $diagnostics['connectivity_test'] = [
206 'can_resolve_dns' => gethostbyname('api.superfrete.com') !== 'api.superfrete.com',
207 'superfrete_ip' => gethostbyname('api.superfrete.com'),
208 ];
209 }
210
211 $diagnostic_json = wp_json_encode($diagnostics, JSON_PRETTY_PRINT);
212
213 Logger::log('SuperFrete', "TIMEOUT DIAGNOSTICS:\n" . $diagnostic_json);
214 error_log('SuperFrete TIMEOUT DIAGNOSTICS: ' . $diagnostic_json);
215
216 // Also log the original error message for backwards compatibility
217 Logger::log('SuperFrete', "WP Error na API ({$endpoint}): " . $error_message);
218
219 // Check if this is a timeout and we have a proxy available for fallback
220 if ((strpos($error_message, 'timeout') !== false || strpos($error_message, 'timed out') !== false) && !empty($proxy_url)) {
221 Logger::log('SuperFrete', "TIMEOUT DETECTED - Attempting automatic proxy fallback");
222
223 $proxy_result = $this->call_via_proxy($proxy_url, $endpoint, $method, $payload);
224 if ($proxy_result !== false) {
225 Logger::log('SuperFrete', "PROXY FALLBACK SUCCESSFUL - Enabling proxy for future requests");
226
227 // Enable force proxy mode to avoid future timeouts
228 update_option('superfrete_force_proxy', 'yes');
229
230 return $proxy_result;
231 } else {
232 Logger::log('SuperFrete', "PROXY FALLBACK ALSO FAILED");
233 }
234 }
235
236 return false;
237 }
238
239 $status_code = wp_remote_retrieve_response_code($response);
240 $raw_body = wp_remote_retrieve_body($response);
241
242 // Debug logging
243 error_log('SuperFrete API Response: Status = ' . $status_code);
244 error_log('SuperFrete API Response: Body = ' . substr($raw_body, 0, 500) . (strlen($raw_body) > 500 ? '...' : ''));
245
246 // Check for HTTP errors
247 if (!in_array($status_code, [200, 201, 204])) {
248 $error_msg = "ERRO NA API ({$endpoint}): CÓDIGO {$status_code}";
249
250 // Special handling for 401 errors
251 if ($status_code == 401) {
252 $error_msg .= " - NÃO AUTENTICADO!";
253 Logger::log('SuperFrete', $error_msg);
254 Logger::log('SuperFrete', "DETALHES [{$environment}]: URL={$full_url}, TOKEN={$token_preview}");
255 Logger::log('SuperFrete', "RESPOSTA: " . (strlen($raw_body) > 200 ? substr($raw_body, 0, 200) . '...' : $raw_body));
256 } else {
257 Logger::log('SuperFrete', $error_msg . "\nDETALHES: " . (strlen($raw_body) > 200 ? substr($raw_body, 0, 200) . '...' : ($raw_body ?: 'SEM DETALHES')));
258 }
259
260 return false;
261 }
262
263 // Handle empty responses (common for DELETE operations)
264 if (empty($raw_body) && $status_code == 204) {
265 Logger::log('SuperFrete', "API call successful ({$endpoint}) - No content returned (HTTP {$status_code})");
266 return true; // Success for DELETE operations
267 }
268
269 $body = json_decode($raw_body, true);
270
271 // Check for JSON decode errors only if there's content to decode
272 if (!empty($raw_body) && json_last_error() !== JSON_ERROR_NONE) {
273 Logger::log('SuperFrete', "JSON decode error na API ({$endpoint}): " . json_last_error_msg() . " - Raw response: " . substr($raw_body, 0, 200));
274 error_log('SuperFrete API JSON Error: ' . json_last_error_msg());
275 return false;
276 }
277
278 // Check for API-level errors
279 if (isset($body['success']) && $body['success'] === false) {
280 $error_message = isset($body['message']) ? $body['message'] : 'Erro desconhecido';
281 $errors = $this->extract_api_errors($body);
282 Logger::log('SuperFrete', "API Error ({$endpoint}): {$error_message}\nDetalhes: {$errors}");
283 error_log('SuperFrete API Error: ' . $error_message . ' - Details: ' . $errors);
284 return false;
285 }
286
287 Logger::log('SuperFrete', "API call successful ({$endpoint}) - Time: {$request_time}ms");
288 return $body;
289
290 } catch (Exception $exc) {
291 Logger::log('SuperFrete', "Exception na API ({$endpoint}): " . $exc->getMessage());
292 error_log('SuperFrete API Exception: ' . $exc->getMessage());
293 return false;
294 }
295 }
296
297 /**
298 * Extract error details from API response
299 */
300 private function extract_api_errors($body) {
301 $errors = [];
302
303 if (isset($body['errors'])) {
304 foreach ($body['errors'] as $field => $field_errors) {
305 if (is_array($field_errors)) {
306 $errors[] = $field . ': ' . implode(', ', $field_errors);
307 } else {
308 $errors[] = $field . ': ' . $field_errors;
309 }
310 }
311 } elseif (isset($body['error'])) {
312 if (is_array($body['error'])) {
313 foreach ($body['error'] as $error) {
314 if (is_array($error)) {
315 $errors[] = implode(', ', $error);
316 } else {
317 $errors[] = $error;
318 }
319 }
320 } else {
321 $errors[] = $body['error'];
322 }
323 }
324
325 return empty($errors) ? 'Sem detalhes' : implode('; ', $errors);
326 }
327
328 /**
329 * Register webhook with SuperFrete API
330 */
331 public function register_webhook($webhook_url, $events = ['order.posted', 'order.delivered'])
332 {
333 Logger::log('SuperFrete', 'Iniciando registro de webhook...');
334 Logger::log('SuperFrete', "Token sendo usado: " . (empty($this->api_token) ? 'VAZIO' : 'Presente'));
335 Logger::log('SuperFrete', "URL da API: " . $this->api_url);
336
337 // First, check for existing webhooks and clean them up
338 Logger::log('SuperFrete', 'Verificando webhooks existentes...');
339 $existing_webhooks = $this->list_webhooks();
340
341 if ($existing_webhooks && is_array($existing_webhooks)) {
342 Logger::log('SuperFrete', 'Encontrados ' . count($existing_webhooks) . ' webhooks existentes');
343
344 foreach ($existing_webhooks as $webhook) {
345 if (isset($webhook['id'])) {
346 Logger::log('SuperFrete', 'Removendo webhook existente ID: ' . $webhook['id']);
347 $this->delete_webhook($webhook['id'], false); // Don't clear options during cleanup
348 }
349 }
350 } else {
351 Logger::log('SuperFrete', 'Nenhum webhook existente encontrado');
352 }
353
354 // Now register the new webhook
355 $payload = [
356 'name' => 'WooCommerce SuperFrete Plugin Webhook',
357 'url' => $webhook_url,
358 'events' => $events
359 ];
360
361 Logger::log('SuperFrete', 'Registrando novo webhook: ' . wp_json_encode($payload));
362
363 $response = $this->call_superfrete_api('/api/v0/webhook', 'POST', $payload, true);
364
365 if ($response && isset($response['secret_token'])) {
366 // Store webhook secret for signature verification
367 update_option('superfrete_webhook_secret', $response['secret_token']);
368 update_option('superfrete_webhook_registered', 'yes');
369 update_option('superfrete_webhook_url', $webhook_url);
370 update_option('superfrete_webhook_id', $response['id'] ?? '');
371
372 Logger::log('SuperFrete', 'Webhook registrado com sucesso. ID: ' . ($response['id'] ?? 'N/A'));
373 return $response;
374 }
375
376 Logger::log('SuperFrete', 'Falha ao registrar webhook: ' . wp_json_encode($response));
377 update_option('superfrete_webhook_registered', 'no');
378 return false;
379 }
380
381 /**
382 * Update existing webhook
383 */
384 public function update_webhook($webhook_id, $webhook_url, $events = ['order.posted', 'order.delivered'])
385 {
386 $payload = [
387 'name' => 'WooCommerce SuperFrete Plugin Webhook',
388 'url' => $webhook_url,
389 'events' => $events
390 ];
391
392 Logger::log('SuperFrete', 'Atualizando webhook ID: ' . $webhook_id);
393
394 $response = $this->call_superfrete_api('/api/v0/webhook/' . $webhook_id, 'PUT', $payload, true);
395
396 if ($response) {
397 update_option('superfrete_webhook_url', $webhook_url);
398 Logger::log('SuperFrete', 'Webhook atualizado com sucesso');
399 return $response;
400 }
401
402 Logger::log('SuperFrete', 'Falha ao atualizar webhook: ' . wp_json_encode($response));
403 return false;
404 }
405
406 /**
407 * Delete webhook from SuperFrete
408 */
409 public function delete_webhook($webhook_id, $clear_options = true)
410 {
411 Logger::log('SuperFrete', 'Removendo webhook ID: ' . $webhook_id);
412
413 $response = $this->call_superfrete_api('/api/v0/webhook/' . $webhook_id, 'DELETE', [], true);
414
415 if ($response !== false) {
416 if ($clear_options) {
417 update_option('superfrete_webhook_registered', 'no');
418 update_option('superfrete_webhook_url', '');
419 update_option('superfrete_webhook_id', '');
420 }
421 Logger::log('SuperFrete', 'Webhook removido com sucesso');
422 return true;
423 }
424
425 Logger::log('SuperFrete', 'Falha ao remover webhook: ' . wp_json_encode($response));
426 return false;
427 }
428
429 /**
430 * List registered webhooks
431 */
432 public function list_webhooks()
433 {
434 Logger::log('SuperFrete', 'Listando webhooks registrados');
435
436 $response = $this->call_superfrete_api('/api/v0/webhook', 'GET', [], true);
437
438 if ($response) {
439 Logger::log('SuperFrete', 'Webhooks listados: ' . wp_json_encode($response));
440 return $response;
441 }
442
443 Logger::log('SuperFrete', 'Falha ao listar webhooks');
444 return false;
445 }
446
447 /**
448 * Make API call via proxy to work around hosting timeouts
449 */
450 private function call_via_proxy($proxy_url, $endpoint, $method, $payload = []) {
451 Logger::log('SuperFrete', "Using proxy for API call: {$proxy_url}");
452
453 $proxy_payload = [
454 'endpoint' => $endpoint,
455 'method' => $method,
456 'headers' => [
457 'Authorization' => 'Bearer ' . $this->api_token,
458 ]
459 ];
460
461 if (!empty($payload)) {
462 $proxy_payload['body'] = $payload;
463 }
464
465 $params = [
466 'headers' => [
467 'Content-Type' => 'application/json',
468 'Accept' => 'application/json',
469 ],
470 'method' => 'POST',
471 'body' => wp_json_encode($proxy_payload),
472 'timeout' => 65, // Slightly higher than proxy timeout
473 ];
474
475 $start_time = microtime(true);
476 $response = wp_remote_post($proxy_url, $params);
477 $end_time = microtime(true);
478 $request_time = round(($end_time - $start_time) * 1000, 2);
479
480 Logger::log('SuperFrete', "Proxy request time: {$request_time} ms");
481
482 if (is_wp_error($response)) {
483 Logger::log('SuperFrete', 'Proxy request failed: ' . $response->get_error_message());
484 return false;
485 }
486
487 $status_code = wp_remote_retrieve_response_code($response);
488 $body = wp_remote_retrieve_body($response);
489 $data = json_decode($body, true);
490
491 if ($status_code !== 200) {
492 Logger::log('SuperFrete', "Proxy returned error: {$status_code} - {$body}");
493 return false;
494 }
495
496 if (!$data || !$data['success']) {
497 Logger::log('SuperFrete', 'Proxy request unsuccessful: ' . wp_json_encode($data));
498 return false;
499 }
500
501 Logger::log('SuperFrete', "Proxy call successful - Duration: {$data['duration']}");
502 return $data['data'];
503 }
504 }
505