Files
affiliate_dash/docs/开放接口-皮肤源头对接.md
T
2026-07-20 16:34:23 +08:00

8.8 KiB
Raw Blame History

店铺订单对接 API 文档

文档对象:发货系统技术人员
版本:v1
更新说明:鉴权为 ApiKey + HMAC 签名,请按本文实现,勿只传 Key。


0. 你们要做什么(一句话)

  1. 我们店铺的订单号order_no)调【订单查询】,确认商品 sku、是否可发货 can_ship
  2. 可发则在你们系统发货
  3. 发货结果调【发货推送】告诉我们,我们同步订单状态

发货记录、积分消耗在你们后台给店铺看;我们这边以订单状态为准。


1. 环境信息

值(示例 / 请替换)
API 根地址 Base URL https://http://221329.cc.cd 开发临时地址
X-Api-Key 测试使用,如 sk_source_dev_key_3ad3d1bbacda0e54
api_secret 测试使用,只用于本地算签名,不要写在 URL/Header, sk_source_dev_secret_5a43ec55cd020d83
时间偏差 默认允许 ±300 秒

统一响应格式:

{
  "code": 0,
  "message": "ok",
  "data": {}
}
  • code = 0:成功
  • code != 0:失败,看 message

2. 鉴权(每次请求必带)

Header 必填 说明
X-Api-Key 店铺分配的 Key
X-Timestamp 当前 Unix 时间戳
X-Nonce 随机串,长度 8~64;同一 Key 在有效期内不可重复
X-Sign 见下方算法

2.1 签名字符串(参数字典序 + & 拼接)

参与签名的参数:

参数名 说明
api_key 与 Header X-Api-Key 相同
timestamp 与 Header X-Timestamp 相同
nonce 与 Header X-Nonce 相同
method 大写:GET / POST
path 仅路径,不要域名、不要 query。例:/api/open/v1/orders/O123
body 原始 HTTP bodyGET 用空字符串

规则:

  1. value 原样拼接,不做 URL encode
  2. 按参数名 ASCII 字典序 排序
  3. 拼成:k1=v1&k2=v2&k3=v3...
  4. X-Sign = hex( HMAC-SHA256( api_secret, 签名字符串 ) )小写十六进制

排序后参数名顺序固定为:

api_key, body, method, nonce, path, timestamp

GET 示例body 为空):

api_key=sk_xxx&body=&method=GET&nonce=a1b2c3d4e5f67890&path=/api/open/v1/orders/O202607201550038000&timestamp=1721450000

POST 示例

api_key=sk_xxx&body={"order_no":"O202607201550038000","ship_status":"success"}&method=POST&nonce=a1b2c3d4e5f67890&path=/api/open/v1/orders/ship-notify&timestamp=1721450000

2.2 Python 参考实现

import hmac, hashlib, time, uuid, requests

API_KEY = "请替换为店铺下发的 key"
API_SECRET = "请替换为店铺下发的 secret"
BASE = "https://api.example.com"  # 请替换

def build_sign_string(api_key: str, timestamp: str, nonce: str, method: str, path: str, body: str = "") -> str:
    params = {
        "api_key": api_key,
        "body": body,
        "method": method.upper(),
        "nonce": nonce,
        "path": path,
        "timestamp": timestamp,
    }
    # 字典序 + & 拼接,value 不 encode
    return "&".join(f"{k}={params[k]}" for k in sorted(params.keys()))

def sign_headers(method: str, path: str, body: str = "") -> dict:
    ts = str(int(time.time()))
    nonce = uuid.uuid4().hex
    raw = build_sign_string(API_KEY, ts, nonce, method, path, body)
    sign = hmac.new(API_SECRET.encode(), raw.encode(), hashlib.sha256).hexdigest()
    return {
        "X-Api-Key": API_KEY,
        "X-Timestamp": ts,
        "X-Nonce": nonce,
        "X-Sign": sign,
    }

# —— 查询订单 ——
path = "/api/open/v1/orders/O202607201550038000"  # 换成真实店铺订单号
print(requests.get(BASE + path, headers=sign_headers("GET", path)).json())

# —— 推送发货成功(body 必须与签名用的 body 完全一致)——
path = "/api/open/v1/orders/ship-notify"
body = '{"order_no":"O202607201550038000","ship_status":"success","provider_order_no":"SRC001"}'
headers = {"Content-Type": "application/json", **sign_headers("POST", path, body)}
print(requests.post(BASE + path, headers=headers, data=body.encode("utf-8")).json())

注意:

  • POST 时签名用的 body 必须与实际发送的 body 字节级一致(不要一边签名一边再改空格/字段顺序)。
  • value 不要 URL encode。
  • 签名失败常见原因:secret 错、path 多了 query、body 不一致、时间戳过期、nonce 重复、参数未按字典序拼接。

3. 接口一:订单查询(发货前置)

3.1 请求

GET /api/open/v1/orders/{order_no}

order_no:店铺订单号(由买家/店铺提供,格式类似 O202607201550038000)。

3.2 成功示例

{
  "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": ""
  }
}

3.3 字段说明

字段 说明
order_no 店铺订单号
status 订单状态,见下表
can_ship 是否允许发货,发货前必须为 true
cannot_ship_reason 不可发原因(can_ship=false 时)
product.sku 发货商品标识(英文固定码),请按此发货
product.name 中文名(展示用)
product.game 游戏,如「和平精英」
buyer_name 买家备注
amount 金额
provider_order_no 若已回传过你们的单号
shipped_at 发货成功时间
ship_fail_reason 最近失败原因

3.4 订单状态与 can_ship

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

3.5 错误

HTTP / code 含义
401 鉴权失败(Key/签名/时间/Nonce
404 订单号不存在
400 参数错误

4. 接口二:发货结果推送

4.1 请求

POST /api/open/v1/orders/ship-notify
Content-Type: application/json
{
  "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 RFC3339success 未传则用服务端时间
fail_reason 失败原因(failed 时建议填)

4.2 ship_status → 我们订单状态

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

4.3 幂等与限制

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

4.4 成功响应示例

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

5. 推荐调用顺序

拿到店铺订单号 order_no
    ↓
GET 订单查询
    ↓
can_ship == false  → 停止,展示 cannot_ship_reason
    ↓ true
(可选)POST ship_status=processing
    ↓
按 product.sku 发货
    ↓
POST ship_status=success 或 failed

发货请以 product.sku 为准,不要只依赖中文名。


6. 联调检查清单

  • 只带 Key 不带签名 → 401
  • Secret 错误 → 401「签名校验失败」
  • 错误/过期 Timestamp、重复 Nonce → 401
  • 不存在的 order_no → 404
  • 未支付订单 can_ship=false
  • 已支付订单 can_ship=true,且 sku 正确
  • success 后订单 delivered
  • 重复 success 幂等成功
  • failed 后可再 query 且 can_ship=true

7. 商品 sku 说明

商品英文码与中文名对照由店铺维护(如:套装-糯粉咩咩 → suit_pink_sheep)。
查询接口返回的 product.sku 即当前订单应发商品。


8. 联系方式

接口问题、密钥申请、联调订单号请联系店铺方技术对接人
本文档由店铺系统生成/维护,以店铺最新版本为准。