feat(wallets): implement wallet and transaction management with associated models, policies, and resources
This commit is contained in:
81
app/Services/WalletService.php
Normal file
81
app/Services/WalletService.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user