Passed
Pull Request — master (#159)
by Бабичев
05:32
created

CommonService::addBalance()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 1
nop 2
dl 0
loc 24
ccs 12
cts 12
cp 1
crap 3
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace Bavix\Wallet\Services;
4
5
use Bavix\Wallet\Exceptions\BalanceIsEmpty;
6
use Bavix\Wallet\Exceptions\InsufficientFunds;
7
use Bavix\Wallet\Interfaces\Mathable;
8
use Bavix\Wallet\Interfaces\Storable;
9
use Bavix\Wallet\Interfaces\Wallet;
10
use Bavix\Wallet\Models\Transaction;
11
use Bavix\Wallet\Models\Transfer;
12
use Bavix\Wallet\Models\Wallet as WalletModel;
13
use Bavix\Wallet\Objects\Bring;
14
use Bavix\Wallet\Objects\Operation;
15
use Bavix\Wallet\Traits\HasWallet;
16
use function app;
17
use function compact;
18
use function max;
19
20
class CommonService
21
{
22
23
    /**
24
     * @param Wallet $from
25
     * @param Wallet $to
26
     * @param int $amount
27
     * @param array|null $meta
28
     * @param string $status
29
     * @return Transfer
30
     */
31 21
    public function transfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer
32
    {
33
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) {
34 21
            $math = app(Mathable::class);
35 21
            $discount = app(WalletService::class)->discount($from, $to);
36 21
            $newAmount = max(0, $math->sub($amount, $discount));
37 21
            $fee = app(WalletService::class)->fee($to, $newAmount);
38 21
            $this->verifyWithdraw($from, $math->add($newAmount, $fee));
0 ignored issues
show
Bug introduced by
$math->add($newAmount, $fee) of type string is incompatible with the type integer expected by parameter $amount of Bavix\Wallet\Services\Co...rvice::verifyWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

38
            $this->verifyWithdraw($from, /** @scrutinizer ignore-type */ $math->add($newAmount, $fee));
Loading history...
39 21
            return $this->forceTransfer($from, $to, $amount, $meta, $status);
0 ignored issues
show
Bug introduced by
$from of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $from of Bavix\Wallet\Services\Co...ervice::forceTransfer(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

39
            return $this->forceTransfer(/** @scrutinizer ignore-type */ $from, $to, $amount, $meta, $status);
Loading history...
40 21
        });
41
    }
42
43
    /**
44
     * @param Wallet $from
45
     * @param Wallet $to
46
     * @param int $amount
47
     * @param array|null $meta
48
     * @param string $status
49
     * @return Transfer
50
     */
51 45
    public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer
52
    {
53
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) {
54 45
            $math = app(Mathable::class);
55 45
            $from = app(WalletService::class)->getWallet($from);
56 45
            $discount = app(WalletService::class)->discount($from, $to);
57 45
            $amount = max(0, $math->sub($amount, $discount));
58
59 45
            $fee = app(WalletService::class)->fee($to, $amount);
60 45
            $placesValue = app(WalletService::class)->decimalPlacesValue($from);
61 45
            $withdraw = $this->forceWithdraw($from, $math->add($amount, $fee, $placesValue), $meta);
0 ignored issues
show
Bug introduced by
$math->add($amount, $fee, $placesValue) of type string is incompatible with the type integer expected by parameter $amount of Bavix\Wallet\Services\Co...ervice::forceWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

61
            $withdraw = $this->forceWithdraw($from, /** @scrutinizer ignore-type */ $math->add($amount, $fee, $placesValue), $meta);
Loading history...
62 45
            $deposit = $this->deposit($to, $amount, $meta);
63
64 45
            $transfers = $this->multiBrings([
65 45
                app(Bring::class)
66 45
                    ->setStatus($status)
67 45
                    ->setDeposit($deposit)
68 45
                    ->setWithdraw($withdraw)
69 45
                    ->setDiscount($discount)
70 45
                    ->setFrom($from)
71 45
                    ->setTo($to)
72
            ]);
73
74 45
            return current($transfers);
75 45
        });
76
    }
77
78
    /**
79
     * @param Wallet $wallet
80
     * @param int $amount
81
     * @param array|null $meta
82
     * @param bool|null $confirmed
83
     * @return Transaction
84
     */
85 74
    public function forceWithdraw(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction
86
    {
87
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) {
88 74
            $walletService = app(WalletService::class);
89 74
            $walletService->checkAmount($amount);
90
91
            /**
92
             * @var WalletModel $wallet
93
             */
94 74
            $wallet = $walletService->getWallet($wallet);
95
96 74
            $transactions = $this->multiOperation($wallet, [
97 74
                app(Operation::class)
98 74
                    ->setType(Transaction::TYPE_WITHDRAW)
99 74
                    ->setConfirmed($confirmed)
100 74
                    ->setAmount(-$amount)
101 74
                    ->setMeta($meta)
102
            ]);
103
104 74
            return current($transactions);
105 74
        });
106
    }
107
108
    /**
109
     * @param Wallet $wallet
110
     * @param int $amount
111
     * @param array|null $meta
112
     * @param bool $confirmed
113
     * @return Transaction
114
     */
115 94
    public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction
116
    {
117
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) {
118 94
            $walletService = app(WalletService::class);
119 94
            $walletService->checkAmount($amount);
120
121
            /**
122
             * @var WalletModel $wallet
123
             */
124 91
            $wallet = $walletService->getWallet($wallet);
125
126 91
            $transactions = $this->multiOperation($wallet, [
127 91
                app(Operation::class)
128 91
                    ->setType(Transaction::TYPE_DEPOSIT)
129 91
                    ->setConfirmed($confirmed)
130 91
                    ->setAmount($amount)
131 91
                    ->setMeta($meta)
132
            ]);
133
134 91
            return current($transactions);
135 94
        });
136
    }
137
138
    /**
139
     * @param Wallet $wallet
140
     * @param int $amount
141
     * @param bool $allowZero
142
     * @return void
143
     * @throws BalanceIsEmpty
144
     * @throws InsufficientFunds
145
     */
146 74
    public function verifyWithdraw(Wallet $wallet, $amount, bool $allowZero = null): void
147
    {
148
        /**
149
         * @var HasWallet $wallet
150
         */
151 74
        if ($amount && !$wallet->balance) {
152 21
            throw new BalanceIsEmpty(trans('wallet::errors.wallet_empty'));
0 ignored issues
show
Bug introduced by
It seems like trans('wallet::errors.wallet_empty') can also be of type array and array; however, parameter $message of Bavix\Wallet\Exceptions\...eIsEmpty::__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 ignore-type  annotation

152
            throw new BalanceIsEmpty(/** @scrutinizer ignore-type */ trans('wallet::errors.wallet_empty'));
Loading history...
153
        }
154
155 65
        if (!$wallet->canWithdraw($amount, $allowZero)) {
156 3
            throw new InsufficientFunds(trans('wallet::errors.insufficient_funds'));
0 ignored issues
show
Bug introduced by
It seems like trans('wallet::errors.insufficient_funds') can also be of type array and array; however, parameter $message of Bavix\Wallet\Exceptions\...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 ignore-type  annotation

156
            throw new InsufficientFunds(/** @scrutinizer ignore-type */ trans('wallet::errors.insufficient_funds'));
Loading history...
157
        }
158 63
    }
159
160
    /**
161
     * Create Operation without DB::transaction
162
     *
163
     * @param Wallet $self
164
     * @param Operation[] $operations
165
     * @return array
166
     */
167 99
    public function multiOperation(Wallet $self, array $operations): array
168
    {
169
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($self, $operations) {
170 99
            $amount = 0;
171 99
            $objects = [];
172 99
            $math = app(Mathable::class);
173 99
            foreach ($operations as $operation) {
174 99
                if ($operation->isConfirmed()) {
175
                    try {
176 88
                        $amount = $math->add($amount, $operation->getAmount());
177
                    } catch (\ErrorException $errorException) {
178
                        var_dump($amount, $operation->getAmount());die;
0 ignored issues
show
Security Debugging Code introduced by
var_dump($amount, $operation->getAmount()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
179
                    }
180
                }
181
182 99
                $objects[] = $operation
183 99
                    ->setWallet($self)
184 99
                    ->create();
185
            }
186
187 99
            $this->addBalance($self, $amount);
0 ignored issues
show
Bug introduced by
It seems like $amount can also be of type string; however, parameter $amount of Bavix\Wallet\Services\CommonService::addBalance() does only seem to accept integer, 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 ignore-type  annotation

187
            $this->addBalance($self, /** @scrutinizer ignore-type */ $amount);
Loading history...
188 99
            return $objects;
189 99
        });
190
    }
191
192
    /**
193
     * Create Bring with DB::transaction
194
     *
195
     * @param Bring[] $brings
196
     * @return array
197
     * @throws
198
     */
199 8
    public function assemble(array $brings): array
200
    {
201
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
202 8
            $self = $this;
203
            return app(DbService::class)->transaction(static function () use ($self, $brings) {
204 8
                return $self->multiBrings($brings);
205 8
            });
206 8
        });
207
    }
208
209
    /**
210
     * Create Bring without DB::transaction
211
     *
212
     * @param array $brings
213
     * @return array
214
     */
215 50
    public function multiBrings(array $brings): array
216
    {
217
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
218 50
            $objects = [];
219 50
            foreach ($brings as $bring) {
220 50
                $objects[] = $bring->create();
221
            }
222
223 50
            return $objects;
224 50
        });
225
    }
226
227
    /**
228
     * @param Wallet $wallet
229
     * @param int $amount
230
     * @return bool
231
     * @throws
232
     */
233 103
    public function addBalance(Wallet $wallet, $amount): bool
234
    {
235
        return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($wallet, $amount) {
236
            /**
237
             * @var WalletModel $wallet
238
             */
239 103
            $balance = app(Storable::class)
240 103
                ->incBalance($wallet, $amount);
241
242
            try {
243 103
                $result = $wallet->update(compact('balance'));
244 2
            } catch (\Throwable $throwable) {
245 2
                app(Storable::class)
246 2
                    ->setBalance($wallet, $wallet->getAvailableBalance());
0 ignored issues
show
Bug introduced by
It seems like $wallet->getAvailableBalance() can also be of type double; however, parameter $amount of Bavix\Wallet\Interfaces\Storable::setBalance() does only seem to accept integer, 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 ignore-type  annotation

246
                    ->setBalance($wallet, /** @scrutinizer ignore-type */ $wallet->getAvailableBalance());
Loading history...
247
248 2
                throw $throwable;
249
            }
250
251 101
            if (!$result) {
252 2
                app(Storable::class)
253 2
                    ->setBalance($wallet, $wallet->getAvailableBalance());
254
            }
255
256 101
            return $result;
257 103
        });
258
    }
259
260
}
261