docs/plans/5.4-store-api-bridges.md
Status: Implemented (PR #13782) Target: Spree 5.4 (next minor) Depends on: None — all changes are serializer/controller renames on existing models Author: Damian + Claude Last updated: 2026-03-17
Introduce 6.0 Store API naming NOW in 5.4, with breaking changes. No aliases, no deprecation periods — clean breaks. This gives storefront developers the final API shape before models are renamed in 6.0.
The approach: change the API surface (serializers, response keys, endpoint paths) in 5.4 while the models stay unchanged until 6.0. Serializers are the translation layer.
Only changes where the API field/endpoint name changes between 5.4 and 6.0. Purely additive features (new endpoints, new fields, new resources) ship with their 6.0 plan and don't need bridging.
Plan: 6.0-remove-master-variant.md
Already done in current API:
default_variant_id field on ProductSerializer ✓expand=default_variant on ProductSerializer ✓Remove now (5.4):
expand=master_variant from ProductSerializeris_master field from VariantSerializer# ProductSerializer — REMOVE:
one :master,
key: :master_variant,
resource: Spree.api.variant_serializer,
if: proc { expand?('master_variant') }
# VariantSerializer — REMOVE:
# is_master attribute (no longer exposed)
SDK: Remove StoreVariant.is_master from TypeScript types. Remove expand=master_variant from SDK methods.
Plan: 6.0-fulfillment-and-delivery.md
Rename in serializers (model stays Shipment until 6.0):
| Before (5.4) | After (5.4) | Serializer change |
|---|---|---|
shipments (nested in Cart/Order) | fulfillments | Change key: on association |
shipping_rates (nested in Shipment) | delivery_rates | Change key: on association |
shipping_method (nested in ShipmentSerializer) | delivery_method | Change key: on association |
ShipmentSerializer fields: state, tracking, shipped_at | status, tracking, fulfilled_at | Rename attributes |
ShippingMethodSerializer | Rename to DeliveryMethodSerializer | New file, delegates to same model |
ShippingRateSerializer | Rename to DeliveryRateSerializer | New file, delegates to same model |
shipment_state (on Order) | fulfillment_status | Rename attribute |
ship_total / display_ship_total (on Cart/Order) | delivery_total / display_delivery_total | Rename attributes |
New fields added:
fulfillment_type on ShipmentSerializer (returns 'shipping' or 'digital' based on current shipping method)fulfilled_at (alias for shipped_at)Endpoint renames:
# Before
GET/PATCH /api/v3/store/carts/:id/shipments
# After
GET/PATCH /api/v3/store/carts/:id/fulfillments
Rename controller file: Carts::ShipmentsController → Carts::FulfillmentsController.
# routes
namespace :carts do
resources :fulfillments, only: [:index, :update]
end
Cart/Order serializer changes:
# CartSerializer — rename keys
many :shipments, # model association unchanged
key: :fulfillments, # API key changes
resource: Spree.api.shipment_serializer # serializer handles field renames
# Rename money fields
attribute :delivery_total do |order|
order.shipment_total
end
attribute :display_delivery_total do |order|
Spree::Money.new(order.shipment_total, currency: order.currency).to_s
end
# Remove: ship_total, display_ship_total
SDK: Rename StoreShipment → StoreFulfillment, StoreShippingMethod → StoreDeliveryMethod, StoreShippingRate → StoreDeliveryRate. Update client.carts.shipments → client.carts.fulfillments.
Plan: 6.0-normalize-state-to-status.md
Models still use state column until 6.0. Serializers translate:
| Serializer | Before (5.4) | After (5.4) |
|---|---|---|
| ShipmentSerializer | state | status (reads object.state) |
| PaymentSerializer | state | status (reads object.state) |
| GiftCardSerializer | state | status (reads object.state) |
| OrderSerializer | shipment_state | fulfillment_status (reads object.shipment_state) |
| OrderSerializer | payment_state | payment_status (reads object.payment_state) |
# ShipmentSerializer (becoming FulfillmentSerializer in API)
attribute :status do |shipment|
shipment.state
end
# Remove: state attribute
# OrderSerializer
attribute :fulfillment_status do |order|
order.shipment_state
end
attribute :payment_status do |order|
order.payment_state
end
# Remove: shipment_state, payment_state attributes
SDK: Rename state → status on StoreFulfillment, StorePayment, StoreGiftCard. Rename shipment_state → fulfillment_status, payment_state → payment_status on StoreOrder.
Plan: 6.0-split-adjustments.md
The model split (Adjustment → TaxLine, Discount, Fee) happens in 6.0. But the serialized totals can be renamed now for clarity:
| Before (5.4) | After (5.4) | On |
|---|---|---|
adjustment_total | adjustment_total | Cart/Order — keep as-is (sum of all adjustments) |
ship_total / display_ship_total | delivery_total / display_delivery_total | Cart/Order (covered in #2 above) |
Add new field:
fee_total / display_fee_total on Cart/Order (returns 0 until Fee model exists in 6.0)adjustment_total stays — it's a genuine total across all adjustment types and renaming it would break every storefront's price breakdown display without adding clarity.
Plan: 6.0-replace-taxons-with-categories.md
The Store API already uses categories naming. No serializer changes needed. The Classification model rename is internal.
All changes implemented in PR #13782. Versions bumped to 0.12.0 for both @spree/sdk and @spree/next.
api/app/serializers/spree/api/v3/fulfillment_serializer.rb ✓api/app/serializers/spree/api/v3/delivery_method_serializer.rb ✓api/app/serializers/spree/api/v3/delivery_rate_serializer.rb ✓api/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb ✓api/app/serializers/spree/api/v3/admin/delivery_method_serializer.rb ✓api/app/serializers/spree/api/v3/admin/delivery_rate_serializer.rb ✓api/app/controllers/spree/api/v3/store/carts/fulfillments_controller.rb ✓api/spec/controllers/spree/api/v3/store/carts/fulfillments_controller_spec.rb ✓api/spec/integration/spree/api/v3/store/fulfillments_spec.rb ✓api/app/serializers/spree/api/v3/shipment_serializer.rb ✓api/app/serializers/spree/api/v3/shipping_method_serializer.rb ✓api/app/serializers/spree/api/v3/shipping_rate_serializer.rb ✓api/app/serializers/spree/api/v3/admin/shipment_serializer.rb ✓api/app/serializers/spree/api/v3/admin/shipping_method_serializer.rb ✓api/app/serializers/spree/api/v3/admin/shipping_rate_serializer.rb ✓api/app/controllers/spree/api/v3/store/carts/shipments_controller.rb ✓api/spec/controllers/spree/api/v3/store/carts/shipments_controller_spec.rb ✓api/spec/integration/spree/api/v3/store/shipments_spec.rb ✓cart_serializer.rb — ship_total → delivery_total, shipments → fulfillments ✓order_serializer.rb — shipment_state → fulfillment_status, payment_state → payment_status, ship_total → delivery_total, shipments → fulfillments ✓admin/order_serializer.rb — shipments → fulfillments, user_id → customer_id, expand=user → expand=customer ✓admin/address_serializer.rb — user_id → customer_id ✓admin/store_credit_serializer.rb — user_id → customer_id ✓admin/credit_card_serializer.rb — user_id → customer_id ✓newsletter_subscriber_serializer.rb — user_id → customer_id ✓product_serializer.rb — removed expand=master_variant ✓admin/product_serializer.rb — removed expand=master_variant ✓variant_serializer.rb — removed is_master ✓payment_serializer.rb — state → status ✓gift_card_serializer.rb — state → status ✓routes.rb — /shipments → /fulfillments ✓dependencies.rb — added new serializer registrations, pointed old keys to new classes ✓core/app/models/spree/shipment.rb — prefix ship → ful ✓core/app/models/spree/shipping_method.rb — prefix shpm → dm ✓core/app/models/spree/shipping_rate.rb — prefix shpr → dr ✓core/app/models/spree/inventory_unit.rb — prefix iu → fi ✓packages/sdk/src/types/generated/ — all TS types ✓packages/sdk/src/zod/generated/ — all Zod schemas ✓packages/admin-sdk/src/types/generated/ — all admin TS types ✓docs/api-reference/store.yaml — OpenAPI spec ✓packages/sdk/src/store-client.ts — shipments → fulfillments ✓packages/sdk/src/types/index.ts — type re-exports updated ✓packages/sdk/tests/ — tests + mocks updated ✓packages/next/src/actions/cart.ts — getShipments → getFulfillments, selectShippingRate → selectDeliveryRate ✓packages/next/src/index.ts — re-exports updated ✓packages/next/tests/ — tests updated ✓packages/sdk/README.md ✓packages/next/README.md ✓docs/developer/sdk/store/cart-checkout.mdx ✓docs/developer/sdk/quickstart.mdx ✓docs/developer/sdk/store/payments.mdx ✓docs/developer/sdk/configuration.mdx ✓docs/developer/storefront/nextjs/spree-next-package.mdx ✓docs/developer/storefront/nextjs/quickstart.mdx ✓| Feature | Status |
|---|---|
images → media | Done in 5.4 (PR #13778) ✓ |
taxons → categories | Already using categories in API ✓ |
expand=default_variant | Already exists ✓ |
default_variant_id on products | Already exists ✓ |
| ProductType, Collections, Stock Reservations, Returns/Claims, Channels/Catalogs | Purely additive — ship with 6.0 |
Prefixed IDs are updated to match the new vocabulary. The find_by_prefix_id! method ignores the prefix during decoding (it splits on _ and decodes only the Sqids portion), so old IDs with old prefixes still resolve correctly.
| Model | Old prefix | New prefix |
|---|---|---|
| Shipment | ship_ | ful_ |
| ShippingMethod | shpm_ | dm_ |
| ShippingRate | shpr_ | dr_ |
| InventoryUnit | iu_ | fi_ |
Event names (shipment.shipped, shipment.canceled) are not changed in 5.4. Webhook subscribers and event listeners depend on these strings. They change in 6.0 to fulfillment.fulfilled, fulfillment.canceled with old names emitted alongside for one release.
shipment, shipping_method, state, master_variant in new endpoints.5.4-6.0-product-media-system.md (same bridge pattern: API naming changed in 5.4, model renamed in 6.0)docs/plans/6.0-*.md