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; } }