Estimated Upgrade Time: 5 to 15 minutes. Most applications only need to update the dependency and clear the cache. The longer end applies if you implemented the
TaxableItem contract, built a custom product model against Stockable, or registered the stock reservation listener yourself.Updating Dependencies
Update the following dependency in your application’scomposer.json file:
shopper/framework to ^2.10
Then run:
High Impact Changes
TaxableItem Contract Replaces Two Methods With One
Likelihood of Impact: High if you implementedTaxableItem directly.
Affects applications with a custom tax adapter or a custom tax calculation provider that builds its own taxable items.
The Shopper\Core\Contracts\TaxableItem contract previously exposed the taxable amount as a unit price plus a quantity. Tax was then calculated per unit and multiplied back, which overcharged exclusive zones by a cent and unbalanced inclusive VAT breakdowns whenever a line total was not evenly divisible by its quantity. The contract now exposes a single line total and the provider taxes it in one pass.
The getTaxableAmount() and getQuantity() methods are removed and replaced by getTaxableTotal():
TaxCalculationProvider that read $item->getTaxableAmount() * $item->getQuantity(), replace that expression with $item->getTaxableTotal(). The built-in CartLineTaxAdapter and OrderItemTaxAdapter are already updated.
Stockable Contract Adds tracksInventory()
Likelihood of Impact: High if you swapped the product or variant model. Affects applications that bound a custom model against theProduct, ProductVariant, or Stockable contract.
Stock reservation now skips products that do not track inventory, such as virtual and external products. To decide this, the Shopper\Core\Models\Contracts\Stockable contract gained a tracksInventory(): bool method. Because Stockable is the base of both the product and variant contracts, any custom model that implements one of them must add the method, or it will fail to satisfy the interface at runtime.
Mirror the logic from the built-in Product model:
true unconditionally.
Medium Impact Changes
Stock Reservation Moved Into the Checkout Transaction
Likelihood of Impact: Medium Affects applications that registered the old reservation listener or instantiateCreateOrderFromCartAction manually.
Stock was previously reserved by a queued ReserveOrderItemStockListener that fired after the order transaction committed, with no row lock. Two concurrent checkouts reading the same last unit could both succeed and oversell. Reservation now happens synchronously inside CreateOrderFromCartAction, under a lockForUpdate row lock, through the new Shopper\Core\Contracts\StockReserver contract. If the available quantity falls short, InsufficientStockException is thrown and the whole order rolls back.
The Shopper\Core\Listeners\Orders\ReserveOrderItemStockListener class is removed. If you registered it in your application’s EventServiceProvider, drop that entry:
CreateOrderFromCartAction also gained a StockReserver constructor dependency. Always resolve it from the container so the dependency is injected for you, and never instantiate it with new:
Guest Carts Cannot Apply Per-User Discounts
Likelihood of Impact: Medium Affects storefronts that let anonymous visitors apply coupons. A coupon withusage_limit_per_user set was only checked when the cart had a customer_id, so a guest could redeem a once-per-customer code on every anonymous checkout. Both DiscountValidator and CreateOrderFromCartAction now reject these coupons for guest carts with the discount.requires_login error.
If your storefront surfaces validation messages, handle this case by prompting the visitor to sign in before applying the coupon. Carts that already have an authenticated customer_id are unaffected.
Low Impact Changes
Admin Authorization Tightened
Likelihood of Impact: Low Affects applications with custom roles that granted coarse permissions. A full audit added explicitauthorize() calls to every mutating Filament action and Livewire method across the admin panel, and locked previously public model properties on the pricing and variant slide-overs. Standard role setups are unaffected. If you built custom roles that relied on a coarse permission to reach a finer operation, for example granting add_products to manage variants, grant the specific permission instead, such as add_product_variants.
Migration Checklist
Update TaxableItem implementers
Replace
getTaxableAmount() and getQuantity() with getTaxableTotal() on any class that implements TaxableItem.Add tracksInventory() to custom models
If you swapped the product or variant model, add
tracksInventory(): bool to satisfy the Stockable contract.Remove the old reservation listener
Drop any reference to
ReserveOrderItemStockListener from your EventServiceProvider.Resolve checkout action from the container
Replace any
new CreateOrderFromCartAction(...) with resolve(CreateOrderFromCartAction::class).Review guest coupon flows
If anonymous visitors can apply coupons, handle the
discount.requires_login error for per-user discounts.