Solidus v4.8 (2026-10-09)
Minimal requirements:
- v3.2
- v7.2
We're excited to announce the release of Solidus v4.8.0! This release brings the storefront and solidus_i18n into the main repository, a long run of performance improvements, and fixes for bugs that have been open for years. Ruby and Rails requirements are unchanged from v4.7.
🚀 Key Highlights
Storefront and I18n Join the Monorepo
The starter storefront (formerly solidus_starter_frontend) and solidus_i18n now live in the Solidus repository, so they are released in lockstep with core and there is one place to file issues.
Existing stores are unaffected by the storefront move: the storefront is an application template, and your app already owns its copy of the code. If you use solidus_i18n, the gem jumps from 2.2.0 to 4.8.0 and now follows the Solidus version. A bare gem "solidus_i18n" keeps working; if you pinned ~> 2.0, change it to ~> 4.8. It also requires solidus_support 0.12.0 or later.
Performance
ikraamg and Martin Meyerhoff removed N+1 queries and redundant work across the admin, API, taxes, promotions, inventory, and payments. Order recalculation and the admin order and product pages should all issue noticeably fewer queries.
- Eager load associations in admin and API collection endpoints
- Eager load line item details on the admin order page
- Preload variants and products in the promotions order adjuster
- Reuse the order's line items when building a shipping manifest
- Avoid redundant queries when recalculating payment totals
Long-Standing Bugs, Finally Fixed
This release closes issues going back as far as 2017. Shipping calculators no longer count cancelled items, taxon touching no longer deadlocks, customer returns can't be created for unshipped units, static preferences fall back to their defaults, and calculators always return BigDecimal. See the breaking changes below for the two that change behaviour you may depend on.
- Don't include cancelled items in shipping calculations
- Touch taxons one at a time in id order
- Prevent customer returns when the order has no shipped units
- Fall back to preference defaults for static preference sources
- Write log entry details unparsed and validate on read
Solidus Promotions
A new PromotionEligibilityChecker service object checks whether a promotion applies to an order without applying it, replacing the old dry-run flow. The TaxonRevenue condition gained an "exclude" match policy, and the promotion permission sets gained a privilege and category.
- Promotions: Create PromotionEligibilityChecker
- TaxonRevenue condition: Add "exclude" match policy
- Add privilege and category to promotion permission sets
📦 Component Updates
Solidus Core
- Refunds: Refunding a payment that isn't in a refundable state is deprecated and becomes a validation error in 5.0 (PR)
- Stock coordination:
create_proposed_shipmentsis delegated toSpree::OrderShipping(PR) - Log entries:
SymbolandHashWithIndifferentAccessare permitted by default (PR) - Dependencies:
discard2.x is required,kt-paperclip8.0 is allowed, andkaminari-activerecordis no longer a dependency ofsolidus_core(PR, PR, PR)
Solidus Admin
solidus_admin is versioned and released separately. The admin work merged this cycle, including new editors for payment methods, tax rates, stores, option types and product taxons, ships in solidus_admin 0.5.0.
⚠️ Deprecations
Each of these warns through Spree.deprecator and will be removed or enforced in Solidus 5.0.
- Orphaned preferences:
allow_return_item_amount_editing,alternative_billing_phone,auto_capture_exchanges,customer_returns_per_page, andorder_capturing_time_windowhave had no effect for years; remove them from your initializer.allocator_classwas never read; the stock allocator is configured withSpree::Config.stock.allocator_class(PR) - Refunds on non-refundable payments: creating a refund for a payment outside
Spree::Config.refundable_payment_states(by defaultcompletedandpending) warns now and fails validation in 5.0. Opt in early withSpree::Config.require_refundable_payment_state = true(PR) SimpleCoordinatorpositional argument: passinventory_units:as a keyword, and acceptinitialize(order, inventory_units: nil)in custom coordinators (PR)- Promotion dry runs: the
dry_runanddry_run_promotionarguments toSolidusPromotions::OrderAdjuster,LoadPromotions,DiscountOrderandBenefit#eligible_by_applicable_conditions?, andPromotion#eligibility_results, are deprecated. UseSpree::Config.promotions.eligibility_checker_class.new(order:, promotion:).callinstead (PR)
🔄 Breaking Changes
- Shipping calculators ignore cancelled items.
Spree::Shipment#to_packageexcludes cancelled inventory units, and short-shipping refreshes shipment rates. Review custom calculators that relied on seeing cancelled units inpackage.contents. - Log entry deserialization no longer raises.
Spree::LogEntry#parsed_detailsreports failures throughRails.errorand returns a failedActiveMerchant::Billing::Responseinstead of raisingDisallowedClass,BadAliasorSerializationError. Code that rescued those should check the returned response. discard2.x is required. Loosen any 1.x pin. Discard 2 alignsdiscard/undiscardcallback ordering with ActiveRecorddestroy; audit custom discard callbacks that assumed the old ordering.kaminari-activerecordis no longer pulled in bysolidus_core.solidus_apiandsolidus_backendstill depend on it, so full-stack stores need nothing. Add it to your Gemfile if you runsolidus_corealone and paginate in your own code.
For the complete list of changes, see the full changelog.