invoiceService = $invoiceService; } public function forUser(User $user): Wallet { return Wallet::firstOrCreate(['user_id' => $user->id]); } public function system(): Wallet { return Wallet::firstOrCreate(['user_id' => null]); } public function deposit(User $user, int $amount, WalletTransactionReasonEnum $reason, ?Payment $payment = null): WalletTransaction { return $this->depositToWallet($this->forUser($user), $amount, $reason, $payment); } public function depositToWallet(Wallet $wallet, int $amount, WalletTransactionReasonEnum $reason, ?Payment $payment = null, ?WithdrawRequest $withdrawRequest = null): WalletTransaction { return DB::transaction(function () use ($wallet, $amount, $reason, $payment, $withdrawRequest) { $wallet = $this->lockedWallet($wallet); $wallet->update(['balance' => $wallet->balance + $amount]); return $wallet->transactions()->create([ 'type' => WalletTransactionTypeEnum::Deposit->value, 'reason' => $reason->value, 'amount' => $amount, 'balance_after' => $wallet->balance, 'payment_id' => $payment?->id, 'withdraw_request_id' => $withdrawRequest?->id, ]); }); } public function withdraw(User $user, int $amount, WalletTransactionReasonEnum $reason, ?Invoice $invoice = null, ?WithdrawRequest $withdrawRequest = null): WalletTransaction { return DB::transaction(function () use ($user, $amount, $reason, $invoice, $withdrawRequest) { $wallet = $this->lockedWallet($this->forUser($user)); if ($wallet->balance < $amount) { throw ValidationException::withMessages(['amount' => 'موجودی کیف پول کافی نیست']); } $wallet->update(['balance' => $wallet->balance - $amount]); return $wallet->transactions()->create([ 'type' => WalletTransactionTypeEnum::Withdrawal->value, 'reason' => $reason->value, 'amount' => $amount, 'balance_after' => $wallet->balance, 'invoice_id' => $invoice?->id, 'withdraw_request_id' => $withdrawRequest?->id, ]); }); } public function payInvoice(User $user, Invoice $invoice): Invoice { return DB::transaction(function () use ($user, $invoice) { if ($invoice->status !== InvoiceStatusEnum::Pending) { throw ValidationException::withMessages(['invoice' => 'این فاکتور قابل پرداخت نیست']); } $this->withdraw($user, $invoice->amount, WalletTransactionReasonEnum::InvoicePayment, $invoice); return $this->invoiceService->markPaid($invoice, InvoicePaymentMethodEnum::Wallet); }); } public function recordGatewayInvoicePayment(Invoice $invoice, Payment $payment): WalletTransaction { $wallet = $this->forUser($invoice->user); return $wallet->transactions()->create([ 'type' => WalletTransactionTypeEnum::Withdrawal->value, 'reason' => WalletTransactionReasonEnum::InvoicePayment->value, 'amount' => $invoice->amount, 'balance_after' => $wallet->balance, 'invoice_id' => $invoice->id, 'payment_id' => $payment->id, ]); } protected function lockedWallet(Wallet $wallet): Wallet { return Wallet::whereKey($wallet->id)->lockForUpdate()->first(); } }