mcpField
S
salla-api-core

salla-api-core

github.com/sallaapp开发290 次安装6 天前更新
代码仓库

关于 salla-api-core

salla-api-core 是用于编写 Salla Admin API 调用的技能,面向需要访问商家资源和应用设置的开发者。它按步骤说明 OAuth 请求、分页、错误与限流处理,并要求依据各端点的 OpenAPI 架构构建调用。

主要功能

  1. 01使用 Bearer 令牌发送 HTTPS 请求
  2. 02按页遍历集合资源
  3. 03处理 API 错误和 429 限流
  4. 04读取、合并并更新应用设置
  5. 05依据端点 OpenAPI 架构校验请求

适用场景

  1. 01实现 Salla 商家 API 客户端
  2. 02获取分页返回的资源集合
  3. 03在限流或验证失败时处理响应
  4. 04安全更新应用设置以避免丢失字段

MCP 相关

该技能针对使用商家 access_token 的 Salla Admin API 运行时调用,并明确区别于 Partners MCP 操作。它也提及可通过 Partners MCP 的文档和工具查找资源信息。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sallaapp/salla-partners-agent-kit --skill salla-api-core

安装后在 Claude Code 中输入/salla-api-core确认已加载。

附带文件references/api-resources.md

详细介绍

Salla 管理 API——核心流程

其他所有 Salla 技能构建于此基础之上:进行身份验证、调用资源、分页、处理错误和速率限制,并安全地读取/写入应用设置。请按顺序完成这些步骤;通过每个关卡后再继续。这是使用商家 access_token(通过 salla-app-auth 获取)运行的代码,并非 Partners MCP 操作。

基础 URL: https://api.salla.dev/admin/v2(以下所有路径均相对于此 URL)。 文档(入门):https://docs.salla.dev/421117m0.md

仅限 HTTPS——普通 HTTP 请求会被拒绝;每次调用都必须携带 Authorization: Bearer <ACCESS_TOKEN>(OAuth 2.0)。TLS 可保护传输中的数据 (文档:https://docs.salla.dev/421121m0.md、https://docs.salla.dev/421118m0.md)。

跨资源通用规则(基础、身份验证、状态码、错误/分页格式、速率限制、版本控制、多语言)摘自官方文档,具有权威性。各资源相关的路径、字段、作用域和封装格式均为示例——实际使用前,请对照当前资源文档(参见 references/api-resources.md)或 Partners MCP (salla_apps / salla_reference)进行确认。令牌机制——存储、刷新、刷新令牌单次使用规则以及刷新互斥锁——由 salla-app-auth 负责。

闭环——由架构驱动的调用(验证,而非猜测)

针对每个资源的调用,都要依据该端点自身记录的 OpenAPI 架构构建,不要凭记忆操作。每个端点的 docs.salla.dev/<id>.md 页面都嵌入了完整的 OpenAPI 3.x 规范,位于 ```yaml 代码块中(请求/响应架构、字段类型、枚举、必填字段)——该代码块是事实来源。按以下流程操作:

  1. 查找端点的文档页面:通过 salla-docs(或 references/api-resources.md)找到页面,并阅读其中嵌入的 openapi: YAML 块。
  2. 构建符合架构的请求——包括必填字段、类型和枚举。
  3. 调用并验证响应是否符合架构;若不匹配,请修正请求后重试(搜索 → 构建 → 验证 → 修正)。

优先采用文档中的 OpenAPI 块,不要依赖假设;如果文档不够明确,Partners MCP(salla_reference)是替代信息来源。

步骤 0——发现

  1. 要调用哪个资源/端点?(订单、商品、客户、设置……)
  2. 是否有有效的商家 access_token?(如果没有 → salla-app-auth)
  3. 要获取单条记录还是集合?(集合 → 需要分页,见步骤 3)

步骤 1——验证身份并识别商家

每个请求都通过 Authorization 请求头中的访问令牌进行身份验证:

Authorization: Bearer <access_token>
Accept: application/json
Content-Type: application/json   # for POST/PUT/PATCH

解析稳定的商家标识一次,并将其缓存:

GET https://accounts.salla.sa/oauth2/user/info
Authorization: Bearer <access_token>

merchant.id(顶层字段——user/info 没有 data 封装)是稳定的内部标识; 缓存一次,并在后续请求中重复使用。

官方规定的五种 401 情况

令牌/授权失败会返回 401,并附带包含 code 和 message 的 error 对象。Responses 文档(https://docs.salla.dev/421123m0.md)明确列出了五种情况:

#情况error.message(示例)操作
1用户已删除The User is not exists.商家已不存在——停止调用;等待重新安装。
2用户未激活حسابك غير مفعل(账号未激活)停止调用;等待重新激活/重新安装。
3刷新令牌已被重用error: "invalid_grant"——撤销访问令牌和刷新令牌商家必须重新安装应用——通过 salla-app-auth 处理。
4不允许该作用域The access token should have access to one of those scopes: products.read_write为应用添加该作用域;令牌无法访问此端点。
5令牌无效/已过期The access token is invalid(已过期、应用已卸载或值无效)刷新令牌(salla-app-auth);若无法刷新,则重新授权。

情况 3 返回原始 OAuth 格式 { "error": "invalid_grant", ... }(而不是 { status, success, error } 封装格式),并且也会使访问令牌失效——并行重用一次性刷新令牌会导致必须完整重新安装。用于防止此情况的刷新互斥锁位于 salla-app-auth 中。

读取 error.message 以便记录日志,但应根据具体情况决定行为:对于 1–3,停止操作并等待重新安装/重新激活;对于 4,修正作用域;对于 5,刷新后重试。

检查点:"GET /oauth2/user/info 是否返回 200,并且你已缓存 merchant.id(顶层字段,而不是 data.merchant.id)?"


第 2 步 — 发起请求

API 采用 URI 版本控制——基础 URL 中的 /admin/v2 就是版本。Salla 只会在发生破坏性变更时升级版本(响应格式变更、类型变更或移除部分 API);非破坏性新增内容会直接上线(文档:https://docs.salla.dev/421126m0.md)。

常见资源端点:

资源方法端点文档
订单GET/ordershttps://docs.salla.dev/5394146e0.md
商品GET/productshttps://docs.salla.dev/5394168e0.md
客户GET/customershttps://docs.salla.dev/841780f0.md

每个资源组对应的入口文档,请参阅 references/api-resources.md(用于“查找正确文档”的索引)。各资源的路径/字段/作用域请查阅相应文档,而非本文档。

**多语言(可选):**部分端点会对响应进行本地化。发送 Accept-Language: <iso_code> 表示语言偏好(发送 Content-Language 声明正文所用语言)。iso_code 的取值来自 Languages 端点;默认值为 ar(文档:https://docs.salla.dev/421122m0.md)。

带正文的 2xx 响应会封装 data(204 不带正文——见下文):

{ "status": 200, "success": true, "data": {} }
代码含义
200OK——请求成功
201已创建——资源已插入/更新
202已接受——资源已删除
204无内容——成功,不带正文

最简请求助手(TypeScript):

const BASE = "https://api.salla.dev/admin/v2";

async function sallaRequest<T>(
  path: string,
  accessToken: string,
  options: RequestInit = {},
): Promise<T> {
  const method = (options.method ?? "GET").toUpperCase();
  const res = await fetch(`${BASE}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${accessToken}`,
      Accept: "application/json",
      // Content-Type only when sending a body
      ...(method === "GET" || method === "DELETE"
        ? {}
        : { "Content-Type": "application/json" }),
      ...options.headers,
    },
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw Object.assign(new Error(err?.error?.message ?? res.statusText), {
      status: res.status,
      body: err,
    });
  }

  if (res.status === 204) return undefined as T;
  return res.json() as Promise<T>;
}

检查点:“简单的 GET 请求(例如 /orders)是否返回 { status, success, data } 封装格式?”


第 3 步 — 对集合进行分页

集合端点接受以下参数(文档:https://docs.salla.dev/421124m0.md):

参数默认值最大值说明
page1—从 1 开始
per_page1560最大值为 60
GET /orders?page=2&per_page=40

响应结构:

{
  "status": 200,
  "success": true,
  "data": [
    /* records */
  ],
  "pagination": {
    "count": 40,
    "total": 120,
    "perPage": 40,
    "currentPage": 2,
    "totalPages": 3,
    "links": {}
  }
}

要遍历所有页面:在 currentPage < totalPages 时循环,并递增 page。

检查点:“你可以使用 currentPage/totalPages 遍历集合的每一页吗?”


第 4 步 — 处理错误和速率限制

错误结构(单字段和多字段错误的结构相同——fields 的映射关系为字段 → 消息; 文档:https://docs.salla.dev/421123m0.md):

{
  "status": 422,
  "success": false,
  "error": {
    "code": "error",
    "message": "alert.invalid_fields",
    "fields": { "first_name": ["الاسم الاول للعميل مطلوب"] }
  }
}

对于任何 4xx 验证错误,fields 下的每个条目都采用 字段 → [消息…] 的形式;多字段错误只会列出更多键。读取 error.fields 以显示每个字段对应的消息。

状态Slug含义
400bad_request参数、字段或筛选条件无效
401unauthorized令牌无效或已过期,缺少权限范围
403forbidden访问被拒绝或暂时受限
404not_found未找到资源或路径
405method_not_allowed不支持此 HTTP 方法
406not_acceptable不接受此格式
410gone资源已不可用
422validation_failed必填字段缺失或无效
429too_many_requests超出速率限制
500server_error内部服务器错误——请稍后重试
503service_unavailable暂时过载或正在维护
速率限制(官方信息——文档:https://docs.salla.dev/421125m0.md)

限制按商店及其套餐执行,并采用漏桶算法:

套餐最大请求数时间窗口超限后的漏出速率
Plus1201 分钟每秒 1 个请求
Pro3601 分钟每秒 1 个请求
Special7201 分钟每秒 1 个请求

每分钟的请求额度用完后,直到时间窗口重置前,你仍可按每秒 1 个请求的速率发送请求(漏出速率)。Customers 端点有更严格的独立上限:每 10 分钟 500 个请求——请相应调整以客户为主的任务的请求频率。

超出限制会返回 429 too_many_requests;持续滥用或异常模式可能导致访问暂时受限(403)。

每个响应中都包含以下标头:

标头含义
X-RateLimit-Limit此套餐每分钟允许的调用次数
X-RateLimit-Remaining当前时间窗口内剩余的调用次数
Retry-After重试前需等待的秒数(429 响应中提供)
X-RateLimit-Reset时间窗口重置时的 UTC epoch 秒数

读取实时标头以调整退避策略;以上套餐数值是规划时可参考的文档所列最大值。

if (response.status === 429) {
  // Fetch Headers: use .get(), not bracket access
  const retryAfter = parseInt(response.headers.get("retry-after") ?? "60", 10);
  await sleep(retryAfter * 1000);
  // retry the request
}

检查点:“你的客户端会读取 error.message/error.fields,并在收到 429 时根据 Retry-After 进行退避吗?”


第 5 步 — 读取和写入应用设置(读取-修改-写入)

GET  /apps/{appId}/settings   # fetch current settings
POST /apps/{appId}/settings   # replace the ENTIRE settings object

**POST 是完整替换,而不是合并。**请求正文中省略的任何键都会被设为 null。 务必先读取、再合并,然后写入完整对象:

// 1. Fetch current settings
const { data: current } = await api.get(`/apps/${appId}/settings`);
// 2. Merge your change
const updated = { ...current, new_key: "value" };
// 3. POST the full merged object
await api.post(`/apps/${appId}/settings`, updated);

在 Partners Portal 中输入的设置(或通过 Partners MCP salla_settings 工具定义的设置——参见 salla-app-settings)可在 App Functions 中通过 context.settings 获取 — 无需在此处调用 API。

检查项:“你的设置更新是否先读取 → 合并 → 再 POST 完整对象(没有丢弃任何键)?”


快速参考文档链接

主题URL
入门指南https://docs.salla.dev/421117m0.md
授权(OAuth 2.0)https://docs.salla.dev/421118m0.md
安全https://docs.salla.dev/421121m0.md
响应和错误https://docs.salla.dev/421123m0.md
分页https://docs.salla.dev/421124m0.md
速率限制https://docs.salla.dev/421125m0.md
版本控制https://docs.salla.dev/421126m0.md
多语言https://docs.salla.dev/421122m0.md
资源索引(查找合适的文档)references/api-resources.md
应用设置指南salla-app-settings 技能(references/form-builder.md)

评论

支持 Markdown