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

Adds support for restricted wallets and description for logs and improve overall type annotations. #9

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 117 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,64 @@
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/hpwebdeveloper/laravel-pay-pocket/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/hpwebdeveloper/laravel-pay-pocket/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
[![Imports](https://github.com/HPWebdeveloper/laravel-pay-pocket/actions/workflows/check_imports.yml/badge.svg?branch=main)](https://github.com/HPWebdeveloper/laravel-pay-pocket/actions/workflows/check_imports.yml)


**Laravel Pay Pocket** is a package designed for Laravel applications, offering the flexibility to manage multiple wallet types within two dedicated database tables, `wallets` and `wallets_logs`.

**Demo** https://github.com/HPWebdeveloper/demo-pay-pocket

**Note:** This package does not handle payments from payment platforms, but instead offers the concept of virtual money, deposit, and withdrawal.

* **Author**: Hamed Panjeh
* **Vendor**: hpwebdeveloper
* **Package**: laravel-pay-pocket
* **Alias name**: Laravel PPP (Laravel Pay Pocket Package)
* **Version**: `1.x`
* **PHP Version**: 8.1+
* **Laravel Version**: `10.x`
* **[Composer](https://getcomposer.org/):** `composer require hpwebdeveloper/laravel-pay-pocket`

- **Author**: Hamed Panjeh
- **Vendor**: hpwebdeveloper
- **Package**: laravel-pay-pocket
- **Alias name**: Laravel PPP (Laravel Pay Pocket Package)
- **Version**: `1.x`
- **PHP Version**: 8.1+
- **Laravel Version**: `10.x`
- **[Composer](https://getcomposer.org/):** `composer require hpwebdeveloper/laravel-pay-pocket`

### Support Policy

| Version | Laravel | PHP | Release date | End of improvements | End of support |
|---------|----------------|---------------|--------------|---------------------|----------------|
| 1.x | ^10.0 | 8.1, 8.2, 8.3 | Nov 30, 2023 | Mar 1, 2024 | | |
| x.x | | | | | | |

| Version | Laravel | PHP | Release date | End of improvements | End of support |
| ------- | ------- | ------------- | ------------ | ------------------- | -------------- | --- |
| 1.x | ^10.0 | 8.1, 8.2, 8.3 | Nov 30, 2023 | Mar 1, 2024 | | |
| x.x | | | | | | |

## Installation:

- **Step 1:** You can install the package via composer:
- **Step 1:** You can install the package via composer:

```bash
composer require hpwebdeveloper/laravel-pay-pocket
```

- **Step 2:** Publish and run the migrations with:
- **Step 2:** Publish and run the migrations with:

```bash
php artisan vendor:publish --tag="pay-pocket-migrations"
php artisan migrate
```

You have successfully added two dedicated database tables, `wallets` and `wallets_logs`, without making any modifications to the `users` table.

- **Step 3:** Publish the wallet types using
- **Step 3:** Publish the wallet types using

```bash
php artisan vendor:publish --tag="pay-pocket-wallets"
```

- **Step 4:** Publish the config file

```bash
php artisan vendor:publish --tag="pay-pocket-config"
```

This command will automatically publish the `WalletEnums.php` file into your application's `app/Enums` directory.

## Preparation

### Prepare User Model

To use this package you need to implement the `WalletOperations` into `User` model and utilize the `ManagesWallet` trait.
To use this package you need to implement the `WalletOperations` into `User` model and utilize the `ManagesWallet` trait.

```php

Expand All @@ -75,9 +79,10 @@ class User extends Authenticatable implements WalletOperations

### Prepare Wallets

In Laravel Pay Pocket, you have the flexibility to define the order in which wallets are prioritized for payments through the use of Enums. The order of wallets in the Enum file determines their priority level. The first wallet listed has the highest priority and will be used first for deducting order values.
In Laravel Pay Pocket, you have the flexibility to define the order in which wallets are prioritized for payments through the use of Enums. The order of wallets in the Enum file determines their priority level. The first wallet listed has the highest priority and will be used first for deducting order values.

For example, consider the following wallet types defined in the Enum class (published in step 3 of installation):

```php
namespace App\Enums;

Expand All @@ -88,58 +93,119 @@ enum WalletEnums: string
}

```
**You have complete freedom to name your wallets as per your requirements and even add more wallet types to the Enum list.**

**You have complete freedom to name your wallets as per your requirements and even add more wallet types to the Enum list.**

In this particular setup, `wallet_1` (`WALLET1`) is given the **highest priority**. When an order payment is processed, the system will first attempt to use `wallet_1` to cover the cost. If `wallet_1` does not have sufficient funds, `wallet_2` (`WALLET2`) will be used next.

### Example:

If the balance in `wallet_1` is 10 and the balance in `wallet_2` is 20, and you need to pay an order value of 15, the payment process will first utilize the entire balance of `wallet_1`. Since `wallet_1`'s balance is insufficient to cover the full amount, the remaining 5 will be deducted from `wallet_2`. After the payment, `wallet_2` will have a remaining balance of 15."

## Usage, APIs and Operations:

### Deposit

```php
deposit(type: 'wallet_1', amount: 123.45, detail: null)
```

Deposit funds into `wallet_1`

```php
$user = auth()->user();
$user->deposit('wallet_1', 123.45);
```

$user->deposit('wallet_1', 123.45); // Deposit funds into 'wallet_1'
Deposit funds into `wallet_2`

$user->deposit('wallet_2', 67.89); // Deposit funds into 'wallet_2'
```php
$user = auth()->user();
$user->deposit('wallet_2', 67.89);
```

// Or using provided facade
Or using provided facade

```php
use HPWebdeveloper\LaravelPayPocket\Facades\LaravelPayPocket;

$user = auth()->user();
LaravelPayPocket::deposit($user, 'wallet_1', 123.45);

```

Note: `wallet_1` and `wallet_2` must already be defined in the `WalletEnums`.

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

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

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

### Pay

```php
pay(amount: 12.34, allowedWallets: [], detail: null)
```

Pay the value using the total combined balance available across all allowed wallets

```php
// Pay the value using the total combined balance available across all wallets
$user = auth()->user();
$user->pay(12.34);

// Or using provided facade
```

Or using provided facade

```php
use HPWebdeveloper\LaravelPayPocket\Facades\LaravelPayPocket;

$user = auth()->user();
LaravelPayPocket::pay($user, 12.34);
```

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

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

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

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

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

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

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

[i8]: https://github.com/HPWebdeveloper/laravel-pay-pocket/issues/8

### Balance

- **Wallets**
- **Wallets**

```php
$user = auth()->user();
$user->walletBalance // Total combined balance available across all wallets

// Or using provided facade

LaravelPayPocket::checkBalance($user);
```

- **Particular Wallet**
- **Particular Wallet**

```php
$user = auth()->user();
$user->getWalletBalanceByType('wallet_1') // Balance available in wallet_1
$user->getWalletBalanceByType('wallet_2') // Balance available in wallet_2

Expand All @@ -149,15 +215,32 @@ LaravelPayPocket::walletBalanceByType($user, 'wallet_1');
```

### Exceptions
Upon examining the `src/Exceptions` directory within the source code,

Upon examining the `src/Exceptions` directory within the source code,
you will discover a variety of exceptions tailored to address each scenario of invalid entry. Review the [demo](https://github.com/HPWebdeveloper/demo-pay-pocket) that accounts for some of the exceptions.

### Log

Getting your transaction logs is a pretty straight forward process

```php
$user = auth()->user();
$query = $user->wallets()
->where('type', \App\Enums\WalletEnums::WALLET1)
->first()
->logs();
```

This will return an instance of `\Illuminate\Database\Eloquent\Relations\MorphMany`
from there you can handle any manipulations with your data.

```php
$logs = $query->get();
```

A typical `wallets_logs` table.
![Laravel Pay Pocket Log](https://github.com/HPWebdeveloper/laravel-pay-pocket/assets/16323354/a242d335-8bd2-4af1-aa38-4e95b8870941)


## Testing

```bash
Expand Down Expand Up @@ -185,9 +268,9 @@ Please review [our security policy](../../security/policy) on how to report secu

## Credits

- [Hamed Panjeh](https://github.com/HPWebdeveloper)
- [All Contributors](../../contributors)
- Icon in the above image: pocket by Creative Mahira from [Noun Project](https://thenounproject.com/browse/icons/term/pocket/) (CC BY 3.0)
- [Hamed Panjeh](https://github.com/HPWebdeveloper)
- [All Contributors](../../contributors)
- Icon in the above image: pocket by Creative Mahira from [Noun Project](https://thenounproject.com/browse/icons/term/pocket/) (CC BY 3.0)

## License

Expand Down
10 changes: 8 additions & 2 deletions config/pay-pocket.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@

// config for HPWebdeveloper/LaravelPayPocket
return [

];
'log_reference_length' => 12,
'log_reference_prefix' => null,
/**
* The log reference generator should be static
* The third array item should contain optional parameters to pass to the generator
*/
'log_reference_generator' => [\Illuminate\Support\Str::class, 'random', [15]],
];
38 changes: 38 additions & 0 deletions database/migrations/update_wallets_logs_table.php.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('wallets_logs', function (Blueprint $table) {
if (!Schema::hasColumn('wallets_logs', 'detail')) {
$table->string('detail')->nullable()->after('status');
}
if (!Schema::hasColumn('wallets_logs', 'reference')) {
$table->string('reference')->nullable()->after('ip');
}
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('wallets_logs', function (Blueprint $table) {
if (Schema::hasColumn('wallets_logs', 'detail')) {
$table->dropColumn('detail');
}
if (Schema::hasColumn('wallets_logs', 'reference')) {
$table->dropColumn('reference');
}
});
}
};
5 changes: 5 additions & 0 deletions src/Facades/LaravelPayPocket.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

/**
* @see \HPWebdeveloper\LaravelPayPocket\Services\PocketServices
*
* @method static void pay($user, int|float $orderValue, array $allowedWallets = [], ?string $detail = null)
* @method static bool deposit($user, string $type, int|float $amount, ?string $detail = null)
* @method static int|float checkBalance($user)
* @method static int|float walletBalanceByType($user, string $type)
*/
class LaravelPayPocket extends Facade
{
Expand Down
48 changes: 43 additions & 5 deletions src/Interfaces/WalletOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,51 @@

interface WalletOperations
{
public function getWalletBalanceAttribute();
/**
* Get User's Wallet Balance
*
* @return int|float
*/
public function getWalletBalanceAttribute(): int|float;

public function getWalletBalanceByType(string $walletType);
/**
* Get the balance of a specific wallet type.
*
*
* @param string $walletType
*
* @return float|int
*/
public function getWalletBalanceByType(string $walletType): float|int;

public function hasSufficientBalance($value): bool;
/**
* Check if User's wallet balance is more than given value
*
* @param int|float $value
*
* @return bool
*/
public function hasSufficientBalance(int|float $value): bool;

public function pay(int|float $orderValue);
/**
* Pay the order value from the user's wallets.
*
* @param int|float $orderValue
* @param array $allowedWallets
* @param ?string $detail
*
* @throws InsufficientBalanceException
*/
public function pay(int|float $orderValue, array $allowedWallets = [], ?string $detail = null): void;

public function deposit(string $type, int|float $amount): bool;
/**
* Deposit an amount to the user's wallet of a specific type.
*
* @param string $type
* @param int|float $amount
* @param ?string $detail
*
* @return bool
*/
public function deposit(string $type, int|float $amount, ?string $detail = null): bool;
}
Loading