1688的物流跟踪主要通过两个接口完成:① 查询订单发货物流信息(alibaba.logistics.trade.ship/ alibaba.logistics.order.get)和 ② 订阅/解析运单轨迹(alibaba.logistics.trace.get)。对于ERP/WMS系统,核心目标是:拿到1688发货的运单号 → 定时拉取物流轨迹 → 回写ERP出库单状态。
一、1688物流对接的两个核心接口
接口 | 用途 | 关键返回 |
|---|---|---|
alibaba.logistics.trade.ship(或 alibaba.logistics.order.get) | 查某采购单的发货记录 | logisticsCode(快递公司码)、billNo(运单号)、sendTime |
alibaba.logistics.trace.get | 根据 companyCode+billNo查实时轨迹 | 签收状态、节点时间、当前城市 |
⚠️ 前提:应用需申请物流查询权限(自用型应用默认可申请),订单须是已发货状态才有数据。
二、Python封装:查运单号 + 拉取轨迹
# ali1688_logistics.py
import hashlib
import time
import requests
import urllib.parse
from typing import Dict, List, Optional
from datetime import datetime, timedelta
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class Ali1688LogisticsClient:
"""
1688 物流跟踪 Client
网关与签名规则同标准1688 Open API
"""
GATEWAY = "https://gw.open.1688.com/openapi/http/2/1"
def __init__(self, app_key: str, app_secret: str, access_token: str):
self.app_key = app_key
self.app_secret = app_secret
self.access_token = access_token
# ─────────────── 签名(MD5) ───────────────
def _sign(self, params: Dict) -> str:
filtered = sorted((k, v) for k, v in params.items() if v is not None)
qs = ''.join(f"{k}{v}" for k, v in filtered)
raw = f"{self.app_secret}{qs}{self.app_secret}"
return hashlib.md5(raw.encode('utf-8')).hexdigest().upper()
def _call(self, method: str, biz: Dict) -> Dict:
api_params = {
"method": method,
"app_key": self.app_key,
"session": self.access_token,
"timestamp": str(int(time.time() * 1000)),
"format": "json",
"v": "2.0",
"sign_method": "md5",
}
api_params["param2"] = urllib.parse.quote_plus(
str(biz).replace("'", '"')
)
api_params["sign"] = self._sign(api_params)
resp = requests.get(self.GATEWAY, params=api_params, timeout=15)
resp.raise_for_status()
data = resp.json()
if "error_response" in data:
err = data["error_response"]
raise Exception(f"1688 Logistics Err[{err.get('code')}]: {err.get('msg')}")
result_key = [k for k in data if k != "error_response"][0]
return data[result_key]
# ─────────────── ① 查订单发货物流 ───────────────
def get_order_logistics(self, order_id: str) -> List[Dict]:
"""
返回该订单下所有物流单
每个元素含: logisticsCode(快递码), billNo(运单号), companyName
"""
biz = {"orderId": order_id}
res = self._call("alibaba.logistics.trade.ship", biz)
orders = res.get("logisticsOrders", []) or []
result = []
for lo in orders:
result.append({
"logistics_code": lo.get("logisticsCode"), # 如 "YTO" "SF"
"logistics_name": lo.get("logisticsCompanyName"),
"bill_no": lo.get("billNo") or lo.get("mailNo"),
"send_time": lo.get("gmtSend"),
"consignee": lo.get("consigneeName")
})
return result
# ─────────────── ② 查运单轨迹 ───────────────
def get_trace(self, company_code: str, bill_no: str) -> Dict:
"""
company_code: 1688返回的 logisticsCode (YTO/ZJS/SF...)
bill_no: 运单号
返回含 signStatus(已签/未签) + traceList
"""
biz = {
"companyCode": company_code,
"billNo": bill_no
}
res = self._call("alibaba.logistics.trace.get", biz)
return {
"sign_status": res.get("signStatus"), # SIGN 已签 / UNSIGN 未签
"sign_time": res.get("signTime"),
"traces": res.get("traceList") or []
}
# =========================================================
# 使用示例:同步1688采购单物流 → 回写ERP
# =========================================================
if __name__ == "__main__":
client = Ali1688LogisticsClient(
app_key="YOUR_APP_KEY",
app_secret="YOUR_APP_SECRET",
access_token="YOUR_ACCESS_TOKEN"
)
ORDER_ID = "2338123456789000" # 1688采购单号
try:
# ① 获取运单
logistics = client.get_order_logistics(ORDER_ID)
if not logistics:
print("⚠️ 该订单尚未发货或无物流信息")
exit()
for lg in logistics:
print(f"\n📦 {lg['logistics_name']} 运单:{lg['bill_no']}")
# ② 查轨迹
trace = client.get_trace(lg["logistics_code"], lg["bill_no"])
print(f" 签收状态: {trace['sign_status']} {trace['sign_time'] or ''}")
for node in (trace["traces"] or []):
print(f" [{node.get('time')}] {node.get('desc')} {node.get('city','')}")
# ③ ERP联动(伪代码)
# if trace['sign_status'] == 'SIGN':
# erp.mark_received(ORDER_ID, sign_time=trace['sign_time'])
except Exception as e:
print(f"❌ {e}")三、1688快递公司码(LogisticsCode)常见值
快递 | logisticsCode | 说明 |
|---|---|---|
圆通 | YTO | 最常用 |
申通 | STO | — |
中通 | ZTO | — |
韵达 | YD | — |
顺丰 | SF | 需买家寄付/月结 |
京东 | JD | — |
邮政EMS | EMS | — |
💡 避坑:1688返回的logisticsCode是平台内部简码,直接传给trace.get即可,不要自己映射汉字。
四、ERP侧同步策略建议
┌──────────────┐ 每30分钟轮询已发货未签收订单 │ 1688 已发货单│ ──────────────────────────────▶ └──────────────┘ │ get_order_logistics() get_trace() │ ┌───────────▼──────────┐ │ ERP出库单状态更新 │ │ • 运输中 → 显示轨迹 │ │ • SIGN → 标记已签收 │ │ • 异常节点 → 告警 │ └──────────────────────┘
- 轮询频率:已发货未签收订单每30min查一次,签收后停止轮询
- 失败重试:物流接口偶发超时,指数退避重试3次
- 轨迹去重:按
(bill_no, time, desc)去重存储,避免重复写状态变更
五、高频避坑点
问题 | 原因 | 解决 |
|---|---|---|
返回空物流 | 订单未发货/ waitsellersend状态 | 先判断订单 status,仅查已发货 |
companyCode无效 | 自己手填汉字快递名 | 必须用1688返回的 logisticsCode |
轨迹长期不更新 | 快递公司未回传 | 正常,按 sign_status判断是否最终签收即可 |
限流429 | QPS过高 | 单应用 sleep≥0.2s 或令牌桶 QPS≤10 |
六、面试/方案一句话
1688物流对接 = 用采购单ID调logistics.trade.ship拿运单号 → 调logistics.trace.get拉轨迹 → 按sign_status回写ERP签收状态,注意只对已发货订单查询且用平台返回的logisticsCode。
需要我把物流定时同步任务(APScheduler/Celery Beat)或ERP签收回写SQL模板补给你吗?