×

🟦《京东开放平台JOS接入全指南:注册、AppKey、OAuth2.0授权与沙箱调试(2026最新)》(附Python源码)

万邦科技Lex 万邦科技Lex 发表于2026-07-02 09:07:26 浏览23 评论0

抢沙发发表评论

🟦《京东开放平台JOS接入全指南:注册、AppKey、OAuth2.0授权与沙箱调试(2026最新)》(附Python源码)

京东开放平台(JOS = JD Open Service)是京东POP商家/ISV对接商品、订单、物流、售后、仓储的官方渠道。爬虫不可取,JOS免费额度内零成本,下面按实际接入顺序给你完整说明 + Python签名/授权/沙箱调用代码。

一、接入四步曲(先记这个)

① 注册京东商家/开发者 → 创建JOS应用(自用/ISV)
   https://jos.jd.com → 获取 AppKey + AppSecret(别名AppPassword)

② 申请接口权限(商品/订单/物流…)

③ OAuth2.0 授权(店铺卖家登录→code→access_token/refresh_token)
   *只有订单/私有店铺数据需要 token,商品公开查询可不传*

④ 沙箱调通签名 → 切生产网关正式同步

二、创建应用 & 获取 AppKey / AppSecret

  1. 登录 京东开放平台控制台 → 创建应用

  2. 应用类型:

    • 商家自用应用(推荐,绑定自己店铺)→ 免ISV入驻

    • ISV应用 → 需软服中心入驻、商家授权托管

  3. 应用审核通过后,详情页复制:

    • App Key(也叫 app_key/ client_id

    • App Secret(也叫 app_password/ client_secret

  4. 接口管理 → 申请权限:勾选所需(如 jingdong.pop.order.searchjingdong.ware.sku.readjingdong.etms.trace.get等)

⚠️ 京东JOS签名用 360buy/ jd参数名规范,详见下文。

三、JOS 签名算法(必须掌握)

京东JOS使用 MD5签名,规则:
  1. 收集所有业务参数 + 公共参数app_key, method, timestamp, v, sign_method,若有 access_token也参入)

    • 剔除:signfile字段、值为空(""/None)

  2. 按参数名 ASCII 升序排序

  3. 拼接key1+value1+key2+value2+...(无=&

  4. 首尾拼 AppSecretAppSecret + 拼接串 + AppSecret

  5. MD5 → 大写

待签名 = APP_SECRET + app_keyxxxformatjsonmethodjd.xxxv2.0timestampxxx + APP_SECRET
sign = MD5(待签名).upper()
📌 京东参数名多为 jd.xxxmethod值如 jingdong.pop.order.searchtimestamp秒级(10位),不同于TOP的毫秒!

四、OAuth2.0 卖家授权获取 AccessToken

订单/私有店铺接口需卖家账号授权
① 引导卖家访问:
https://auth.jd.com/oauth2/toLogin.action
 ?response_type=code
 &client_id=YOUR_APP_KEY
 &redirect_uri=URLENCODE(你在应用配置的回调地址)
 &state=erp_jd

② 回调 → redirect_uri?code=xxx&state=erp_jd

③ 换 token:
POST https://auth.jd.com/oauth2/accessToken
  grant_type=authorization_code
  client_id=APP_KEY
  client_secret=APP_SECRET
  code=xxx
  redirect_uri=同上
→ {access_token, refresh_token, expires_in, uid, user_nick}

五、Python完整封装(签名 + OAuth + 沙箱/生产调用)

# jd_jos_client.py
"""
京东开放平台(JOS) API Client — 2026版
网关:
  生产: https://api.jd.com/routerjson
  沙箱: https://api.sandbox.jd.com/routerjson  (部分接口支持)
签名: MD5(AppSecret + KV_sorted + AppSecret) → upper
timestamp: 秒级(10位)
"""
import hashlib
import time
import requests
import urllib.parse
from typing import Dict, Optional
from datetime import datetime, timedelta

# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class JdJosClient:
    PROD = "https://api.jd.com/routerjson"
    SANDBOX = "https://api.sandbox.jd.com/routerjson"

    def __init__(self, app_key: str, app_secret: str, sandbox: bool = False):
        self.ak = app_key
        self.as_ = app_secret
        self.gw = self.SANDBOX if sandbox else self.PROD

    # ─────────────── 签名 ───────────────
    def _sign(self, params: Dict) -> str:
        filt = sorted((k, v) for k, v in params.items()
                       if v is not None and str(v).strip() != '' and k != 'sign')
        qs = ''.join(f"{k}{v}" for k, v in filt)
        raw = f"{self.as_}{qs}{self.as_}"
        return hashlib.md5(raw.encode('utf-8')).hexdigest().upper()

    # ─────────────── 通用调用 ───────────────
    def call(self, method: str, biz: Dict, access_token: str = None) -> Dict:
        """
        method: 如 'jingdong.pop.order.search'
        biz:    业务参数字典(会变成 JSON 串放 '360buy_param_json')
        """
        api_params = {
            "app_key": self.ak,
            "method": method,
            "timestamp": str(int(time.time())),   # ← 秒级!
            "format": "json",
            "v": "2.0",
            "sign_method": "md5",
            "360buy_param_json": json_dumps(biz)
        }
        if access_token:
            api_params["access_token"] = access_token

        api_params["sign"] = self._sign(api_params)

        # JOS推荐 POST x-www-form-urlencoded
        r = requests.post(self.gw, data=api_params, timeout=15)
        r.raise_for_status()
        d = r.json()

        # JOS返回结构: {method_response: {error_response / xxx_response}}
        resp_key = method.replace(".", "_") + "_response"
        if resp_key not in d:
            # 有些接口直接用 method名作为key
            for k in d:
                if k.endswith("_response"):
                    resp_key = k
                    break
        data = d.get(resp_key, d)

        if "error_response" in str(data):
            # 精细提取
            err = d.get(resp_key, {}).get("error_response") if resp_key in d else d.get("error_response")
            if err:
                raise Exception(f"JOS Err[{err.get('code')}]: {err.get('zh_desc') or err.get('en_desc')} "
                                f"sub:{err.get('sub_code')}")
            raise Exception(f"JOS 未知错误: {d}")
        return data

    # ─────────────── 示例:商品详情(公开可不传token)────────
    def get_sku_detail(self, sku_id: str) -> Dict:
        return self.call(
            "jingdong.ware.sku.read.findSkuById",
            {"skuId": sku_id, "fields": "skuId,wareId,title,price,jdPrice,stockNum"}
        ).get("skuReadResult", {}).get("skuEntity", {})

    # ─────────────── 示例:订单列表(需token)────────
    def list_orders(self, access_token: str,
                    start_modified: str, end_modified: str,
                    page=1, page_size=50) -> Dict:
        return self.call(
            "jingdong.pop.order.search",
            {
                "start_modified": start_modified,
                "end_modified": end_modified,
                "order_state": "WAIT_SELLER_STOCK_OUT",  # 已付款待发货示例
                "page": page,
                "page_size": min(page_size, 100)
            },
            access_token=access_token
        ).get("popOrderSearch", {}).get("orderSearch", {})

    # ─────────────── 物流轨迹(需token)────────
    def get_trace(self, access_token: str, waybill_code: str,
                  customer_code: str = None) -> Dict:
        biz = {"waybillCode": waybill_code}
        if customer_code:
            biz["customerCode"] = customer_code
        return self.call("jingdong.etms.trace.get", biz, access_token
                        ).get("etmsTraceGetResponse", {}).get("traceApiResult", {})


# ── OAuth 换 token 辅助 ──
def jd_oauth_exchange(app_key, app_secret, code, redirect_uri):
    r = requests.post("https://auth.jd.com/oauth2/accessToken", data={
        "grant_type": "authorization_code",
        "client_id": app_key,
        "client_secret": app_secret,
        "code": code,
        "redirect_uri": redirect_uri,
        "state": ""
    }, timeout=15)
    r.raise_for_status()
    return r.json()   # access_token / refresh_token / expires_in / uid


def json_dumps(d: Dict) -> str:
    import json
    return json.dumps(d, ensure_ascii=False, separators=(',', ':'))

# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
# =========================================================
# 使用示例
# =========================================================
if __name__ == "__main__":
    client = JdJosClient(
        app_key="YOUR_JD_APP_KEY",
        app_secret="YOUR_JD_APP_SECRET",
        sandbox=True   # 生产切 False
    )

    try:
        # ① 公开商品(沙箱返回mock,生产需真实skuId)
        sku = client.get_sku_detail("100012345678")
        print("✅ SKU查询签名通!title=", sku.get("title"))

        # ② 订单(需卖家AccessToken,沙箱多返回空)
        # from datetime import datetime,timedelta
        # now = datetime.now()
        # start = (now - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S")
        # end   = now.strftime("%Y-%m-%d %H:%M:%S")
        # orders = client.list_orders("SELLER_ACCESS_TOKEN", start, end)
        # print("订单数:", orders.get("orderTotal"))

    except Exception as e:
        print("❌", e)

六、沙箱 vs 生产注意点

项目
沙箱
生产
网关
api.sandbox.jd.com/routerjson
api.jd.com/routerjson
数据
Mock(商品ID原样回显/空订单)
真实店铺数据
订单接口
返回空列表正常
需卖家AccessToken + 申请权限
AppKey
同一应用Key
同一应用Key
淘宝客类
不适用
京东无淘宝客API
沙箱能调通签名+JSON结构 = 生产一定通,返回空不报错属预期。

七、高频避坑

现象
解决
timestamp 毫秒
Invalid Timestamp
JOS用秒级 int(time.time()),不是毫秒!
360buy_param_json格式错
Missing Required Parameter
值是紧凑JSON字符串(无空格),中文不URL编码但 ensure_ascii=False
订单403
个人应用/未申请
创建商家自用应用 + 申请 jingdong.pop.order.search+ 卖家OAuth
session传买家token
空/403
须是店铺卖家授权换的 access_token
sign mismatch
参入 sign / 空值参入
严格过滤空值+剔除sign再排序
沙箱空订单慌
正常,切生产验证

八、面试/方案一句话

京东JOS接入 = 创建商家自用应用拿AppKey/Secret → MD5签名(参按ASCII升序拼AppSecret+KV+AppSecret,秒级timestamp) → 360buy_param_json放业务JSON → 订单类接口需卖家OAuth AccessToken;沙箱验签通后切生产网关取真实数据,基础接口有免费额度。
需要我补 京东订单增量同步APScheduler(断点续跑+Token刷新)京东商品全量SKU翻页同步完整字段解析 吗?


群贤毕至

访客