'参数不全', '-2' => '账户或密码错误', '-3' => '账户余额不足', '-4' => '账户余额已冻结', '30' => '验证码间隔太短', '40' => '账户不存在', '41' => '内容包含敏感词', '42' => '账户余额不足', ]; public function send(string $phone, string $content): void { $url = (string) config('mini_api.sms_endpoint', '') . '?' . http_build_query([ 'u' => (string) config('mini_api.sms_user', ''), 'p' => (string) config('mini_api.sms_password', ''), 'm' => $phone, 'c' => $content, ]); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_FOLLOWLOCATION => false, ]); $raw = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($raw === false || $status !== 200) { Log::warning('mini_api.sms_transport', ['http' => $status, 'transport_error' => $error]); throw new ApiException(50302, '短信服务暂时不可用,请稍后再试', 503); } $code = trim((string) $raw); if ($code === '0') { return; } // Provider status only. The request URL carries the password, so it is never logged. Log::warning('mini_api.sms_rejected', ['status' => $code, 'reason' => self::FAILURES[$code] ?? 'unknown']); throw new ApiException(50302, '短信发送失败,请稍后再试', 503); } }