CommonService::verifyWithdraw()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

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

37
            $this->verifyWithdraw($from, /** @scrutinizer ignore-type */ $math->add($newAmount, $fee));
Loading history...
38
39 22
            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 22
        });
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 46
    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 46
            $math = app(Mathable::class);
55 46
            $from = app(WalletService::class)->getWallet($from);
56 46
            $discount = app(WalletService::class)->discount($from, $to);
57 46
            $amount = max(0, $math->sub($amount, $discount));
58
59 46
            $fee = app(WalletService::class)->fee($to, $amount);
60 46
            $placesValue = app(WalletService::class)->decimalPlacesValue($from);
61 46
            $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 46
            $deposit = $this->deposit($to, $amount, $meta);
63
64 46
            $transfers = $this->multiBrings([
65 46
                app(Bring::class)
66 46
                    ->setStatus($status)
67 46
                    ->setDeposit($deposit)
68 46
                    ->setWithdraw($withdraw)
69 46
                    ->setDiscount($discount)
70 46
                    ->setFrom($from)
71 46
                    ->setTo($to),
72
            ]);
73
74 46
            return current($transfers);
75 46
        });
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 80
    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 80
            $walletService = app(WalletService::class);
89 80
            $walletService->checkAmount($amount);
90
91
            /**
92
             * @var WalletModel $wallet
93
             */
94 80
            $wallet = $walletService->getWallet($wallet);
95
96 80
            $transactions = $this->multiOperation($wallet, [
97 80
                app(Operation::class)
98 80
                    ->setType(Transaction::TYPE_WITHDRAW)
99 80
                    ->setConfirmed($confirmed)
100 80
                    ->setAmount(-$amount)
101 80
                    ->setMeta($meta),
102
            ]);
103
104 80
            return current($transactions);
105 80
        });
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 101
    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 101
            $walletService = app(WalletService::class);
119 101
            $walletService->checkAmount($amount);
120
121
            /**
122
             * @var WalletModel $wallet
123
             */
124 98
            $wallet = $walletService->getWallet($wallet);
125
126 98
            $transactions = $this->multiOperation($wallet, [
127 98
                app(Operation::class)
128 98
                    ->setType(Transaction::TYPE_DEPOSIT)
129 98
                    ->setConfirmed($confirmed)
130 98
                    ->setAmount($amount)
131 98
                    ->setMeta($meta),
132
            ]);
133
134 98
            return current($transactions);
135 101
        });
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 76
    public function verifyWithdraw(Wallet $wallet, $amount, bool $allowZero = null): void
147
    {
148
        /**
149
         * @var HasWallet $wallet
150
         */
151 76
        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 67
        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 65
    }
159
160
    /**
161
     * Create Operation without DB::transaction.
162
     *
163
     * @param Wallet $self
164
     * @param Operation[] $operations
165
     * @return array
166
     */
167 110
    public function multiOperation(Wallet $self, array $operations): array
168
    {
169
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($self, $operations) {
170 110
            $amount = 0;
171 110
            $objects = [];
172 110
            $math = app(Mathable::class);
173 110
            foreach ($operations as $operation) {
174 110
                if ($operation->isConfirmed()) {
175 95
                    $amount = $math->add($amount, $operation->getAmount());
176
                }
177
178 110
                $objects[] = $operation
179 110
                    ->setWallet($self)
180 110
                    ->create();
181
            }
182
183 110
            $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

183
            $this->addBalance($self, /** @scrutinizer ignore-type */ $amount);
Loading history...
184
185 110
            return $objects;
186 110
        });
187
    }
188
189
    /**
190
     * Create Bring with DB::transaction.
191
     *
192
     * @param Bring[] $brings
193
     * @return array
194
     * @throws
195
     */
196 8
    public function assemble(array $brings): array
197
    {
198
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
199 8
            $self = $this;
200
201
            return app(DbService::class)->transaction(static function () use ($self, $brings) {
202 8
                return $self->multiBrings($brings);
203 8
            });
204 8
        });
205
    }
206
207
    /**
208
     * Create Bring without DB::transaction.
209
     *
210
     * @param array $brings
211
     * @return array
212
     */
213 51
    public function multiBrings(array $brings): array
214
    {
215
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
216 51
            $objects = [];
217 51
            foreach ($brings as $bring) {
218 51
                $objects[] = $bring->create();
219
            }
220
221 51
            return $objects;
222 51
        });
223
    }
224
225
    /**
226
     * @param Wallet $wallet
227
     * @param int $amount
228
     * @return bool
229
     * @throws
230
     */
231 114
    public function addBalance(Wallet $wallet, $amount): bool
232
    {
233
        return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($wallet, $amount) {
234
            /**
235
             * @var WalletModel $wallet
236
             */
237 114
            $balance = app(Storable::class)
238 114
                ->incBalance($wallet, $amount);
239
240
            try {
241 114
                $result = $wallet->update(compact('balance'));
242 2
            } catch (\Throwable $throwable) {
243 2
                app(Storable::class)
244 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

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