$vars */ public function sendTemplate(string $to, string $project, array $vars): SmsSendResult { $appId = trim((string) config('sms.submail.app_id')); $appKey = trim((string) config('sms.submail.app_key')); $endpoint = rtrim((string) config('sms.submail.endpoint'), '?&'); $timeout = (int) config('sms.timeout', 5); $signName = $this->normalizeSignName((string) config('sms.submail.sign_name')); $payload = [ 'appid' => $appId, 'to' => $to, 'project' => $project, 'signature' => $appKey, 'vars' => json_encode($vars, JSON_UNESCAPED_UNICODE), ]; if ($signName !== '') { $payload['sms_signature'] = $signName; } try { $response = Http::timeout($timeout) ->acceptJson() ->asForm() ->post($endpoint, $payload); } catch (\Throwable $e) { Log::error('sms.submail network_error', [ 'project' => $project, 'mobile_mask' => $this->maskMobile($to), 'error' => $e->getMessage(), ]); return SmsSendResult::failure( SmsVerification::PROVIDER_SUBMAIL, null, 'NETWORK_ERROR', 'Submail SMS request failed: '.$e->getMessage(), ['error' => 'network_error', 'exception' => $e::class] ); } $json = $response->json(); $summary = $this->responseSummary(is_array($json) ? $json : [], $response->status()); $status = is_array($json) && isset($json['status']) ? (string) $json['status'] : ''; $sendId = is_array($json) && isset($json['send_id']) && is_scalar($json['send_id']) ? (string) $json['send_id'] : null; $code = is_array($json) && isset($json['code']) && is_scalar($json['code']) ? (string) $json['code'] : (string) $response->status(); $message = is_array($json) && isset($json['msg']) && is_scalar($json['msg']) ? (string) $json['msg'] : ''; if ($response->successful() && $status === 'success') { return SmsSendResult::success( SmsVerification::PROVIDER_SUBMAIL, $sendId, $code !== '' ? $code : 'success', $message !== '' ? $message : 'OK', $summary ); } if (! $response->successful() && $code === (string) $response->status()) { $code = 'HTTP_'.$response->status(); } return SmsSendResult::failure( SmsVerification::PROVIDER_SUBMAIL, $sendId, $code !== '' ? $code : 'SEND_FAILED', $message !== '' ? $message : 'Submail SMS send failed.', $summary ); } /** * @param array $response * @return array */ private function responseSummary(array $response, int $httpStatus): array { return [ 'http_status' => $httpStatus, 'status' => $response['status'] ?? null, 'send_id' => $response['send_id'] ?? null, 'fee' => $response['fee'] ?? null, 'code' => $response['code'] ?? null, 'msg' => $response['msg'] ?? null, ]; } private function maskMobile(string $mobile): string { if (strlen($mobile) < 11) { return '***'; } return substr($mobile, 0, 3).'****'.substr($mobile, -4); } private function normalizeSignName(string $signName): string { $signName = trim($signName); $stripped = preg_replace('/^[\[【]+|[\]】]+$/u', '', $signName); return is_string($stripped) ? trim($stripped) : $signName; } }