[特殊字符]《京东订单API(jd.order.detail.get)对接ERP:企业认证+OAuth授权避坑指南》(附Python源码)

📅 2026/7/4 1:39:09 👁️ 阅读次数 📝 编程学习
[特殊字符]《京东订单API(jd.order.detail.get)对接ERP:企业认证+OAuth授权避坑指南》(附Python源码)

🧾《京东订单API(jd.order.detail.get)对接ERP:企业认证+OAuth授权避坑指南》(附Python源码)

直接说结论先:

京东订单类接口(jingdong.pop.order.search/jd.order.detail.get/jingdong.etms.waybill.send个人开发者应用无权限,必须:

  1. 企业支付宝/企业对公认证​ 创建「商家自用型应用」

  2. 申请订单相关接口权限(需填场景说明)

  3. 店铺卖家账号OAuth 2.0授权获取access_token(session参数)

个人应用调会返回403 no permission / invalid method,属正常限制。


一、现象对照表

你做的

返回

原因

个人应用 +jd.order.detail.get

403 no permission/invalid method

个人号无订单接口权限

企业应用未申请接口

同上 403

控制台→API权限→申请jingdong.pop.order.search/jd.order.detail.get

传了买家​ AccessToken

空/403

必须是店铺卖家​ OAuth 换的 token

沙箱调订单

返回 mock/空

沙箱不支持真实订单,仅验签名

session 过期

Invalid access_token

refresh_token刷新


二、企业认证 + OAuth授权流程(关键!)

  1. 企业认证

    JOS控制台 → 账户管理 → 企业实名(营业执照 + 企业对公/企业支付宝)

  2. 创建应用

    应用类型选「商家自用型应用」(ISV需额外软服中心入驻)

  3. 申请接口权限

    应用→接口权限→申请:

    • jingdong.pop.order.search(订单列表)

    • jd.order.detail.get(订单明细)

    • jingdong.etms.waybill.send(发货回填运单)

    • jingdong.etms.trace.get(物流轨迹)

    📌 场景说明示例:"ERP系统同步本店铺已付款订单生成内部销售单,并回写发货物流,仅访问授权店铺数据"

  4. 卖家OAuth授权换取 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 ③ 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, user_nick}

    access_token= JOS接口中的access_token(session) 参数


三、Python:订单列表 + 明细调用封装(含权限提示)

# jd_order_sync.py """ 京东订单同步 Demo(企业应用 + 卖家AccessToken) jingdong.pop.order.search → jd.order.detail.get 依赖: requests (pip install requests) """ import hashlib import json import requests import time from datetime import datetime, timedelta from typing import Dict, List # 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex class JdOrderClient: GW = "https://api.jd.com/routerjson" def __init__(self, app_key: str, app_secret: str): self.ak = app_key self.as_ = app_secret # ─── JOS MD5签名(秒级timestamp)─── def _sign(self, p: Dict) -> str: filt = sorted((k, v) for k, v in p.items() if v is not None and str(v).strip() != '' and k != 'sign') qs = ''.join(f"{k}{v}" for k, v in filt) return hashlib.md5(f"{self.as_}{qs}{self.as_}".encode() ).hexdigest().upper() def _call(self, method: str, biz: Dict, access_token: str): api_p = { "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, ensure_ascii=False, separators=(',', ':')), "access_token": access_token } api_p["sign"] = self._sign(api_p) r = requests.post(self.GW, data=api_p, timeout=15) r.raise_for_status() d = r.json() resp_key = method.replace(".", "_") + "_response" if resp_key not in d: for k in d: if k.endswith("_response"): resp_key = k break data = d.get(resp_key, d) # 权限/业务错误检测 if isinstance(data, dict): err = data.get("error_response") or d.get("error_response") if err: code = str(err.get("code", "")) zh = err.get("zh_desc") or err.get("en_desc") if "no permission" in zh or "invalid method" in zh: raise PermissionError( "❌ 【无权限】订单接口需:\n" " 1) 企业实名商家应用\n" " 2) 已申请 jingdong.pop.order.search / jd.order.detail.get\n" " 3) access_token 须是【卖家】OAuth授权所得(非买家token)\n" f" 原始: [{code}] {zh}" ) raise Exception(f"JOS [{code}]: {zh} sub:{err.get('sub_code')}") return data # ─── 增量拉取订单列表 ─── def list_orders(self, access_token: str, minutes_back: int = 30, order_state: str = "WAIT_SELLER_STOCK_OUT", page: int = 1, page_size: int = 50) -> Dict: now = datetime.now() start = (now - timedelta(minutes=minutes_back)).strftime("%Y-%m-%d %H:%M:%S") end = now.strftime("%Y-%m-%d %H:%M:%S") return self._call( "jingdong.pop.order.search", { "start_modified": start, "end_modified": end, "order_state": order_state, # WAIT_SELLER_STOCK_OUT=已付待发 "page": page, "page_size": min(page_size, 100) }, access_token ).get("popOrderSearch", {}).get("orderSearch", {}) # ─── 订单明细 ─── def get_detail(self, access_token: str, order_id: str) -> Dict: return self._call( "jd.order.detail.get", {"orderId": order_id}, access_token ).get("orderDetail", {}).get("orderInfo", {}) # ========================================================= # 使用示例 # ========================================================= if __name__ == "__main__": client = JdOrderClient( app_key="YOUR_JD_ENTERPRISE_APP_KEY", app_secret="YOUR_JD_APP_SECRET" ) SELLER_TOKEN = "SELLER_ACCESS_TOKEN" # ← OAuth2 换取的卖家 token try: result = client.list_orders(SELLER_TOKEN, minutes_back=30) orders = result.get("orderInfoList", []) or [] total = result.get("orderTotal", 0) print(f"✅ 近30分钟变更订单: {len(orders)} / 共计{total}") for o in orders[:3]: detail = client.get_detail(SELLER_TOKEN, str(o.get("orderId"))) print(f" 单 {detail.get('orderId')} {detail.get('orderState')} " f"¥{detail.get('orderPrice')}") except PermissionError as pe: print(pe) print("\n➡ 解决:企业实名→创建自用型应用→申请订单权限→卖家OAuth授权→填入SELLER_TOKEN") except Exception as e: print("❌", e)

四、OAuth Token 交换最简片段(补全用)

def jd_exchange_token(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 }, timeout=15) r.raise_for_status() return r.json() # access_token / refresh_token / expires_in / user_nick

五、避坑清单(京东订单对接必看)

现象

解决

个人应用调订单

403 no permission

企业实名商家自用应用

接口未申请

同上 403

应用→API权限→申请订单接口

传买家 token

空/403

必须用店铺卖家​ OAuth 换的 AccessToken

token 过期

Invalid access_token

refresh_token提前刷新(建议过期前7天)

沙箱返回空订单

正常

沙箱只验签,用生产网关

ISV应用403

未入驻软服/未绑定店铺

完成 ISV 入驻并绑定授权店铺

timestamp 毫秒

Invalid Timestamp

JOS用秒级int(time.time())


六、面试/方案一句话

京东订单API(jingdong.pop.order.search/jd.order.detail.get)须企业实名商家应用 + 申请订单权限 + 卖家OAuth AccessToken(session参数);增量按start_modified/end_modified时间窗拉取防超量,遇403先确认以上三点,沙箱仅验签名不返回真实订单。

需要我补APScheduler 定时增量订单同步(断点续跑+Token自动刷新)​ 或京东发货回填jingdong.etms.waybill.send完整参数​ 吗?