Files
affiliate_dash/docs/开放接口-皮肤源头对接.md
T
yml2213 89cdd32181 对接皮肤源头开放接口:查询、发货推送与 HMAC 签名鉴权
- 新增订单查询与发货结果推送开放接口,支持 can_ship 与幂等
- 鉴权采用 X-Api-Key + Timestamp + Nonce + HMAC-SHA256 签名
- 订单扩展发货字段与 ship_logs,管理端增加发货记录与开放文档页
2026-07-20 16:06:44 +08:00

8.4 KiB
Raw Blame History

皮肤源头开放接口对接文档

版本:v1
Base URLhttps://{你们的域名}(开发环境示例:http://localhost:8080
统一响应:

{
  "code": 0,
  "message": "ok",
  "data": {}
}
  • code = 0 表示成功
  • code != 0 表示失败,以 message 为准

1. 鉴权(ApiKey + 签名)

开放接口采用 双因子

凭证 说明
api_key 标识对接方,放在请求头 X-Api-Key
api_secret 仅用于本地算签名,禁止放在 Header / 前端 / 日志明文传输

每次请求必须带齐:

Header 必填 说明
X-Api-Key 对接方 Key
X-Timestamp Unix 级时间戳
X-Nonce 随机串,长度 8~64,同一 Key 下有效期内不可重复
X-Sign HMAC-SHA256 签名,小写十六进制

1.1 签名算法

待签名字符串(UTF-8,行之间用 \n,共 6 行):

{api_key}
{timestamp}
{nonce}
{METHOD}
{path}
{body}
规则
METHOD 大写,如 GET / POST
path URL.Path不含 query,如 /api/open/v1/orders/O123
body 原始请求体字符串;GET 无 body 时用 空字符串(仍保留最后一行空内容,即末尾仍有换行结构中的 body 段为空)

计算:

X-Sign = hex( HMAC-SHA256( api_secret, string_to_sign ) )
  • 使用 小写 hex
  • 时间戳与服务器偏差超过 300 秒(可配置)则拒绝
  • 同一 api_key + nonce 在有效期内重复使用 → 拒绝(防重放)

1.2 Python 示例

import hmac, hashlib, time, uuid, requests

API_KEY = "sk_source_dev_key_change_me"
API_SECRET = "sk_source_dev_secret_change_me"
BASE = "http://localhost:8080"

def sign(method: str, path: str, body: str = "") -> dict:
    ts = str(int(time.time()))
    nonce = uuid.uuid4().hex
    string_to_sign = "\n".join([API_KEY, ts, nonce, method.upper(), path, body])
    sign_hex = hmac.new(
        API_SECRET.encode(), string_to_sign.encode(), hashlib.sha256
    ).hexdigest()
    return {
        "X-Api-Key": API_KEY,
        "X-Timestamp": ts,
        "X-Nonce": nonce,
        "X-Sign": sign_hex,
    }

# 查询订单
path = "/api/open/v1/orders/O202607201550038000"
r = requests.get(BASE + path, headers=sign("GET", path))
print(r.json())

# 发货推送
path = "/api/open/v1/orders/ship-notify"
body = '{"order_no":"O202607201550038000","ship_status":"success","provider_order_no":"SRC001"}'
headers = {"Content-Type": "application/json", **sign("POST", path, body)}
r = requests.post(BASE + path, headers=headers, data=body.encode())
print(r.json())

注意POST 签名用的 body 必须与实际发送的 body 字节级一致(不要先 json.dumps 又改空格)。

1.3 鉴权失败示例

{ "code": 401, "message": "签名校验失败" }

常见原因:secret 错误、body 不一致、timestamp 过期、nonce 重复、path 写错(多了 query / 域名)。

1.4 环境变量(我方)

变量 说明 开发默认
OPEN_API_KEY Api Key sk_source_dev_key_change_me
OPEN_API_SECRET 签名密钥 sk_source_dev_secret_change_me
OPEN_SIGN_SKEW 时间偏差秒数 300

生产务必更换 Key / Secret。

2. 对接流程

1. 买家在店铺下单并完成支付
2. 上游拿到「店铺订单号 order_no」
3. 调用【订单查询】确认商品 sku、can_ship
4. can_ship=true 时执行发货
5. 发货结果调用【发货推送】回传
6. 我方同步订单状态;重复推送 success 幂等成功

3. 订单查询(发货前置)

请求

GET /api/open/v1/orders/{order_no}
X-Api-Key: {api_key}
X-Timestamp: {unix_seconds}
X-Nonce: {random_8_to_64}
X-Sign: {hmac_sha256_hex}

成功响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "O202607201550038000",
    "status": "paid",
    "can_ship": true,
    "cannot_ship_reason": "",
    "product": {
      "name": "套装-糯粉咩咩",
      "sku": "suit_pink_sheep",
      "game": "和平精英"
    },
    "buyer_name": "测试买家",
    "amount": 0,
    "provider_order_no": "",
    "created_at": "2026-07-20T15:50:03+08:00",
    "shipped_at": null,
    "ship_fail_reason": ""
  }
}

字段说明

字段 类型 说明
order_no string 店铺订单号
status string 订单状态,见下表
can_ship bool 是否允许发货,请以此为准
cannot_ship_reason string 不可发货原因(can_ship=false 时)
product.name string 商品中文名
product.sku string 商品英文固定标识,发货请用此字段
product.game string 游戏,如「和平精英」
buyer_name string 买家备注名
amount number 订单金额
provider_order_no string 上游单号(若已回传)
created_at string 下单时间
shipped_at string|null 发货成功时间
ship_fail_reason string 最近一次发货失败原因

订单状态 status

含义 can_ship
pending 待支付 false
paid 已支付 true
delivering 发货中 false
delivered 已交付 false
ship_failed 发货失败(可重试) true
cancelled 已取消 false

错误

HTTP message
401 鉴权失败(Key / 签名 / 时间 / Nonce
404 订单不存在
400 订单号不能为空

调用说明

请使用上一节 Python 示例生成签名后请求;仅带 X-Api-Key 而不带签名会被拒绝。


4. 发货结果推送

请求

POST /api/open/v1/orders/ship-notify
Content-Type: application/json
X-Api-Key: {api_key}
X-Timestamp: {unix_seconds}
X-Nonce: {random_8_to_64}
X-Sign: {hmac_sha256_hex}
{
  "order_no": "O202607201550038000",
  "ship_status": "success",
  "provider_order_no": "SRC20260720001",
  "shipped_at": "2026-07-20T16:00:00+08:00",
  "fail_reason": ""
}

请求字段

字段 必填 说明
order_no 店铺订单号
ship_status success / failed / processing
provider_order_no 上游发货单号
shipped_at 发货时间,RFC3339;success 时缺省用服务端时间
fail_reason 失败原因(failed 时建议填写)

ship_status 与订单状态映射

ship_status 订单变为 说明
processing delivering 已接单/发货中
success delivered 发货成功
failed ship_failed 发货失败,允许再次查询后重试

幂等

  • 订单已是 delivered,再次推送 success返回成功,不重复处理
  • 订单已是 delivered,推送 failed → 拒绝
  • 订单 cancelled → 拒绝更新

成功响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "O202607201550038000",
    "status": "delivered",
    "message": "发货成功,订单已交付"
  }
}

调用说明

Body 与签名字符串中的 body 必须完全一致。完整示例见 §1.2 Python

ship_status body 示例
processing {"order_no":"O...","ship_status":"processing","provider_order_no":"SRC..."}
success {"order_no":"O...","ship_status":"success","provider_order_no":"SRC...","shipped_at":"2026-07-20T16:00:00+08:00"}
failed {"order_no":"O...","ship_status":"failed","fail_reason":"账号不存在"}

5. 推荐调用顺序

query → can_ship?
  ├─ false → 停止,展示 cannot_ship_reason
  └─ true
       →(可选)push processing
       → 执行发货
       → push success / failed

发货务必使用查询结果中的 product.sku,不要仅依赖中文名。


6. 联调检查清单

  • 缺少 Sign / 错误 Secret 返回 401
  • 错误 ApiKey 返回 401
  • 过期 Timestamp / 重复 Nonce 返回 401
  • 不存在的订单号返回 404
  • pending 订单 can_ship=false
  • paid 订单 can_ship=true 且 sku 正确
  • success 推送后状态为 delivered
  • 重复 success 幂等成功
  • failed 后可再次 query 且 can_ship=true

7. 联系方式

接口问题、密钥申请、联调环境请联系店铺方技术对接人。
商品英文 sku 对照见:docs/商品英文名对照.md