Documentation Index
Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt
Use this file to discover all available pages before exploring further.
Before proceeding to upgrade, please ensure you’re at Spree 5.4.
Upgrade steps
Update gems
Fetch and run missing migrations
Migrate legacy variant-pinned media
In 5.5 the product is the default owner of media. Existing variant-pinned images keep rendering, but new admin uploads attach to the product. To consolidate both into a single gallery, run:Spree::Media::MigrateProductAssetsJob per product onto the images queue — make sure your job runner is processing that queue. Each job is idempotent, so re-running the task is safe; it skips products that no longer have variant-pinned assets.
For larger catalogs, tune the batching with BATCH_SIZE:
Run the channels upgrade
Spree 5.5 promotesOrder#channel from a free-text string column to a real Spree::Channel FK. Run this rake task immediately after db:migrate — it seeds the channel rows your existing stores need and backfills spree_orders.channel_id from the legacy string column:
-
spree:channels:create_defaults— creates a defaultonlinechannel for every store that doesn’t already have one. New 5.5+ stores get this automatically via theStoreafter_create callback; this sub-task only matters for stores that already exist when you upgrade. The channel’s after_create hook seeds the three default routing rules (Preferred Location → Minimize Splits → Default Location). -
spree:channels:backfill_order_channel_ids— scans each store’s orders, creates oneSpree::Channelper distinct legacychannelvalue (claimingNULL/blank rows under theonlinedefault channel), and writeschannel_id.
channel_id via the model’s before_validation callback, so the backfill is only strictly required for orders that aren’t touched again post-upgrade — but running it once at upgrade time avoids surprises later.
The legacy channel string column is kept on spree_orders and ignored by ActiveRecord (Spree::Order declares it in ignored_columns). It will be dropped in a later Spree release once enough time has passed for everyone to have run the backfill — until then it’s harmless DB ballast that lets the rake task be re-run if you need to.
Schedule the Stock Reservations expiry job
Spree 5.5 introduces time-limited stock reservations during checkout to prevent two customers from buying the same last unit at the same time. Abandoned checkouts leave behind expired reservation rows, and Spree does not auto-schedule the cleanup — your application’s job runner must runSpree::StockReservations::ExpireJob periodically (every minute is the recommended cadence).
If you skip this step, expired reservations accumulate in the table indefinitely. The Quantifier still ignores them at availability-check time (so customers see correct stock), but the table grows unbounded.
sidekiq-cron
solid_queue
good_job
(Optional) Tune the reservation TTL
The default reservation TTL is 10 minutes. To override globally:(Optional) Disable Stock Reservations
Stock reservations are enabled by default. To opt out and revert to pre-5.5 behavior (no holds during checkout, Quantifier returns rawcount_on_hand):
false, so there’s no runtime cost and no table growth.
(Optional) Opt out of rules-based Order Routing
Spree 5.5 introduces Order Routing — a configurable, per-channel pipeline that decides which stock locations fulfill an order. Every store and every channel ships with three default rules (Preferred Location → Minimize Splits → Default Location) that produce sensible behavior out of the box, with no migration work required. If you’ve heavily customized fulfillment in Spree 5.4 and aren’t ready to adopt the new rules engine, you can keep the legacy pre-5.5 routing by switching the store’s strategy toSpree::OrderRouting::Strategy::Legacy:
Spree::Stock::Coordinator, which is the exact pre-5.5 packing pipeline — every active stock location is packed, the Prioritizer distributes inventory units across the resulting packages, and no merchant routing rules are consulted. Your existing customizations on Coordinator, Packer, Prioritizer, and the splitters keep working unchanged.
Behavior changes worth knowing
Cart changes during checkout can now fail with insufficient stock
When a customer is in checkout and tries to add an item, increase a quantity, or remove a line item, Spree now re-checks whether the cart still fits in available stock (subtracting what other customers are holding in their own active checkouts). If it doesn’t, the change is rejected up front instead of silently completing and failing later at order submission. Storefronts and custom integrations that act on the cart should expect this new failure path and surface the error to the customer.Storefront availability drops faster under contention
Other customers now see availability reduced by all active reservations, not just by completed orders. This is the intended fix to overselling — but if you have a real-time inventory dashboard that readscount_on_hand directly (rather than going through Spree’s availability checks), you’ll want to expose a “Reserved” axis to merchants so they can see in-checkout demand.
Payment method type on the wire is now a shorthand, not a Rails class name
The type attribute on Spree::PaymentMethod (returned by both the Store API and the Admin API) used to be the full Rails STI class name — "SpreeStripe::Gateway", "SpreeAdyen::Gateway", "Spree::PaymentMethod::Check". In 5.5 it switches to a stable shorthand derived from Spree::Base.api_type:
| Class | Pre-5.5 type | 5.5+ type |
|---|---|---|
Spree::PaymentMethod::Check | Spree::PaymentMethod::Check | check |
Spree::PaymentMethod::StoreCredit | Spree::PaymentMethod::StoreCredit | store_credit |
SpreeStripe::Gateway | SpreeStripe::Gateway | stripe |
SpreeAdyen::Gateway | SpreeAdyen::Gateway | adyen |
SpreePaypalCheckout::Gateway | SpreePaypalCheckout::Gateway | paypal_checkout |
SpreeRazorpayCheckout::Gateway | SpreeRazorpayCheckout::Gateway | razorpay_checkout |
POST /api/v3/admin/payment_methods now expects as type when creating a new method, and what GET /api/v3/admin/payment_methods/types returns in its type field.
This is a breaking change for any storefront or integration that string-matches the payment method type to pick which payment-gateway SDK to load. The official Spree Next.js storefront resolves the gateway in src/lib/utils/payment-gateway.ts; update its map to key on the new shorthand:
"SpreeStripe::Gateway", "SpreePaypalCheckout::Gateway", etc.) and add the corresponding 5.5+ shorthand alongside each one. The symptom of missing this is checkout rendering a generic “this payment method is not yet supported” placeholder instead of the gateway’s SDK form.
Order Routing chooses location order via merchant rules instead of database order
The default routing strategy (Spree::OrderRouting::Strategy::Rules) packs the same set of stock locations as before, but the order in which locations are tried is now determined by the routing rules — Preferred Location → Minimize Splits → Default Location — rather than by raw database row order. The unit distribution (Prioritizer + Adjuster) is unchanged: top-ranked location’s packages get first pick of on-hand inventory, the rest spills over.
For most stores this is invisible: when one location can fulfill the entire cart, that location now wins consistently (instead of depending on database iteration order). When the cart needs to split across locations, the same multi-location split happens — just with the location order driven by rules.
If you rely on the legacy “every location packed in iteration order, no rule consulted” behavior, see Opt out of rules-based Order Routing above.
