Files
boursecu_server/app/Services/SmsBilling/SmsGatewayService.php
T
2026-07-07 17:42:09 +03:30

158 lines
4.7 KiB
PHP

<?php
namespace App\Services\SmsBilling;
use App\Exceptions\SmsBlockedException;
use App\Models\Setting;
use App\Models\User;
use App\Services\SmsBilling\Contracts\SmsProviderInterface;
use App\Services\SmsBilling\Providers\CentralSmsApiProvider;
use App\Services\SmsBilling\Providers\MeliPayamakProvider;
use Illuminate\Support\Facades\Log;
class SmsGatewayService
{
/** @var list<SmsProviderInterface> */
private array $providers;
public function __construct(
private SmsWalletService $walletService,
private SmsSegmentCalculator $segmentCalculator,
private SmsAlertService $alertService,
MeliPayamakProvider $meliPayamakProvider,
CentralSmsApiProvider $centralSmsApiProvider,
) {
$this->providers = [
$meliPayamakProvider,
$centralSmsApiProvider,
];
}
public function send(
string $to,
string $message,
?string $source = null,
bool $isAlert = false
): SmsSendResult {
$config = $this->buildProviderConfig();
$billingOperational = SmsWalletService::isBillingOperational();
if ($billingOperational && $this->walletService->isBlocked()) {
return SmsSendResult::blocked();
}
$provider = $this->resolveProvider($config);
if (! $provider) {
Log::warning('SmsGateway: no SMS provider configured');
return SmsSendResult::failed('سرویس پیامک پیکربندی نشده است.');
}
$text = trim($message);
if ($text === '') {
return SmsSendResult::failed('متن پیامک خالی است.');
}
try {
$result = $provider->send($to, $text, $config);
if (! ($result['success'] ?? false)) {
Log::warning('SmsGateway: provider send failed', [
'provider' => $this->providerLabel($provider),
'to' => $to,
'raw' => $result['raw'] ?? null,
]);
return SmsSendResult::failed('ارسال پیامک ناموفق بود.');
}
$parts = $this->segmentCalculator->countParts($text);
if ($parts <= 0) {
return SmsSendResult::failed('محاسبه پارت پیامک نامعتبر است.');
}
$providerName = $this->providerLabel($provider);
if ($billingOperational) {
$debit = $this->walletService->debitSuccessfulSms(
$to,
$text,
$parts,
$providerName,
$source,
$isAlert
);
if (! $isAlert) {
$this->alertService->maybeSendLowBalanceAlerts($debit['wallet']);
}
}
return SmsSendResult::success($providerName);
} catch (\Throwable $e) {
Log::error('SmsGateway: exception during send', [
'to' => $to,
'message' => $e->getMessage(),
]);
return SmsSendResult::failed($e->getMessage());
}
}
public function sendOrThrow(
string $to,
string $message,
?string $source = null,
bool $isAlert = false
): SmsSendResult {
$result = $this->send($to, $message, $source, $isAlert);
if ($result->blocked) {
throw new SmsBlockedException();
}
return $result;
}
private function buildProviderConfig(): array
{
return [
'melipayamak_user' => Setting::where('key', 'melipayamak_user')->value('value'),
'melipayamak_password' => Setting::where('key', 'melipayamak_password')->value('value'),
'melipayamak_from' => Setting::where('key', 'melipayamak_from')->value('value'),
'name_fa' => Setting::where('key', 'name_fa')->value('value'),
'name_en' => Setting::where('key', 'name_en')->value('value'),
];
}
private function resolveProvider(array $config): ?SmsProviderInterface
{
$meliUser = trim((string) ($config['melipayamak_user'] ?? ''));
if (strlen($meliUser) > 1) {
return $this->providerByClass(MeliPayamakProvider::class);
}
return $this->providerByClass(CentralSmsApiProvider::class);
}
private function providerByClass(string $class): ?SmsProviderInterface
{
foreach ($this->providers as $provider) {
if ($provider instanceof $class) {
return $provider;
}
}
return null;
}
private function providerLabel(SmsProviderInterface $provider): string
{
return $provider->name();
}
}