Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement allowed wallets feature. #12

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea
.phpunit.cache
.DS_Store
build
composer.lock
coverage
Expand Down
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ If the balance in `wallet_1` is 10 and the balance in `wallet_2` is 20, and you
### Deposit

```php
deposit(type: 'wallet_1', amount: 123.45, notes: null)
deposit(type: string, amount: float|int, notes: string null)
```

Deposit funds into `wallet_1`
Expand Down Expand Up @@ -143,13 +143,13 @@ When you need to add descriptions for a specific transaction, the `$notes` param

```php
$user = auth()->user();
$user->deposit('wallet_1', 67.89, 'You ordered pizza.');
$user->deposit('wallet_1', 67.89, 'You sold pizza.');
```

### Pay

```php
pay(amount: 12.34, notes: null)
pay(amount: int, allowedWallets: array [], notes: string null)
```

Pay the value using the total combined balance available across all allowed wallets
Expand All @@ -168,6 +168,28 @@ $user = auth()->user();
LaravelPayPocket::pay($user, 12.34);
```

By default the sytem will attempt to pay using all available wallets unless the `allowedWallets` param is provided.

#### Allowed Wallets ([#8][i8])

Sometimes you want to mark certain wallets as allowed so that when the `pay()` method is called, the system does not attempt to charge other wallets, a possible use case is an escrow system, the `$allowedWallets` param of the pay method allows you to do just that.

```php
$user = auth()->user();
$user->pay(12.34, ['wallet_1']);
```

When the `$allowedWallets` param is provided and is not an empty array, the system would attempt to charge only the wallets specified in the array.

#### Transaction Notes ([#8][i8])

In a case where you want to enter descriptions for a particular transaction, the `$note` param allows you to provide information about why a transaction happened.

```php
$user = auth()->user();
$user->pay(12.34, [], 'You ordered pizza.');
```

### Balance

- **Wallets**
Expand Down
4 changes: 2 additions & 2 deletions src/Interfaces/WalletOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function hasSufficientBalance(int|float $value): bool;
*
* @throws InsufficientBalanceException
*/
public function pay(int|float $orderValue, ?string $notes = null): void;
public function pay(int|float $orderValue, array $allowedWallets = [], ?string $notes = null): void;

/**
* Deposit an amount to the user's wallet of a specific type.
Expand All @@ -37,4 +37,4 @@ public function deposit(string $type, int|float $amount, ?string $notes = null):
* Get user's wallet balance.
*/
public function getWalletBalance(): int|float;
}
}
6 changes: 3 additions & 3 deletions src/Services/PocketServices.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public function deposit(WalletOperations $user, string $type, int|float $amount,
*
* @throws InsufficientBalanceException
*/
public function pay(WalletOperations $user, int|float $orderValue, ?string $notes = null): void
public function pay(WalletOperations $user, int|float $orderValue, array $allowedWallets = [], ?string $notes = null): void
{
$user->pay($orderValue, $notes);
$user->pay($orderValue, $allowedWallets, $notes);
}

/**
Expand All @@ -40,4 +40,4 @@ public function walletBalanceByType(WalletOperations $user, string $type): int|f
{
return $user->getWalletBalanceByType($type);
}
}
}
19 changes: 16 additions & 3 deletions src/Traits/HandlesPayment.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,35 @@ trait HandlesPayment
*
* @throws InsufficientBalanceException
*/
public function pay(int|float $orderValue, ?string $notes = null): void
public function pay(int|float $orderValue, array $allowedWallets = [], ?string $notes = null): void
{
if (! $this->hasSufficientBalance($orderValue)) {
throw new InsufficientBalanceException('Insufficient balance to cover the order.');
}

DB::transaction(function () use ($orderValue, $notes) {
DB::transaction(function () use ($orderValue, $notes, $allowedWallets) {
$remainingOrderValue = $orderValue;

/**
* @var \Illuminate\Support\Collection<TKey, \HPWebdeveloper\LaravelPayPocket\Models\Wallet>
*/
$walletsInOrder = $this->wallets()->whereIn('type', $this->walletsInOrder())->get();

/**
* @param string|\App\Enums\WalletEnums
* @return bool $useWallet
* */
$useWallet = function (string|\App\Enums\WalletEnums $wallet) use ($allowedWallets) {
return count($allowedWallets) < 1 ||
in_array($wallet, $allowedWallets) ||
in_array($wallet->value, $allowedWallets);
};

/**
* @var BalanceOperation $wallet
*/
foreach ($walletsInOrder as $wallet) {
if (! $wallet || ! $wallet->hasBalance()) {
if (! $wallet || ! $wallet->hasBalance() || !$useWallet($wallet->type)) {
continue;
}

Expand Down
3 changes: 0 additions & 3 deletions tests/ExceptionsWithoutFacadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,5 @@

$user = User::factory()->create();

$type = 'wallet_1';

$user->getWalletBalanceByType('wallet_2');

})->throws(WalletNotFoundException::class);
19 changes: 18 additions & 1 deletion tests/OperationsWithFacadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@

$description = \Illuminate\Support\Str::random();
LaravelPayPocket::deposit($user, $type, 234.56);
LaravelPayPocket::pay($user, 234.56, $description);
LaravelPayPocket::pay($user, 234.56, [$type], $description);

expect(WalletsLog::where('notes', $description)->exists())->toBe(true);
});
Expand All @@ -129,3 +129,20 @@

expect(WalletsLog::whereNotNull('reference')->exists())->toBe(true);
});

test('only the allowed wallets should be charged.', function () {

$user = User::factory()->create();

$type = 'wallet_1';

LaravelPayPocket::deposit($user, $type, 234.56);
LaravelPayPocket::pay($user, 234.56, [$type]);

$last = $user->wallets()
->where('type', \App\Enums\WalletEnums::WALLET1)
->first()
->logs()->latest()->first();

expect($last->value)->toBeFloat(234.56);
});
19 changes: 18 additions & 1 deletion tests/OperationsWithoutFacadeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@

$description = \Illuminate\Support\Str::random();
$user->deposit($type, 234.56);
$user->pay(234.56, $description);
$user->pay(234.56, [$type], $description);

expect(WalletsLog::where('notes', $description)->exists())->toBe(true);
});
Expand All @@ -130,3 +130,20 @@

expect(WalletsLog::whereNotNull('reference')->exists())->toBe(true);
});

test('only the allowed wallets should be charged.', function () {

$user = User::factory()->create();

$type = 'wallet_1';

$user->deposit($type, 234.56);
$user->pay(234.56, [$type]);

$last = $user->wallets()
->where('type', \App\Enums\WalletEnums::WALLET1)
->first()
->logs()->latest()->first();

expect($last->value)->toBeFloat(234.56);
});
12 changes: 6 additions & 6 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ public function getEnvironmentSetUp($app)
$migration->up();
*/

$migration = include __DIR__.'/database/migrations/create_users_tables.php';
$migration = include __DIR__ . '/database/migrations/create_users_tables.php';
$migration->up();

$migration = include __DIR__.'/../database/migrations/create_wallets_logs_table.php.stub';
$migration = include __DIR__ . '/../database/migrations/create_wallets_logs_table.php.stub';
$migration->up();

$migration = include __DIR__.'/../database/migrations/create_wallets_table.php.stub';
$migration = include __DIR__ . '/../database/migrations/create_wallets_table.php.stub';
$migration->up();

$migration = include __DIR__.'/../database/migrations/add_notes_and_reference_columns_to_wallets_logs_table.php.stub';
$migration = include __DIR__ . '/../database/migrations/add_notes_and_reference_columns_to_wallets_logs_table.php.stub';
$migration->up();
}

Expand All @@ -46,9 +46,9 @@ protected function setUp(): void
*/

Factory::guessFactoryNamesUsing(
fn (string $modelName) => 'HPWebdeveloper\\LaravelPayPocket\\Tests\\Database\\Factories\\'.class_basename(
fn (string $modelName) => 'HPWebdeveloper\\LaravelPayPocket\\Tests\\Database\\Factories\\' . class_basename(
$modelName
).'Factory'
) . 'Factory'
);
}

Expand Down