Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Wallet Laravel Package

bavix/laravel-wallet

Virtual wallet system for Laravel: manage balances, deposits/withdrawals, transfers, and multi-wallet support with robust transaction history and concurrency safety. Well-tested, benchmarked, and extensible for payments, loyalty points, and in-app credits.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require bavix/laravel-wallet
    

    Publish the package config (if needed):

    php artisan vendor:publish --provider="Bavix\Wallet\WalletServiceProvider"
    
  2. Basic Model Integration: Add HasWallet trait and Wallet interface to your model (e.g., User):

    use Bavix\Wallet\Traits\HasWallet;
    use Bavix\Wallet\Interfaces\Wallet;
    
    class User extends Model implements Wallet
    {
        use HasWallet;
    }
    

    Run migrations:

    php artisan migrate
    
  3. First Use Case: Deposit/withdraw funds:

    $user = User::first();
    $user->deposit(100);       // Deposit 100 units
    $user->withdraw(20);       // Withdraw 20 units
    $user->balance;           // Check balance (80)
    

Implementation Patterns

Core Workflows

  1. Wallet Management:

    • Single Wallet: Use HasWallet for models with one wallet.
      User::with('wallet')->get(); // Eager load wallet
      
    • Multi-Wallet: Use HasWallets for models with multiple wallets (e.g., User with Currency).
      User::with('wallets')->get();
      
  2. Transactions:

    • Deposits/Withdrawals:
      $user->deposit(50);               // Add funds
      $user->withdraw(10);              // Subtract funds
      $user->forceWithdraw(100, ['description' => 'Tax']); // Force withdraw (allows negative balance)
      
    • Floating-Point Support (for currencies):
      use Bavix\Wallet\Traits\HasWalletFloat;
      use Bavix\Wallet\Interfaces\WalletFloat;
      
      class User implements Wallet, WalletFloat
      {
          use HasWalletFloat;
      }
      $user->depositFloat(1.99); // Deposit 1.99 units
      
  3. Purchases:

    • Unlimited Products (e.g., digital goods):
      use Bavix\Wallet\Interfaces\ProductInterface;
      
      class Item implements ProductInterface
      {
          public function getAmountProduct(Customer $customer): int|string { return 100; }
          public function getMetaProduct(): ?array { return ['title' => 'Product']; }
      }
      $user->pay($item); // Deduct cost from user's balance
      
    • Limited Products (e.g., inventory):
      use Bavix\Wallet\Interfaces\ProductLimitedInterface;
      
      class Item implements ProductLimitedInterface
      {
          public function canBuy(Customer $customer, int $quantity = 1): bool { /* Logic */ }
          // ... other methods
      }
      $user->safePay($item); // Attempt purchase (returns bool)
      
  4. Refunds and Queries:

    $user->refund($item); // Refund purchase
    app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($user, $item)); // Check purchase status
    
  5. Eager Loading:

    // Single wallet
    User::with('wallet')->get();
    
    // Multi-wallet
    User::with('wallets')->get();
    

Integration Tips

  1. Custom Transactions: Extend the Transaction model or create custom services by implementing TransactionServiceInterface.

  2. Merchant Fees: Use the HasGift trait to handle discounts or fees:

    use Bavix\Wallet\Traits\HasGift;
    
    class User implements Wallet
    {
        use HasGift;
    }
    $user->setGift(10); // Apply a 10-unit discount
    
  3. Shopping Cart: Override PurchaseServiceInterface to batch-check purchases:

    class CartPurchaseService implements PurchaseServiceInterface
    {
        public function check(PurchaseQuery $query): bool { /* Batch logic */ }
    }
    
  4. Events: Listen to wallet events (e.g., WalletDeposited, WalletWithdrawn) for notifications or logging:

    event(new WalletDeposited($user, $amount));
    
  5. Testing: Use the WalletTestCase trait for unit tests:

    use Bavix\Wallet\Tests\WalletTestCase;
    
    class UserTest extends WalletTestCase { ... }
    

Gotchas and Tips

Pitfalls

  1. Negative Balances:

    • withdraw() throws an exception if insufficient funds exist.
    • Use forceWithdraw() to allow negative balances (e.g., for overdrafts or tax deductions).
  2. Floating-Point Precision:

    • HasWalletFloat uses brick/math for accurate decimal arithmetic.
    • Avoid manual arithmetic on balanceFloat; use provided methods (depositFloat, withdrawFloat).
  3. Multi-Wallet Conflicts:

    • Ensure walletable_id and walletable_type are correctly set in the wallets table.
    • Use HasWallets for models with dynamic wallet types (e.g., User + Currency).
  4. Purchase Locking:

    • ProductLimitedInterface requires canBuy() logic. Omit this for unlimited products.
    • For shopping carts, implement PurchaseServiceInterface to avoid N+1 queries.
  5. Event Order:

    • Events like WalletDeposited fire after the balance is updated. Use creating/updating model events for pre-processing.

Debugging

  1. Transaction Logs: Enable debug mode in config/wallet.php:

    'debug' => env('WALLET_DEBUG', false),
    

    Logs will appear in storage/logs/laravel-wallet.log.

  2. Balance Mismatches:

    • Verify balance vs. balanceInt/balanceFloat consistency.
    • Check for race conditions in concurrent transactions (use database transactions or locks).
  3. Query Issues:

    • Use tap() to inspect PurchaseQuery objects:
      tap(PurchaseQuery::create($user, $item), fn($query) => dd($query->toArray()));
      

Extension Points

  1. Custom Wallets: Create a new wallet type by extending Wallet and registering it in WalletServiceProvider:

    $this->app->bind(WalletInterface::class, CustomWallet::class);
    
  2. Transaction Services: Override TransactionServiceInterface for custom logic (e.g., logging, analytics):

    class CustomTransactionService implements TransactionServiceInterface
    {
        public function create(array $data): Transaction { ... }
    }
    
  3. Purchase Validation: Extend PurchaseQueryHandlerInterface for custom purchase rules:

    class CustomPurchaseHandler implements PurchaseQueryHandlerInterface
    {
        public function one(PurchaseQuery $query): bool { ... }
    }
    
  4. Currency Support: Use the Currency model to manage multi-currency wallets:

    $user->wallets()->attach($currencyId, ['balance' => 100]);
    
  5. Webhooks: Dispatch events to trigger external services (e.g., Stripe, PayPal):

    event(new WalletDeposited($user, $amount))
        ->each(fn($event) => $this->notifyExternalService($event));
    

Configuration Quirks

  1. Decimal Places: Set in config/wallet.php:

    'decimal_places' => 2, // For floating-point wallets
    
  2. Default Currency: Configure in config/wallet.php:

    'default_currency' => 'USD',
    
  3. Database Schema:

    • The package creates wallets and transactions tables by default.
    • Customize via WalletServiceProvider::boot():
      Schema::create('custom_wallets', function (Blueprint $table) { ... });
      
  4. Caching: Disable caching in config/wallet.php for development:

    'cache' => [
        'enabled' => env('WALLET_CACHE', false),
    ],
    

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata