bavix/laravel-wallet

Easy work with virtual wallet.

9.6.1 2023-01-30 20:35 UTC

README

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 - Easy work with virtual wallet.

[Benchmark] [Documentation] [Get Started]

  • Vendor: bavix
  • Package: laravel-wallet
  • Version: Latest Stable Version
  • PHP Version: 8.0+
  • Laravel Version: 9.x
  • Composer: composer require bavix/laravel-wallet

Support Policy

Version Laravel PHP End of improvements End of support
7.x ^6.0,^7.0,^8.0 7.4,8.0,8.1 1 Mar 2022 6 Sep 2022
8.x ^9.0 8.0,8.1 1 May 2022 1 Jun 2022
9.x [LTS] ^9.0,^10.0 8.0,8.1,8.2 1 Feb 2023 6 Nov 2023

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:

  • For an unlimited number of products (ProductInterface);
  • For a limited number of products (ProductLimitedInterface);

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.0 9.1 9.2 9.3 9.4 9.5 9.6
Atomic:Blocks - - - - 789ms 548ms 586ms 664ms 593ms
Cart:EagerLoaderPay 31.2s 781ms 782ms 694ms 668ms 651ms 607ms 647ms 580ms
Cart:Pay 1.99s 527ms 492ms 432ms 478ms 409ms 377ms 428ms 331ms
Cart:PayFree 1.87s 459ms 404ms 367ms 394ms 342ms 329ms 380ms 338ms
Cart:PayOneItemXPieces 846ms 140ms 74ms 68.3ms 72.1ms 71.3ms 62.5ms 79.1ms 66.1ms
Gift:Gift 65.4ms 61.4ms 70.4ms 61ms 64.1ms 60.8ms 55.6ms 69.5ms 59.8ms
Gift:Refund 149ms 125ms 155ms 136ms 142ms 124ms 118ms 143ms 127ms
Solo:Deposit 30.4ms 32.9ms 32.3ms 26.3ms 31.7ms 31.8ms 24.4ms 33.4ms 25.5ms
Solo:EagerLoading 1.29s 1.32s 1.47s 1.2s 1.18s 1.06s 975ms 1.15s 1.04s
Solo:ForceWithdraw 30.6ms 32.1ms 32.3ms 26.1ms 31.7ms 31.6ms 24.7ms 33.3ms 25.8ms
Solo:GetBalance 19.4ms 22.7ms 22.4ms 17.5ms 23.4ms 24.7ms 17.2ms 23.7ms 17.4ms
Solo:Transfer 55.9ms 48.5ms 51ms 42.4ms 46.8ms 45ms 38.7ms 49.6ms 39.9ms
Solo:Withdraw 38.3ms 35.8ms 39ms 31.9ms 37ms 36ms 29.7ms 39.4ms 31.7ms
State:InTransaction 771ms 619ms 772ms 746ms 749ms 624ms 647ms 787ms 520ms
State:RefreshInTransaction 42.3ms 44.1ms 43.8ms 35.4ms 46.4ms 44.5ms 33.7ms 45ms 36.3ms
State:TransactionRollback 36.6ms 34.1ms 36.5ms 28.9ms 35.3ms 35.5ms 27.9ms 37.3ms 29.9ms

Major compare:

Name 7.x 8.x 9.x
Cart:EagerLoaderPay 31.2s 781ms 646ms
Cart:Pay 1.99s 527ms 398ms
Cart:PayFree 1.87s 459ms 349ms
Cart:PayOneItemXPieces 846ms 140ms 72ms
Gift:Gift 65.4ms 61.4ms 62.3ms
Gift:Refund 149ms 125ms 127ms
Solo:Deposit 30.4ms 32.9ms 30.3ms
Solo:EagerLoading 1.29s 1.32s 1.11s
Solo:ForceWithdraw 30.6ms 32.1ms 30.2ms
Solo:GetBalance 19.4ms 22.7ms 20.7ms
Solo:Transfer 55.9ms 48.5ms 46.2ms
Solo:Withdraw 38.3ms 35.8ms 36.5ms
State:InTransaction 771ms 619ms 669ms
State:RefreshInTransaction 42.3ms 44.1ms 40.4ms
State:TransactionRollback 36.6ms 34.1ms 34.1ms

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]. 68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f6c61726176656c2d77616c6c65742f636f6e7472696275746f72732e7376673f77696474683d38393026627574746f6e3d66616c7365

Financial Contributors

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

Individuals

68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f6c61726176656c2d77616c6c65742f696e646976696475616c732e7376673f77696474683d383930

Organizations

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