You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
4.0 KiB

<?php
namespace App\Services\Sms;
use App\Models\SmsVerification;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class SubmailSmsClient
{
/**
* @param array<string, string> $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<string, mixed> $response
* @return array<string, mixed>
*/
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;
}
}