> ## 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.

# Upgrading to Spree 5.4

> This guide covers upgrading a Spree 5.3 application to Spree 5.4.

<Info>
  Before proceeding to upgrade, please ensure you're at [Spree 5.3](/developer/upgrades/5.2-to-5.3)
</Info>

## 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:

```bash theme={"theme":"night-owl"}
bundle remove spree_sample
```

### Update gems

```bash theme={"theme":"night-owl"}
bundle update
```

### Fetch and run missing migrations

```bash theme={"theme":"night-owl"}
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:

```ruby theme={"theme":"night-owl"}
class Spree::AdminUser < Spree::Base
  include Spree::UserMethods
  
  # ... other model code ...
end
```

You need to change it to this:

```ruby theme={"theme":"night-owl"}
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):

```bash theme={"theme":"night-owl"}
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:

```ruby theme={"theme":"night-owl"}
module MyApp
  class OrderAuditSubscriber < Spree::Subscriber
    subscribes_to 'order.completed'

    on 'order.completed', :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:

```ruby theme={"theme":"night-owl"}
Spree::Order.find(event.payload[:id])
```

to:

```ruby theme={"theme":"night-owl"}
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:

```ruby theme={"theme":"night-owl"}
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](/developer/core-concepts/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:

```bash theme={"theme":"night-owl"}
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

<Warning>
  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**.
</Warning>

#### Deprecated Store methods

The following Store methods now emit deprecation warnings and will be removed in Spree 5.5:

| Deprecated method      | Replacement                                                         |
| ---------------------- | ------------------------------------------------------------------- |
| `store.checkout_zone`  | Use [Markets](/developer/core-concepts/markets) to manage countries |
| `store.checkout_zone=` | Use [Markets](/developer/core-concepts/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:

```ruby theme={"theme":"night-owl"}
# 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 class                | Spree replacement               |
| ----------------------------------- | ------------------------------- |
| `ActiveMerchant::Billing::Response` | `Spree::PaymentResponse`        |
| `ActiveMerchant::ConnectionError`   | `Spree::PaymentConnectionError` |

`Spree::PaymentResponse` is a drop-in replacement with the same constructor signature:

```ruby theme={"theme":"night-owl"}
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:

```ruby theme={"theme":"night-owl"}
# 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`:

```ruby theme={"theme":"night-owl"}
# Before
rescue ActiveMerchant::ConnectionError => e

# After
rescue Spree::PaymentConnectionError => e
```

#### Using `spree_gateway` or other ActiveMerchant-based extensions

If you use the [`spree_gateway`](https://github.com/spree/spree_gateway) gem or another extension that depends on ActiveMerchant, add `activemerchant` directly to your application's `Gemfile`:

```ruby theme={"theme":"night-owl"}
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:

```bash theme={"theme":"night-owl"}
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`:

```ruby theme={"theme":"night-owl"}
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`:

```ruby theme={"theme":"night-owl"}
gem 'jsonapi-rspec', group: :test
```

And run `bundle install`

### (Optional) Install Spree Multi Store

[Multi-store](/developer/multi-store/quickstart) features like multiple store management & custom domains were moved to a separate gem `spree_multi_store`.

If you rely on these features please run:

```bash theme={"theme":"night-owl"}
bundle add spree_multi_store
```

<Info>
  Spree Multi-Store is licensed under the [AGPL v3 License](https://opensource.org/licenses/AGPL-3.0).

  If you would like to use it in a commercial application, please [contact us](https://spreecommerce.org/get-started/) to obtain a commercial license.
</Info>

### (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](/developer/core-concepts/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:

```bash theme={"theme":"night-owl"}
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.

<Warning>
  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.
</Warning>

### (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:

```bash theme={"theme":"night-owl"}
bundle add spree_posts
```
