格物 开放 API快速开始

REST API · 接入准备

REST API 快速开始

接口都是 HTTPS + JSON,不需要 SDK:任何能发 HTTP 请求、能算 HMAC-SHA256 的语言都能接。下面用大约 10 分钟,从拿到凭证走到发出第一个签名请求, 再以终端用户的身份调用接口。

  1. 在控制台拿到 appKey 与签名密钥
  2. 发出第一个请求:GET /v1/access-clients/me
  3. 搜索商品,并以终端用户的身份调用
  4. 看懂错误,排查签名

所有调用都放在你自己的服务端。签名密钥 app_secret 不能进小程序、App 或网页:包可以被解开,前端请经你的后端中转。

1. 拿到凭证

  • 用手机号登录开发者控制台,第一次登录会自动开通体验应用(7 天、每天 1000 次调用,可跑通查商品与下单)。 正式上线在控制台申请正式接入,审核通过后会出现一个新的正式应用。
  • 进入应用详情 →「凭证」:appKey 直接显示;签名密钥(app_secret)点「生成」后只显示这一次,立即保存。 丢了就重置,旧的立即失效。生成过密钥后,这个 appKey 的所有请求都必须签名。
  • 把两者放进服务端的环境变量(如 GOWU_APP_KEY、GOWU_APP_SECRET),不要写进代码仓库。
  • 应用详情里的「权限」列出这个 appKey 能调哪些接口;不在列表里的接口会返回 403,需要的话联系平台开通。

2. 发出第一个请求

下面是一个完整可运行的脚本:前半段是签名函数(可以原样抄进你的服务端),后半段调用 GET /v1/access-clients/me。 返回 200 并看到自己的 appKey 与开通的接口列表,就说明凭证和签名都对了。

签名的要点(完整规则见鉴权与签名):

  • 每个请求带 x-app-key、x-timestamp(Unix 秒)、x-nonce(每次一个新的随机串)、x-sign-version: 2、x-signature。
  • 签名串是 8 段用换行连接:OCP-SIGN-V2、时间戳、nonce、大写的方法、纯路径、规范化 query、请求体的 sha256、x-open-user。
  • 请求体先序列化成字符串,同一个字符串既算哈希又发出去;不要让 HTTP 库再序列化一次。
1// quickstart.mjs —— Node.js 18+:GOWU_APP_KEY=… GOWU_APP_SECRET=… node quickstart.mjs2import crypto from "node:crypto";3 4// 只在你自己的服务端运行:app_secret 不能进小程序 / App / 网页5// path 可带 query(如 /v1/lm/orders?page=2),不含域名6function gowuHeaders({ appKey, appSecret, method, path, body = "", openUser = "" }) {7  const ts = String(Math.floor(Date.now() / 1000));8  const nonce = crypto.randomUUID();9  const bodyHash = crypto.createHash("sha256").update(body).digest("hex");10  // v2:nonce 与规范化 query 都进签名(解码后按 key/value 升序、各自 encodeURIComponent)11  const [pathname, search = ""] = path.split("?");12  const query = [...new URLSearchParams(search).entries()]13    .sort(([ak, av], [bk, bv]) => (ak < bk ? -1 : ak > bk ? 1 : av < bv ? -1 : av > bv ? 1 : 0))14    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)15    .join("&");16  const canonical = ["OCP-SIGN-V2", ts, nonce, method.toUpperCase(), pathname, query, bodyHash, openUser].join("\n");17  const signature = crypto.createHmac("sha256", appSecret).update(canonical).digest("hex");18  return {19    "content-type": "application/json",20    "x-app-key": appKey,21    "x-timestamp": ts,22    "x-nonce": nonce,23    "x-sign-version": "2",24    "x-signature": signature,25    ...(openUser ? { "x-open-user": openUser } : {})26  };27}28 29const BASE = "https://api.gowu.net";30 31/** 发一个签名请求:body 先序列化成字符串,同一个字符串既算签名又发出去 */32async function gowu(method, path, { body, openUser } = {}) {33  const raw = body === undefined ? "" : JSON.stringify(body);34  const res = await fetch(BASE + path, {35    method,36    headers: gowuHeaders({37      appKey: process.env.GOWU_APP_KEY,38      appSecret: process.env.GOWU_APP_SECRET,39      method, path, body: raw, openUser40    }),41    body: raw || undefined42  });43  const data = await res.json();44  if (!res.ok) throw new Error(`${res.status} ${data.code}: ${data.message}`);45  return data;46}47 48// 第一个请求:确认 appKey、签名都对,并看到这个 appKey 开通了哪些接口49console.log(await gowu("GET", "/v1/access-clients/me"));
1{2  "appKey": "string",3  "remark": "string",4  "createdAt": "string",5  "apiCallCount": 06}

3. 搜索商品,并以终端用户的身份调用

查商品这类接口只要 appKey 与签名;下单、订单、售后、地址、额度这类「用户态」接口,还要用 x-open-user 说明这次是哪个用户在操作 (接口参考里标「用户态」的那些)。

  • x-open-user 放你系统里的用户 ID,必须取自你服务端的登录态,不能直接用前端传上来的值;它也参与签名(第 8 段)。
  • 平台按「appKey + 用户 ID」隔离数据:同一个用户 ID 在不同 appKey 下是不同的人,第一次出现时自动建档。
  • 员工福利 / 企业采购的用户还要先由应用负责人绑定到企业,才能用企业额度下单,见企业与员工;消费者购物直接用即可。
  • GET 请求的 query 也参与签名:把 query 原样写进 path,签名函数会按规则规范化。
1// 接着 quickstart.mjs 里的 gowu() 往下写2 3// 1) 搜索商品(不需要终端用户)4//    员工福利 / 企业采购用 /v1/enterprise/products/search,消费者购物用 /v1/lm/products/search,参数相同5const found = await gowu("POST", "/v1/enterprise/products/search", {6  body: { version: 2, query: { text: "保温杯" }, limit: 5 }7});8// 返回 { hits: [{ product, ... }], total, nextCursor }9console.log(found.hits.map((h) => `${h.product.id} ${h.product.title} ¥${h.product.price}`));10 11// 2) 以某个用户的身份查他的订单:openUser 取自你服务端的登录态,参与签名12//    query 直接写在 path 里,gowuHeaders 会按规范化规则把它算进签名13const orders = await gowu("GET", "/v1/enterprise/orders?page=1&pageSize=10", {14  openUser: req.session.userId15});

4. 看懂错误,排查签名

出错时 HTTP 状态码表意,响应体是 { "code": "...", "message": "..." },message 会说明具体原因。全部错误码见状态码与错误。

现象最常见的原因
401 签名不对请求体算哈希用的字符串与实际发送的不一致(被 HTTP 库重新序列化、多了空格或换行);path 带了域名;query 没算进签名;x-open-user 签名时的值与请求头不一致;用了 app_secret 以外的密钥。
401 时间戳过期服务器时钟偏差超过 ±300 秒(容器、虚拟机常见),开启 NTP 同步;时间戳是秒,不是毫秒。
401 nonce 重复重试时复用了上一次的请求头。每次请求(包括重试)都要重新生成 nonce 与签名。
403这个接口不在 appKey 的权限里(控制台「权限」可查);或体验应用已到期。员工福利 / 企业采购还可能是用户没绑定企业、不是管理员。
429超出限流或体验应用当天的调用量,稍后重试,见限流。

排查签名时,把你算出的 8 段签名串逐段打印出来对照:绝大多数问题出在第 6 段(query)与第 7 段(请求体哈希)。

5. 下一步

  • 按你的场景读接入指南:员工福利 / 企业采购从企业与员工开始,消费者购物从用户与地址开始。
  • 查字段:接口参考里每个接口都有参数表、cURL / Node.js / Python 调用示例与返回示例。
  • 调试工具:OpenAPI 规格按场景各一份,可导入 Postman、Apifox、Swagger UI,或生成客户端: 员工福利购物、企业采购、消费者购物。
  • 订单、售后结果用 Webhook 推给你,在控制台「凭证」里配置回调地址。
  • 给 AI 写代码用:llms.txt 是按场景分好的接口索引,llms-full.txt 含完整入参出参与示例。不想自己做签名,让 Agent 直接用 MCP 或 命令行 gowu。

本篇用到的接口