×

《京东联盟API爬坑:不用店铺授权也能拿商品详情,但库存只有“有货/无货”》(附Python源码)

万邦科技Lex 万邦科技Lex 发表于2026-07-15 09:31:28 浏览23 评论0

抢沙发发表评论

这是京东开放体系里最容易混淆、也最容易踩坑的点之一:

京东联盟API(jd.union.*)≠ 京东开放平台JOS(商家ERP接口),两者在数据深度与授权要求上完全不同。

一、核心结论先给

  • 京东联盟API(jd.union.open.goods.detail.query
    不需要店铺授权,只要京东联盟实名应用 + AppKey/Secret 就能调
    库存只返回状态(有货 / 无货 / 预售),不返回具体可售库存数量
    ✅ 适合:选品、比价、佣金监控、CPS推广、价格追踪

  • 京东开放平台JOS(jingdong.ware.read.get/ jingdong.stock.get
    需要企业认证 + 店铺卖家OAuth授权
    返回真实SKU库存数量、区域库存、锁定库存
    ✅ 适合:ERP商品同步、防超卖、WMS出入库、店铺自营管理

如果你做电商ERP、库存预占、发货回写,联盟接口库存不够用,必须走JOS商家接口。

二、京东联盟接口“坑点”拆解

坑序号
现象
真相
以为不用授权就能拿全量数据
联盟无店铺权限,看不到真实库存、成本价、商家编码
skuList[].stock要么没有,要么是1/0
联盟返回的是 stockState(1=有货 / 0=无货),不是 stockNum
价格字段和京东前台不一致
联盟返回促销价/券后价/佣金价,不是商家后台“基准库存价”
调订单/物流接口报403
联盟只有商品/推广/订单概览,没有 jd.order.detail.get这种商家接口
以为AppKey通用
联盟应用 ≠ JOS应用,网关不同,权限体系隔离

三、Python:京东联盟商品详情调用(含库存状态解析)

# jd_union_detail_demo.py
"""
京东联盟API 商品详情查询
jd.union.open.goods.detail.query
- 无需店铺授权
- 库存仅返回 有货(1) / 无货(0) / 预售 状态
依赖: requests
"""
import hashlib
import json
import time
import requests
from typing import Dict, List


class JdUnionClient:
    GW = "https://api.jd.com/routerjson"

    def __init__(self, app_key: str, app_secret: str):
        self.ak = app_key
        self.ask = app_secret

    # ───── JOS/联盟通用 MD5签名(秒级timestamp)─────
    def _sign(self, params: Dict) -> str:
        filt = sorted(
            (k, v) for k, v in params.items()
            if k != "sign" and v is not None and str(v).strip() != ""
        )
        qs = "".join(f"{k}{v}" for k, v in filt)
        raw = f"{self.ask}{qs}{self.ask}"
        return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()

    def get_goods_detail(self, sku_ids: List[int], fields: str = None) -> Dict:
        """
        sku_ids: 最多20个SKU ID
        fields 建议: skuId,productName,price,promotionPrice,couponInfo,
                      stockState,commissionInfo,salesCount,shopName
        """
        fields = fields or (
            "skuId,productName,price,promotionPrice,couponInfo,"
            "stockState,commissionInfo,salesCount,shopName,logisticsType"
        )
        biz = {"skuIds": sku_ids, "fields": fields}

        params = {
            "app_key": self.ak,
            "method": "jd.union.open.goods.detail.query",
            "timestamp": str(int(time.time())),   # ← 秒级!
            "format": "json",
            "v": "2.0",
            "sign_method": "md5",
            "360buy_param_json": json.dumps(biz, ensure_ascii=False,
                                             separators=(',', ':'))
        }
        params["sign"] = self._sign(params)

        r = requests.post(self.GW, data=params, timeout=15)
        r.raise_for_status()
        d = r.json()

        resp_key = "jd_union_open_goods_detail_query_response"
        data = d.get(resp_key, {})
        if "error_response" in str(data):
            err = data.get("error_response")
            raise Exception(f"Union API Err: {err.get('code')} {err.get('zh_desc')}")
        return data

    # ───── 解析库存状态 ─────
    def parse_stock(self, item: Dict) -> str:
        """
        联盟接口库存只有状态,不是数量
        stockState: 1=有货, 0=无货, 可能含预售标识
        """
        state = item.get("stockState")
        if state == 1:
            return "有货"
        elif state == 0:
            = "无货"
        else:
            return f"未知状态({state})"

        # 有些返回在 skuList[].stock 用 1/0 表示
        skus = item.get("skuList", [])
        if skus:
            print("   SKU库存示意:", [(s.get("skuId"), s.get("stock", "N/A"))
                                       for s in skus[:3]])


# =========================================================
# 使用示例
# =========================================================
if __name__ == "__main__":
    client = JdUnionClient("YOUR_JD_APP_KEY", "YOUR_JD_APP_SECRET")
    SKU_IDS = [100012345678, 100087654321]   # ← 替换真实SKU ID

    try:
        result = client.get_goods_detail(SKU_IDS)
        data_list = result.get("data", [])
        if isinstance(data_list, dict):
            data_list = data_list.get("data") or []

        for item in (data_list or []):
            title = item.get("productName")
            price = item.get("promotionPrice") or item.get("price")
            stock_flag = client.parse_stock(item)
            commission = item.get("commissionInfo", {}).get("commission", "N/A")

            print(f"✅ SKU:{item.get('skuId')}")
            print(f"   标题:{title}")
            print(f"   促销价:¥{price}")
            print(f"   库存状态:{stock_flag}  ← 仅状态,无具体库存数")
            print(f"   预估佣金:{commission}")
            client.parse_stock(item)  # 打印sku示意

    except Exception as e:
        print("❌", e)
        print("→ 确认: 京东联盟应用已实名、jd.union.open.goods.detail.query 已申请")

四、如果你必须要真实库存数量,怎么办?

京东JOS商家接口(需企业+店铺授权):
接口
能力
jingdong.ware.sku.read/ jingdong.ware.stock.get
返回 stockNum(可售库存)、lockStock(锁定)、hsStockNum(在途)
jingdong.stock.get
多仓库存、区域库存
access_token
卖家OAuth2授权(不是联盟token)
这部分代码在之前《京东商品详情API(jingdong.item.read.get)》《京东库存API(jd.stock.get)实时同步方案》中已经给过完整Python封装。

五、选型一句话总结(面试/方案)

京东联盟API(jd.union.open.goods.detail.query)不用店铺授权就能拿商品标题/价格/券/佣金/销量,但库存只有“有货/无货”状态;要做ERP库存同步、防超卖、发货回写,必须用京东开放平台JOS商家接口(jingdong.ware.read.get/ jd.stock.get)并走企业认证+卖家OAuth授权获取真实stockNum
需要我帮你把“联盟选品监控 + JOS商家库存校正”写成一套双通道同步Demo(Python)吗?


群贤毕至

访客