Skip to main content
Before proceeding to upgrade, please ensure you’re at Spree 5.3

Upgrade steps

Remove spree_sample gem from your Gemfile

Samples are now included in the spree gem. If you’ve added previously spree_sample to your Gemfile, remove it by running:
bundle remove spree_sample

Update gems

bundle update

Fetch and run missing migrations

bin/rake spree:install:migrations && bin/rails db:migrate

Fix your Admin user class

Your admin user model needs to include the Spree::AdminUserMethods concern instead of Spree::UserMethods. Previously UserMethods bundled AdminUserMethods but that caused all kinds of issues. So if you have a model like this:
class Spree::AdminUser < Spree::Base
  include Spree::UserMethods
  
  # ... other model code ...
end
You need to change it to this:
class Spree::AdminUser < Spree::Base
  include Spree::AdminUserMethods
  
  # ... other model code ...
end
By default that file is located at app/models/spree/admin_user.rb.

Backfill Image Thumbnail IDs

Spree 5.4 adds a primary_media association to both Product and Variant models. This greatly speeds up rendering Product lists everywhere (API, Admin Dashboard and spree_storefront), as previously we’ve had to load all media/images for each product and determine which one is the one to render a thumbnail. To generate the new association you will need to run a rake task (both in development and production environments):
bin/rails spree:media:backfill_primary_media
As above, this process can take a while depending on the size of your product catalog. For new products this will be set automatically and you won’t need to run this rake task more than once.

Prefixed IDs

Spree 5.4 by default uses prefixed IDs for resources (eg. prod_6VsgxZbenF) instead of just IDs from the database. These IDs are now used in Admin Dashboard, API v3, Events system and Webhooks.

Event Subscribers

If you previously added Subscribers for Events, eg. order.completed with code such as:
module MyApp
  class OrderAuditSubscriber < Spree::Subscriber
    subscribes_to 'order.complete'

    on 'order.complete', :log_order_completed

    private

    def find_order(event)
      Spree::Order.find(event.payload[:id])
    end
  end
end
You will need to update your code to use the new prefixed IDs, from:
Spree::Order.find(event.payload[:id])
to:
Spree::Order.find_by_prefix_id!(event.payload[:id])
You can also use find_by_prefix_id without the bang (!) to gracefully handle cases where the record might not exist.

Adding Prefixed IDs support to your custom models

All models inheriting from Spree::Base (or Spree.base_class) can support Prefixed IDs by adding the following code to your model:
class MyModel < Spree::Base
  has_prefix_id :mm
  
  # ...
end
Prefix length isn’t limited by default, but we recommend to keep it short.

Migrate Checkout Zones to Markets

Spree 5.4 introduces Markets as the primary way to manage which countries are available for checkout, along with their currencies and locales. The legacy checkout_zone field on the Store model is now deprecated and will be removed in Spree 6.0. New stores automatically get a default market created from their default_country — no manual setup needed. For existing stores that used checkout_zone, run the migration rake task:
bin/rake spree:markets:migrate_checkout_zones
This will:
  1. Read the checkout_zone countries from each store
  2. Create a default Market with those countries, currency, and locale
  3. Clear the checkout_zone_id column
You will need to run this rake task locally and on your production environment. After running, verify your markets are set up correctly in the Admin Dashboard under Settings → Markets.

Deprecated Store methods

The following Store methods now emit deprecation warnings and will be removed in Spree 5.5:
Deprecated methodReplacement
store.checkout_zoneUse Markets to manage countries
store.checkout_zone=Use Markets to manage countries
The store.default_country reader now returns the first country (by name) from the store’s default market. The store.default_country_iso= setter still works and is used to set the country when creating a store — a default market is automatically created from it.

Code that references checkout_zone for tax

If your application used store.checkout_zone as a tax zone fallback, update it to use Spree::Zone.default_tax instead:
# Before
zone = current_store.checkout_zone

# After
zone = Spree::Zone.default_tax

ActiveMerchant removed from Spree Core

Spree 5.4 no longer depends on the activemerchant gem. All internal usages have been replaced with lightweight Spree-native classes:
ActiveMerchant classSpree replacement
ActiveMerchant::Billing::ResponseSpree::PaymentResponse
ActiveMerchant::ConnectionErrorSpree::PaymentConnectionError
Spree::PaymentResponse is a drop-in replacement with the same constructor signature:
Spree::PaymentResponse.new(success, message, params = {}, options = {})
It supports the same methods: success?, message, params, authorization, avs_result, cvv_result, test?, and YAML serialization (for log entries).

Updating custom payment methods

If you have a custom payment method (gateway) that returns ActiveMerchant::Billing::Response, update it to return Spree::PaymentResponse instead:
# Before
def authorize(money, source, options = {})
  # ...
  ActiveMerchant::Billing::Response.new(true, 'Success', {},
    authorization: transaction_id, test: test?)
end

# After
def authorize(money, source, options = {})
  # ...
  Spree::PaymentResponse.new(true, 'Success', {},
    authorization: transaction_id, test: test?)
end
If your gateway rescues ActiveMerchant::ConnectionError, update it to rescue Spree::PaymentConnectionError:
# Before
rescue ActiveMerchant::ConnectionError => e

# After
rescue Spree::PaymentConnectionError => e

Using spree_gateway or other ActiveMerchant-based extensions

If you use the spree_gateway gem or another extension that depends on ActiveMerchant, add activemerchant directly to your application’s Gemfile:
gem 'activemerchant'
These extensions will continue to work — Spree’s LogEntry#parsed_details still deserializes ActiveMerchant::Billing::Response objects when the gem is present.

Address hash method renamed

Spree::Address#active_merchant_hash has been renamed to #gateway_hash. A backwards-compatible alias is provided, so existing code continues to work without changes.

(Optional) Install Legacy API v2 extension

Spree 5.4 ships with API v3, which marks deprecation of API v2. API v2 is still available but you need to install it separately. If you currently use API v2, you can do this by running the following command:
bundle add spree_legacy_api_v2
If you have tests for API v2 endpoints in your application, you will need to include some files in your rails_helper.rb:
require 'jsonapi/rspec'
require 'spree_legacy_api_v2/testing_support/v2/base'
require 'spree_legacy_api_v2/testing_support/factories'
require 'spree_legacy_api_v2/testing_support/v2/current_order'
require 'spree_legacy_api_v2/testing_support/v2/platform_contexts'
require 'spree_legacy_api_v2/testing_support/v2/serializers_params'

RSpec.configure do |config|
  config.include JSONAPI::RSpec, type: :request # required for API v2 request specs
end
Also, add jsonapi-rspec gem to your Gemfile:
gem 'jsonapi-rspec', group: :test
And run bundle install

(Optional) Install Spree Multi Store

Multi-store features like multiple store management & custom domains were moved to a separate gem spree_multi_store. If you rely on these features please run:
bundle add spree_multi_store
Spree Multi-Store is licensed under the AGPL v3 License.If you would like to use it in a commercial application, please contact us to obtain a commercial license.

(Optional) Install Legacy Product Properties extension

Product Properties have been extracted from Spree core to a separate gem spree_legacy_product_properties. Product Properties were already deprecated in favor of Metafields and disabled by default since Spree 5.1. If you have product_properties_enabled: true in your Spree configuration or rely on Product Properties in any way, install the extension:
bundle add spree_legacy_product_properties
No data migration is needed — the gem uses the same database tables. All existing product properties, property definitions, and associations will continue to work as before.
If you skip this step and had Product Properties enabled, any code referencing product.product_properties, product.property(), product.set_property(), or the admin Properties page will stop working.

(Optional) Install Spree Posts extension

Posts and Post Categories management were extracted from Spree core into a separate gem spree_posts. If you use these features before run this command to restore them:
bundle add spree_posts