getMessage(), ['error' => 'config_incomplete'] ); } $endpoint = rtrim((string) config('sms.tencentcloud.endpoint'), '?&'); $sdkAppId = (string) config('sms.tencentcloud.sdk_app_id'); $appKey = (string) config('sms.tencentcloud.app_key'); $timeout = (int) config('sms.timeout', 5); $random = (string) random_int(100000, 999999999); $timestamp = time(); $mobile = (string) $record->mobile; $payload = [ 'ext' => '', 'extend' => '', 'params' => [ (string) $record->code, (string) data_get($record->request_payload_json, 'ttl_minutes', '5'), ], 'sig' => hash( 'sha256', "appkey={$appKey}&random={$random}&time={$timestamp}&mobile={$mobile}" ), 'sign' => (string) config('sms.tencentcloud.sign_name'), 'tel' => [ 'mobile' => $mobile, 'nationcode' => '86', ], 'time' => $timestamp, 'tpl_id' => (int) config('sms.tencentcloud.template_id'), ]; $url = $endpoint.'?'.http_build_query([ 'sdkappid' => $sdkAppId, 'random' => $random, ], '', '&', PHP_QUERY_RFC3986); try { $response = Http::timeout($timeout) ->acceptJson() ->asJson() ->post($url, $payload); } catch (\Throwable $e) { return SmsSendResult::failure( SmsVerification::PROVIDER_TENCENTCLOUD, null, 'NETWORK_ERROR', 'Tencent Cloud SMS request failed: '.$e->getMessage(), ['error' => 'network_error', 'exception' => $e::class] ); } $json = $response->json(); if (! $response->successful()) { $responseSummary = is_array($json) ? $this->responseSummary($json, $response->status()) : ['http_status' => $response->status(), 'error' => 'http_error']; $providerMessage = is_array($json) && isset($json['errmsg']) && is_scalar($json['errmsg']) ? (string) $json['errmsg'] : 'Tencent Cloud SMS returned HTTP '.$response->status().'.'; return SmsSendResult::failure( SmsVerification::PROVIDER_TENCENTCLOUD, is_array($json) ? $this->requestId($json) : null, 'HTTP_'.$response->status(), $providerMessage, $responseSummary ); } if (! is_array($json)) { return SmsSendResult::failure( SmsVerification::PROVIDER_TENCENTCLOUD, null, 'INVALID_JSON_RESPONSE', 'Tencent Cloud SMS returned invalid JSON.', ['error' => 'invalid_json_response', 'http_status' => $response->status()] ); } $rawResult = $json['result'] ?? null; if (! is_int($rawResult) && (! is_string($rawResult) || preg_match('/^-?\d+$/', $rawResult) !== 1)) { return SmsSendResult::failure( SmsVerification::PROVIDER_TENCENTCLOUD, $this->requestId($json), 'PROTOCOL_ERROR', 'Tencent Cloud SMS response is missing an integer result field.', $this->responseSummary($json, $response->status()) ); } $result = (int) $rawResult; $requestId = $this->requestId($json); $providerCode = (string) $result; $providerMessage = isset($json['errmsg']) && is_scalar($json['errmsg']) ? (string) $json['errmsg'] : ''; if ($result === 0) { return SmsSendResult::success( SmsVerification::PROVIDER_TENCENTCLOUD, $requestId, $providerCode, $providerMessage, $this->responseSummary($json, $response->status()) ); } return SmsSendResult::failure( SmsVerification::PROVIDER_TENCENTCLOUD, $requestId, $providerCode, $providerMessage !== '' ? $providerMessage : 'Tencent Cloud SMS send failed.', $this->responseSummary($json, $response->status()) ); } /** @param array $response */ private function requestId(array $response): ?string { $sid = $response['sid'] ?? null; return is_string($sid) || is_numeric($sid) ? (string) $sid : null; } /** * @param array $response * @return array */ private function responseSummary(array $response, int $httpStatus): array { return [ 'http_status' => $httpStatus, 'result' => $response['result'] ?? null, 'errmsg' => $response['errmsg'] ?? null, 'sid' => $response['sid'] ?? null, 'fee' => $response['fee'] ?? null, 'ext' => $response['ext'] ?? null, ]; } }