| @@ -8,15 +8,15 @@ | ||
| 8 | 8 | use SyncBasalam\Jobs\Exceptions\NonRetryableException; |
| 9 | 9 | |
| 10 | 10 | defined('ABSPATH') || exit; |
| 11 | 11 | |
| 12 | -class ApiResponseHandler | |
| 13 | -{ | |
| 14 | - public function handle($response, $url = ''): array | |
| 15 | - { | |
| 16 | - if (is_wp_error($response)) { | |
| 17 | - return $this->handleWpError($response, $url); | |
| 18 | - } | |
| 12 | +class ApiResponseHandler | |
| 13 | +{ | |
| 14 | + public function handle($response, $url = ''): array | |
| 15 | + { | |
| 16 | + if (is_wp_error($response)) { | |
| 17 | + return $this->handleWpError($response, $url); | |
| 18 | + } | |
| 19 | 19 | |
| 20 | 20 | $body = wp_remote_retrieve_body($response); |
| 21 | 21 | |
| 22 | 22 | $statusCodeRaw = wp_remote_retrieve_response_code($response); |
| @@ -25,50 +25,51 @@ | ||
| 25 | 25 | return $this->handleHttpStatusCode($statusCode, $body, $url); |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | 28 | |
| 29 | - private function handleWpError(\WP_Error $response, string $url = ''): array | |
| 30 | - { | |
| 31 | - $errorMessage = $response->get_error_message(); | |
| 32 | - $category = RequestStatusTracker::recordWpError($response, $url); | |
| 33 | - | |
| 34 | - if ($category === 'blocked_http') { | |
| 35 | - throw new BlockedHttpRequestException($errorMessage); | |
| 36 | - } | |
| 37 | - | |
| 38 | - if ($category === 'timeout') { | |
| 39 | - throw RetryableException::apiTimeout('درخواست با تایماوت مواجه شد: ' . $errorMessage); | |
| 40 | - } | |
| 41 | - | |
| 42 | - if (in_array($category, ['dns_error', 'ssl_error', 'connection_error', 'network_error'], true)) { | |
| 43 | - throw RetryableException::networkError('خطای شبکه: ' . $errorMessage); | |
| 44 | - } | |
| 45 | - | |
| 46 | - throw RetryableException::temporary('خطای موقت در درخواست: ' . $errorMessage); | |
| 47 | - } | |
| 48 | - | |
| 49 | - private function handleHttpStatusCode(int $statusCode, $body, $url): array | |
| 50 | - { | |
| 51 | - RequestStatusTracker::recordHttpStatus($statusCode, $url); | |
| 52 | - | |
| 53 | - if ($statusCode === 0) { | |
| 54 | - throw new BlockedHttpRequestException('پاسخ نامعتبر از درخواست HTTP (کد وضعیت نامشخص)'); | |
| 55 | - } | |
| 29 | + private function handleWpError(\WP_Error $response, string $url = ''): array | |
| 30 | + { | |
| 31 | + $errorMessage = $response->get_error_message(); | |
| 32 | + $category = RequestStatusTracker::recordWpError($response, $url); | |
| 33 | + $reason = RequestStatusTracker::describeCategoryFa($category); | |
| 56 | 34 | |
| 35 | + if ($category === 'blocked_http') { | |
| 36 | + throw new BlockedHttpRequestException(esc_html($reason . ' | جزئیات فنی: ' . $errorMessage)); | |
| 37 | + } | |
| 38 | + | |
| 39 | + if ($category === 'timeout') { | |
| 40 | + throw RetryableException::apiTimeout(esc_html($reason . ' | جزئیات فنی: ' . $errorMessage)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + if (in_array($category, ['dns_error', 'ssl_error', 'connection_error', 'network_error'], true)) { | |
| 44 | + throw RetryableException::networkError(esc_html($reason . ' | جزئیات فنی: ' . $errorMessage)); | |
| 45 | + } | |
| 46 | + | |
| 47 | + throw RetryableException::temporary(esc_html($reason . ' | جزئیات فنی: ' . $errorMessage)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + private function handleHttpStatusCode(int $statusCode, $body, $url): array | |
| 51 | + { | |
| 52 | + RequestStatusTracker::recordHttpStatus($statusCode, $url); | |
| 53 | + | |
| 54 | + if ($statusCode === 0) { | |
| 55 | + throw new BlockedHttpRequestException(esc_html(RequestStatusTracker::describeHttpStatusFa(0))); | |
| 56 | + } | |
| 57 | + | |
| 57 | 58 | if (in_array($statusCode, [200, 201, 202], true)) { |
| 58 | 59 | return $this->successResponse($body, $statusCode); |
| 59 | 60 | } |
| 60 | 61 | |
| 61 | 62 | if (in_array($statusCode, [408, 504], true)) { |
| 62 | - throw RetryableException::apiTimeout('درخواست با تایماوت مواجه شد'); | |
| 63 | + throw RetryableException::apiTimeout(esc_html(RequestStatusTracker::describeHttpStatusFa($statusCode))); | |
| 63 | 64 | } |
| 64 | 65 | |
| 65 | 66 | if ($statusCode === 429) { |
| 66 | - throw RetryableException::rateLimit('محدودیت تعداد درخواستها - لطفا کمی صبر کنید'); | |
| 67 | + throw RetryableException::rateLimit(esc_html(RequestStatusTracker::describeHttpStatusFa(429))); | |
| 67 | 68 | } |
| 68 | 69 | |
| 69 | 70 | if (in_array($statusCode, [500, 502, 503], true)) { |
| 70 | - throw RetryableException::serverError('خطای سمت سرور (کد ' . $statusCode . ')'); | |
| 71 | + throw RetryableException::serverError(esc_html(RequestStatusTracker::describeHttpStatusFa($statusCode))); | |
| 71 | 72 | } |
| 72 | 73 | |
| 73 | 74 | if ($statusCode === 401) { |
| 74 | 75 | if ($this->isBasalamDomain($url)) { |
| @@ -76,9 +77,9 @@ | ||
| 76 | 77 | SettingsConfig::TOKEN => null, |
| 77 | 78 | SettingsConfig::REFRESH_TOKEN => null, |
| 78 | 79 | ]); |
| 79 | 80 | } |
| 80 | - throw NonRetryableException::unauthorized('دسترسی غیرمجاز - لطفا دوباره وارد شوید'); | |
| 81 | + throw NonRetryableException::unauthorized(esc_html(RequestStatusTracker::describeHttpStatusFa(401))); | |
| 81 | 82 | } |
| 82 | 83 | |
| 83 | 84 | $clientErrors = [ |
| 84 | 85 | 400 => 'درخواست نامعتبر', |
| @@ -87,16 +88,31 @@ | ||
| 87 | 88 | 422 => 'خطا در پردازش دادهها', |
| 88 | 89 | ]; |
| 89 | 90 | |
| 90 | 91 | if (isset($clientErrors[$statusCode])) { |
| 91 | - $errorMessage = $this->extractErrorMessageFromBody($body) ?: $clientErrors[$statusCode]; | |
| 92 | - throw NonRetryableException::permanent($errorMessage . ' (کد ' . $statusCode . ')'); | |
| 92 | + $bodyMessage = $this->extractErrorMessageFromBody($body); | |
| 93 | + $base = $bodyMessage ?: $clientErrors[$statusCode]; | |
| 94 | + $reason = RequestStatusTracker::describeHttpStatusFa($statusCode); | |
| 95 | + throw (new NonRetryableException(esc_html($base . $reason), $statusCode)) | |
| 96 | + ->setResponseData($this->decodeBody($body)); | |
| 93 | 97 | } |
| 94 | 98 | |
| 95 | 99 | // Unknown error - treat as retryable |
| 96 | - throw RetryableException::temporary('خطای غیرمنتظره (کد ' . $statusCode . ')'); | |
| 100 | + throw RetryableException::temporary(esc_html(RequestStatusTracker::describeHttpStatusFa($statusCode))); | |
| 97 | 101 | } |
| 98 | 102 | |
| 103 | + private function decodeBody($body): ?array | |
| 104 | + { | |
| 105 | + if (is_array($body)) return $body; | |
| 106 | + | |
| 107 | + if (is_string($body) && $body !== '') { | |
| 108 | + $decoded = json_decode($body, true); | |
| 109 | + if (is_array($decoded)) return $decoded; | |
| 110 | + } | |
| 111 | + | |
| 112 | + return null; | |
| 113 | + } | |
| 114 | + | |
| 99 | 115 | private function extractErrorMessageFromBody($body): ?string |
| 100 | 116 | { |
| 101 | 117 | if (empty($body)) return null; |
| 102 | 118 | |
| @@ -135,10 +151,10 @@ | ||
| 135 | 151 | 'success' => true |
| 136 | 152 | ]; |
| 137 | 153 | } |
| 138 | 154 | |
| 139 | - public function handleTimeout(string $url): array | |
| 140 | - { | |
| 141 | - RequestStatusTracker::recordCategory('timeout', ['url' => $url]); | |
| 142 | - throw RetryableException::apiTimeout('درخواست تایماوت شد'); | |
| 143 | - } | |
| 144 | -} | |
| 155 | + public function handleTimeout(string $url): array | |
| 156 | + { | |
| 157 | + RequestStatusTracker::recordCategory('timeout', ['url' => $url]); | |
| 158 | + throw RetryableException::apiTimeout(esc_html(RequestStatusTracker::describeCategoryFa('timeout'))); | |
| 159 | + } | |
| 160 | +} | |