82 lines
2.5 KiB
PHP
82 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Enums\WalletTransactionType;
|
|
use App\Models\User;
|
|
use App\Models\Wallet;
|
|
use App\Models\WalletTransaction;
|
|
use Illuminate\Support\Facades\DB;
|
|
use InvalidArgumentException;
|
|
use RuntimeException;
|
|
|
|
class WalletService
|
|
{
|
|
public function createForUser(User $user): Wallet
|
|
{
|
|
return Wallet::query()->firstOrCreate(
|
|
['user_id' => $user->id],
|
|
['balance' => 0, 'is_active' => true],
|
|
);
|
|
}
|
|
|
|
public function credit(
|
|
Wallet $wallet,
|
|
int $amount,
|
|
?string $description = null,
|
|
?User $performedBy = null,
|
|
): WalletTransaction {
|
|
return $this->apply($wallet, WalletTransactionType::Credit, $amount, $description, $performedBy);
|
|
}
|
|
|
|
public function debit(
|
|
Wallet $wallet,
|
|
int $amount,
|
|
?string $description = null,
|
|
?User $performedBy = null,
|
|
): WalletTransaction {
|
|
return $this->apply($wallet, WalletTransactionType::Debit, $amount, $description, $performedBy);
|
|
}
|
|
|
|
private function apply(
|
|
Wallet $wallet,
|
|
WalletTransactionType $type,
|
|
int $amount,
|
|
?string $description,
|
|
?User $performedBy,
|
|
): WalletTransaction {
|
|
if ($amount <= 0) {
|
|
throw new InvalidArgumentException('مبلغ باید بزرگتر از صفر باشد.');
|
|
}
|
|
|
|
return DB::transaction(function () use ($wallet, $type, $amount, $description, $performedBy): WalletTransaction {
|
|
$wallet = Wallet::query()->lockForUpdate()->findOrFail($wallet->id);
|
|
|
|
if (! $wallet->is_active) {
|
|
throw new RuntimeException('کیف پول غیرفعال است.');
|
|
}
|
|
|
|
$balanceBefore = $wallet->balance;
|
|
$balanceAfter = $type === WalletTransactionType::Credit
|
|
? $balanceBefore + $amount
|
|
: $balanceBefore - $amount;
|
|
|
|
if ($balanceAfter < 0) {
|
|
throw new RuntimeException('موجودی کیف پول کافی نیست.');
|
|
}
|
|
|
|
$wallet->update(['balance' => $balanceAfter]);
|
|
|
|
return WalletTransaction::query()->create([
|
|
'wallet_id' => $wallet->id,
|
|
'type' => $type,
|
|
'amount' => $amount,
|
|
'balance_before' => $balanceBefore,
|
|
'balance_after' => $balanceAfter,
|
|
'description' => $description,
|
|
'created_by' => $performedBy?->id,
|
|
]);
|
|
});
|
|
}
|
|
}
|