feat: add withdraw request functionality and UI components

This commit is contained in:
2026-08-27 02:39:57 +03:30
parent aeef95339d
commit 4ce663c669
115 changed files with 4064 additions and 315 deletions
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace App\Services\Wallet;
use App\Enums\InvoicePaymentMethodEnum;
use App\Enums\InvoiceStatusEnum;
use App\Enums\WalletTransactionReasonEnum;
use App\Enums\WalletTransactionTypeEnum;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\User;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Models\WithdrawRequest;
use App\Services\Invoice\InvoiceService;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class WalletService
{
protected InvoiceService $invoiceService;
public function __construct(InvoiceService $invoiceService)
{
$this->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();
}
}