bavix / laravel-wallet

It's easy to work with a virtual wallet
https://bavix.github.io/laravel-wallet/
MIT License
1.15k stars 230 forks source link
balance composer eloquent laravel laravel-wallet multi-wallet php purchase virtual wallet

Laravel Wallet

Maintainability Test Coverage Financial Contributors on Open Collective Mutation testing badge

Latest Stable Version Latest Unstable Version License composer.lock

Sparkline

laravel-wallet - It's easy to work with a virtual wallet.

[Benchmark] [Documentation] [Get Started]

Support Policy

Version Laravel PHP Release date End of improvements End of support
10.x [LTS] ^10.0,^11.0 8.1,8.2,8.3,8.4 Jul 8, 2023 May 1, 2024 Feb 4, 2025
11.x ^11.0 8.2,8.3,8.4 Mar 14, 2024 May 1, 2025 Sep 6, 2025

[!IMPORTANT] If you are using laravel-wallet ^10.0 with laravel ^11, then the minimum php version is 8.2.

Upgrade Guide

To perform the migration, you will be helped by the instruction.

Community

I want to create a cozy place for developers using the wallet package. This will help you find bugs faster, get feedback and discuss ideas.

telegram

Telegram: @laravel_wallet

Extensions

Extension Description
Swap Addition to the laravel-wallet library for quick setting of exchange rates
uuid Addition to laravel-wallet to support model uuid keys
Warm Up Addition to the laravel-wallet library for refresh balance wallets

Usage

Add the HasWallet trait and Wallet interface to model.

use Bavix\Wallet\Traits\HasWallet;
use Bavix\Wallet\Interfaces\Wallet;

class User extends Model implements Wallet
{
    use HasWallet;
}

Now we make transactions.

$user = User::first();
$user->balanceInt; // 0

$user->deposit(10);
$user->balance; // 10
$user->balanceInt; // int(10)

$user->withdraw(1);
$user->balance; // 9

$user->forceWithdraw(200, ['description' => 'payment of taxes']);
$user->balance; // -191

Purchases

Add the CanPay trait and Customer interface to your User model.

use Bavix\Wallet\Traits\CanPay;
use Bavix\Wallet\Interfaces\Customer;

class User extends Model implements Customer
{
    use CanPay;
}

Add the HasWallet trait and interface to Item model.

Starting from version 9.x there are two product interfaces:

An example with an unlimited number of products:

use Bavix\Wallet\Traits\HasWallet;
use Bavix\Wallet\Interfaces\Customer;
use Bavix\Wallet\Interfaces\ProductInterface;

class Item extends Model implements ProductInterface
{
    use HasWallet;

    public function getAmountProduct(Customer $customer): int|string
    {
        return 100;
    }

    public function getMetaProduct(): ?array
    {
        return [
            'title' => $this->title, 
            'description' => 'Purchase of Product #' . $this->id,
        ];
    }
}

Example with a limited number of products:

use Bavix\Wallet\Traits\HasWallet;
use Bavix\Wallet\Interfaces\Customer;
use Bavix\Wallet\Interfaces\ProductLimitedInterface;

class Item extends Model implements ProductLimitedInterface
{
    use HasWallet;

    public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool
    {
        /**
         * This is where you implement the constraint logic. 
         * 
         * If the service can be purchased once, then
         *  return !$customer->paid($this);
         */
        return true; 
    }

    public function getAmountProduct(Customer $customer): int|string
    {
        return 100;
    }

    public function getMetaProduct(): ?array
    {
        return [
            'title' => $this->title, 
            'description' => 'Purchase of Product #' . $this->id,
        ];
    }
}

I do not recommend using the limited interface when working with a shopping cart. If you are working with a shopping cart, then you should override the PurchaseServiceInterface interface. With it, you can check the availability of all products with one request, there will be no N-queries in the database.

Proceed to purchase.

$user = User::first();
$user->balance; // 100

$item = Item::first();
$user->pay($item); // If you do not have enough money, throw an exception
var_dump($user->balance); // 0

if ($user->safePay($item)) {
  // try to buy again
}

var_dump((bool)$user->paid($item)); // bool(true)

var_dump($user->refund($item)); // bool(true)
var_dump((bool)$user->paid($item)); // bool(false)

Eager Loading

// When working with one wallet
User::with('wallet');

// When using the multi-wallet functionality
User::with('wallets');

How to work with fractional numbers?

Add the HasWalletFloat trait and WalletFloat interface to model.

use Bavix\Wallet\Traits\HasWalletFloat;
use Bavix\Wallet\Interfaces\WalletFloat;
use Bavix\Wallet\Interfaces\Wallet;

class User extends Model implements Wallet, WalletFloat
{
    use HasWalletFloat;
}

Now we make transactions.

$user = User::first();
$user->balance; // 100
$user->balanceFloat; // 1.00

$user->depositFloat(1.37);
$user->balance; // 237
$user->balanceFloat; // 2.37

Performance Comparison

All versions: Name 7.3 8.4 9.6 10.1 11.0
Atomic:Blocks - - 484ms 493ms 493ms
Cart:EagerLoaderPay 22s 679ms 493ms 530ms 652ms
Cart:Pay 1.36s 472ms 288ms 298ms 336ms
Cart:PayFree 1.3s 415ms 281ms 291ms 287ms
Cart:PayOneItemXPieces 565ms 118ms 59.1ms 64.6ms 66.2ms
Gift:Gift 44.8ms 53.5ms 54.3ms 58.4ms 64.3ms
Gift:Refund 106ms 112ms 108ms 111ms 139ms
Solo:Deposit 27.4ms 31.8ms 31ms 33.3ms 30.1ms
Solo:EagerLoading 904ms 1.09s 876ms 927ms 1.02s
Solo:ForceWithdraw 27.6ms 31.8ms 30.7ms 32.9ms 30ms
Solo:GetBalance 20.8ms 24ms 23.7ms 23.4ms 20ms
Solo:Transfer 39.4ms 45.7ms 42ms 44.9ms 46.6ms
Solo:Withdraw 31.1ms 36.3ms 34.9ms 37.3ms 37.8ms
State:InTransaction 570ms 566ms 419ms 425ms 427ms
State:RefreshInTransaction 32.3ms 41.2ms 41.2ms 45.6ms 47.2ms
State:TransactionRollback 29.7ms 34.1ms 32.9ms 37.2ms 36.9ms

Table generated using benchmark. Pull Request.


Supported by

Supported by JetBrains

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]