1 | <?php |
||||
2 | |||||
3 | namespace Moecasts\Laravel\Wallet\Models; |
||||
4 | |||||
5 | use Illuminate\Database\Eloquent\Model; |
||||
6 | use Illuminate\Database\Eloquent\Relations\HasMany; |
||||
7 | use Illuminate\Database\Eloquent\Relations\MorphMany; |
||||
8 | use Illuminate\Database\Eloquent\Relations\MorphTo; |
||||
9 | use Illuminate\Support\Facades\DB; |
||||
10 | use Moecasts\Laravel\Wallet\Exceptions\AmountInvalid; |
||||
11 | use Moecasts\Laravel\Wallet\Exceptions\ExchangeInvalid; |
||||
12 | use Moecasts\Laravel\Wallet\Exceptions\InsufficientFunds; |
||||
13 | use Moecasts\Laravel\Wallet\Interfaces\Assemblable; |
||||
14 | use Moecasts\Laravel\Wallet\Interfaces\Exchangeable; |
||||
15 | use Moecasts\Laravel\Wallet\Interfaces\Transferable; |
||||
16 | use Moecasts\Laravel\Wallet\Models\Transaction; |
||||
17 | use Moecasts\Laravel\Wallet\Tax; |
||||
18 | use Moecasts\Laravel\Wallet\Traits\CanPay; |
||||
19 | use Moecasts\Laravel\Wallet\WalletProxy; |
||||
20 | use Ramsey\Uuid\Uuid; |
||||
21 | |||||
22 | class Wallet extends Model |
||||
23 | { |
||||
24 | use CanPay; |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
25 | |||||
26 | protected $fillable = [ |
||||
27 | 'holder_type', |
||||
28 | 'holder_id', |
||||
29 | 'currency', |
||||
30 | 'balance' |
||||
31 | ]; |
||||
32 | |||||
33 | 24 | public function holder(): MorphTo |
|||
34 | { |
||||
35 | 24 | return $this->morphTo(); |
|||
36 | } |
||||
37 | |||||
38 | 23 | public function transactions(): HasMany |
|||
39 | { |
||||
40 | 23 | return $this->hasMany(Transaction::class); |
|||
41 | } |
||||
42 | |||||
43 | 4 | public function holderTransfers(): MorphMany |
|||
44 | { |
||||
45 | 4 | return $this->holder->transfers(); |
|||
46 | } |
||||
47 | |||||
48 | 3 | public function transfers(): HasMany |
|||
49 | { |
||||
50 | 3 | return $this->hasMany(Transfer::class, 'from_wallet_id'); |
|||
51 | } |
||||
52 | |||||
53 | 22 | public function deposit(float $amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
54 | { |
||||
55 | 22 | $this->checkAmount($amount); |
|||
0 ignored issues
–
show
$amount of type double is incompatible with the type integer expected by parameter $amount of Moecasts\Laravel\Wallet\...s\Wallet::checkAmount() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
56 | |||||
57 | 22 | $amount = (int) ($amount * $this->coefficient($this->currency)); |
|||
58 | |||||
59 | 22 | return $this->change(Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed); |
|||
60 | } |
||||
61 | |||||
62 | 23 | private function checkAmount(int $amount): void |
|||
63 | { |
||||
64 | 23 | if ($amount < 0) { |
|||
65 | 1 | throw new AmountInvalid(trans('wallet::errors.price_positive')); |
|||
0 ignored issues
–
show
It seems like
trans('wallet::errors.price_positive') can also be of type array ; however, parameter $message of Moecasts\Laravel\Wallet\...tInvalid::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
66 | } |
||||
67 | 23 | } |
|||
68 | |||||
69 | 23 | protected function change(string $type, int $amount, ?array $meta, bool $confirmed): Transaction |
|||
70 | { |
||||
71 | return DB::transaction(function () use ($type, $amount, $meta, $confirmed) { |
||||
72 | 23 | if ($confirmed) { |
|||
73 | 23 | $this->addBalance($amount); |
|||
74 | } |
||||
75 | |||||
76 | 23 | return $this->transactions()->create([ |
|||
77 | 23 | 'type' => $type, |
|||
78 | 23 | 'holder_type' => $this->holder->getMorphClass(), |
|||
79 | 23 | 'holder_id' => $this->holder->getKey(), |
|||
80 | 23 | 'wallet_id' => $this->getKey(), |
|||
81 | 23 | 'uuid' => Uuid::uuid4()->toString(), |
|||
82 | 23 | 'confirmed' => $confirmed, |
|||
83 | 23 | 'amount' => $amount, |
|||
84 | 23 | 'meta' => $meta, |
|||
85 | ]); |
||||
86 | 23 | }); |
|||
87 | } |
||||
88 | |||||
89 | 23 | protected function addBalance(float $amount): bool |
|||
90 | { |
||||
91 | 23 | $newBalance = $this->attributes['balance'] + $amount; |
|||
92 | 23 | $this->balance = $newBalance; |
|||
93 | 23 | $finalBalance = $newBalance / $this->coefficient($this->attributes['currency']); |
|||
94 | |||||
95 | return |
||||
96 | // update database wallet |
||||
97 | 23 | $this->save() && |
|||
98 | |||||
99 | // update static wallet |
||||
100 | 23 | WalletProxy::set($this->getKey(), $finalBalance); |
|||
101 | } |
||||
102 | |||||
103 | 24 | public function getBalanceAttribute(): float |
|||
104 | { |
||||
105 | 24 | $this->exists or $this->save(); |
|||
106 | |||||
107 | 24 | if (! WalletProxy::has($this->getKey())) { |
|||
108 | 5 | $balance = $this->attributes['balance'] / $this->coefficient($this->attributes['currency']); |
|||
109 | 5 | WalletProxy::set($this->getKey(), (float) ($balance ?? 0)); |
|||
110 | } |
||||
111 | |||||
112 | 24 | return WalletProxy::get($this->getKey()); |
|||
113 | } |
||||
114 | |||||
115 | 1 | public function safeTransfer(Transferable $transferable, float $amount, ?array $meta = null, string $action = Transfer::ACTION_TRANSFER): ?Transfer |
|||
116 | { |
||||
117 | try { |
||||
118 | 1 | return $this->transfer($transferable, $amount, $meta, $action); |
|||
119 | 1 | } catch (\Throwable $throwable) { |
|||
120 | 1 | return null; |
|||
121 | } |
||||
122 | } |
||||
123 | |||||
124 | 9 | public function transfer(Transferable $transferable, float $amount, ?array $meta = null, string $action = Transfer::ACTION_TRANSFER): Transfer |
|||
125 | { |
||||
126 | 9 | $wallet = $transferable->getReceiptWallet($this->currency); |
|||
127 | |||||
128 | return DB::transaction(function () use ($transferable, $amount, $wallet, $meta, $action) { |
||||
129 | 9 | $fee = Tax::fee($transferable, $wallet, $amount); |
|||
130 | 9 | $withdraw = $this->withdraw($amount + $fee, $meta); |
|||
131 | 7 | $deposit = $wallet->deposit($amount, $meta); |
|||
132 | 7 | return $this->assemble($transferable, $wallet, $withdraw, $deposit, $action); |
|||
133 | 9 | }); |
|||
134 | } |
||||
135 | |||||
136 | 12 | public function withdraw(float $amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
137 | { |
||||
138 | 12 | if (! $this->canWithdraw($amount)) { |
|||
139 | 5 | throw new InsufficientFunds(trans('wallet::errors.insufficient_funds')); |
|||
0 ignored issues
–
show
It seems like
trans('wallet::errors.insufficient_funds') can also be of type array ; however, parameter $message of Moecasts\Laravel\Wallet\...entFunds::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
140 | } |
||||
141 | |||||
142 | 10 | return $this->forceWithdraw($amount, $meta, $confirmed); |
|||
143 | } |
||||
144 | |||||
145 | 12 | public function canWithdraw($amount): bool |
|||
146 | { |
||||
147 | 12 | return $this->balance >= $amount; |
|||
148 | } |
||||
149 | |||||
150 | 14 | public function forceWithdraw(float $amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
151 | { |
||||
152 | 14 | $amount = (int) ($amount * $this->coefficient($this->currency)); |
|||
153 | |||||
154 | 14 | $this->checkAmount($amount); |
|||
155 | |||||
156 | 14 | return $this->change(Transaction::TYPE_WITHDRAW, -$amount, $meta, $confirmed); |
|||
157 | } |
||||
158 | |||||
159 | 11 | protected function assemble(Assemblable $transferable, Wallet $wallet, Transaction $withdraw, Transaction $deposit, string $action = Transfer::ACTION_PAID): Transfer |
|||
160 | { |
||||
161 | 11 | return \app('moecasts.wallet::transfer')->create([ |
|||
0 ignored issues
–
show
The method
create() does not exist on Illuminate\Contracts\Foundation\Application .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
162 | 11 | 'action' => $action, |
|||
163 | 11 | 'deposit_id' => $deposit->getKey(), |
|||
164 | 11 | 'withdraw_id' => $withdraw->getKey(), |
|||
165 | 11 | 'from_type' => $this->holder->getMorphClass(), |
|||
166 | 11 | 'from_id' => $this->holder->getKey(), |
|||
167 | 11 | 'from_wallet_id' => $this->getKey(), |
|||
168 | 11 | 'to_type' => $transferable->getMorphClass(), |
|||
0 ignored issues
–
show
The method
getMorphClass() does not exist on Moecasts\Laravel\Wallet\Interfaces\Assemblable . It seems like you code against a sub-type of said class. However, the method does not exist in Moecasts\Laravel\Wallet\Interfaces\Transferable or Moecasts\Laravel\Wallet\Interfaces\Exchangeable or Moecasts\Laravel\Wallet\Interfaces\Product . Are you sure you never get one of those?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
169 | 11 | 'to_id' => $transferable->getKey(), |
|||
0 ignored issues
–
show
The method
getKey() does not exist on Moecasts\Laravel\Wallet\Interfaces\Assemblable . It seems like you code against a sub-type of said class. However, the method does not exist in Moecasts\Laravel\Wallet\Interfaces\Transferable or Moecasts\Laravel\Wallet\Interfaces\Exchangeable or Moecasts\Laravel\Wallet\Interfaces\Product . Are you sure you never get one of those?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
170 | 11 | 'to_wallet_id' => $wallet->getKey(), |
|||
171 | 11 | 'fee' => \abs($withdraw->amount) - \abs($deposit->amount), |
|||
172 | 11 | 'uuid' => Uuid::uuid4()->toString(), |
|||
173 | ]); |
||||
174 | } |
||||
175 | |||||
176 | 2 | public function forceTransfer(Transferable $transferable, float $amount, ?array $meta = null, string $action = Transfer::ACTION_TRANSFER): Transfer |
|||
177 | { |
||||
178 | 2 | $wallet = $transferable->getReceiptWallet($this->currency); |
|||
179 | |||||
180 | return DB::transaction(function () use ($transferable, $amount, $wallet, $meta, $action) { |
||||
181 | 2 | $fee = Tax::fee($transferable, $wallet, $amount); |
|||
182 | 2 | $withdraw = $this->forceWithdraw($amount + $fee, $meta); |
|||
183 | 2 | $deposit = $wallet->deposit($amount, $meta); |
|||
184 | 2 | return $this->assemble($transferable, $wallet, $withdraw, $deposit, $action); |
|||
185 | 2 | }); |
|||
186 | } |
||||
187 | |||||
188 | 4 | public function exchange(string $currency, float $amount, ?array $meta = null, $action = Transfer::ACTION_EXCHANGE): Transfer |
|||
189 | { |
||||
190 | 4 | $wallet = $this->holder->getWallet($currency); |
|||
191 | |||||
192 | 4 | $exchangeRate = config('wallet.exchange.' . $this->currency . '.' . $currency); |
|||
193 | |||||
194 | 4 | if (! $exchangeRate || |
|||
195 | 4 | ! $this->holder instanceof Exchangeable) { |
|||
196 | 3 | throw new ExchangeInvalid(trans('wallet::errors.exchange_unsupported')); |
|||
0 ignored issues
–
show
It seems like
trans('wallet::errors.exchange_unsupported') can also be of type array ; however, parameter $message of Moecasts\Laravel\Wallet\...eInvalid::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
197 | } |
||||
198 | |||||
199 | 1 | $exchangedAmount = $amount * $exchangeRate; |
|||
200 | |||||
201 | return DB::transaction(function () use ($wallet, $amount, $exchangedAmount, $meta, $action) { |
||||
202 | 1 | $fee = Tax::fee($this->holder, $wallet, $amount); |
|||
203 | 1 | $withdraw = $this->withdraw($amount + $fee, $meta); |
|||
204 | 1 | $deposit = $wallet->deposit($exchangedAmount, $meta); |
|||
205 | 1 | return $this->assemble($this->holder, $wallet, $withdraw, $deposit, $action); |
|||
206 | 1 | }); |
|||
207 | } |
||||
208 | |||||
209 | 1 | public function safeExchange(string $currency, float $amount, ?array $meta = null, $action = Transfer::ACTION_EXCHANGE): ?Transfer |
|||
210 | { |
||||
211 | try { |
||||
212 | 1 | return $this->exchange($currency, $amount, $meta, $action); |
|||
213 | 1 | } catch (\Throwable $throwable) { |
|||
214 | 1 | return null; |
|||
215 | } |
||||
216 | } |
||||
217 | |||||
218 | 3 | public function forceExchange(string $currency, float $amount, ?array $meta = null, $action = Transfer::ACTION_EXCHANGE): Transfer |
|||
219 | { |
||||
220 | 3 | $wallet = $this->holder->getWallet($currency); |
|||
221 | |||||
222 | 3 | $exchangeRate = config('wallet.exchange.' . $this->currency . '.' . $currency); |
|||
223 | |||||
224 | 3 | if (! $exchangeRate || |
|||
225 | 3 | ! $this->holder instanceof Exchangeable) { |
|||
226 | 2 | throw new ExchangeInvalid(trans('wallet::errors.exchange_unsupported')); |
|||
0 ignored issues
–
show
It seems like
trans('wallet::errors.exchange_unsupported') can also be of type array ; however, parameter $message of Moecasts\Laravel\Wallet\...eInvalid::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
227 | } |
||||
228 | |||||
229 | 1 | $exchangedAmount = $amount * $exchangeRate; |
|||
230 | |||||
231 | return DB::transaction(function () use ($wallet, $amount, $exchangedAmount, $meta, $action) { |
||||
232 | 1 | $fee = Tax::fee($this->holder, $wallet, $amount); |
|||
233 | 1 | $withdraw = $this->forceWithdraw($amount + $fee, $meta); |
|||
234 | 1 | $deposit = $wallet->deposit($exchangedAmount, $meta); |
|||
235 | 1 | return $this->assemble($this->holder, $wallet, $withdraw, $deposit, $action); |
|||
236 | 1 | }); |
|||
237 | } |
||||
238 | |||||
239 | 3 | public function refreshBalance(): bool |
|||
240 | { |
||||
241 | 3 | $balance = $this->getAvailableBalance(); |
|||
242 | |||||
243 | 3 | $this->attributes['balance'] = $balance; |
|||
244 | |||||
245 | 3 | WalletProxy::set($this->getKey(), $balance); |
|||
246 | |||||
247 | 3 | return $this->save(); |
|||
248 | } |
||||
249 | |||||
250 | 3 | public function getAvailableBalance(): int |
|||
251 | { |
||||
252 | 3 | return $this->transactions() |
|||
253 | 3 | ->where('wallet_id', $this->getKey()) |
|||
254 | 3 | ->where('confirmed', true) |
|||
255 | 3 | ->sum('amount'); |
|||
256 | } |
||||
257 | |||||
258 | 24 | public function coefficient(string $currency = ''): float |
|||
259 | { |
||||
260 | 24 | return config('wallet.coefficient.' . $currency , 100.); |
|||
261 | } |
||||
262 | } |
||||
263 |