GithubHelp home page GithubHelp logo

bavix / laravel-wallet Goto Github PK

View Code? Open in Web Editor NEW
1.1K 41.0 212.0 3.87 MB

Easy work with virtual wallet

Home Page: https://bavix.github.io/laravel-wallet/

License: MIT License

PHP 99.88% JavaScript 0.12%
wallet virtual laravel balance eloquent php laravel-wallet composer purchase multi-wallet

laravel-wallet's Introduction

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
  • Composer: composer require bavix/laravel-wallet

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 Jul 8, 2023 May 1, 2024 Feb 4, 2025
11.x ^11.0 8.2,8.3 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:

  • 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.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]

laravel-wallet's People

Contributors

adesege avatar adriangonzales avatar akhedrane avatar bavixbot avatar beagon avatar cispl-shaswatad avatar dependabot-preview[bot] avatar dependabot[bot] avatar emontano-bit avatar gkmk avatar hsharghi avatar justinkekeocha avatar keatliang2005 avatar mend-bolt-for-github[bot] avatar moecasts avatar monkeywithacupcake avatar nathanwritescode-uk avatar omarhen avatar reedknight avatar rez1dent3 avatar scrutinizer-auto-fixer avatar snyk-bot avatar thiritin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-wallet's Issues

support request for newbie

Sorry to ask you what is provably a newbie question, but I'm trying just the basic usage with no luck. My doubt is where should I use this?:

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

class User extends Model implements Wallet
{
    use HasWallet;
}

Of course, laravel already has a User class which extends the Model class. Should I modify that class in Illuminate\Foundation\Auth ?? I don't think so. But then? Sorry, I'm lost. Thanks for your help.

Santa goes to minus

The situation, Santa gives a gift and pays tax. But validation for write-off does not take into account tax, santa goes into minus)

Problems with payments

I've tried to use this on my project but I have this error:

Declaration of App\Item::canBuy(App\Customer $customer, bool $force = false): bool must be compatible with Bavix\Wallet\Interfaces\Product::canBuy(Bavix\Wallet\Interfaces\Customer $customer, bool $force = false): bool

New feature "shopping Cart"

  • Ability to buy goods in groups
  • To return the item groups
  • It is safe to buy a lot of goods
  • Simple syntax
  • Cover with unit tests

Product price based on customer attributes

Hey guys.

Is it possible to generate product price based on customer attributes? I mean in Bavix\Wallet\Interfaces\Product method canBuy there is customer variable available, but I need this variable in getAmountProduct as well so I can calculate price for specific user. Is it possible to do without package modification?

Thanks in advance

No fee exchange will set fee in transfer table

As title.

After some investigation, the cause is from the forceExchange() in canExchange.php. The forceExchange uses

  $transfers = app(CommonService::class)->multiBrings([
                    (new Bring())
                        ->setStatus(Transfer::STATUS_EXCHANGE)
                        ->setDeposit($deposit)
                        ->setWithdraw($withdraw)
                        ->setFrom($from)
                        ->setTo($to)
                ]);

and in Bring.php

    public function toArray(): array
    {
        return [
            'status' => $this->getStatus(),
            'deposit_id' => $this->getDeposit()->getKey(),
            'withdraw_id' => $this->getWithdraw()->getKey(),
            'from_type' => $this->getFrom()->getMorphClass(),
            'from_id' => $this->getFrom()->getKey(),
            'to_type' => $this->getTo()->getMorphClass(),
            'to_id' => $this->getTo()->getKey(),
             // THE CAUSE IS HERE
            'fee' => abs($this->getWithdraw()->amount) - abs($this->getDeposit()->amount), 
            'uuid' => $this->getUuid(),
        ];
    }

The transfer's deposit and withdraw are from different wallet (or currency) with rate. So the minus result here usually not correct.

How does a deposit work?

Hello!)
Why do I get zero? But after the update I get a real balance? And so every time. Thank.

INSERT INTO `wallets` (`id`, `holder_type`, `holder_id`, `name`, `slug`, `description`, `balance`, `created_at`, `updated_at`) VALUES
(2, 'App\\Models\\User', 1, 'Default Wallet', 'default', NULL, 290, '2019-06-17 03:43:43', '2019-06-17 04:04:40');
        //$user = User::find(auth()->user()->id);
        $user = User::with('wallet')->find(auth()->user()->id);
        echo $user->balance; // int(0)
        $user->deposit(10);
        echo $user->balance; // int(10)
        $user->wallet->refreshBalance();
        echo $user->balance; // int(270);
        die();

race condition

Hi) i error race condition))
i have this code

        $walletRefill = $user->getWalletRefill();
        $walletHold = $user->getWalletHold();

        try {
            $walletRefill->transfer(
                $walletHold,
                $type,
                [
                    'description' => 'Перевод средств для покупки очереди: ' . $type . '$'
                ]);

        } catch (Exception $e) {
            
            info(__CLASS__ . '(' . __LINE__ . ') ' . $e->getMessage());
            return redirect(route('queue.buy.index', [
                'user' => $user->id
            ]))->with('error', __($e->getMessage()));

        }

when many users run this code, then I see such an error

App\Http\Controllers\Front\Queue\BuyController(69) SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (SQL: update `wallets` set `balance` = 3820, `wallets`.`updated_at` = 2019-08-05 00:18:53 where `id` = 12)
--

Do you have any idea how to fix this?)))
tnx)

problem orderBy('id', 'desc')

this is run
$user = User::find(1);
$data= $user->transactions()->where(['type'=>Transaction::TYPE_DEPOSIT])->paginate(15);
exit;

this error
$user = User::find(1);
$data= $user->transactions()->where(['type'=>Transaction::TYPE_DEPOSIT])->paginate(15)->orderBy('id', 'desc');

orderBy('id', 'desc')

Capture

Is the doc for payment wrong

According to the doc, User model use CanBePaid trait, Item use hasWallet trait. I think it should be swapt. Such as User model use hasWallet trait, Item use CanBePaid trait.

Refund does not work correctly

Hello.

$wallet = $user->getWallet('myWallet');
$wallet->balance; // 16 
// product price = 13
$wallet->pay($product); // success
$wallet->balance; // 3
$wallet->refund($product); // success
$wallet->balance; // 3. Wtf?

how check negative numbers ?

how check negative numbers
$user = User::first();
$user->balance; // int(10)
$user->withdraw(11);

how balance > withdraw
{
$user->withdraw(11);
echo 'Yes withdraw'
}else
{
echo 'No withdraw'
}

balance - 11 = - 1 (negative)
notification not enough balance

getting problems with CreateTransactionsTable

Getting the following error when i try to migrate the db
Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'json null, uuidchar(36) not null,created_attimestamp null,updated_att' at line 1 (SQL: create tabletransactions (idint unsigned not null auto_increment primary key,payable_typevarchar(255) not null,payable_idbigint unsigned not null,typeenum('deposit', 'withdraw') not null,amountbigint not null,confirmedtinyint(1) not null,metajson null,uuidchar(36) not null,created_attimestamp null,updated_at timestamp null) default character set utf8 collate 'utf8_general_ci' engine = InnoDB)

Uuid wallets

Olá, estou tentando criar modelos estendendo Bavix\Wallet\Models\Wallet para utilizar UUID's no lugar de ID's inteiros convencionais, porém, não estou conseguindo, há como fazer isto de alguma forma?

Error after create

Hi)) I do this:
1.

    public function index(User $user)
    {
        if(is_null($user->getWallet('refill'))){
            $user->createWallet([
                'name' => ucfirst('refill') . 'Wallet',
                'slug' => 'refill',
            ]);
        }
        //$user->fresh();
        //$user->wallet->fresh();

        dd($user->getWallet('refill'));
    }

  1. i have error
Illuminate \ Database \ QueryException (23000)
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'App\Models\User-2-refill' for key 'wallets_holder_type_holder_id_slug_unique' (SQL: insert into `wallets` (`name`, `slug`, `holder_id`, `holder_type`, `updated_at`, `created_at`) values (RefillWallet, refill, 2, App\Models\User, 2019-07-05 19:05:32, 2019-07-05 19:05:32))
  1. refresh page
Wallet {#638 ▼
  #fillable: array:6 [▶]
  #casts: array:1 [▶]
  #connection: "mysql"
  #table: "wallets"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:9 [▶]
  #original: array:9 [▶]
  #changes: []
  #dates: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #guarded: array:1 [▶]
}

What am I doing wrong? Thank.

how insert data bank_method transactions table ?

In transactions(
payable_type,
payable_id,
wallet_id,
type,
amount,
confirmed,
meta,
uuid,
created_at,
updated_at
) table

Then i added a column name bank_method

$user = User::first();
$user->balance; // int(0)
$user->deposit(10);

$bank_method ='VietComBank';

Now , i want insert bank_method into $user->deposit(10,$bank_method);

how i can do that ?

Capture

Wallet balance should be refreshed after lock acquired

For example, in CommonService#forceWithdraw:

public function forceWithdraw(Wallet $wallet, int $amount, ?array $meta, bool $confirmed = true): Transaction
{
    return app(LockService::class)->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) {
        $walletService = app(WalletService::class);
        $walletService->checkAmount($amount);

        /**
         * @var WalletModel $wallet
         */
        $wallet = $walletService->getWallet($wallet);

        // we should refresh balance here or anywhere after lock acquired

        $transactions = $this->multiOperation($wallet, [
            (new Operation())
                ->setType(Transaction::TYPE_WITHDRAW)
                ->setConfirmed($confirmed)
                ->setAmount(-$amount)
                ->setMeta($meta)
        ]);

        return current($transactions);
    });
}

After $wallet = $walletService->getWallet($wallet);, we should refresh the wallet balance, or we could run into an issue caused by race condition, like the following snippet:

$wallet = $user->wallet; // balance now 100

// another thread complete withdraw, real balance now 50

$user->forceWithdraw(100); // balance now 0, which is incorrect, it should be -50

For now, the solution for me is acquiring my own lock, and refresh balance after locked.

Trying to install this on themosis but having problems

Hi! I am trying to integrate this to Themosis Framework using Laravel Framework on top of wordpress but for some reason when i use composer i am having problems with the depencies deleting the illuminate package on vendor directory and re-inserting the laravel framework.

What would be the best way to implement this while still being able to use composer?

How to add Cards features like PayPal to our project ???

Hello.

This is a real project. We want to launch an electronic payment application based on Laravel Bavix Ewallet and we wish to add him a feature of cards (prepaid, credit and flow). This feature will have to allow our customers to make deposits and withdrawals with their different cards. So we wish to know if this library can help us put in place such a functionality exactly as Paypal.

Thank you.

refund ?

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

$user->withdraw(1);
$user->balance; // int(99)

how refund 1 for user User ?

Remove legacy code

  • Deprecated
  • Take protected methods to services
  • The possibility of expanding the code, simple inheritance code

amount in transaction bug on withdrawFloat

Hi, I found some bug of withdrawFloat

my code:

public function withdraw($amount, ?array $desc = null, $confirmed = 0)
    {
        $user = User::find(Auth::id());
        if ($amount > 0) {
            return $user->withdrawFloat($amount, $desc, $confirmed);
        }

        return $user->balanceFloat;
    }

$this->withdraw(2556.72, [], true);

when I withdraw amount:
2556.72 in db transaction amount is show -255671 // 2556.71 it sholud be 2556.72

Thank you. sorry for my english.

How to solve these problems

image

 $user->load('wallets.transfers')
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field 'holder_type' doesn't have a default value (SQL: insert into `wallets` (`updated_at`, `created_at`) values (2019-08-03 14:21:07, 2019-08-03 14:21:07))'
php artisan ide-helper:models
Exception: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'holder_id' cannot be null (SQL: insert into `wallets` (`holder_id`, `holder_type`, `name`, `slug`, `balance`, `updated_at`, `created_at`) values (?, App\User, Cny, cny, 0, 2019-08-03 14:21:57, 2019-08-03 14:21:57))
Could not analyze class App\User.
Exception: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'holder_id' cannot be null (SQL: insert into `wallets` (`holder_id`, `holder_type`, `name`, `slug`, `balance`, `updated_at`, `created_at`) values (?, App\Models\PayOrder, Cny, cny, 0, 2019-08-03 14:21:57, 2019-08-03 14:21:57))
Could not analyze class App\Models\Product.

Cannot use HasWallet and CanPay together on the same model

Hi,

Just started using the package. I added the traits HasWallet and CanPay to the User Model and I get the following error

Trait method deposit has not been applied, because there are collisions with other trait methods on App\\Shared\\Models\\User at /Users/app-server/vendor/coolAppName/shared/src/Models/User.php:15

Can I not use both the traits together?

Question on working with the package

@rez1dent3

First thanks for you laravel wallet plugin it's really awesome and helpful i just have some mis-understanding in some logic .
for my case :
1- i have a hotel and this hotel have some units where i can book for customers .
2- i book the unit that means Reservation Model using trait Has HasWalletFloat
3- then i save the reservation in reservations table and in transactions table in the first status it become a withdraw . so in transactions table i have the morph relation with payable_type ( App\Reservation ) and payable_id ( the reservation id ) .
4- for now everything is working for me like a charm , but the issue am facing right now .
5- sometimes i want to add a transaction which is not related to any reservation ( ex: transaction made to plumber ) it's already a withdraw transaction but is not related to any reservation .
6- hopfully i have Team Model ( yeah am using spark ) how do i achieve something like i described in ( 5 ) that's my question

Thanks for your time reading this

Originally posted by @emad-sure in #99 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.