关于 spree-legacy-api-v2
spree-legacy-api-v2 面向维护 Spree API v2 集成或迁移至 v3 的开发者,说明旧版接口结构与差异。内容覆盖认证、响应格式及逐端点迁移要点。
主要功能
- 01Storefront 与 Platform API 差异
- 02OAuth2 认证与令牌请求头
- 03JSON:API 响应结构与分页
- 04v2 至 v3 的认证、ID 和筛选变更
- 05旧版 SDK 与 Webhook 迁移说明
适用场景
- 01维护现有的 v2 集成
- 02规划逐步迁移到 API v3
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ spree/ agent-skills --skill spree-legacy-api-v2安装后在 Claude Code 中输入/spree-legacy-api-v2确认已加载。
详细介绍
Spree API v2 — 旧版
默认建议:不要基于 v2 开发新功能。 Spree 5.4+ 将 API v3 作为面向未来的 API;v2 仍可使用,但已冻结功能。新端点、新认证模式(publishable + secret + JWT)、带前缀的 ID、expand、类型化 SDK——这些都属于 v3。新的集成应从 v3 开始。
此技能适用于两种实际情况:
- 你接手了一个 v2 集成,需要维护它。
- 你正在从 v2 迁移到 v3,需要了解变更内容。
如果两种情况都不适用,请改用 spree-api-v3 技能。
v2 的形式
/api/v2/ 下有两个不同的 API 界面:
| 界面 | 路径 | 面向对象 |
|---|---|---|
| Storefront API | /api/v2/storefront/* | 面向客户——购物车、商品、结账 |
| Platform API | /api/v2/platform/* | 管理/后台——完整的 CRUD |
两者都采用 JSON:API 风格(https://jsonapi.org/)——使用包含 data、attributes、relationships、included 的严格封装。封装形式与 v3 不同。
v2 中的认证
| 界面 | 请求头 | 令牌 |
|---|---|---|
| Storefront(匿名浏览) | 无 | 公开访问 |
| Storefront(访客购物车) | X-Spree-Order-Token | 购物车的订单令牌 |
| Storefront(已登录客户) | Authorization: Bearer <oauth_token> | OAuth2 客户令牌 |
| Platform(管理员) | Authorization: Bearer <oauth_token> | OAuth2 管理员令牌 |
v2 使用 OAuth2 Doorkeeper 签发令牌——通过 /spree_oauth/token 端点使用 password 或 client_credentials 授权方式。没有 publishable key,也没有 secret key。OAuth 作用域存在,但粒度较粗(admin、write、read——Platform API 需要 read/admin 才能读取,需要 write/admin 才能写入);不像 v3 的 API key 作用域那样细分为 read_products/write_orders。
# Get a Platform API token (admin)
curl -X POST https://my-spree.example.com/spree_oauth/token \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=password&[email protected]&password=…"
# => { "access_token": "…", "token_type": "Bearer", "expires_in": 2592000, ... }
# (tokens last 1 month by default — `access_token_expires_in 1.month`)
# Use it
curl -H "Authorization: Bearer …" \
https://my-spree.example.com/api/v2/platform/orders
封装形式
{
"data": {
"id": "42", // raw integer PK as string
"type": "product",
"attributes": {
"name": "Cool shirt",
"price": "29.99",
"currency": "USD",
"created_at": "2025-01-01T00:00:00Z"
},
"relationships": {
"variants": {
"data": [{ "id": "100", "type": "variant" }]
}
}
},
"included": [
{
"id": "100",
"type": "variant",
"attributes": { "sku": "COOL-S", ... }
}
],
"meta": { "total_count": 152, "total_pages": 7 }
}
与 v3 相比:
- v2:原始整数 ID(
"42")。v3:带前缀的 ID("prod_…")。 - v2:嵌套的
attributes+relationships。v3:顶层扁平字段。 - v2:
include查询参数将相关资源提取到included数组中。v3:expand查询参数将它们直接内联到响应中(?expand=variants,media,点号嵌套最多 4 层)。 - v2(Platform API):筛选条件使用
filter[name_cont]=…。v3:原理相同,但使用q[name_cont]=…(Ransack)。Storefront API 则使用各端点固定的筛选条件名称——见下文步骤 4。 - v2:排序使用
sort=-created_at。v3:完全相同——sort=-created_at可原样使用(v3 ResourceController 会在内部将-fieldJSON:API 表示法转换为 Ransacks)。
分页
GET /api/v2/storefront/products?page=2&per_page=25
meta.total_count、meta.total_pages。v2 不支持键集/游标分页,仅支持偏移分页。
使用 v2 时
“我需要在 v2 响应中添加字段”
装饰 JSON:API 序列化器(底层使用 jsonapi-serializer gem):
module Spree::V2::Storefront::ProductSerializerDecorator
def self.prepended(base)
base.attribute :custom_field do |product|
product.custom_field_value
end
end
Spree::V2::Storefront::ProductSerializer.prepend self
end
“我需要一个新的 v2 端点”
不要添加到 v2。改为添加到 v3——有关 spree:api_resource 生成器,请参阅 spree-resource 技能。如果你的 v2 客户端确实无法与 v3 通信,可以编写一个轻量兼容封装,返回 v2 封装形式的 JSON,但内部从 v3 资源读取数据。
“v2 返回了错误,错误格式是什么?”
{
"error": "The resource was not found.",
"errors": { ... } // on 422
}
普通 JSON,而不是 JSON:API 错误对象。v3 使用结构化的 { error: { code, message, details } } 封装——请参阅 spree-api-v3。
从 v2 迁移到 v3
迁移按端点逐个进行。无需一次性全部切换;v2 和 v3 可以共存。
步骤 1:迁移身份验证
| v2 | v3 |
|---|---|
| 使用 OAuth2 密码授权登录客户 | 使用 JWT 在 /api/v3/store/auth/login 登录(返回 token + refresh_token + user) |
| 使用 OAuth2 client_credentials 授权管理应用 | 使用具有指定权限范围的密钥(sk_*) |
Authorization: Bearer <token> | X-Spree-Api-Key: <pk_…|sk_…> + 可选的 Authorization: Bearer <jwt> |
访客购物车使用 X-Spree-Order-Token | 仍使用 X-Spree-Token(已重命名) |
对于管理应用:通过管理界面生成密钥(Settings → Developers → API Keys,密钥类型为 'Secret'),并设置所需的权限范围。完全弃用 OAuth2。
对于客户流程:将 OAuth2 登录调用替换为 JWT 登录。新流程也会返回刷新令牌——请参阅管理端身份验证文档,了解基于 Cookie 的刷新模式。
步骤 2:迁移 ID
v2 将整数 ID 作为字符串返回;v3 返回带前缀的 ID。如果客户端存储了 ID:
// Old
const productId = response.data.id // "42"
// New
const productId = response.data.id // "prod_86Rf07xd4z"
如果你已在自己的数据库中持久化 v2 ID,并且需要根据 v3 响应查找这些 ID,整数 PK 保持不变——只需使用整数调用 v3 搜索端点:
GET /api/v3/admin/products?q[id_eq]=42
请注意,在客户端代码中公开原始整数 ID 是 v2 的反模式,v3 已对此进行修正;新 ID 可安全用于 URL,且不会泄露记录数量。
步骤 3:拆除响应封装
// v2 JSON:API
const product = response.data
const name = product.attributes.name
const price = product.attributes.price
const variantIds = product.relationships.variants.data.map(v => v.id)
const variants = response.included.filter(r => r.type === 'variant')
// v3 flat
const product = response // single endpoint — no envelope
const name = product.name
const price = product.price
const defaultVariantId = product.default_variant_id // direct field
// With ?expand=variants: product.variants is an array of full variant objects
如果无法一次性重构所有调用点,可以编写一个轻量级适配器:
function unwrapV2<T>(response: V2Response<T>): T {
return {
id: response.data.id,
...response.data.attributes,
...buildRelationships(response.data.relationships, response.included),
} as T
}
步骤 4:筛选语法
| v2 | v3 |
|---|---|
filter[name_cont]=shirt(平台 API) | q[name_cont]=shirt |
filter[price_gteq]=20(平台 API) | q[price_gteq]=20 |
sort=-created_at | sort=-created_at(不变) |
对于平台 API,filter[...] 键是 Ransack 谓词,与 v3 的 q[...] 一一对应——filter[name_cont] 变为 q[name_cont]。Storefront API 不使用 Ransack:每个端点都有由 finder 类处理的固定筛选名称,因此需要进行对应转换——filter[name] → q[name_cont](或 q[search]),filter[taxons] → q[in_categories],filter[price]=10,50 → q[price_gte]=10&q[price_lte]=50。
@spree/sdk 将扁平的 Ransack 谓词作为带类型的列表参数接受,例如 client.products.list({ name_cont: 'shirt' }),并自动生成 q[name_cont]=...——详见 spree-typescript-sdk。
步骤 5:切换 SDK
如果你正在使用旧版 @spree/storefront-api-v2-sdk 包(JS),请将其替换为 @spree/sdk(Store API)或 @spree/admin-sdk(Admin API)。新版 SDK 具有类型支持和内置重试功能,并遵循 v3 其他接口使用的相同协议。
步骤 6:Webhooks(如适用)
如果你接收 v2 Webhooks:v2 Webhooks 使用原始 ID 和旧版负载格式触发。v3 Webhooks 使用带前缀的 ID,以及 {id, name, created_at, data, metadata} 封装格式。如果将端点切换为 v3,Webhook 接收器必须能够处理新的负载格式——详见 spree-events-webhooks。
v2 何时停止提供
spree_api 不再捆绑 v2。从 Spree 5.5 开始,v2 位于单独的弃用 gem spree_legacy_api_v2 中,该 gem 可与 Spree 5 配合使用。若要继续运行现有的 v2 集成,请添加此 gem(bundle add spree_legacy_api_v2);对于全新应用,还需运行 bin/rails g spree:legacy_api_v2:install 来安装其迁移(从早期 Spree 版本升级的应用已包含这些迁移)。计划在下一个主版本中移除 API v2,且不会再添加新功能——新字段、端点和能力(例如 Markets、新定价引擎)仅会在 v3 中推出。
如果你维护着重要的 v2 集成,请安排迁移;否则只会与后续版本差距越来越大。
延伸阅读
- v3 API 协议:
spree-api-v3技能——目标文档。 - v3 TypeScript SDK:
spree-typescript-sdk技能。 - 在 v3 上添加自定义端点:
spree-resource技能。 - v2 源码:
spree_legacy_api_v2gem——控制器和序列化器(基于 jsonapi-serializer)。v2 不再随spree_api一同发布。 - v2 OpenAPI 规范:
docs/api-reference/storefront.yaml和docs/api-reference/platform.yaml。v3 规范为docs/api-reference/store.yaml(也随@spree/docsnpm 包一同发布,位于node_modules/@spree/docs/dist/api-reference/store.yaml)。