calculateTotalInterest($goldAmount, $months, $interestType, $interestValue); $interestPerMonth = $months > 0 ? $totalInterestGold / $months : 0; $installments = []; for ($i = 1; $i <= $months; $i++) { $soot = $baseSootPerMonth + ($i === $months ? $remainder : 0); $principalGold = $soot / 1000; $interestGold = round($interestPerMonth, 6); $installments[] = [ 'installment_number' => $i, 'amount_soot' => $soot, 'principal_gold' => $principalGold, 'interest_gold' => $interestGold, 'total_gold' => round($principalGold + $interestGold, 6), ]; } return [ 'installments' => $installments, 'total_interest_gold' => round($totalInterestGold, 6), 'total_payable_gold' => round($goldAmount + $totalInterestGold, 6), ]; } public function createCredit(array $data, ?int $adminId = null, string $source = 'admin_direct'): GoldCredit { $customer = Customer::findOrFail($data['customer_id']); $active = GoldCredit::where('customer_id', $customer->id) ->whereIn('status', ['pending', 'active']) ->exists(); if ($active) { throw new UnprocessableEntityHttpException('این کاربر اعتبار فعال دارد'); } $plan = $this->buildInstallmentPlan( (float) $data['gold_amount'], (int) $data['months'], $data['interest_type'] ?? 'none', (float) ($data['interest_value'] ?? 0) ); return DB::transaction(function () use ($data, $adminId, $source, $customer, $plan) { $cfg = app(ConfigService::class); $credit = GoldCredit::create([ 'customer_id' => $customer->id, 'gold_amount' => $data['gold_amount'], 'months' => $data['months'], 'interest_type' => $data['interest_type'] ?? 'none', 'interest_value' => $data['interest_value'] ?? 0, 'delivery_method' => $data['delivery_method'] ?? 'manual', 'status' => 'active', 'source' => $source, 'total_interest_gold' => $plan['total_interest_gold'], 'total_payable_gold' => $plan['total_payable_gold'], 'created_by' => $adminId, 'notes' => $data['notes'] ?? null, 'has_check' => (bool) ($data['has_check'] ?? false), 'has_contract' => (bool) ($data['has_contract'] ?? false), 'check_note' => $data['check_note'] ?? null, 'contract_note' => $data['contract_note'] ?? null, 'penalty_type' => $data['penalty_type'] ?? $cfg->get('gold_credit_penalty_type', 'rial'), 'penalty_amount' => $data['penalty_amount'] ?? $cfg->get('gold_credit_penalty_amount', '1000000'), 'grace_hours' => $data['grace_hours'] ?? $cfg->getInt('gold_credit_grace_hours', 24), ]); $startDate = Carbon::now(); foreach ($plan['installments'] as $item) { GoldCreditInstallment::create([ 'gold_credit_id' => $credit->id, 'installment_number' => $item['installment_number'], 'due_date' => $startDate->copy()->addMonths($item['installment_number'])->toDateString(), 'amount_soot' => $item['amount_soot'], 'principal_gold' => $item['principal_gold'], 'interest_gold' => $item['interest_gold'], 'total_gold' => $item['total_gold'], 'status' => 'pending', ]); } if (($data['delivery_method'] ?? 'manual') === 'auto') { $this->deliverCredit($credit, $adminId, $data['notes'] ?? 'اعتبار طلایی'); } return $credit->load('installments', 'customer'); }); } public function deliverCredit(GoldCredit $credit, ?int $adminId, string $note = 'اعتبار طلایی'): void { $customer = Customer::findOrFail($credit->customer_id); $walletOwnerType = $customer->resolveWalletOwnerType(); $walletInterface = new WalletInterface($customer->id, $walletOwnerType); $withdraw = Withdraw::create([ 'user_id' => $adminId, 'customer_id' => $customer->id, 'gold' => $credit->gold_amount, 'note' => $note, 'sts' => 4, ]); new TransactionInterface($walletInterface, [ 'cash' => 0, 'gold' => $credit->gold_amount, 'expire_at' => now(), 'note' => $note, 'withdraw_id' => $withdraw->id, 'active' => true, ], 7, true, false, 0); } public function approveRequest(int $requestId, array $creditData, int $adminId): GoldCredit { $request = GoldCreditRequest::where('status', 'pending')->findOrFail($requestId); $creditData['customer_id'] = $request->customer_id; if (!isset($creditData['gold_amount'])) { $creditData['gold_amount'] = $request->requested_gold; } if (!isset($creditData['months'])) { $creditData['months'] = $request->requested_months ?? (int) app(ConfigService::class)->getInt('gold_credit_default_months', 12); } return DB::transaction(function () use ($request, $creditData, $adminId) { $credit = $this->createCredit($creditData, $adminId, 'user_request'); $request->update([ 'status' => 'approved', 'gold_credit_id' => $credit->id, 'admin_note' => $creditData['admin_note'] ?? $creditData['notes'] ?? null, ]); return $credit; }); } public function rejectRequest(int $requestId, ?string $adminNote = null): GoldCreditRequest { $request = GoldCreditRequest::where('status', 'pending')->findOrFail($requestId); $request->update([ 'status' => 'rejected', 'admin_note' => $adminNote, ]); return $request; } public function payInstallments( GoldCredit $credit, array $installmentIds, string $payMethod, ?float $goldAmount = null, ?int $rialAmount = null, ?int $adminId = null ): array { $installments = GoldCreditInstallment::where('gold_credit_id', $credit->id) ->whereIn('id', $installmentIds) ->orderBy('installment_number') ->get(); if ($installments->isEmpty()) { throw new UnprocessableEntityHttpException('قسطی یافت نشد'); } $this->assertSequentialPayable($credit, $installments); $totalDueGold = (float) $installments->sum(fn ($i) => (float) $i->total_gold + (float) $i->penalty_gold); $totalDueSoot = (int) $installments->sum('amount_soot'); $totalPenaltyRial = (int) $installments->sum('penalty_rial'); [$payGold, $payRial] = $this->resolvePaymentAmounts( $payMethod, $totalDueGold, $goldAmount, $rialAmount ); $payRial += $totalPenaltyRial; $customer = Customer::findOrFail($credit->customer_id); $this->deductFromWallet($customer, $payGold, $payRial, $adminId); $now = now(); foreach ($installments as $installment) { $isPrepaid = $installment->due_date->isFuture(); $installment->update([ 'paid_gold' => (float) $installment->total_gold + (float) $installment->penalty_gold, 'paid_rial' => (int) $installment->penalty_rial, 'status' => $isPrepaid ? 'prepaid' : 'paid', 'paid_at' => $now, ]); } $credit->increment('paid_gold', $totalDueGold); $credit->increment('paid_rial', $payRial); $remaining = GoldCreditInstallment::where('gold_credit_id', $credit->id) ->whereIn('status', ['pending', 'partial', 'overdue']) ->count(); if ($remaining === 0) { $credit->update(['status' => 'completed']); } $this->refreshOverdueStatuses($credit); $this->notifyAdminsPayment($customer, $credit, $installments, $totalDueSoot, $payGold, $payRial); return [ 'paid_installments' => $installments->pluck('id'), 'total_soot' => $totalDueSoot, 'paid_gold' => $payGold, 'paid_rial' => $payRial, ]; } public function refreshOverdueStatuses(GoldCredit $credit): void { GoldCreditInstallment::where('gold_credit_id', $credit->id) ->where('status', 'pending') ->where('due_date', '<', now()->toDateString()) ->update(['status' => 'overdue']); } public function updateCredit(int $creditId, array $data): GoldCredit { $credit = GoldCredit::with('installments')->findOrFail($creditId); if (!in_array($credit->status, ['pending', 'active'], true)) { throw new UnprocessableEntityHttpException('فقط اعتبارهای فعال قابل ویرایش هستند'); } if (isset($data['notes'])) { $credit->notes = $data['notes']; } foreach (['has_check', 'has_contract'] as $boolField) { if (array_key_exists($boolField, $data)) { $credit->{$boolField} = (bool) $data[$boolField]; } } foreach (['check_note', 'contract_note', 'penalty_type'] as $textField) { if (array_key_exists($textField, $data)) { $credit->{$textField} = $data[$textField]; } } if (array_key_exists('penalty_amount', $data)) { $credit->penalty_amount = $data['penalty_amount']; } if (array_key_exists('grace_hours', $data)) { $credit->grace_hours = $data['grace_hours']; } if (isset($data['status']) && in_array($data['status'], ['cancelled'], true)) { $credit->status = $data['status']; $credit->save(); return $credit; } $needsRecalc = isset($data['gold_amount']) || isset($data['months']) || isset($data['interest_type']) || isset($data['interest_value']); if ($needsRecalc) { $paidNumbers = $credit->installments ->whereIn('status', ['paid', 'prepaid']) ->pluck('installment_number') ->toArray(); if (count($paidNumbers) > 0) { throw new UnprocessableEntityHttpException('پس از پرداخت قسط، امکان تغییر مبلغ/مدت وجود ندارد'); } $goldAmount = (float) ($data['gold_amount'] ?? $credit->gold_amount); $months = (int) ($data['months'] ?? $credit->months); $interestType = $data['interest_type'] ?? $credit->interest_type; $interestValue = (float) ($data['interest_value'] ?? $credit->interest_value); $plan = $this->buildInstallmentPlan($goldAmount, $months, $interestType, $interestValue); $credit->installments()->delete(); $startDate = Carbon::parse($credit->created_at); foreach ($plan['installments'] as $item) { GoldCreditInstallment::create([ 'gold_credit_id' => $credit->id, 'installment_number' => $item['installment_number'], 'due_date' => $startDate->copy()->addMonths($item['installment_number'])->toDateString(), 'amount_soot' => $item['amount_soot'], 'principal_gold' => $item['principal_gold'], 'interest_gold' => $item['interest_gold'], 'total_gold' => $item['total_gold'], 'status' => 'pending', ]); } $credit->fill([ 'gold_amount' => $goldAmount, 'months' => $months, 'interest_type' => $interestType, 'interest_value' => $interestValue, 'total_interest_gold' => $plan['total_interest_gold'], 'total_payable_gold' => $plan['total_payable_gold'], ]); } $credit->save(); return $credit->fresh(['installments', 'customer']); } public function getGramPriceRial(): int { $price = Price::query()->orderByDesc('id')->value('price'); return (int) ($price ?? 0); } /** * هشدار SMS در سررسید + جریمه روزانه پس از مهلت (grace) + یادآوری ۱ و ۳ روز قبل. * پرداخت خودکار نیست — فقط بدهی جریمه اضافه می‌شود. * * @return array{warnings:int,penalties:int,reminders:int} */ public function processOverdueInstallments(): array { $cfg = app(ConfigService::class); if (! $cfg->isGoldCreditEnabled() || ! Schema::hasTable('gold_credit_installments')) { return ['warnings' => 0, 'penalties' => 0, 'reminders' => 0]; } $smsEnabled = $cfg->getBool('gold_credit_overdue_sms_enabled', true); $warnings = 0; $penalties = 0; $reminders = $this->processUpcomingDueReminders(); $installments = GoldCreditInstallment::with(['goldCredit.customer']) ->whereIn('status', ['pending', 'overdue']) ->where('due_date', '<=', now()->toDateString()) ->get(); foreach ($installments as $installment) { $credit = $installment->goldCredit; if (!$credit || $credit->status !== 'active') { continue; } if ($installment->status === 'pending') { $installment->status = 'overdue'; $installment->save(); } if ($smsEnabled && !$installment->warning_sms_sent_at) { $this->sendOverdueWarningSms($installment); $installment->warning_sms_sent_at = now(); $installment->save(); $warnings++; } if ($this->applyDailyPenaltyIfDue($installment, $credit)) { $penalties++; } } return compact('warnings', 'penalties', 'reminders'); } /** یادآوری ۳ روز و ۱ روز مانده به سررسید — برای مشتری و ادمین */ public function processUpcomingDueReminders(): int { $cfg = app(ConfigService::class); if (!$cfg->getBool('gold_credit_upcoming_sms_enabled', true)) { return 0; } $sent = 0; $today = now()->startOfDay(); foreach ([3 => 'reminder_3d_sent_at', 1 => 'reminder_1d_sent_at'] as $days => $field) { $targetDate = $today->copy()->addDays($days)->toDateString(); $installments = GoldCreditInstallment::with(['goldCredit.customer']) ->where('status', 'pending') ->whereNull($field) ->whereDate('due_date', $targetDate) ->get(); foreach ($installments as $installment) { $credit = $installment->goldCredit; if (!$credit || $credit->status !== 'active') { continue; } $this->sendUpcomingReminderSms($installment, $days); $installment->{$field} = now(); $installment->save(); $sent++; } } return $sent; } public function notifyAdminsLoanRequest(GoldCreditRequest $request): void { $cfg = app(ConfigService::class); if (!$cfg->getBool('gold_credit_admin_sms_enabled', true)) { return; } $customer = Customer::find($request->customer_id); $company = $cfg->get('name_fa', ''); $message = sprintf( "درخواست اعتبار طلایی جدید\nمشتری: %s\nموبایل: %s\nمقدار: %s گرم\nمدت: %s ماه\n%s", $customer->name ?? '-', $customer->mobile ?? '-', $request->requested_gold, $request->requested_months ?? '-', $company ); $this->notifyAdminsSms($message, 'gold_credit_request'); } private function notifyAdminsPayment( Customer $customer, GoldCredit $credit, $installments, int $totalSoot, float $payGold, int $payRial ): void { $cfg = app(ConfigService::class); if (!$cfg->getBool('gold_credit_admin_sms_enabled', true)) { return; } $numbers = $installments->pluck('installment_number')->implode(','); $company = $cfg->get('name_fa', ''); $message = sprintf( "پرداخت قسط اعتبار طلایی\nمشتری: %s\nموبایل: %s\nاقساط: %s\nمبلغ: %s سوت\nطلا: %s گرم / ریال: %s\n%s", $customer->name ?? '-', $customer->mobile ?? '-', $numbers, number_format($totalSoot), $payGold, number_format($payRial), $company ); $this->notifyAdminsSms($message, 'gold_credit_payment'); } private function notifyAdminsSms(string $message, string $source): void { User::where('send_sms', 1)->get()->each(function ($admin) use ($message, $source) { if (!$admin->mobile) { return; } try { app(\App\Services\SmsBilling\SmsGatewayService::class) ->send($admin->mobile, $message, $source); } catch (\Throwable $e) { Log::warning('gold credit admin sms failed', [ 'admin_id' => $admin->id, 'source' => $source, 'error' => $e->getMessage(), ]); } }); } private function sendUpcomingReminderSms(GoldCreditInstallment $installment, int $daysLeft): void { $customer = $installment->goldCredit?->customer; $cfg = app(ConfigService::class); $company = $cfg->get('name_fa', ''); $soot = number_format((int) $installment->amount_soot); $customerMsg = sprintf( "%s عزیز، %d روز تا سررسید قسط شماره %d اعتبار طلایی باقی مانده است.\nمبلغ قسط: %s سوت\nلطفاً از بخش اعتبار طلایی پرداخت کنید.\n%s", $customer->name ?? 'کاربر', $daysLeft, $installment->installment_number, $soot, $company ); if ($customer?->mobile) { try { app(\App\Services\SmsBilling\SmsGatewayService::class) ->send($customer->mobile, $customerMsg, 'gold_credit_upcoming'); } catch (\Throwable $e) { Log::warning('gold credit upcoming sms failed', [ 'installment_id' => $installment->id, 'error' => $e->getMessage(), ]); } } if ($cfg->getBool('gold_credit_admin_sms_enabled', true)) { $adminMsg = sprintf( "اخطار سررسید اعتبار طلایی\n%d روز مانده\nمشتری: %s\nموبایل: %s\nقسط %d — %s سوت\n%s", $daysLeft, $customer->name ?? '-', $customer->mobile ?? '-', $installment->installment_number, $soot, $company ); $this->notifyAdminsSms($adminMsg, 'gold_credit_upcoming_admin'); } } public function resolvePenaltyConfig(GoldCredit $credit): array { $cfg = app(ConfigService::class); $type = $credit->penalty_type ?: $cfg->get('gold_credit_penalty_type', 'rial'); $type = in_array($type, ['rial', 'gold'], true) ? $type : 'rial'; $amount = $credit->penalty_amount !== null ? (float) $credit->penalty_amount : (float) $cfg->get('gold_credit_penalty_amount', '1000000'); $grace = $credit->grace_hours !== null ? (int) $credit->grace_hours : $cfg->getInt('gold_credit_grace_hours', 24); return [ 'type' => $type, 'amount' => $amount, 'grace_hours' => max(0, $grace), ]; } private function sendOverdueWarningSms(GoldCreditInstallment $installment): void { $customer = $installment->goldCredit?->customer; if (!$customer?->mobile) { return; } $cfg = app(ConfigService::class); $grace = $this->resolvePenaltyConfig($installment->goldCredit)['grace_hours']; $penalty = $this->resolvePenaltyConfig($installment->goldCredit); $penaltyLabel = $penalty['type'] === 'gold' ? number_format($penalty['amount'], 4) . ' گرم طلا' : number_format((int) $penalty['amount']) . ' ریال'; $template = $cfg->get( 'gold_credit_overdue_sms_text', 'قسط شماره {n} سررسید شده است. در صورت عدم پرداخت تا {hours} ساعت آینده، روزانه {penalty} به بدهی شما اضافه می‌شود.' ); $message = str_replace( ['{n}', '{hours}', '{penalty}', '{name}'], [ (string) $installment->installment_number, (string) $grace, $penaltyLabel, $customer->name ?? '', ], (string) $template ); $message .= "\n" . $cfg->get('name_fa', ''); try { app(\App\Services\SmsBilling\SmsGatewayService::class) ->send($customer->mobile, $message, 'gold_credit_overdue'); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::warning('gold credit overdue sms failed', [ 'installment_id' => $installment->id, 'error' => $e->getMessage(), ]); } } private function applyDailyPenaltyIfDue(GoldCreditInstallment $installment, GoldCredit $credit): bool { if (!$installment->warning_sms_sent_at) { return false; } $config = $this->resolvePenaltyConfig($credit); $graceEnds = Carbon::parse($installment->warning_sms_sent_at)->addHours($config['grace_hours']); if (now()->lt($graceEnds)) { return false; } if (!$installment->penalty_started_at) { $installment->penalty_started_at = now(); } $last = $installment->last_penalty_at ? Carbon::parse($installment->last_penalty_at)->startOfDay() : null; $today = now()->startOfDay(); if ($last && $last->equalTo($today)) { return false; } if ($config['type'] === 'gold') { $installment->penalty_gold = round((float) $installment->penalty_gold + $config['amount'], 6); } else { $installment->penalty_rial = (int) $installment->penalty_rial + (int) round($config['amount']); } $installment->penalty_days = (int) $installment->penalty_days + 1; $installment->last_penalty_at = now(); $installment->status = 'overdue'; $installment->save(); return true; } private function calculateTotalInterest( float $goldAmount, int $months, string $interestType, float $interestValue ): float { return match ($interestType) { 'percent' => round($goldAmount * ($interestValue / 100) * $months, 6), 'fixed_gram' => round($interestValue * $months, 6), default => 0, }; } private function assertSequentialPayable(GoldCredit $credit, $installments): void { $allPending = GoldCreditInstallment::where('gold_credit_id', $credit->id) ->whereIn('status', ['pending', 'overdue', 'partial']) ->orderBy('installment_number') ->get(); $expectedIds = $allPending->take($installments->count())->pluck('id')->sort()->values(); $actualIds = $installments->pluck('id')->sort()->values(); if ($expectedIds->toArray() !== $actualIds->toArray()) { throw new UnprocessableEntityHttpException('اقساط باید به ترتیب و بدون پرش پرداخت شوند'); } foreach ($installments as $installment) { if (!in_array($installment->status, ['pending', 'overdue', 'partial'], true)) { throw new UnprocessableEntityHttpException('یکی از اقساط انتخاب‌شده قبلاً پرداخت شده است'); } } } private function resolvePaymentAmounts( string $payMethod, float $totalDueGold, ?float $goldAmount, ?int $rialAmount ): array { return match ($payMethod) { 'gold' => [$totalDueGold, 0], 'rial' => [0, $this->goldToRial($totalDueGold)], 'mixed' => [ (float) ($goldAmount ?? 0), (int) ($rialAmount ?? 0), ], default => throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است'), }; } public function defaultTiers(): array { return [ ['min_gram' => 0.5, 'max_gram' => 1, 'credit_gram' => 3, 'monthly_soot' => 250, 'months' => 12], ['min_gram' => 1, 'max_gram' => 2, 'credit_gram' => 4, 'monthly_soot' => 333, 'months' => 12], ['min_gram' => 2, 'max_gram' => 3, 'credit_gram' => 5, 'monthly_soot' => 417, 'months' => 12], ]; } public function getTiers(): array { $cfg = app(ConfigService::class); $raw = $cfg->getString('gold_credit_tiers', ''); if ($raw === '') { return $this->defaultTiers(); } $decoded = json_decode($raw, true); if (!is_array($decoded) || count($decoded) === 0) { return $this->defaultTiers(); } return array_values(array_map(function ($tier) { return [ 'min_gram' => (float) ($tier['min_gram'] ?? 0), 'max_gram' => (float) ($tier['max_gram'] ?? 0), 'credit_gram' => (float) ($tier['credit_gram'] ?? 0), 'monthly_soot' => (int) ($tier['monthly_soot'] ?? 0), 'months' => (int) ($tier['months'] ?? 12), ]; }, $decoded)); } public function resolveTierForBalance(float $goldBalance): ?array { $tiers = $this->getTiers(); $count = count($tiers); foreach ($tiers as $i => $tier) { $min = (float) $tier['min_gram']; $max = (float) $tier['max_gram']; $isLast = $i === $count - 1; if ($goldBalance >= $min && ($isLast ? $goldBalance <= $max : $goldBalance < $max)) { return $tier; } } return null; } public function getCustomerGoldBalance(int $customerId): float { $customer = Customer::findOrFail($customerId); $walletOwnerType = $customer->resolveWalletOwnerType(); $wallet = Wallet::where('owner', $customerId) ->where('owner_type', $walletOwnerType) ->whereNull('fund_id') ->whereNull('product_id') ->first(); return round((float) ($wallet->gold ?? 0), 4); } public function getPublicConfig(): array { $cfg = app(ConfigService::class); $defaultInfo = "اعتبار طلایی بر اساس موجودی طلای شما در قلک اعطا می‌شود.\n" . "• کاربران با موجودی ۵۰۰ سوت تا ۱ گرم: ۳ گرم اعتبار — بازپرداخت ماهانه ۲۵۰ سوت\n" . "• کاربران با موجودی ۱ تا ۲ گرم: ۴ گرم اعتبار — بازپرداخت ماهانه ۳۳۳ سوت\n" . "• کاربران با موجودی ۲ تا ۳ گرم: ۵ گرم اعتبار — بازپرداخت ماهانه ۴۱۷ سوت\n" . "بازپرداخت همه طرح‌ها ۱۲ ماهه است."; $defaultSuccess = 'درخواست شما ثبت شد و در حال بررسی است. تا ۲۴ ساعت آینده با شما تماس گرفته می‌شود و توضیحات تکمیلی توسط کارشناس ارائه خواهد شد.'; return [ 'min_balance_gram' => (float) $cfg->get('gold_credit_min_balance_gram', '0.5'), 'info_text' => $cfg->getString('gold_credit_info_text', $defaultInfo), 'request_success_text' => $cfg->getString('gold_credit_request_success_text', $defaultSuccess), 'default_months' => $cfg->getInt('gold_credit_default_months', 12), 'pay_methods' => [ 'gold' => $cfg->getBool('gold_credit_pay_gold_enabled', true), 'rial' => $cfg->getBool('gold_credit_pay_rial_enabled', false), 'mixed' => $cfg->getBool('gold_credit_pay_mixed_enabled', false), ], 'tiers' => $this->getTiers(), ]; } public function assertPayMethodAllowed(string $payMethod): void { $methods = $this->getPublicConfig()['pay_methods']; if (empty($methods[$payMethod])) { throw new UnprocessableEntityHttpException('این روش پرداخت فعال نیست'); } } private function goldToRial(float $gold): int { $gramPrice = $this->getGramPriceRial(); if ($gramPrice <= 0) { throw new UnprocessableEntityHttpException('قیمت طلا در دسترس نیست'); } return (int) ceil($gold * $gramPrice); } private function deductFromWallet(Customer $customer, float $payGold, int $payRial, ?int $adminId): void { $walletOwnerType = $customer->resolveWalletOwnerType(); $wallet = Wallet::where('owner', $customer->id) ->where('owner_type', $walletOwnerType) ->whereNull('fund_id') ->whereNull('product_id') ->first(); if ($payGold > 0) { $available = $wallet ? (float) $wallet->gold : 0; if ($payGold > $available) { throw new UnprocessableEntityHttpException('موجودی طلای کیف پول کافی نیست'); } } if ($payRial > 0) { $mainWallet = app(MainWalletProductService::class); foreach (app(ConfigService::class)->mainWalletProductIds() as $productId) { $mainWallet->consolidateOrphanedForOwner($customer->id, $walletOwnerType, (int) $productId); } $availableCash = $mainWallet->effectiveMainWalletCash($customer->id, $walletOwnerType); if ($payRial > $availableCash) { throw new UnprocessableEntityHttpException('موجودی ریالی کیف پول کافی نیست'); } } $walletInterface = new WalletInterface($customer->id, $walletOwnerType); $note = 'بازپرداخت اعتبار طلایی'; if ($payGold > 0) { $withdraw = Withdraw::create([ 'user_id' => $adminId, 'customer_id' => $customer->id, 'gold' => -$payGold, 'note' => $note, 'sts' => 4, ]); new TransactionInterface($walletInterface, [ 'cash' => 0, 'gold' => -$payGold, 'expire_at' => now(), 'note' => $note, 'withdraw_id' => $withdraw->id, 'active' => true, ], 7, false, false, 0); } if ($payRial > 0) { $withdraw = Withdraw::create([ 'user_id' => $adminId, 'customer_id' => $customer->id, 'cash' => -$payRial, 'note' => $note, 'sts' => 4, ]); new TransactionInterface($walletInterface, [ 'cash' => -$payRial, 'gold' => 0, 'expire_at' => now(), 'note' => $note, 'withdraw_id' => $withdraw->id, 'active' => true, ], 4, false, false); } } }