Skip to content

Upgrade Guide

1.x.x → 2.x.x

Replace ::with('balance') to ::with('wallet')

2.1.x → 2.2.x

Replace CanBePaid to CanPay.

Replace CanBePaidFloat to CanPayFloat.

2.2.x → 2.4.x

Replace calculateBalance to refreshBalance

2.4.x → 3.0.x

Replace path bavix.wallet::transaction to Bavix\Wallet\Models\Transaction::class

Replace path bavix.wallet::transfer to Bavix\Wallet\Models\Transfer::class

Replace path bavix.wallet::wallet to Bavix\Wallet\Models\Wallet::class

php
// old
app('bavix.wallet::transaction'); 
// new
app(Bavix\Wallet\Models\Transaction::class);

Add the $quantity parameter to the canBuy method.

php
// old
public function canBuy(Customer $customer, bool $force = false): bool
// new
public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool

Add method getUniqueId to Interface Product

php
class Item extends Model implements Product
{
    
    // Your method
    
    public function getUniqueId(): string
    {
        return (string)$this->getKey();
    }
    
}

3.0.x → 3.1.x

Replace Taxing to Taxable.

3.1.x → 4.0.x

If you are using PHP 7.1, then v4.0.0 is not available to you. You need to update php.

Removed support for older versions of laravel/cashier. We support 7+.

If you use payment for goods

You must add the argument Customer $customer to the getAmountProduct method of your model.

Your code on 3.x:

php
    public function getAmountProduct(): int
    {
        return $this->price;
    }

Your code on 4.x:

php
    public function getAmountProduct(Customer $customer): int
    {
        return $this->price;
    }

4.0.x → 5.0.x

By updating the library from 4.x to 5.x you lose strong typing. This solution was necessary to support APM (Arbitrary Precision Mathematics).

In your goods:

Your code on 4.x:

php
    public function getAmountProduct(Customer $customer): int  { ... }

    public function getFeePercent(): float  { ... }

    public function getMinimalFee(): int { ... }

Your code on 5.x:

php
    public function getAmountProduct(Customer $customer) { ... }

    public function getFeePercent() { ... }

    public function getMinimalFee() { ... }

In the exchange rate processing service:

Your code on 4.x:

php
    protected function rate(Wallet $wallet): float { ... }

    public function convertTo(Wallet $wallet): float { ... }

Your code on 5.x:

php
    protected function rate(Wallet $wallet) { ... }

    public function convertTo(Wallet $wallet) { ... }

5.x.x → 6.0.x

Go to config/wallet.php file (if you have it) and edit it.

Removing unnecessary code.

php
$bcLoaded = extension_loaded('bcmath');	
$mathClass = Math::class;	
switch (true) {	
    case class_exists(BigDecimal::class):	
        $mathClass = BrickMath::class;	
        break;	
    case $bcLoaded:	
        $mathClass = BCMath::class;	
        break;	
}

Replace your math class ($mathClass) with brick/math.

Your code on 5.x:

php
    'mathable' => $mathClass,

Your code on 6.x:

php
    'mathable' => BrickMath::class,

6.x.x → 6.2.4

You need to update to the latest version for all migrations to appear.

6.2.4 → 7.x.x

Update config/wallet.php

The config/wallet.php config has changed a lot, if you have it in your project, then replace it run.

bash
php artisan vendor:publish --tag=laravel-wallet-config --force

Then return your settings. The package configuration has changed globally and there is no point in describing each key 🔑


UUID for wallet

The uuid field has been added to the wallet table, which is now actively used. If you have a highload, then I recommend that you add the field yourself and mark the migration (UpdateWalletsUuidTable) completed. If you have mysql, it is better to do this via pt-online-schema-change.

If you have a small project and a small wallet base, then the migration will be applied automatically.


That's it, you can use all 7.x functions to the fullest. The contract did not change globally, added more stringency and toned down the performance of the package. On a basket of 150 products, the acceleration is a whopping 24x.

All changes can be found in the pull request. The kernel has changed globally; I do not recommend switching to v7.0.0 at the very beginning, because there may be bugs. I advise you should at least 7.0.1.

7.x.x → 8.0.x

Nothing needs to be done.

8.0.x → 8.1.x

Replace getAvailableBalance to getAvailableBalanceAttribute (method) or available_balance (property).


Cart methods now support fluent-dto. It is necessary to replace the old code with a new one, for example:

php
// old
$cart = app(\Bavix\Wallet\Objects\Cart::class)
    ->addItems($products)
    ->addItem($product)
    ->setMeta(['hello' => 'world']);
    
$cart->addItem($product);

// new. fluent
$cart = app(\Bavix\Wallet\Objects\Cart::class)
    ->withItems($products)
    ->withItem($product)
    ->withMeta(['hello' => 'world']);

$cart = $cart->withItem($product);

8.1.x+ → 9.0.x

The logic of storing transfers between accounts has changed. Previously, money could be credited to the user directly, but starting from v9.0.0, all transactions go strictly between wallets. Thanks to this approach, finally, there will be full-fledged work with uuid identifiers in the project.

To migrate to the correct structure, you need to run the command:

artisan bx:transfer:fix

If the command fails, then the command must be restarted. Continue until the command starts executing immediately (no bad entries left).


The product has been divided into two interfaces:

  • ProductLimitedInterface. Needed to create limited goods;
  • ProductInterface. Needed for an infinite number of products;

The old Product interface should be replaced with one of these.

Replace Bavix\Wallet\Interfaces\Product to Bavix\Wallet\Interfaces\ProductLimitedInterface.

9.x.x → 10.0.x

  1. If you have a custom BookkeeperServiceInterface, then you need to update the contract.
  2. If you catch a LockProviderNotFoundException, then you need to remove the check. This exception no longer exists.
  3. If you have specific requests for transfers using the MorphMany relation, then you need to rewrite it to the HasMany relation.

10.x.x → 11.0.x

  1. If you have mariadb, then the minimum supported version is 10.10. More details here: https://github.com/laravel/framework/pull/48455;
  2. Perform new package migrations, support for soft deleted has been added;
  3. If you used delete methods, then they need to be replaced with forceDelete (if soft delete support is not needed);
  4. Obsolete columns from_type, to_type in the transfers table have been physically removed. Make sure you don't use them;
  5. An extra column has been added to the transfers table. Don't forget to apply all new migrations;
  6. The Bavix\Wallet\Interfaces\Wallet contract has been extended with the receivedTransfers method. If you overridden the implementation, then implement the new method;

11.x.x → 12.0.x

  1. Minimum Laravel version is now ^13.0;

  2. Deprecated constants were removed:

    • Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW;
    • Transfer::STATUS_EXCHANGE, Transfer::STATUS_TRANSFER, Transfer::STATUS_PAID, Transfer::STATUS_REFUND, Transfer::STATUS_GIFT;
  3. Use enums instead:

    • Bavix\Wallet\Enums\TransactionType;
    • Bavix\Wallet\Enums\TransferStatus;
  4. Deprecated UUID factory support was removed:

    • UuidFactoryServiceInterface, UuidFactoryService;
    • wallet.internal.uuid config key;
  5. Customer::paid() / CartPay::paid() were removed. Use PurchaseQuery + PurchaseQueryHandlerInterface for purchase checks.

  6. PurchaseServiceInterface::already() and PurchaseService are deprecated. They are now legacy extension points and will be removed in v14. New integrations should use PurchaseQueryHandlerInterface.

  7. For transaction state projections (for example issue #1015), use custom Assembler pattern:

    • Create custom TransactionDtoAssemblerInterface implementation
    • Use TransactionStateService (from Internal\Service\TransactionStateService) to track state
    • Use MathServiceInterface for arithmetic (add/sub)
    • Implement custom TransactionDtoTransformerInterface to persist state columns
    • Compute state_hash in your app code (business rule stays outside package core)

Example:

php
// Custom assembler with state tracking
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;
    }
}

Register in config:

php
'assemblers' => [
    'transaction' => \App\Wallet\StateAwareAssembler::class,
],
  1. For wallet state projections, use WalletBatchProjectorInterface. Projector adds custom wallet columns in same balance update flow.

Example migration for purchase checks:

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;

12.0.x → 12.1.x

  1. No code changes are required. Perform new package migrations;

  2. Four redundant indexes are removed:

    • transactions.payable_type_payable_id_ind — a duplicate of the morphs() index;
    • transactions.payable_type_ind — a prefix of payable_type_confirmed_ind;
    • transactions.transactions_payable_type_payable_id_index — a prefix of payable_type_confirmed_ind;
    • wallets.wallets_holder_type_holder_id_index — a prefix of the unique (holder_type, holder_id, slug);
  3. Every removed index was a duplicate or a leading prefix of a wider one, so no query loses its access path. EXPLAIN on the hot wallet queries gives the same plans before and after (on mysql they are identical — the optimizer used payable_confirmed_ind anyway). Measured on 1M transactions and 200k wallets:

    mysqlpgsql
    index size685 → 329 MB (-52%)265 → 156 MB (-41%)
    insert of 50k rows-63% time-32% time

    The gain is in write throughput and disk footprint, reads stay the same;

  4. If you have a highload project, drop the indexes yourself (pt-online-schema-change for mysql, DROP INDEX CONCURRENTLY for pgsql), then add both migration names to the migrations table so they are not executed again. For a small project php artisan migrate is enough;

Thanks to @jonhassall for these changes.