### Install Laravel Wallet Swap via Composer Source: https://bavix.github.io/laravel-wallet/guide/additions/swap.html Use Composer to add the laravel-wallet-swap package to your project. This is the recommended installation method. ```bash composer req bavix/laravel-wallet-swap ``` -------------------------------- ### User Model Setup for Wallets Source: https://bavix.github.io/laravel-wallet/guide/cqrs/create-wallet.html Implement the HasWallet, HasWallets traits, and the Wallet interface in your User model to enable wallet functionality. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Traits\HasWallets; use Bavix\Wallet\Interfaces\Wallet; class User extends Model implements Wallet { use HasWallet, HasWallets; } ``` -------------------------------- ### Install Laravel Wallet via Composer Source: https://bavix.github.io/laravel-wallet/guide/introduction/installation.html Use this command to add the Laravel Wallet package to your project using Composer. ```bash composer req bavix/laravel-wallet ``` -------------------------------- ### Item Model with Product and Taxable Interfaces Source: https://bavix.github.io/laravel-wallet/guide/purchases/commissions.html Add the HasWallet trait and ProductInterface (or ProductLimitedInterface) interface to your Item model. This example includes getFeePercent for commission calculation. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Taxable; use Bavix\Wallet\Interfaces\ProductLimitedInterface; use Bavix\Wallet\External\Api\PurchaseQuery; use Bavix\Wallet\External\Api\PurchaseQueryHandlerInterface; class Item extends Model implements ProductLimitedInterface, Taxable { use HasWallet; public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool { /** * If the service can be purchased once, then * return ! app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($customer, $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, ]; } public function getFeePercent() { return 0.03; // 3% } } ``` -------------------------------- ### Batch Withdrawal with Balance Check Source: https://bavix.github.io/laravel-wallet/guide/high-performance/batch-transactions.html This example demonstrates how to perform batch withdrawals while ensuring wallet balances are sufficient before processing. It combines atomic operations with consistency checks and API handlers. ```php use Bavix\Wallet\External\Api\TransactionQuery; use Bavix\Wallet\External\Api\TransactionQueryHandlerInterface; use Bavix\Wallet\Services\AtomicServiceInterface; use Bavix\Wallet\Services\ConsistencyServiceInterface; app(AtomicServiceInterface::class)->blocks($wallets, function () use ($wallets, $amount) { foreach ($wallets as $wallet) { app(ConsistencyServiceInterface::class)->checkPotential($wallet, $amount); } app(TransactionQueryHandlerInterface::class)->apply( array_map( static fn (Wallet $wallet) => TransactionQuery::createWithdraw($wallet, $amount, null), $wallets ) ); }); ``` -------------------------------- ### Payment Process with Minimal Fee Example Source: https://bavix.github.io/laravel-wallet/guide/purchases/merchant-fee-deductible.html Illustrates a payment scenario using MerchantFeeDeductible and MinimalTaxable, where the customer pays the product price and a minimal fee is applied to the merchant's payout. ```php $user = User::first(); $user->balance; // 100 ``` ```php $item = Item::first(); $item->getAmountProduct($user); // 100 ``` ```php $user->pay($item); // success, customer pays $100 $user->balance; // 0 ``` -------------------------------- ### Migration Example for Purchase Checks Source: https://bavix.github.io/laravel-wallet/guide/introduction/upgrade.html Demonstrates how to check if a customer has already purchased a product using `PurchaseQuery` and `PurchaseQueryHandlerInterface`. This replaces the deprecated `Customer::paid()` and `CartPay::paid()` methods. ```php use Bavix\Wallet\External\Api\PurchaseQuery; use Bavix\Wallet\External\Api\PurchaseQueryHandlerInterface; $transfer = app(PurchaseQueryHandlerInterface::class) ->one(PurchaseQuery::create($customer, $product)); $isPurchased = (bool) $transfer; ``` -------------------------------- ### Call Custom Wallet Method Source: https://bavix.github.io/laravel-wallet/guide/introduction/configuration.html Example of calling a custom method defined in an extended Wallet model. ```php echo $user->wallet->helloWorld(); ``` -------------------------------- ### Example: Multi-Wallet Transaction Counting Source: https://bavix.github.io/laravel-wallet/guide/multi/transaction-filter.html Demonstrates how to use `transactions` and `walletTransactions` methods with multiple wallets. The `transactions` method counts all transactions for the owner, while `walletTransactions` counts transactions for the specific wallet instance. ```php $user->transactions()->count(); // 0 // Multi wallets and default wallet can be used together // default wallet $user->deposit(100); $user->wallet->deposit(200); $user->wallet->withdraw(1); // usd $usd = $user->createWallet(['name' => 'USD']); $usd->deposit(100); // eur $eur = $user->createWallet(['name' => 'EUR']); $eur->deposit(100); $user->transactions()->count(); // 5 $user->wallet->transactions()->count(); // 5 $usd->transactions()->count(); // 5 $eur->transactions()->count(); // 5 // the transactions method returns data relative to the owner of the wallet, for all transactions $user->walletTransactions()->count(); // 3. we get the default wallet $user->wallet->walletTransactions()->count(); // 3 $usd->walletTransactions()->count(); // 1 $eur->walletTransactions()->count(); // 1 ``` -------------------------------- ### Example Transfer Contract Source: https://bavix.github.io/laravel-wallet/guide/single/transfer.html Demonstrates the structure of a transfer operation with optional extra parameters for deposit, withdraw, and general extra data. ```php $transfer = $user1->transfer( $user2, 511, new Extra( deposit: [ 'type' => 'extra-deposit', ], withdraw: new Option( [ 'type' => 'extra-withdraw', ], false // confirmed ), extra: [ 'msg' => 'hello world', ], ) ); ``` -------------------------------- ### Check Item Cost and Create Receiving Wallet Source: https://bavix.github.io/laravel-wallet/guide/purchases/receiving.html Retrieve an item, get its product amount for a customer, and create a new wallet for receiving funds with specified meta information. ```php $item = Item::first(); $item->getAmountProduct($user); // 100 $receiving = $item->createWallet([ 'name' => 'Dollar', 'meta' => [ 'currency' => 'USD', ], ]); ``` -------------------------------- ### Configure Lock Service for Race Conditions Source: https://bavix.github.io/laravel-wallet/guide/db/race-condition.html Configure the 'lock' driver in the `wallet.php` configuration file to manage race conditions. The 'array' driver is shown as an example. ```php /** * A system for dealing with race conditions. */ 'lock' => [ 'driver' => 'array', 'seconds' => 1, ], ``` -------------------------------- ### Item Model for Unlimited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/receiving.html Add the HasWallet trait and interface to your Item model. Use HasWallets for multi-wallet support. This example is for an unlimited number of products. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Traits\HasWallets; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\ProductInterface; class Item extends Model implements ProductInterface { use HasWallet, HasWallets; public function getAmountProduct(Customer $customer): int|string { return 100; } public function getMetaProduct(): ?array { return [ 'title' => $this->title, 'description' => 'Purchase of Product #' . $this->id, ]; } } ``` -------------------------------- ### Install Laravel Wallet UUID Package Source: https://bavix.github.io/laravel-wallet/guide/additions/uuid.html Use this Composer command to install the UUID extension for Laravel Wallet. Ensure you have Composer installed and are in your project's root directory. ```bash composer req bavix/laravel-wallet-uuid ``` -------------------------------- ### Configure Cache Service for Wallet State Source: https://bavix.github.io/laravel-wallet/guide/db/race-condition.html Configure the 'cache' driver in the `wallet.php` configuration file for storing wallet balance states. The 'array' driver is shown as an example. ```php /** * Storage of the state of the balance of wallets. */ 'cache' => ['driver' => 'array'], ``` -------------------------------- ### Perform Free Purchase Source: https://bavix.github.io/laravel-wallet/guide/purchases/payment-free.html Execute a free payment for an item using the `payFree` method on the user. This method handles the transaction and updates balances. The example also shows how to verify the purchase using `PurchaseQueryHandlerInterface`. ```php $user->payFree($item); (bool) app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($user, $item)); // bool(true) $user->balance; // 100 $item->balance; // 0 ``` -------------------------------- ### Item Model for Limited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/receiving.html Add the HasWallet trait and interface to your Item model for multi-wallet support. This example uses the ProductLimitedInterface for a limited number of products. It's not recommended for shopping carts. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Traits\HasWallets; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\ProductLimitedInterface; class Item extends Model implements ProductLimitedInterface { use HasWallet, HasWallets; public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool { 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, ]; } } ``` -------------------------------- ### Custom Exchange Rate Service Implementation Source: https://bavix.github.io/laravel-wallet/guide/single/exchange.html Implement the ExchangeServiceInterface to define custom currency conversion logic. This example uses a hardcoded array for rates and a MathServiceInterface for calculations. ```php use Bavix\Wallet\Internal\Service\MathServiceInterface; use Bavix\Wallet\Services\ExchangeServiceInterface; class MyExchangeService implements ExchangeServiceInterface { private array $rates = [ 'USD' => [ 'RUB' => 67.61, ], ]; private MathServiceInterface $mathService; public function __construct(MathServiceInterface $mathService) { $this->mathService = $mathService; foreach ($this->rates as $from => $rates) { foreach ($rates as $to => $rate) { if (empty($this->rates[$to][$from])) { $this->rates[$to][$from] = $this->mathService->div(1, $rate); } } } } /** @param float|int|string $amount */ public function convertTo(string $fromCurrency, string $toCurrency, $amount): string { return $this->mathService->mul($amount, $this->rates[$fromCurrency][$toCurrency] ?? 1); } } ``` -------------------------------- ### Get User and Check Balance Source: https://bavix.github.io/laravel-wallet/guide/single/withdraw.html Retrieve a user and access their current balance. The balance is available as a float or an integer. ```php $user = User::first(); $user->balance; // 100 $user->balanceInt; // 100 ``` -------------------------------- ### Setting and Using Credit Limit on a Wallet Source: https://bavix.github.io/laravel-wallet/guide/single/credit-limits.html Demonstrates how to set a credit limit on an existing wallet and shows a successful transaction that utilizes the credit. ```php /** * @var \Bavix\Wallet\Interfaces\Customer $customer * @var \Bavix\Wallet\Models\Wallet $wallet * @var \Bavix\Wallet\Interfaces\ProductInterface $product */ $wallet = $customer->wallet; // get default wallet $wallet->meta['credit'] = 10000; // credit limit $wallet->save(); // update credit limit $wallet->balanceInt; // 0 $product->getAmountProduct($customer); // 500 $wallet->pay($product); // success $wallet->balanceInt; // -500 ``` -------------------------------- ### Creating a Multi-Wallet with a Credit Limit Source: https://bavix.github.io/laravel-wallet/guide/single/credit-limits.html Shows how to create a new wallet for a user with a specified credit limit using the `createWallet` method. ```php /** @var \Bavix\Wallet\Traits\HasWallets $user */ $wallet = $user->createWallet([ 'name' => 'My Wallet', 'meta' => ['credit' => 500], ]); ``` -------------------------------- ### Get Item Price Source: https://bavix.github.io/laravel-wallet/guide/purchases/payment.html Retrieve the price of an item for a specific user. This value is determined by the `getAmountProduct` method in the Item model. ```php $item = Item::first(); $item->getAmountProduct($user); // 100 ``` -------------------------------- ### Create Wallets and Initial Deposit Source: https://bavix.github.io/laravel-wallet/guide/multi/transfer.html Create new wallets for the identified users and make an initial deposit to the first user's wallet. Verifies initial balances. ```php $firstWallet = $first->createWallet(['name' => 'First User Wallet']); $lastWallet = $last->createWallet(['name' => 'Second User Wallet']); $firstWallet->deposit(100); $firstWallet->balance; // 100 $lastWallet->balance; // 0 ``` -------------------------------- ### Make a Deposit Source: https://bavix.github.io/laravel-wallet/guide/single/deposit.html Credit the user's wallet with a specified amount using the deposit method. The balance will be updated accordingly. ```php $user->deposit(10); $user->balance; // 10 $user->balanceInt; // 10 ``` -------------------------------- ### Add $quantity parameter to canBuy method Source: https://bavix.github.io/laravel-wallet/guide/introduction/upgrade.html Starting from version 2.4.x, the `canBuy` method requires a `$quantity` parameter, defaulting to 1. ```php // old public function canBuy(Customer $customer, bool $force = false): bool // new public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool ``` -------------------------------- ### Publish Laravel Wallet Migrations Source: https://bavix.github.io/laravel-wallet/guide/introduction/installation.html Run this Artisan command to publish the migration files for Laravel Wallet. ```bash php artisan vendor:publish --tag=laravel-wallet-migrations ``` -------------------------------- ### Configure User Model for Multi-Wallets Source: https://bavix.github.io/laravel-wallet/guide/additions/swap.html Set up your User model to implement the Wallet interface and use the HasWallet and HasWallets traits to enable multi-wallet functionality. ```php use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Traits\HasWallets; use Bavix\Wallet\Traits\HasWallet; class User extends Model implements Wallet { use HasWallet, HasWallets; } ``` -------------------------------- ### Create Wallet with Currency Meta Source: https://bavix.github.io/laravel-wallet/guide/single/exchange.html Set the currency for a wallet using the 'meta' attribute during creation. This is useful for defining the base currency of a wallet. ```php $user->createWallet([ 'name' => 'My USD Wallet', 'meta' => ['currency' => 'USD'], ]); ``` -------------------------------- ### Retrieve a Specific Wallet by Slug Source: https://bavix.github.io/laravel-wallet/guide/multi/new-wallet.html Get a specific wallet associated with a user using its unique 'slug'. This allows direct access to the wallet's properties and methods. ```php $myWallet = $user->getWallet('my-wallet'); $myWallet->balance; // 100 $myWallet->balanceFloatNum; // 1.00 ``` -------------------------------- ### Initialize User Balance Source: https://bavix.github.io/laravel-wallet/guide/purchases/cart.html Retrieve the first user and check their current balance, which is initially 0. ```php $user = User::first(); $user->balance; // 0 ``` -------------------------------- ### Create and Confirm a Transaction Source: https://bavix.github.io/laravel-wallet/guide/single/confirm.html Demonstrates how to create a deposit transaction without immediate confirmation and then confirm it later. Note that you can only confirm a transaction with the wallet that originally made the payment. ```php $user->balance; // 0 $transaction = $user->deposit(100, null, false); // not confirm $transaction->confirmed; // bool(false) $user->balance; // 0 $user->confirm($transaction); // bool(true) $transaction->confirmed; // bool(true) $user->balance; // 100 ``` -------------------------------- ### Publish Laravel Wallet Configuration Source: https://bavix.github.io/laravel-wallet/guide/db/race-condition.html Run this command to publish the Laravel Wallet configuration file to your project. ```bash php artisan vendor:publish --tag=laravel-wallet-config ``` -------------------------------- ### Item Model for Unlimited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/cart.html Implement the ProductInterface and HasWallet trait for items that can be purchased in unlimited quantities. Define the product amount and metadata. ```php 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 round($this->price * 100); } public function getMetaProduct(): ?array { return [ 'title' => $this->title, 'description' => 'Purchase of Product #' . $this->id, ]; } } ``` -------------------------------- ### Creating a WalletCreated Event Listener Source: https://bavix.github.io/laravel-wallet/guide/events/wallet-created-event.html Implement a listener class that handles the WalletCreatedEventInterface. This class will contain the logic to execute when a wallet is created. ```php use Bavix\Wallet\Internal\Events\WalletCreatedEventInterface; class MyWalletCreatedListener { public function handle(WalletCreatedEventInterface $event): void { // And then the implementation... } } ``` -------------------------------- ### Retrieving Product for Gift Source: https://bavix.github.io/laravel-wallet/guide/purchases/gift.html Fetch the first available item from the database to be used as the gift. This step identifies the product that will be purchased. ```php $item = Item::first(); $item->getAmountProduct($first); // 100 $item->balance; // 0 ``` -------------------------------- ### Perform Atomic Operation on a Single Wallet Source: https://bavix.github.io/laravel-wallet/guide/db/atomic-service.html Use the `block` method to perform atomic operations on a single wallet. This method starts a transaction and locks the wallet, ensuring that subsequent operations within the closure are executed atomically. If any operation fails (e.g., insufficient funds), the entire transaction is rolled back. ```php use Bavix\Wallet\Services\AtomicServiceInterface; app(AtomicServiceInterface::class)->block($wallet, function () use ($wallet, $entity) { $entity->increaseSales(); // update entity set sort_at=NOW() where id=123; $wallet->withdraw(100); }); ``` -------------------------------- ### Create Wallets for Exchange Source: https://bavix.github.io/laravel-wallet/guide/single/exchange.html Create two distinct wallets, each with a specified currency meta. This sets up the wallets for a currency exchange operation. ```php $usd = $user->createWallet([ 'name' => 'My Dollars', 'meta' => ['currency' => 'USD'], ]); $rub = $user->createWallet([ 'name' => 'My Ruble', 'meta' => ['currency' => 'RUB'], ]); ``` -------------------------------- ### Populate and Pay Shopping Cart Source: https://bavix.github.io/laravel-wallet/guide/purchases/cart.html Add multiple items to a cart, calculate the total, deposit funds, and then pay for the cart. This demonstrates adding items by slug and then overriding price per item. ```php use Bavix\Wallet\Objects\Cart; $list = [ 'potato' => 3, 'carrot' => 10, ]; $products = Item::query() ->whereIn('slug', ['potato', 'carrot']) ->get(); $cart = app(Cart::class); foreach ($products as $product) { $cart = $cart->withItem($product, quantity: $list[$product->slug]); } $cartTotal = $cart->getTotal($user); // 15127 $user->deposit($cartTotal); $user->balanceInt; // 15127 $user->balanceFloat; // 151.27 $cart = $cart->withItem(current($products), pricePerItem: 500); // 15127+500 $user->deposit(500); $user->balanceInt; // 15627 $user->balanceFloat; // 156.27 (bool)$user->payCart($cart); // true $user->balanceFloat; // 0 ``` -------------------------------- ### Item Model for Unlimited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/gift.html Configure your Item model to be a product with an unlimited supply. Implement the ProductInterface and define the amount and metadata for the product. ```php 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, ]; } } ``` -------------------------------- ### Action Handler for Dispatching Wallet Creation Source: https://bavix.github.io/laravel-wallet/guide/cqrs/create-wallet.html This handler receives wallet creation requests, creates a command message, and dispatches it for asynchronous processing. It returns a 202 Accepted response. ```php use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Response as ResponseFactory; ... public function __invoke(User $user, Request $request): Response { $name = $request->get('wallet_name'); $uuid = $request->get('wallet_uuid'); $message = new CreateWalletCommandMessage($user, $name, $uuid); dispatch($message); return ResponseFactory::json([], 202); } ``` -------------------------------- ### Create a New Wallet for a User Source: https://bavix.github.io/laravel-wallet/guide/multi/new-wallet.html Create a new wallet for a user, ensuring the 'slug' is unique. This snippet demonstrates creating a wallet, checking its existence, and performing an initial deposit. ```php $user = User::first(); $user->hasWallet('my-wallet'); // bool(false) $wallet = $user->createWallet([ 'name' => 'New Wallet', 'slug' => 'my-wallet', ]); $user->hasWallet('my-wallet'); // bool(true) $wallet->deposit(100); $wallet->balance; // 100 $wallet->balanceFloatNum; // 1.00 ``` -------------------------------- ### Performing a Refund with Laravel Wallet Source: https://bavix.github.io/laravel-wallet/guide/purchases/refund.html Demonstrates how to initiate a refund for a purchased item. It involves checking balances and using the refund method on the user or the PurchaseQueryHandlerInterface. ```php $user = User::first(); $user->balance; // 0 ``` ```php $item = Item::first(); $item->balance; // 100 ``` ```php (bool) app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($user, $item)); // bool(true) (bool)$user->refund($item); // bool(true) $item->balance; // 0 $user->balance; // 100 ``` -------------------------------- ### Implement WalletStateBatchProjectorInterface Source: https://bavix.github.io/laravel-wallet/guide/events/wallet-state-projection.html Implement the WalletBatchProjectorInterface to define custom projection rules for wallet states. This includes calculating and returning custom fields like balance_after, held_balance, and state_hash. ```php use Bavix\Wallet\Internal\Projector\WalletBatchProjectorInterface; final readonly class WalletStateBatchProjector implements WalletBatchProjectorInterface { public function project(array $balances, array $walletsById): array { $rows = []; foreach ($balances as $walletId => $resultingBalance) { $wallet = $walletsById[$walletId] ?? null; if ($wallet === null) { continue; } $heldBalance = (string) ($wallet->getAttribute('held_balance') ?? '0'); $rows[$walletId] = [ 'balance_after' => $resultingBalance, 'held_balance' => $heldBalance, 'state_hash' => hash('sha256', $wallet->uuid.':'.$resultingBalance.':'.$heldBalance), ]; } return $rows; } } ``` -------------------------------- ### Perform Simple Wallet Transactions Source: https://bavix.github.io/laravel-wallet/guide/introduction/basic-usage.html Demonstrates basic deposit, withdraw, and forceWithdraw operations on a user's wallet balance. ```php $user = User::first(); $user->balance; // 0 $user->deposit(10); $user->balance; // 10 $user->withdraw(1); $user->balance; // 9 $user->forceWithdraw(200, ['description' => 'payment of taxes']); $user->balance; // -191 ``` -------------------------------- ### Retrieve Users and Wallets (Lazy Loading) Source: https://bavix.github.io/laravel-wallet/guide/introduction/basic-usage.html This code retrieves all users and then iterates through them, accessing wallet balances. This method results in an N+1 query problem. ```php $users = User::all(); foreach ($users as $user) { // echo $user->wallet->balance; echo $user->balance; // Abbreviated notation } ``` -------------------------------- ### Process and Refund Purchases Source: https://bavix.github.io/laravel-wallet/guide/introduction/basic-usage.html Demonstrates how to use the pay and safePay methods to purchase an item, and how to refund it. ```php $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) app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($user, $item))); // bool(true) var_dump($user->refund($item)); // bool(true) var_dump((bool) app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($user, $item))); // bool(false) ``` -------------------------------- ### Perform Transactions with Fractional Numbers Source: https://bavix.github.io/laravel-wallet/guide/introduction/basic-usage.html Demonstrates how to interact with user balances when using fractional numbers, including deposits and accessing float balances. ```php $user = User::first(); $user->balance; // 100 $user->balanceFloat; // 1.00 $user->depositFloat(1.37); $user->balance; // 237 $user->balanceFloat; // 2.37 ``` -------------------------------- ### Item Model for Limited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/cart.html Implement the ProductLimitedInterface and HasWallet trait for items with purchase constraints. Use PurchaseQuery and PurchaseQueryHandlerInterface for cart checks. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\ProductLimitedInterface; use Bavix\Wallet\External\Api\PurchaseQuery; use Bavix\Wallet\External\Api\PurchaseQueryHandlerInterface; 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 ! app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($customer, $this)); */ return true; } public function getAmountProduct(Customer $customer): int|string { return round($this->price * 100); } public function getMetaProduct(): ?array { return [ 'title' => $this->title, 'description' => 'Purchase of Product #' . $this->id, ]; } } ``` -------------------------------- ### Create Wallets with Specific Currencies Source: https://bavix.github.io/laravel-wallet/guide/additions/swap.html Create new wallets for a user, specifying their name, a unique slug, and associated meta-information like currency. ```php $usd = $user->createWallet([ 'name' => 'My Dollars', 'slug' => 'usd', 'meta' => ['currency' => 'USD'], ]); $rub = $user->createWallet([ 'name' => 'My Ruble', 'slug' => 'rub', 'meta' => ['currency' => 'RUB'], ]); ``` -------------------------------- ### Register Wallet Projector Source: https://bavix.github.io/laravel-wallet/guide/events/wallet-state-projection.html Register your custom wallet projector in the application's configuration file. ```php 'projectors' => [ 'wallet' => \App\Wallet\WalletStateBatchProjector::class, ], ``` -------------------------------- ### Command Handler for Asynchronous Wallet Creation Source: https://bavix.github.io/laravel-wallet/guide/cqrs/create-wallet.html This handler processes the CreateWalletCommandMessage to create a new wallet for the user with the provided UUID, name, and other details. ```php public function __invoke(CreateWalletCommandMessage $message): void { $user = $message->getUser(); $user->createWallet([ 'uuid' => $message->getWalletUuid(), 'name' => $message->getWalletName(), ]); } ``` -------------------------------- ### Implement Wallet Interface and HasWalletFloat Trait Source: https://bavix.github.io/laravel-wallet/guide/fractional/withdraw.html Extend your User model to implement the Wallet interface and use the HasWalletFloat trait to enable wallet functionality. ```php use Bavix\Wallet\Traits\HasWalletFloat; use Bavix\Wallet\Interfaces\Wallet; class User extends Model implements Wallet { use HasWalletFloat; } ``` -------------------------------- ### Configure Custom Event Assembler in Wallet Settings Source: https://bavix.github.io/laravel-wallet/guide/events/customize.html Update the 'wallet.php' configuration file to use your custom assembler for balance updated events. ```php 'assemblers' => [ 'balance_updated_event' => MyUpdatedEventAssembler::class, ], ``` -------------------------------- ### Item Model for Limited Products Source: https://bavix.github.io/laravel-wallet/guide/purchases/gift.html Configure your Item model to be a product with a limited supply using the ProductLimitedInterface. Implement the canBuy method to enforce purchase constraints and define product amount and metadata. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\ProductLimitedInterface; use Bavix\Wallet\External\Api\PurchaseQuery; use Bavix\Wallet\External\Api\PurchaseQueryHandlerInterface; 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 ! app(PurchaseQueryHandlerInterface::class)->one(PurchaseQuery::create($customer, $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, ]; } } ``` -------------------------------- ### Minimal Taxing: Item Model Configuration Source: https://bavix.github.io/laravel-wallet/guide/purchases/commissions.html Implement the MinimalTaxable interface in the Item model to set a minimum fee percentage. This ensures a minimum commission is always applied. ```php use Bavix\Wallet\Traits\HasWallet; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\MinimalTaxable; use Bavix\Wallet\Interfaces\ProductInterface; class Item extends Model implements ProductInterface, MinimalTaxable { 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, ]; } public function getFeePercent() { return 0.03; // 3% } public function getMinimalFee() { return 5; // 3%, minimum 5 } } ``` -------------------------------- ### Implement State-Aware Transaction Assembler Source: https://bavix.github.io/laravel-wallet/guide/events/transaction-state-projection.html Create a custom assembler that computes balance before and after a transaction and pushes this state to `TransactionStateService`. This is used when state tracking is required. ```php use Bavix\Wallet\Enums\TransactionType; use Bavix\Wallet\Internal\Assembler\TransactionDtoAssembler; use Bavix\Wallet\Internal\Assembler\TransactionDtoAssemblerInterface; use Bavix\Wallet\Internal\Dto\TransactionDtoInterface; use Bavix\Wallet\Internal\Service\MathServiceInterface; use Bavix\Wallet\Internal\Service\TransactionStateService; use Bavix\Wallet\Services\RegulatorServiceInterface; use Illuminate\Database\Eloquent\Model; final readonly class StateAwareTransactionAssembler implements TransactionDtoAssemblerInterface { public function __construct( private TransactionDtoAssembler $base, private TransactionStateService $stateService, // Your service instance private RegulatorServiceInterface $regulator, private MathServiceInterface $mathService, ) {} public function create( Model $payable, int $walletId, TransactionType $type, float|int|string $amount, bool $confirmed, ?array $meta, ?string $uuid ): TransactionDtoInterface { $dto = $this->base->create($payable, $walletId, $type, $amount, $confirmed, $meta, $uuid); $before = $this->regulator->amount($payable); $after = $before; if ($confirmed) { $after = $type === TransactionType::Deposit ? $this->mathService->add($before, $amount) : $this->mathService->sub($before, $amount); } // Push state to your service $this->stateService->push($dto->getUuid(), $walletId, [ 'balance' => $before, ], [ 'balance' => $after, ]); return $dto; } } ``` -------------------------------- ### Update product model methods for v5.x Source: https://bavix.github.io/laravel-wallet/guide/introduction/upgrade.html In version 5.x, strong typing was removed from product model methods like `getAmountProduct`, `getFeePercent`, and `getMinimalFee` to support Arbitrary Precision Mathematics. ```php public function getAmountProduct(Customer $customer): int { ... } public function getFeePercent(): float { ... } public function getMinimalFee(): int { ... } ``` ```php public function getAmountProduct(Customer $customer) { ... } public function getFeePercent() { ... } public function getMinimalFee() { ... } ``` -------------------------------- ### Configure Default Wallet Source: https://bavix.github.io/laravel-wallet/guide/introduction/configuration.html Customize the name, slug, and meta information for the default wallet. ```php 'default' => [ 'name' => 'Ethereum', 'slug' => 'ETH', 'meta' => [], ], ``` -------------------------------- ### Payment Process with MerchantFeeDeductible Source: https://bavix.github.io/laravel-wallet/guide/purchases/merchant-fee-deductible.html Demonstrates the payment flow where the customer pays only the product price, and the fee is deducted from the merchant's payout. ```php $user = User::first(); $user->balance; // 100 ``` ```php $item = Item::first(); $item->getAmountProduct($user); // 100 ``` ```php $user->pay($item); // success, customer pays $100 (product price) $user->balance; // 0 ``` -------------------------------- ### Retrieve Users with Eager Loaded Wallets Source: https://bavix.github.io/laravel-wallet/guide/introduction/basic-usage.html Use the `with` method to eager load wallet relationships, reducing the number of database queries from N+1 to 2. ```php $users = User::with('wallet')->all(); foreach ($users as $user) { // echo $user->wallet->balance; echo $user->balance; // Abbreviated notation } ``` -------------------------------- ### Checking User Balances Source: https://bavix.github.io/laravel-wallet/guide/purchases/gift.html Retrieve the first and last users from the database and display their current balances. This is a prerequisite step before initiating a gift purchase. ```php $first = User::first(); $last = User::orderBy('id', 'desc')->first(); // last user $first->getKey() !== $last->getKey(); // true $first->balance; // 115 $last->balance; // 0 ``` -------------------------------- ### Tax Process: User Balance and Item Cost Source: https://bavix.github.io/laravel-wallet/guide/purchases/commissions.html Retrieve the user and item, then check their respective balance and cost before a transaction. ```php $user = User::first(); $user->balance; // 103 ``` ```php $item = Item::first(); $item->getAmountProduct($user); // 100 ``` -------------------------------- ### Implement Transaction State Transformer Source: https://bavix.github.io/laravel-wallet/guide/events/transaction-state-projection.html Create a transformer to extract and persist the computed balance before, balance after, and a state hash into the transaction record. This ensures state is saved atomically. ```php use Bavix\Wallet\Internal\Dto\TransactionDtoInterface; use Bavix\Wallet\Internal\Service\TransactionStateService; use Bavix\Wallet\Internal\Transform\TransactionDtoTransformer; use Bavix\Wallet\Internal\Transform\TransactionDtoTransformerInterface; final readonly class TransactionStateDtoTransformer implements TransactionDtoTransformerInterface { public function __construct( private TransactionDtoTransformer $base, private TransactionStateService $stateService, ) {} public function extract(TransactionDtoInterface $dto): array { $result = $this->base->extract($dto); if (!$this->stateService->has($dto->getUuid())) { return $result; } $before = $this->stateService->before($dto->getUuid()); $after = $this->stateService->after($dto->getUuid()); $result['balance_before'] = $before['balance']; $result['balance_after'] = $after['balance']; $result['state_hash'] = hash('sha256', $dto->getUuid().':'.$dto->getAmount().':'.$before['balance'].':'.$after['balance']); return $result; } } ``` -------------------------------- ### Perform a Simple Transfer Source: https://bavix.github.io/laravel-wallet/guide/multi/transfer.html Transfer a specified amount from the first user's wallet to the last user's wallet. Updates and verifies balances after the transfer. ```php $firstWallet->transfer($lastWallet, 5); $firstWallet->balance; // 95 $lastWallet->balance; // 5 ``` -------------------------------- ### Creating Transactions with Client-Generated UUIDs Source: https://bavix.github.io/laravel-wallet/guide/high-performance/batch-transactions.html Demonstrates how to create deposit and withdrawal transactions with a client-provided UUID to ensure uniqueness. Supports both integer and float amounts. ```php use Bavix\Wallet\External\Api\TransactionQuery; use Bavix\Wallet\External\Api\TransactionFloatQuery; // int version TransactionQuery::createDeposit($wallet, $amount, null, uuid: '5f7820d1-1e82-4d03-9414-05d0c44da9a1'); TransactionQuery::createWithdraw($wallet, $amount, null, uuid: '6e87dbf2-7be7-48c2-b688-f46ba4e25786'); // float version TransactionFloatQuery::createDeposit($wallet, $amountFloat, null, uuid: '5f7820d1-1e82-4d03-9414-05d0c44da9a1'); TransactionFloatQuery::createWithdraw($wallet, $amountFloat, null, uuid: '6e87dbf2-7be7-48c2-b688-f46ba4e25786'); ``` -------------------------------- ### Custom Assembler with State Tracking Source: https://bavix.github.io/laravel-wallet/guide/introduction/upgrade.html Implement a custom assembler to track transaction state changes. This involves creating a new class that implements `TransactionDtoAssemblerInterface` and utilizes `TransactionStateService` for state management and `MathServiceInterface` for arithmetic operations. ```php final class StateAwareAssembler implements TransactionDtoAssemblerInterface { public function __construct( private TransactionDtoAssembler $base, private TransactionStateService $stateService, // Your instance private RegulatorServiceInterface $regulator, private MathServiceInterface $mathService, ) {} public function create(...): TransactionDtoInterface { $dto = $this->base->create(...); $before = $this->regulator->amount($payable); $after = $ confirmado ? $this->mathService->add($before, $amount) // or sub for withdraw : $before; $this->stateService->push($dto->getUuid(), $walletId, [ 'balance' => $before, ], [ 'balance' => $after, ]); return $dto; } } ``` ```php 'assemblers' => [ 'transaction' => \App\Wallet\StateAwareAssembler::class, ], ``` -------------------------------- ### Extend Base Wallet Model Source: https://bavix.github.io/laravel-wallet/guide/introduction/configuration.html Create a custom Wallet model by extending the base `Bavix\Wallet\Models\Wallet` class. ```php use Bavix\Wallet\Models\Wallet as WalletBase; class MyWallet extends WalletBase { public function helloWorld(): string { return "hello world"; } } ``` -------------------------------- ### Creating a Custom Balance Update Listener Source: https://bavix.github.io/laravel-wallet/guide/events/balance-updated-event.html Implement a custom listener class that handles the BalanceUpdatedEventInterface. This class will contain the logic to execute whenever a user's balance is updated. ```php use Bavix\Wallet\Internal\Events\BalanceUpdatedEventInterface; class MyBalanceUpdatedListener { public function handle(BalanceUpdatedEventInterface $event): void { // And then the implementation... } } ``` -------------------------------- ### Basic Transaction with Wallet Operation Source: https://bavix.github.io/laravel-wallet/guide/db/transaction.html Demonstrates a basic transaction block using Illuminate Support Facades DB. The wallet is blocked when accessing its balance within the transaction and unlocked upon commit. ```php use Illuminate\Support\Facades\DB; DB::beginTransaction(); $wallet->balanceInt; // now the wallet is blocked doingMagic(); // running for a long time. DB::commit(); // here will unlock the wallet ``` -------------------------------- ### Implement Custom Balance Updated Event Source: https://bavix.github.io/laravel-wallet/guide/events/customize.html Create a custom event class that extends the standard BalanceUpdatedEventInterface and implements ShouldBroadcast for real-time updates. ```php use Bavix\Wallet\Internal\Events\BalanceUpdatedEventInterface; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; final class MyUpdatedEvent implements BalanceUpdatedEventInterface, ShouldBroadcast { public function __construct( private \Bavix\Wallet\Models\Wallet $wallet, private DateTimeImmutable $updatedAt, ) {} public function getWalletId(): int { return $this->wallet->getKey(); } public function getWalletUuid(): string { return $this->wallet->uuid; } public function getBalance(): string { return $this->wallet->balanceInt; } public function getUpdatedAt(): DateTimeImmutable { return $this->updatedAt; } public function broadcastOn(): array { return $this->wallet->getAttributes(); } } ``` -------------------------------- ### Efficient Batch Deposit using API Handler Source: https://bavix.github.io/laravel-wallet/guide/high-performance/batch-transactions.html This snippet shows how to perform multiple deposit transactions efficiently using the `TransactionQueryHandlerInterface`. It maps wallets to `TransactionQuery` objects for batch processing. ```php use Bavix\Wallet\External\Api\TransactionQuery; use Bavix\Wallet\External\Api\TransactionQueryHandlerInterface; app(TransactionQueryHandlerInterface::class)->apply( array_map( static fn (Wallet $wallet) => TransactionQuery::createDeposit($wallet, $amount, null), $wallets ) ); ``` -------------------------------- ### Basic Atomic Operation for Multiple Wallets Source: https://bavix.github.io/laravel-wallet/guide/high-performance/batch-transactions.html This snippet demonstrates a basic atomic operation to process multiple wallet transactions sequentially. It's suitable for scenarios where operations might depend on each other. ```php use Bavix\Wallet\Services\AtomicServiceInterface; app(AtomicServiceInterface::class)->blocks($wallets, function () use ($amount, $wallets) { foreach ($wallets as $wallet) { $wallet->deposit($amount); } }); ``` -------------------------------- ### Implement Custom Event Assembler Source: https://bavix.github.io/laravel-wallet/guide/events/customize.html Create a custom assembler class to instantiate your custom event. This class implements the BalanceUpdatedEventAssemblerInterface. ```php use Bavix\Wallet\Internal\Assembler\BalanceUpdatedEventAssemblerInterface; class MyUpdatedEventAssembler implements BalanceUpdatedEventAssemblerInterface { public function create(\Bavix\Wallet\Models\Wallet $wallet) : \Bavix\Wallet\Internal\Events\BalanceUpdatedEventInterface { return new MyUpdatedEvent($wallet, new DateTimeImmutable()); } } ``` -------------------------------- ### User Model Configuration for Gifts Source: https://bavix.github.io/laravel-wallet/guide/purchases/gift.html Configure your User model to be a customer and enable payment capabilities by using the CanPay trait and Customer interface. Ensure the CanPay trait is used, as it already includes HasWallet. ```php use Bavix\Wallet\Traits\CanPay; use Bavix\Wallet\Interfaces\Customer; class User extends Model implements Customer { use CanPay; } ``` -------------------------------- ### Perform a Standard Float Transfer with Extra Options Source: https://bavix.github.io/laravel-wallet/guide/fractional/transfer.html Use the `transferFloat` method to move funds between wallets. This method supports specifying extra options for deposit, withdrawal, and general extra data, including an option to control confirmation. ```php $transfer = $user1->transferFloat( $user2, 5.11, new Extra( deposit: [ 'type' => 'extra-deposit', ], withdraw: new Option( [ 'type' => 'extra-withdraw', ], false // confirmed ), extra: [ 'msg' => 'hello world', ], ) ); ``` -------------------------------- ### Perform Atomic Operations on Multiple Wallets Source: https://bavix.github.io/laravel-wallet/guide/db/atomic-service.html Use the `blocks` method to perform atomic operations on multiple wallets simultaneously. This is useful when you need to debit funds from several wallets as a single atomic unit. If any of the wallets do not have sufficient funds, the entire operation is canceled and rolled back. ```php use Bavix\Wallet\Services\AtomicServiceInterface; app(AtomicServiceInterface::class)->blocks([$wallet1, $wallet2], function () use ($wallet1, $wallet2) { $wallet1->withdraw(100); $wallet2->withdraw(100); }); ``` -------------------------------- ### Register Extended Wallet Model Source: https://bavix.github.io/laravel-wallet/guide/introduction/configuration.html Register your custom Wallet model in the `config/wallet.php` configuration file. ```php 'wallet' => [ 'table' => 'wallets', 'model' => MyWallet::class, 'creating' => [], 'default' => [ 'name' => 'Default Wallet', 'slug' => 'default', 'meta' => [], ], ], ``` -------------------------------- ### Exchange Balances Between Wallets Source: https://bavix.github.io/laravel-wallet/guide/additions/swap.html Retrieve existing wallets and exchange funds from one to another. The exchange rate is handled automatically based on the meta information. ```php $rub = $user->getWallet('rub'); $usd = $user->getWallet('usd'); $usd->balance; // 200 $rub->balance; // 0 $usd->exchange($rub, 10); $usd->balance; // 190 $rub->balance; // 622 ``` -------------------------------- ### Find User Model Source: https://bavix.github.io/laravel-wallet/guide/single/deposit.html Retrieve the first user from the database to perform wallet operations. ```php $user = User::first(); ``` -------------------------------- ### Check User Balance Source: https://bavix.github.io/laravel-wallet/guide/single/deposit.html Access the user's balance using the 'balance' or 'balanceInt' properties. Initially, the balance is zero. ```php $user->balance; // 0 $user->balanceInt; // 0 ``` -------------------------------- ### Minimal Taxing: Failed Purchase Attempt Source: https://bavix.github.io/laravel-wallet/guide/purchases/commissions.html Demonstrates a failed purchase attempt when the user's balance is insufficient to cover the product price plus the minimal fee. safePay is used to prevent the transaction. ```php $user = User::first(); $user->balance; // 103 ``` ```php $item = Item::first(); $item->getAmountProduct($user); // 100 ``` ```php $user->safePay($item); // failed, 100 (product) + 5 (minimal fee) = 105 $user->balance; // 103 ```