关于 spree-api-v3
spree-api-v3 是面向开发者的技能,介绍 Spree 6 中供店铺前台、管理员和市场卖家使用的 REST API。内容涵盖认证、权限、资源约定、筛选、分页及购物车到订单的流程。
主要功能
- 01介绍 Store、Admin 和 Seller API
- 02说明认证与权限机制
- 03记录购物车、结账和订单流程
- 04涵盖筛选、分页与扩展字段
- 05说明 ID、字段和响应格式
适用场景
- 01构建 Spree 店铺前台集成
- 02连接后台或 ERP 系统
- 03实现卖家面板 API 访问
- 04迁移或排查 API 请求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ spree/ agent-skills --skill spree-api-v3安装后在 Claude Code 中输入/spree-api-v3确认已加载。
附带文件references/scopes.md
详细介绍
Spree API v3
Spree 6 在 /api/v3/ 下提供三个 REST 接口,它们遵循同一套约定:
| 接口 | 路径 | 调用方 | 身份验证 | 授权 |
|---|---|---|---|---|
| Store API | /api/v3/store/* | 店面、移动应用、购物者 | X-Spree-Api-Key: pk_… + 可选的 Authorization: Bearer <jwt>(aud: store_api)+ 访客 X-Spree-Token | 所有权(参见 spree-auth-permissions) |
| Admin API | /api/v3/admin/* | 集成、后台应用、仪表板 | X-Spree-Api-Key: sk_… 或 Authorization: Bearer <jwt>(aud: admin_api) | 密钥作用域 / 角色权限键 |
| Seller API | /api/v3/seller/* | 市场卖家面板 | 卖家 JWT(aud: seller_api)+ X-Spree-Seller-Id | 卖家角色键 + current_seller 作用域(参见 spree-marketplace) |
为某个受众签发的令牌在其他接口上会被拒绝。Spree 6 中不存在 v2 Storefront/Platform API。如果你要迁移旧集成,请参见 node_modules/@spree/docs/dist/api-reference/store-api/migrating-from-storefront-api-v2.md。
Store API
- 匿名浏览只需要
X-Spree-Api-Key: pk_…。可公开密钥可安全地用于客户端代码。 - 访客购物车 / 结账 / 订单:还需发送
X-Spree-Token: <cart.token>。 - 已登录客户:还需发送
POST /api/v3/store/auth/login返回的Authorization: Bearer <jwt>。响应正文包含token、refresh_token和user。通过POST /auth/refresh和{ "refresh_token": "…" }轮换令牌。 - 上下文标头:
X-Spree-Country用于解析市场,并据此设置默认语言区域和货币。X-Spree-Locale和X-Spree-Currency可覆盖这些默认值。X-Spree-Channel: <code>用于选择渠道;如果未提供,则使用商店默认值。绑定到渠道的可公开密钥会决定渠道本身;如果标头与之冲突,则返回channel_mismatch。
- 响应仅包含适合客户查看的内容:不包含成本价或内部备注。对于向访客隐藏价格的渠道,货币字段会返回
null,因此应将其类型设为string | null。 - Store API 默认只读。写入操作仅限于购物车、结账、客户自己的账户(
/customers/me/*)、心愿单、客户本人订单的退货 / 索赔,以及新闻邮件 / 公司自助服务。
curl -H "X-Spree-Api-Key: pk_xxx" -H "X-Spree-Country: DE" \
"https://shop.example.com/api/v3/store/products?q[name_cont]=shirt&expand=default_variant"
购物车和订单是不同的资源
购物车(cart_…、/api/v3/store/carts)可修改且没有状态。完成购物车后会创建不可变的订单(or_…)。
POST /carts → cart (token in response)
POST /carts/{id}/items { variant_id, quantity }
PATCH /carts/{id} { email, shipping_address, billing_address, use_shipping, … }
PATCH /carts/{id}/fulfillments/{id} { selected_delivery_rate_id }
POST /carts/{id}/payment_sessions … gateway flow …
POST /carts/{id}/complete → the Order (or an OrderGroup for split marketplace checkouts)
GET /orders/{id} → order by or_ id, OR by the original cart_ id
- 完成后,
GET /carts/{id}会返回 404(cart_not_found),因为已完成的购物车不再出现在购物车查询结果中。使用GET /orders/{cart_id}读取结果。订单接口接受购物车的带前缀 ID,并将其解析为由该购物车创建的订单(order.cart_id)。访客继续使用同一个X-Spree-Token。 POST /carts/{id}/complete可以安全重试:如果支付 webhook 已完成购物车,重试会返回现有订单。- 购物车上的
requirements[]会列出仍阻止完成购物车的事项,格式为{ step, field, code, message }(例如{ "step": "delivery", "field": "delivery_method", "code": "delivery_method_required" })。步骤仅供参考;完成购物车才是唯一的硬性关卡。应根据requirements驱动结账界面,而不是根据状态名称。参见spree-checkout。 - 优惠券:
POST /carts/{id}/discount_codes { code }。购物车和订单都会公开coupon_code。 - 已登录客户的订单历史位于
GET /customers/me/orders。
你将使用的订单字段
status(Admin API):draft|placed|canceled。使用q[status_eq]=placed筛选。payment_status:none、authorized、partially_paid、paid、partially_refunded、refunded、overcharged、voided。fulfillment_status:unfulfilled、partial、fulfilled、delivered、backorder、canceled(迁移的订单中可能会出现旧版pending/ready/shipped)。- 总额:
item_total、delivery_total、discount_total、tax_total(included_/additional_)、fee_total、gift_card_total、store_credit_total、total、amount_due,每项都配有对应的display_*。 withdrawal_period_ends_at/within_withdrawal_period(欧盟撤回权)。
没有 state、payment_state 或 shipment_state,也没有 ship_total/promo_total/adjustment 行。金额按税费明细行、折扣和费用逐项列出(参见 spree-order-totals)。
Admin API
Secret key(集成):X-Spree-Api-Key: sk_…。密钥决定其对应的商店。
JWT(人员):POST /api/v3/admin/auth/login { email, password }(或使用 { provider, … } 进行 SSO)。响应正文为 { token, user }。刷新令牌会作为 HttpOnly cookie 写入,作用域限定为 /api/v3/admin/auth,且 POST /auth/refresh 不需要请求正文。Admin JWT 默认 5 分钟后过期。对于多商店主机,请发送 X-Spree-Store-Id。
如果请求同时携带两种凭据,则以 JWT 为准。
权限
每个 Admin 控制器都会将其操作映射到一个权限键:index/show → read_<resource>,其他所有操作(包括 /orders/{id}/cancel 等单条记录操作)→ write_<resource>。write_* 隐含 read_* 权限。
- Secret key 会根据其作用域进行检查。检查失败时返回 403
access_denied,并附带details.required_scope。 - JWT 员工用户会根据其在此商店中的角色权限键进行检查。检查失败时返回 403,并附带
details.required_permission。如果 403 响应中没有该详情,则表示记录级规则拒绝了请求——这些规则来自Spree.ability_class(可通过Spree::Dependencies.ability_class替换;secret key 则使用Spree::ApiKeyAbility)。参见spree-auth-permissions。 - 密钥不会决定你能看到哪些行:每个端点都会限定在当前商店的范围内。
- Secret key 的作用域在创建时固定。要更改作用域,请创建新密钥并撤销旧密钥。
完整的作用域列表及其分组和涵盖的资源见 references/scopes.md。运行时,GET /api/v3/admin/permissions 会返回相同的列表,Ruby 中的 Spree::ApiKey.known_scopes 也会提供该列表。常见选择:
Order sync out ............ read_orders (+ read_payments, read_fulfillments)
Warehouse / 3PL ........... read_orders, write_fulfillments, write_stock
ERP product feed .......... write_products (+ write_categories / write_collections)
Refund automation ......... read_orders, write_refunds
Staff provisioning ........ write_staff
Everything (avoid) ........ write_all
员工角色是数据(/api/v3/admin/roles)。参见 spree-auth-permissions。
仅限 Admin 的响应特征
- 时间戳、内部备注、成本价格、
status,以及店面不可见的自定义字段。 - 操作者信息对。 会记录操作者的记录(
canceler、approver、created_by、refunder等)会公开*_id和*_type。ID 对员工用户为adm_…,对 secret key 为key_…;*_type为admin_user或api_key。请将*_type视为开放列表。expand=canceler会返回{ id, type, label }。
共享约定
带前缀的 ID
读取和写入时,ID 始终是 Stripe 风格的带前缀字符串("variant_id": "variant_k5nR8xLq")。绝不要发送或期望整数 ID。常见前缀:
| 前缀 | 资源 | 前缀 | 资源 |
|---|---|---|---|
prod_ | 商品 | variant_ | 变体 |
cart_ | 购物车 | or_ | 订单 |
ogrp_ | 订单组(拆分结账) | li_ | 行项目 |
py_ | 付款 | re_ | 退款 |
ful_ | 履约 | ret_ | 退货 |
tl_ | 税费行 | disc_ | 折扣 |
fee_ | 费用 | cust_ | 客户 |
adm_ | 管理员用户 | addr_ | 地址 |
ctg_ | 类别 | coll_ | 集合 |
sl_ | 库存水平 | sloc_ | 库存位置 |
mkt_ | 市场 | ch_ | 渠道 |
sel_ | 卖家 | media_ | 媒体 |
cf_ | 自定义字段 | promo_ | 促销 |
gc_ | 礼品卡 | role_ | 角色 |
key_ | ApiKey 记录(不是 pk_/sk_ 令牌) | store_ | 商店 |
前缀用于标识资源类型,因此封装对象上没有 type 字段。使用 grep -rn has_prefix_id spree/core/app/models 验证其他前缀。不存在 adj_,因为 Spree 6 中没有 Adjustment。
封装对象、分页与排序
列表返回 { data: [...], meta: { page, limit, count, pages, from, to, in, previous, next } }。单条记录以裸对象形式返回。分页:?page=2&limit=50(默认值为 25,最大值为 100)。排序:?sort=-completed_at,number。
扩展与字段
?expand=default_variant,media,variants.prices会附带关联数据;点号表示法可访问嵌套关联。允许的键因资源而异(参见 OpenAPI 规范)。- Admin API 的扩展会按路径段进行权限检查:
order(s)需要read_orders,payment(s)/payment_splits需要read_payments,customer(s)需要read_customers,gift_card(s)需要read_gift_cards,store_credit(s)需要read_store_credits(因此orders.payments需要同时具备两项权限)。调用方无权访问的路径会被静默丢弃,而非返回 403。除非调用方持有read_gift_cards权限,否则礼品卡的code会被遮蔽(除末四位外全部隐藏)。 ?fields=name,slug会返回稀疏响应(始终包含id)。
过滤(Ransack)
q[<attr>_<predicate>]=value,例如 q[name_cont]=shirt、q[status_eq]=placed、q[completed_at_gteq]=2026-09-01。谓词包括 _eq、_not_eq、_in、_cont、_start、_gt、_gteq、_lt、_lteq、_present、_blank。模型的 Ransack 允许列表之外的属性会被静默忽略:请求仍返回 200,但过滤条件会被丢弃。使用 Spree.ransack.add_attribute(Spree::Product, :brand_id) 将属性加入允许列表。
允许列表会根据受众范围缩小(Ransack auth_object:Admin 使用 nil,Store 使用 :store,Seller 使用 :seller):
- Store API 仅使用模型的
storefront_ransackable_associations(商品:tags、categories、collections;其他所有模型均为空),并排除模型的private_ransackable_attributes[:store](例如订单的email、considered_risky、coupon_code以及search/multi_search作用域;变体的cost_price;商店信用的memo)。 - Seller API 不使用任何关联,并排除
private_ransackable_attributes[:seller](订单的email、considered_risky、coupon_code,以及search/multi_search作用域)。 - Admin API 可使用完整的允许列表。因此,
Spree.ransack.add_association(Spree::Product, :brand)会使q[brand_name_eq]仅对员工生效——若要让购物者也能通过此关联过滤,请添加Spree::Product.storefront_ransackable_associations |= %w[brand](仅限店面可查看其数据的关联)。通过add_attribute添加的属性会对所有受众开放,除非将其列为私有属性。
地址
使用 first_name、last_name、address1、city、postal_code、country_code(ISO2)和 state_code。对于购物车,use_shipping: true 会将配送地址复制为账单地址。响应中仍会出现 country_iso/state_abbr,但它们已弃用。
富文本
富文本属性会返回两次:description 为纯文本,description_html 为经过清理的 HTML。渲染 *_html,并写入纯文本名称(发送的值为 HTML)。Spree 会在保存时清理 HTML。
金额
金额是十进制字符串("29.99"),每个金额都配有一个格式化后的 display_* 字符串。隐藏价格时,值为 null。写入时,价格是数组:"prices": [{ "currency": "USD", "amount": "29.99" }]。
幂等性
在 POST/PUT/PATCH/DELETE 请求中发送 Idempotency-Key: <uuid>(最多 255 个字符)。在 24 小时内,使用相同键和请求体重试会重放已存储的响应(响应头 Idempotent-Replayed: true)。使用不同请求体重用同一个键会返回 422 idempotency_key_reused。5xx 响应不会缓存。SDK 和 CLI 会自动添加该键。
错误
{ "error": { "code": "validation_error", "message": "Email can't be blank",
"details": { "email": ["can't be blank"] } } }
| 状态 | 常见的 code |
|---|---|
| 401 | authentication_required、invalid_token、authentication_failed、invalid_refresh_token |
| 403 | access_denied(details.required_scope / required_permission)、channel_mismatch |
| 404 | record_not_found、cart_not_found、order_not_found、line_item_not_found、variant_not_found |
| 422 | validation_error(每个属性对应一个 details,表单级错误使用 base)、cart_cannot_complete、insufficient_stock、payment_failed、idempotency_key_reused |
| 413 | request_too_large(请求体超过 max_request_body_size,即 100 KB) |
| 429 | rate_limit_exceeded |
速率限制
基于 Rails rate_limit 构建,计数器存储在 Rails.cache 中。此缓存必须在多个进程间共享(Solid Cache 或 Redis)。可通过 Spree::Api::Config 或以下环境变量进行配置:
| 配置项(环境变量) | 默认值 | 限制范围 |
|---|---|---|
rate_limit_per_key(SPREE_RATE_LIMIT_PER_KEY) | 每个时间窗口 300 次 | 发布密钥 + 客户端 IP(按访问者计算);未发送密钥时按客户端 IP 计算 |
rate_limit_per_secret_key(SPREE_RATE_LIMIT_PER_SECRET_KEY) | 每个时间窗口 600 次 | 每个密钥,在整个 API 范围内计算 |
rate_limit_window(SPREE_RATE_LIMIT_WINDOW) | 60 s | |
rate_limit_login | 5 | 按 IP 计算:商店/管理员/卖家登录、接受邀请 |
rate_limit_register | 3 | 按 IP 计算:客户注册、订阅新闻简报 |
rate_limit_refresh | 10 | 按 IP 计算:刷新、注销、管理员身份验证提供方 |
rate_limit_password_reset | 3 | 按 IP 计算 |
响应包含 X-RateLimit-Limit、X-RateLimit-Remaining,达到限制时还会包含 Retry-After。入站支付/履约 Webhook 端点有自己的固定限制:每分钟 120 次。
调试指南
- 所有请求都返回 401:将
pk_密钥发送到了 Admin API,或将sk_密钥发送到了 Store API;密钥已撤销;管理员 JWT 已超过 5 分钟(通过 Cookie 刷新);客户令牌发送到了 Admin API(aud错误)。 - Admin API 返回 403:查看
details.required_scope/required_permission。对于密钥,创建一个具有相应范围的新密钥。对于员工,将该密钥添加到其在此商店中的角色中。 - 结账后购物车立即返回 404:这是预期行为。获取
GET /orders/{cart_id}。 - 列表为空,但记录确实存在:渠道错误(
X-Spree-Channel或绑定了渠道的密钥);商品未在该渠道发布;所请求币种或市场没有价格;或 Ransack 筛选条件被静默丢弃。 - 从终端进行探测:
spree api get /orders/or_xxx --expand payments,fulfillments(参见spree-cli)。
了解更多
- OpenAPI 规范(权威):
node_modules/@spree/docs/dist/api-reference/store.yaml、admin.yaml、seller.yaml - Store API 指南:
node_modules/@spree/docs/dist/api-reference/store-api/(身份验证、本地化、幂等性、速率限制、查询、关联) - Admin API 指南:
node_modules/@spree/docs/dist/api-reference/admin-api/(身份验证、错误、查询) - Webhook 事件:
node_modules/@spree/docs/dist/api-reference/webhooks-events.md - 相关技能:
spree-auth-permissions、spree-typescript-sdk、spree-cli、spree-resource(添加端点)、spree-checkout、spree-marketplace(Seller API)、spree-events-webhooks。