| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace Bavix\Wallet\Traits; |
||||
| 4 | |||||
| 5 | use function app; |
||||
| 6 | use Bavix\Wallet\Interfaces\Mathable; |
||||
| 7 | use Bavix\Wallet\Interfaces\Storable; |
||||
| 8 | use Bavix\Wallet\Interfaces\Wallet; |
||||
| 9 | use Bavix\Wallet\Models\Transaction; |
||||
| 10 | use Bavix\Wallet\Models\Transfer; |
||||
| 11 | use Bavix\Wallet\Models\Wallet as WalletModel; |
||||
| 12 | use Bavix\Wallet\Services\CommonService; |
||||
| 13 | use Bavix\Wallet\Services\DbService; |
||||
| 14 | use Bavix\Wallet\Services\WalletService; |
||||
| 15 | use function config; |
||||
| 16 | use Illuminate\Database\Eloquent\Relations\MorphMany; |
||||
| 17 | use Illuminate\Support\Collection; |
||||
| 18 | use Throwable; |
||||
| 19 | |||||
| 20 | /** |
||||
| 21 | * Trait HasWallet. |
||||
| 22 | * |
||||
| 23 | * |
||||
| 24 | * @property-read Collection|WalletModel[] $wallets |
||||
| 25 | * @property-read int $balance |
||||
| 26 | */ |
||||
| 27 | trait HasWallet |
||||
| 28 | { |
||||
| 29 | use MorphOneWallet; |
||||
| 30 | |||||
| 31 | /** |
||||
| 32 | * The input means in the system. |
||||
| 33 | * |
||||
| 34 | * @param int $amount |
||||
| 35 | * @param array|null $meta |
||||
| 36 | * @param bool $confirmed |
||||
| 37 | * |
||||
| 38 | * @return Transaction |
||||
| 39 | * @throws |
||||
| 40 | */ |
||||
| 41 | 92 | public function deposit($amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
| 42 | { |
||||
| 43 | 92 | $self = $this; |
|||
| 44 | |||||
| 45 | return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { |
||||
| 46 | 92 | return app(CommonService::class) |
|||
| 47 | 92 | ->deposit($self, $amount, $meta, $confirmed); |
|||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 48 | 92 | }); |
|||
| 49 | } |
||||
| 50 | |||||
| 51 | /** |
||||
| 52 | * Magic laravel framework method, makes it |
||||
| 53 | * possible to call property balance. |
||||
| 54 | * |
||||
| 55 | * Example: |
||||
| 56 | * $user1 = User::first()->load('wallet'); |
||||
| 57 | * $user2 = User::first()->load('wallet'); |
||||
| 58 | * |
||||
| 59 | * Without static: |
||||
| 60 | * var_dump($user1->balance, $user2->balance); // 100 100 |
||||
| 61 | * $user1->deposit(100); |
||||
| 62 | * $user2->deposit(100); |
||||
| 63 | * var_dump($user1->balance, $user2->balance); // 200 200 |
||||
| 64 | * |
||||
| 65 | * With static: |
||||
| 66 | * var_dump($user1->balance, $user2->balance); // 100 100 |
||||
| 67 | * $user1->deposit(100); |
||||
| 68 | * var_dump($user1->balance); // 200 |
||||
| 69 | * $user2->deposit(100); |
||||
| 70 | * var_dump($user2->balance); // 300 |
||||
| 71 | * |
||||
| 72 | * @return int|float |
||||
| 73 | * @throws |
||||
| 74 | */ |
||||
| 75 | 118 | public function getBalanceAttribute() |
|||
| 76 | { |
||||
| 77 | 118 | return app(Storable::class)->getBalance($this); |
|||
| 78 | } |
||||
| 79 | |||||
| 80 | /** |
||||
| 81 | * all user actions on wallets will be in this method. |
||||
| 82 | * |
||||
| 83 | * @return MorphMany |
||||
| 84 | */ |
||||
| 85 | 110 | public function transactions(): MorphMany |
|||
| 86 | { |
||||
| 87 | 110 | return ($this instanceof WalletModel ? $this->holder : $this) |
|||
| 88 | 110 | ->morphMany(config('wallet.transaction.model', Transaction::class), 'payable'); |
|||
|
0 ignored issues
–
show
It seems like
morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 89 | } |
||||
| 90 | |||||
| 91 | /** |
||||
| 92 | * This method ignores errors that occur when transferring funds. |
||||
| 93 | * |
||||
| 94 | * @param Wallet $wallet |
||||
| 95 | * @param int $amount |
||||
| 96 | * @param array|null $meta |
||||
| 97 | * @return null|Transfer |
||||
| 98 | */ |
||||
| 99 | 3 | public function safeTransfer(Wallet $wallet, $amount, ?array $meta = null): ?Transfer |
|||
| 100 | { |
||||
| 101 | try { |
||||
| 102 | 3 | return $this->transfer($wallet, $amount, $meta); |
|||
| 103 | 3 | } catch (Throwable $throwable) { |
|||
| 104 | 3 | return null; |
|||
| 105 | } |
||||
| 106 | } |
||||
| 107 | |||||
| 108 | /** |
||||
| 109 | * A method that transfers funds from host to host. |
||||
| 110 | * |
||||
| 111 | * @param Wallet $wallet |
||||
| 112 | * @param int $amount |
||||
| 113 | * @param array|null $meta |
||||
| 114 | * @return Transfer |
||||
| 115 | * @throws |
||||
| 116 | */ |
||||
| 117 | 8 | public function transfer(Wallet $wallet, $amount, ?array $meta = null): Transfer |
|||
| 118 | { |
||||
| 119 | 8 | app(CommonService::class)->verifyWithdraw($this, $amount); |
|||
|
0 ignored issues
–
show
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet 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
Loading history...
|
|||||
| 120 | |||||
| 121 | 8 | return $this->forceTransfer($wallet, $amount, $meta); |
|||
| 122 | } |
||||
| 123 | |||||
| 124 | /** |
||||
| 125 | * Withdrawals from the system. |
||||
| 126 | * |
||||
| 127 | * @param int $amount |
||||
| 128 | * @param array|null $meta |
||||
| 129 | * @param bool $confirmed |
||||
| 130 | * |
||||
| 131 | * @return Transaction |
||||
| 132 | */ |
||||
| 133 | 39 | public function withdraw($amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
| 134 | { |
||||
| 135 | 39 | app(CommonService::class)->verifyWithdraw($this, $amount); |
|||
|
0 ignored issues
–
show
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet 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
Loading history...
|
|||||
| 136 | |||||
| 137 | 32 | return $this->forceWithdraw($amount, $meta, $confirmed); |
|||
| 138 | } |
||||
| 139 | |||||
| 140 | /** |
||||
| 141 | * Checks if you can withdraw funds. |
||||
| 142 | * |
||||
| 143 | * @param int $amount |
||||
| 144 | * @param bool $allowZero |
||||
| 145 | * @return bool |
||||
| 146 | */ |
||||
| 147 | 69 | public function canWithdraw($amount, bool $allowZero = null): bool |
|||
| 148 | { |
||||
| 149 | 69 | $math = app(Mathable::class); |
|||
| 150 | |||||
| 151 | /** |
||||
| 152 | * Allow to buy for free with a negative balance. |
||||
| 153 | */ |
||||
| 154 | 69 | if ($allowZero && ! $math->compare($amount, 0)) { |
|||
| 155 | 11 | return true; |
|||
| 156 | } |
||||
| 157 | |||||
| 158 | 66 | return $math->compare($this->balance, $amount) >= 0; |
|||
| 159 | } |
||||
| 160 | |||||
| 161 | /** |
||||
| 162 | * Forced to withdraw funds from system. |
||||
| 163 | * |
||||
| 164 | * @param int $amount |
||||
| 165 | * @param array|null $meta |
||||
| 166 | * @param bool $confirmed |
||||
| 167 | * |
||||
| 168 | * @return Transaction |
||||
| 169 | * @throws |
||||
| 170 | */ |
||||
| 171 | 47 | public function forceWithdraw($amount, ?array $meta = null, bool $confirmed = true): Transaction |
|||
| 172 | { |
||||
| 173 | 47 | $self = $this; |
|||
| 174 | |||||
| 175 | return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { |
||||
| 176 | 47 | return app(CommonService::class) |
|||
| 177 | 47 | ->forceWithdraw($self, $amount, $meta, $confirmed); |
|||
|
0 ignored issues
–
show
$self of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet 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
Loading history...
|
|||||
| 178 | 47 | }); |
|||
| 179 | } |
||||
| 180 | |||||
| 181 | /** |
||||
| 182 | * the forced transfer is needed when the user does not have the money and we drive it. |
||||
| 183 | * Sometimes you do. Depends on business logic. |
||||
| 184 | * |
||||
| 185 | * @param Wallet $wallet |
||||
| 186 | * @param int $amount |
||||
| 187 | * @param array|null $meta |
||||
| 188 | * @return Transfer |
||||
| 189 | * @throws |
||||
| 190 | */ |
||||
| 191 | 8 | public function forceTransfer(Wallet $wallet, $amount, ?array $meta = null): Transfer |
|||
| 192 | { |
||||
| 193 | 8 | $self = $this; |
|||
| 194 | |||||
| 195 | return app(DbService::class)->transaction(static function () use ($self, $amount, $wallet, $meta) { |
||||
| 196 | 8 | return app(CommonService::class) |
|||
| 197 | 8 | ->forceTransfer($self, $wallet, $amount, $meta); |
|||
|
0 ignored issues
–
show
$self 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
Loading history...
|
|||||
| 198 | 8 | }); |
|||
| 199 | } |
||||
| 200 | |||||
| 201 | /** |
||||
| 202 | * the transfer table is used to confirm the payment |
||||
| 203 | * this method receives all transfers. |
||||
| 204 | * |
||||
| 205 | * @return MorphMany |
||||
| 206 | */ |
||||
| 207 | 39 | public function transfers(): MorphMany |
|||
| 208 | { |
||||
| 209 | 39 | return app(WalletService::class) |
|||
| 210 | 39 | ->getWallet($this, false) |
|||
|
0 ignored issues
–
show
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $object of Bavix\Wallet\Services\WalletService::getWallet().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 211 | 39 | ->morphMany(config('wallet.transfer.model', Transfer::class), 'from'); |
|||
| 212 | } |
||||
| 213 | } |
||||
| 214 |