114 lines
4.2 KiB
PHP
114 lines
4.2 KiB
PHP
<?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();
|
|
}
|
|
}
|