| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- <?php
- declare(strict_types=1);
- namespace app\miniapi\service;
- use app\miniapi\exception\ApiException;
- use app\miniapi\http\ApiResponse;
- use support\Log;
- /**
- * The provider credential is an MD5 password or API key, passed through unchanged.
- * Provider status codes are logged without exposing credentials or message contents.
- */
- final class SmsbaoSender implements SmsSender
- {
- private const FAILURES = [
- '-1' => '参数不全',
- '-2' => '账户或密码错误',
- '-3' => '账户余额不足',
- '-4' => '账户余额已冻结',
- '30' => '账户密码错误',
- '40' => '账户不存在',
- '41' => '账户余额不足',
- '43' => '来源IP受限',
- '50' => '内容包含敏感词',
- '51' => '手机号码不正确',
- ];
- public function send(string $phone, string $content): void
- {
- $signature = trim((string) config('mini_api.sms_signature', ''));
- if ($signature === '') {
- throw new ApiException(50301, '短信签名未配置,请联系管理员', 503);
- }
- if (!str_starts_with($signature, '【') || !str_ends_with($signature, '】')) {
- $signature = '【' . $signature . '】';
- }
- $request = \request();
- $requestId = $request ? ApiResponse::requestId($request) : null;
- $credential = (string) config('mini_api.sms_password', '');
- $url = (string) config('mini_api.sms_endpoint', '') . '?' . http_build_query([
- 'u' => (string) config('mini_api.sms_user', ''),
- 'p' => $credential,
- 'm' => $phone,
- 'c' => $signature . $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);
- $errorNumber = curl_errno($ch);
- $error = curl_error($ch);
- curl_close($ch);
- if ($raw === false || $status !== 200) {
- $redactions = array_filter([$url, rawurlencode($credential), $credential]);
- Log::warning('mini_api.sms_transport', [
- 'request_id' => $requestId,
- 'http' => $status,
- 'curl_errno' => $errorNumber,
- 'transport_error' => str_replace($redactions, '[redacted]', $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', [
- 'request_id' => $requestId,
- 'status' => preg_match('/^-?\d+$/D', $code) ? $code : 'unexpected_response',
- 'reason' => self::FAILURES[$code] ?? 'unknown',
- ]);
- throw new ApiException(50302, '短信发送失败,请稍后再试', 503);
- }
- }
|