Files
boursecu_server/app/Services/SmsBilling/SmsWalletService.php
T
2026-07-04 11:55:10 +03:30

180 lines
5.2 KiB
PHP

<?php
namespace App\Services\SmsBilling;
use App\Models\Setting;
use App\Models\SmsLedger;
use App\Models\SmsSendLog;
use App\Models\SmsWallet;
use Illuminate\Support\Facades\DB;
class SmsWalletService
{
public const SUPER_ADMIN_MOBILE = '09128081392';
public const TYPE_CHARGE = 'charge';
public const TYPE_DEBIT = 'debit';
public const TYPE_ALERT_DEBIT = 'alert_debit';
/** Billing/panel فعال است مگر sms_panel صریحاً false باشد. */
public static function isBillingEnabled(): bool
{
$value = Setting::where('key', 'sms_panel')->value('value');
if ($value === null || trim((string) $value) === '') {
return true;
}
$normalized = strtolower(trim((string) $value));
return ! in_array($normalized, ['false', '0', 'no', 'off'], true);
}
public function getWallet(): SmsWallet
{
return SmsWallet::query()->firstOrCreate([], [
'balance' => 0,
'price_per_part' => 0,
'is_blocked' => false,
'alert_days_sent' => 0,
]);
}
public function isBlocked(): bool
{
if (! self::isBillingEnabled()) {
return false;
}
return (bool) $this->getWallet()->is_blocked;
}
public function setBlocked(bool $blocked): SmsWallet
{
$wallet = $this->getWallet();
$wallet->is_blocked = $blocked;
$wallet->save();
return $wallet->fresh();
}
public function updatePricePerPart(int $pricePerPart): SmsWallet
{
$wallet = $this->getWallet();
$wallet->price_per_part = max(0, $pricePerPart);
$wallet->save();
return $wallet;
}
public function charge(int $amount, ?int $adminUserId = null, ?string $note = null): SmsWallet
{
if ($amount <= 0) {
throw new \InvalidArgumentException('مبلغ شارژ باید بزرگتر از صفر باشد.');
}
return DB::transaction(function () use ($amount, $adminUserId, $note) {
$wallet = SmsWallet::query()->lockForUpdate()->firstOrCreate([], [
'balance' => 0,
'price_per_part' => 0,
'is_blocked' => false,
'alert_days_sent' => 0,
]);
$wallet->balance += $amount;
if ($wallet->balance > 0) {
$wallet->alert_days_sent = 0;
$wallet->last_alert_date = null;
}
$wallet->save();
SmsLedger::create([
'type' => self::TYPE_CHARGE,
'amount' => $amount,
'balance_after' => $wallet->balance,
'note' => $note,
'created_by' => $adminUserId,
'created_at' => now(),
]);
return $wallet;
});
}
/**
* @return array{log: SmsSendLog, wallet: SmsWallet}
*/
public function debitSuccessfulSms(
string $receiver,
string $body,
int $parts,
string $provider,
?string $source = null,
bool $isAlert = false
): array {
return DB::transaction(function () use ($receiver, $body, $parts, $provider, $source, $isAlert) {
$wallet = SmsWallet::query()->lockForUpdate()->firstOrCreate([], [
'balance' => 0,
'price_per_part' => 0,
'is_blocked' => false,
'alert_days_sent' => 0,
]);
$pricePerPart = (int) $wallet->price_per_part;
$totalAmount = $parts * $pricePerPart;
$wallet->balance -= $totalAmount;
$wallet->save();
$log = SmsSendLog::create([
'receiver' => $receiver,
'body' => $body,
'parts' => $parts,
'price_per_part' => $pricePerPart,
'total_amount' => $totalAmount,
'provider' => $provider,
'source' => $source,
'is_alert' => $isAlert,
'created_at' => now(),
]);
SmsLedger::create([
'type' => $isAlert ? self::TYPE_ALERT_DEBIT : self::TYPE_DEBIT,
'amount' => -$totalAmount,
'balance_after' => $wallet->balance,
'price_per_part' => $pricePerPart,
'parts' => $parts,
'sms_send_log_id' => $log->id,
'note' => $isAlert ? 'هشدار موجودی پیامک' : null,
'created_at' => now(),
]);
return ['log' => $log, 'wallet' => $wallet->fresh()];
});
}
public static function isSmsPanelSuperAdmin(?object $user): bool
{
if (! $user) {
return false;
}
return self::normalizeMobile((string) ($user->mobile ?? '')) === self::SUPER_ADMIN_MOBILE;
}
public static function normalizeMobile(string $mobile): string
{
$digits = preg_replace('/\D/', '', $mobile) ?? '';
if (str_starts_with($digits, '98') && strlen($digits) === 12) {
$digits = '0'.substr($digits, 2);
} elseif (strlen($digits) === 10 && str_starts_with($digits, '9')) {
$digits = '0'.$digits;
}
return $digits;
}
}