Completed
Pull Request — master (#83)
by Бабичев
11:30
created

CommonService   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Test Coverage

Coverage 13.75%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 71
c 1
b 0
f 0
dl 0
loc 217
ccs 11
cts 80
cp 0.1375
rs 10
wmc 16

9 Methods

Rating   Name   Duplication   Size   Complexity  
A multiOperation() 0 17 3
A transfer() 0 5 1
A deposit() 0 20 1
A multiBrings() 0 9 2
A forceWithdraw() 0 20 1
A assemble() 0 6 1
A addBalance() 0 17 2
A verifyWithdraw() 0 11 4
A forceTransfer() 0 20 1
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\Wallet;
8
use Bavix\Wallet\Models\Transaction;
9
use Bavix\Wallet\Models\Transfer;
10
use Bavix\Wallet\Models\Wallet as WalletModel;
11
use Bavix\Wallet\Objects\Bring;
12
use Bavix\Wallet\Objects\Operation;
13
use Bavix\Wallet\Traits\HasWallet;
14
use Illuminate\Support\Facades\DB;
15
use function app;
16
use function compact;
17
18
class CommonService
19
{
20
21
    /**
22
     * @param Wallet $from
23
     * @param Wallet $to
24
     * @param int $amount
25
     * @param array|null $meta
26
     * @param string $status
27
     * @return Transfer
28
     */
29
    public function transfer(Wallet $from, Wallet $to, int $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer
30
    {
31
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) {
32
            $this->verifyWithdraw($from, $amount);
33
            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

33
            return $this->forceTransfer(/** @scrutinizer ignore-type */ $from, $to, $amount, $meta, $status);
Loading history...
34
        });
35
    }
36
37
    /**
38
     * @param Wallet $from
39
     * @param Wallet $to
40
     * @param int $amount
41
     * @param array|null $meta
42
     * @param string $status
43
     * @return Transfer
44
     */
45 3
    public function forceTransfer(Wallet $from, Wallet $to, int $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer
46
    {
47
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) {
48
            $fee = app(WalletService::class)->fee($to, $amount);
49
            $withdraw = $this->forceWithdraw($from, $amount + $fee, $meta);
50
            $deposit = $this->deposit($to, $amount, $meta);
51
52
            $from = app(WalletService::class)
53
                ->getWallet($from);
54
55
            $transfers = $this->multiBrings([
56
                (new Bring())
57
                    ->setStatus($status)
58
                    ->setDeposit($deposit)
59
                    ->setWithdraw($withdraw)
60
                    ->setFrom($from)
61
                    ->setTo($to)
62
            ]);
63
64
            return current($transfers);
65 3
        });
66
    }
67
68
    /**
69
     * @param Wallet $wallet
70
     * @param int $amount
71
     * @param array|null $meta
72
     * @param bool|null $confirmed
73
     * @return Transaction
74
     */
75 4
    public function forceWithdraw(Wallet $wallet, int $amount, ?array $meta, bool $confirmed = true): Transaction
76
    {
77
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) {
78
            $walletService = app(WalletService::class);
79
            $walletService->checkAmount($amount);
80
81
            /**
82
             * @var WalletModel $wallet
83
             */
84
            $wallet = $walletService->getWallet($wallet);
85
86
            $transactions = $this->multiOperation($wallet, [
87
                (new Operation())
88
                    ->setType(Transaction::TYPE_WITHDRAW)
89
                    ->setConfirmed($confirmed)
90
                    ->setAmount(-$amount)
91
                    ->setMeta($meta)
92
            ]);
93
94
            return current($transactions);
95 4
        });
96
    }
97
98
    /**
99
     * @param Wallet $wallet
100
     * @param int $amount
101
     * @param array|null $meta
102
     * @param bool $confirmed
103
     * @return Transaction
104
     */
105 39
    public function deposit(Wallet $wallet, int $amount, ?array $meta, bool $confirmed = true): Transaction
106
    {
107
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) {
108
            $walletService = app(WalletService::class);
109
            $walletService->checkAmount($amount);
110
111
            /**
112
             * @var WalletModel $wallet
113
             */
114
            $wallet = $walletService->getWallet($wallet);
115
116
            $transactions = $this->multiOperation($wallet, [
117
                (new Operation())
118
                    ->setType(Transaction::TYPE_DEPOSIT)
119
                    ->setConfirmed($confirmed)
120
                    ->setAmount($amount)
121
                    ->setMeta($meta)
122
            ]);
123
124
            return current($transactions);
125 39
        });
126
    }
127
128
    /**
129
     * @param Wallet $wallet
130
     * @param int $amount
131
     * @param bool $allowZero
132
     * @return void
133
     * @throws BalanceIsEmpty
134
     * @throws InsufficientFunds
135
     */
136 9
    public function verifyWithdraw(Wallet $wallet, int $amount, bool $allowZero = null): void
137
    {
138
        /**
139
         * @var HasWallet $wallet
140
         */
141 9
        if ($amount && !$wallet->balance) {
142 7
            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; 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

142
            throw new BalanceIsEmpty(/** @scrutinizer ignore-type */ trans('wallet::errors.wallet_empty'));
Loading history...
143
        }
144
145 2
        if (!$wallet->canWithdraw($amount, $allowZero)) {
146
            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; 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

146
            throw new InsufficientFunds(/** @scrutinizer ignore-type */ trans('wallet::errors.insufficient_funds'));
Loading history...
147
        }
148 2
    }
149
150
    /**
151
     * Create Operation without DB::transaction
152
     *
153
     * @param Wallet $self
154
     * @param array $operations
155
     * @return array
156
     */
157
    public function multiOperation(Wallet $self, array $operations): array
158
    {
159
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($self, $operations) {
160
            $amount = 0;
161
            $objects = [];
162
            foreach ($operations as $operation) {
163
                if ($operation->isConfirmed()) {
164
                    $amount += $operation->getAmount();
165
                }
166
167
                $objects[] = $operation
168
                    ->setWallet($self)
169
                    ->create();
170
            }
171
172
            $this->addBalance($self, $amount);
173
            return $objects;
174
        });
175
    }
176
177
    /**
178
     * Create Bring with DB::transaction
179
     *
180
     * @param Bring[] $brings
181
     * @return array
182
     * @throws
183
     */
184
    public function assemble(array $brings): array
185
    {
186
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
187
            $self = $this;
188
            return DB::transaction(static function () use ($self, $brings) {
189
                return $self->multiBrings($brings);
190
            });
191
        });
192
    }
193
194
    /**
195
     * Create Bring without DB::transaction
196
     *
197
     * @param array $brings
198
     * @return array
199
     */
200
    public function multiBrings(array $brings): array
201
    {
202
        return app(LockService::class)->lock($this, __FUNCTION__, function () use ($brings) {
203
            $objects = [];
204
            foreach ($brings as $bring) {
205
                $objects[] = $bring->create();
206
            }
207
208
            return $objects;
209
        });
210
    }
211
212
    /**
213
     * @param Wallet $wallet
214
     * @param int $amount
215
     * @return bool
216
     * @throws
217
     */
218
    public function addBalance(Wallet $wallet, int $amount): bool
219
    {
220
        return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($wallet, $amount) {
221
            /**
222
             * @var ProxyService $proxy
223
             * @var WalletModel $wallet
224
             */
225
            $proxy = app(ProxyService::class);
226
            $balance = $wallet->balance + $amount;
227
            if ($proxy->has($wallet->getKey())) {
0 ignored issues
show
Bug introduced by
It seems like $wallet->getKey() can also be of type boolean and null; however, parameter $key of Bavix\Wallet\Services\ProxyService::has() 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

227
            if ($proxy->has(/** @scrutinizer ignore-type */ $wallet->getKey())) {
Loading history...
228
                $balance = $proxy->get($wallet->getKey()) + $amount;
0 ignored issues
show
Bug introduced by
It seems like $wallet->getKey() can also be of type boolean and null; however, parameter $key of Bavix\Wallet\Services\ProxyService::get() 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

228
                $balance = $proxy->get(/** @scrutinizer ignore-type */ $wallet->getKey()) + $amount;
Loading history...
229
            }
230
231
            $result = $wallet->update(compact('balance'));
232
            $proxy->set($wallet->getKey(), $balance);
0 ignored issues
show
Bug introduced by
It seems like $wallet->getKey() can also be of type boolean and null; however, parameter $key of Bavix\Wallet\Services\ProxyService::set() 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

232
            $proxy->set(/** @scrutinizer ignore-type */ $wallet->getKey(), $balance);
Loading history...
233
234
            return $result;
235
        });
236
    }
237
238
}
239