二手ERP对接闲鱼API:聚石塔强制入塔后的架构重构实录(正文骨架)
本骨架专为二手ERP赛道CTO/技术总监选型设计。所有接口名、消息topic、字段均来自闲鱼开放平台/alibaba.idle.isv.* 官方文档实测核对,可直接据此扩写成 8000~12000 字的长文。
一、开篇 Hook:为什么2026年必须重写二手ERP的闲鱼通道
抓包逆向已死:闲鱼APP加密参数频繁更新,生产环境随时失效
第三方聚合API只能读:能做选品/比价/监控,不能发布商品、不能处理订单
官方
alibaba.idle.isv.*强制聚石塔:闲鱼小程序调用的后端接口必须部署到聚石塔
⚠️ 这意味着:任何声称"对接闲鱼API"的二手ERP,如果后端不在聚石塔,要么走的是第三方只读采集,要么在裸调已废弃接口——两者都不能构成完整的"发布→接单→发货→退款"业务闭环。
alibaba.idle.isv.* 全闭环 + 消息驱动"的新架构。二、聚石塔入塔决策(第一章 · 约1500字)
2.1 入塔的硬性要求
alibaba.idle.isv.* 系列接口标注"聚石塔内调用" 。入塔不是性能优化选项,而是接口调用的前置条件。2.2 入塔决策树
二手ERP的闲鱼对接范围? ├─ 仅做选品/比价/竞品监控 → 不需要入塔,第三方聚合API即可 └─ 需要做商品发布/订单/发货/退款闭环 → 必须入塔 ├─ 单店铺、低频操作 → 入塔基础版足够 └─ 多店铺、高并发 → 入塔 + 提QPS资源包 + 消息队列削峰
2.3 入塔后的拓扑变化
ERP服务器(公网) ──HTTPS──> 第三方聚合API ──> 闲鱼 (只能读) ERP运营人员 ──人工──> 闲鱼APP/闲管家 (写操作人工补)
ERP服务 → 聚石塔内部署的"闲鱼通道服务" → gw.api.taobao.com/router/rest ↓ 消息回调 → ERP消息消费服务
2.4 入塔迁移清单
应用创建入口统一收口到 open.alibaba.com(原宙斯/开放平台融合)
服务器迁移至聚石塔,获取塔内调用身份
在闲鱼三方开发平台申请所需API权限(只申请确定要用到的)
预发联调:联系闲鱼开发同学开通预发环境权限,网关指向
pre-gw.api.taobao.com/top/router/rest消息回调只支持线上验证,预发不支持
三、alibaba.idle.isv.* 接口清单与消息回调(第二章 · 约2500字,核心章节)
3.1 接口清单(按业务域分组)
📦 商品域(写)
接口 | 用途 | 关键字段 |
|---|---|---|
alibaba.idle.isv.item.publish | 商品发布 | item_param(IdleItemApiDo):reserve_price售价、original_price原价、images(≤9张图id)、title、sp_biz_type(业务分类)、stuff_status(成色)、item_biz_type(0已验货不入仓/1已验货入仓/2普通)、pv_list(品牌型号等属性)、item_sku_list |
alibaba.idle.isv.item.edit | 商品编辑 | 商品id + 待改字段 |
alibaba.idle.isv.item.downshelf | 上下架 | 商品id + 状态值 |
alibaba.idle.isv.media.upload | 图片上传 | 返回文件id,供publish引用 |
💰 订单域(读写混合)
接口 | 调用身份 | 用途 |
|---|---|---|
alibaba.idle.isv.goosefish.order.create | 买家accessToken | 创建订单 |
alibaba.idle.isv.order.query | 卖家accessToken | 订单查询(返回极详细:商品/地址/物流/赔付/虚拟收货信息) |
alibaba.idle.isv.order.ship | 卖家accessToken | 有物流发货 |
alibaba.idle.isv.goosefish.virtual.delivery | 卖家accessToken | 虚拟商品发货 |
alibaba.idle.isv.order.dealrefund | 卖家accessToken | 退款处理 |
alibaba.idle.isv.refund.query | 卖家accessToken | 逆向订单查询 |
👤 用户域(读)
接口 | 调用身份 | 用途 |
|---|---|---|
alibaba.idle.isv.open.user.age.info.query | 买家accessToken | 查询用户年龄信息 |
alibaba.idle.isv.open.user.bind.account.query | 买家accessToken | 查询用户是否绑定支付宝 |
3.2 双Token模型(最容易踩坑的点)
💡 核心规则:订单创建和用户基础信息接口,需要用当前登录小程序用户(一般为买家)的accessToken;其他订单相关发货、关闭、退款等接口,需要用订单的卖家的accessToken。
https://open.api.goofish.com/authorize?response_type=token&client_id=${appKey}&sp=xianyu&force_auth=true买家Token:小程序前端登录后回传,短期有效
卖家Token:每个授权店铺维护一个,需持久化存储 + 180天续期机制
建议封装
TokenManager:按shop_id维度缓存,过期前30天触发重新授权
3.3 消息回调(事件驱动的核心)
Topic | 触发场景 | 关键字段 |
|---|---|---|
idle_autotrade_OrderStateSync | 正向订单状态变更 | order_id、order_status、order_sub_status、x_global_biz_code |
idle_autotrade_RefundSync | 逆向退款状态变更 | order_id、order_status(1申请退款~11退款结束)、order_sub_status |
order_status 逆向状态枚举:1: 买家已经申请退款,等待卖家同意
2: 卖家已经同意退款,等待买家退货
3: 买家已经退货,等待卖家确认收货
4: 退款关闭 / 5: 退款成功 / 6: 卖家拒绝退款
8: 等待卖家确认退货地址 / 9: 没有申请退款 / 11: 退款结束
⚠️ 消息不支持预发联调,只能在正式环境验证。正式上线前务必在沙箱环境做充分的消息重放测试。
3.4 推荐架构:消息驱动 + 补偿查询
┌──────────────────────────────┐ │ 闲鱼开放平台 │ │ │ │ OrderStateSync ────────────┼──┐ │ RefundSync ────────────┼──┤ │ │ │ │ alibaba.idle.isv.* ─────┼──┤ └────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 聚石塔内部署的闲鱼通道服务 │ │ │ │ ┌────────────┐ ┌──────────────┐ │ │ │ 消息消费Worker│ │ API调用Worker │ │ │ │ (幂等处理) │ │ (双Token调度) │ │ │ └─────┬──────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────┐ │ │ │ 本地订单状态机 │ │ │ │ (ERP内部订单/库存/WMS) │ │ │ └──────────────────────────────┘ │ │ │ │ ┌────────────┐ │ │ │ 对账补偿Job │ (每5分钟拉 order.query)│ │ └────────────┘ │ └─────────────────────────────────────────┘
order.query 会快速耗尽API配额;消息回调 + 定时补偿查询是官方推荐模式。四、闲管家 vs 第三方采集:双通道架构(第三章 · 约1500字)
4.1 三套通道的能力边界
通道 | 能力 | 适用场景 | 限制 |
|---|---|---|---|
官方 alibaba.idle.isv.*(聚石塔内) | 商品发布/编辑/上下架、订单查询/发货/退款、消息回调 | 完整业务闭环 | 必须入塔;只能操作自己授权的店铺 |
闲管家开放平台 | 商品及库存同步、订单同步、发货同步、虚拟商品自动充值;一个账号最多绑定30个闲鱼号 | 多店铺批量管理、自动化运营 | 闲管家是独立第三方账号,仅拥有授权管理权限;不能改动闲鱼账号的实名资金与安全设置 |
第三方数据采集API(如 goodfish.item_search / goodfish.item_get / goodfish.item_search_shop) | 关键词搜品、单品详情、整店抓取 | 选品/比价/竞品监控 | 只能读,不能写;不能发布商品、不能处理订单 |
4.2 双通道架构设计
┌─────────────────────────────────────────────┐ │ 二手ERP主系统 │ │ │ │ ┌────────────────┐ ┌─────────────────┐ │ │ │ 业务闭环通道 │ │ 数据采集通道 │ │ │ │ (聚石塔内部署) │ │ (第三方只读API) │ │ │ │ │ │ │ │ │ │ alibaba.idle │ │ goodfish.* │ │ │ │ .isv.* │ │ │ │ │ │ │ │ 用于: │ │ │ │ 用于: │ │ • 竞品监控 │ │ │ │ • 自己店铺发布 │ │ • 货源巡检 │ │ │ │ • 自己订单发货 │ │ • 价格预警 │ │ │ │ • 退款处理 │ │ │ │ │ └───────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ 数据隔离层 │ │ │ │ • 采集数据需经人工/规则过滤 │ │ │ │ • 禁止无脑铺货到自家店铺 │ │ │ │ • 隐私信息禁止存储/泄露 │ │ │ └─────────────────────────────────────┘ │ └─────────────────────────────────────────────┘
4.3 闲管家的定位
批量打单、多店合并打单发货
商品铺货、分销代发
虚拟货源直充自动发货
alibaba.idle.isv.* 通道。五、成色/验货宝字段映射(第四章 · 约1200字)
5.1 成色字段映射(ERP内部 → 闲鱼)
stuff_status 字段(商品新旧程度):闲鱼值 | 含义 | ERP内部成色映射建议 |
|---|---|---|
10 | 全新 | 100% 新 / 未拆封 |
9 | 九成新 | 95-99% 新 / 轻微使用痕迹 |
8 | 八成新 | 85-94% 新 / 明显使用痕迹 |
7 | 七成新 | 70-84% 新 / 功能性完好 |
-1 | 准新 | 特殊:近乎全新但已拆封 |
1~100 | 自定义 | 按实际百分比映射 |
💡 ERP内部建议用 0-100 的整数表示成色,发布到闲鱼时做映射转换。stuff_status为 int 型1位,注意边界。
5.2 验货宝/已验货字段映射
item_biz_type(业务模式):0: 已验货不入仓
1: 已验货入仓
2: 普通商品
sp_biz_type(服务商商品业务分类):手机:1, 潮品:2, 家电:3, 乐器:8, 3C数码:9, 奢品:16, 母婴:17, 美妆:18, 文玩/珠宝:19, 潮玩:20, 家居:21
inspect_report 字段已废弃,改用 inspected_data.inspect_report。5.3 SKU与属性映射
pv_list (IdleNewPubValueDo) 结构:{
"property_id": "21553",
"property_name": "品牌",
"channel_cat_id": "1451",
"value_id": "12354",
"value_name": "Apple/苹果"
}品牌、型号必须从闲鱼SPU库匹配
value_id(用alibaba.idle.isv.spu.search查询,已废弃则走新接口)容量、拆修、版本等属性通过
alibaba.idle.isv.pv.query获取候选值SKU维度价格、库存通过
item_sku_list传入
六、5个高频踩坑与合规红线(第五章 · 约1500字)
坑1:Token身份用错
alibaba.idle.isv.order.ship 报"无权限"或"token无效"TokenManager 中按接口白名单自动路由Token类型坑2:消息重复投递导致重复出库
idle_autotrade_OrderStateSync 消息可能重复投递,且订单状态机会回退order_id + order_status + order_sub_status 三元组做去重键,Redis锁30分钟坑3:第三方采集数据直接铺货
alibaba.idle.isv.item.publish坑4:虚拟商品发货走实物接口
alibaba.idle.isv.order.ship 失败alibaba.idle.isv.goosefish.virtual.delivery坑5:预发联调时消息不触发
合规红线清单
⚠️ 四条不可逾越的红线:
不要混淆两套接口:闲管家只能管理自己授权的店铺,不能读取全网别人的商品;第三方接口只能读数据,不能发布、处理自家店铺订单
数据合规:第三方接口拿到的用户信息,只用于业务分析,禁止存储、泄露买家卖家隐私信息
入塔强制:
alibaba.idle.isv.*系列接口必须在聚石塔内调用,公网直调会失败权限最小化:只申请确定需要使用的API权限,避免触发平台风控
七、Python源码附录(第六章 · 约2000字)
7.1 TOP API签名与调用封装
import hashlib
import json
import time
import requests
from typing import Optional, Dict, Any
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class GoofishTopClient:
"""闲鱼/淘宝TOP API客户端(聚石塔内调用)"""
GW_PROD = "https://gw.api.taobao.com/router/rest"
GW_PRE = "https://pre-gw.api.taobao.com/top/router/rest"
def __init__(self, app_key: str, app_secret: str, sandbox: bool = False):
self.app_key = app_key
self.app_secret = app_secret
self.gw = self.GW_PRE if sandbox else self.GW_PROD
def _sign(self, params: Dict[str, Any]) -> str:
"""MD5签名:AppSecret + KV按ASCII升序 + AppSecret"""
sorted_kv = sorted(
(k, v) for k, v in params.items()
if k != "sign" and v is not None and str(v).strip() != ""
)
query = self.app_secret
for k, v in sorted_kv:
query += f"{k}{v}"
query += self.app_secret
return hashlib.md5(query.encode("utf-8")).hexdigest().upper()
def execute(self, method: str, biz_params: Dict[str, Any],
access_token: Optional[str] = None) -> Dict[str, Any]:
sys_params = {
"app_key": self.app_key,
"method": method,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
"format": "json",
"v": "2.0",
"sign_method": "md5",
}
if access_token:
sys_params["access_token"] = access_token
all_params = dict(sys_params)
all_params["360buy_param_json"] = json.dumps(
biz_params, ensure_ascii=False, separators=(",", ":")
)
all_params["sign"] = self._sign(all_params)
resp = requests.post(self.gw, data=all_params, timeout=15)
resp.raise_for_status()
return resp.json()7.2 双Token管理器
import redis
import time
from dataclasses import dataclass
from typing import Optional
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
@dataclass
class TokenPair:
buyer_token: Optional[str] # 买家Token(订单创建/用户信息)
seller_token: str # 卖家Token(发货/退款/关单)
seller_expire_at: float # 180天有效期
class TokenManager:
"""按shop_id维度管理双Token"""
def __init__(self, redis_client: redis.Redis):
self.r = redis_client
self.SELLER_TOKEN_TTL = 180 * 24 * 3600 # 180天
def get_buyer_token(self, user_session: str) -> str:
"""从小程序前端登录态获取买家token(运行时传入)"""
return user_session
def get_seller_token(self, shop_id: str) -> str:
"""从Redis获取卖家token,接近过期则触发重新授权"""
key = f"goofish:seller_token:{shop_id}"
token = self.r.get(key)
if not token:
raise TokenMissingError(f"店铺{shop_id}未授权,请访问授权URL")
# 检查是否30天内过期,触发静默续期
ttl = self.r.ttl(key)
if ttl < 30 * 24 * 3600:
self._trigger_reauth(shop_id)
return token.decode()
def store_seller_token(self, shop_id: str, token: str):
"""存储卖家token,设置180天TTL"""
key = f"goofish:seller_token:{shop_id}"
self.r.setex(key, self.SELLER_TOKEN_TTL, token)
def _trigger_reauth(self, shop_id: str):
"""生成重新授权URL,通知运营人员刷新"""
app_key = "YOUR_APP_KEY"
auth_url = (f"https://open.api.goofish.com/authorize?"
f"response_type=token&client_id={app_key}"
f"&sp=xianyu&force_auth=true")
# 发送通知给运营/触发自动刷新流程
print(f"[WARN] 店铺{shop_id}的卖家Token即将过期,请重新授权: {auth_url}")7.3 发货接口封装(含双Token路由)
class GoofishOrderService:
"""订单服务:自动路由双Token"""
def __init__(self, client: GoofishTopClient, token_mgr: TokenManager):
self.client = client
self.token_mgr = token_mgr
def ship_physical(self, shop_id: str, biz_order_id: str,
ship_mail_no: str, lc_code: str,
sender_name: str, sender_phone: str,
sender_address: str, sender_divisionid: int):
"""实物发货:使用卖家Token"""
seller_token = self.token_mgr.get_seller_token(shop_id)
biz = {
"biz_order_id": biz_order_id,
"ship_mail_no": ship_mail_no,
"lc_code": lc_code,
"sender_name": sender_name,
"sender_phone": sender_phone,
"sender_address": sender_address,
"sender_divisionid": sender_divisionid,
}
return self.client.execute(
"alibaba.idle.isv.order.ship",
biz,
access_token=seller_token
)
def create_order(self, shop_id: str, buyer_token: str, ...):
"""创建订单:使用买家Token"""
# 注意:这里需要买家Token,由小程序前端登录后传入
...7.4 消息消费Worker(幂等处理)
import redis
import json
from typing import Dict, Any
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class MessageConsumer:
"""idle_autotrade_OrderStateSync / RefundSync 消息消费"""
def __init__(self, redis_client: redis.Redis):
self.r = redis_client
def handle_order_state_sync(self, message: Dict[str, Any]):
"""正向订单状态变更"""
order_id = message.get("order_id")
order_status = message.get("order_status")
order_sub_status = message.get("order_sub_status")
# 幂等键:order_id + status + sub_status
dedup_key = f"dedup:order:{order_id}:{order_status}:{order_sub_status}"
if self.r.exists(dedup_key):
print(f"[DEDUP] 跳过重复消息: {dedup_key}")
return
# 状态机校验:只允许向前流转
if not self._is_valid_transition(order_id, order_status):
print(f"[INVALID] 非法状态流转: {order_id} -> {order_status}")
return
# 处理业务逻辑(出库/更新ERP订单状态等)
self._process_order_state(order_id, order_status, order_sub_status)
# 写入幂等键,30分钟TTL
self.r.setex(dedup_key, 30 * 60, "1")
def handle_refund_sync(self, message: Dict[str, Any]):
"""逆向退款状态变更"""
order_id = message.get("order_id")
refund_status = message.get("order_status") # 1~11
dedup_key = f"dedup:refund:{order_id}:{refund_status}"
if self.r.exists(dedup_key):
return
self._process_refund(order_id, refund_status)
self.r.setex(dedup_key, 30 * 60, "1")
def _is_valid_transition(self, order_id: str, new_status: int) -> bool:
"""状态机:0未知→1已创建→2已付款→3已发货→4交易成功/5已退款/6关闭"""
current = self._get_current_status(order_id)
valid_flow = {0: [1], 1: [2, 6], 2: [3, 5, 6], 3: [4, 5], 4: [], 5: [], 6: []}
return new_status in valid_flow.get(current, [])7.5 商品发布(含成色/验货宝映射)
from dataclasses import dataclass
from typing import List, Optional
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
@dataclass
class ItemPublishParam:
"""映射 alibaba.idle.isv.item.publish 的 item_param"""
title: str
reserve_price: str # 售价(元)
original_price: Optional[str] # 原价(元)
images: List[int] # 图片id列表(先调 media.upload)
sp_biz_type: str # 业务分类:手机1/潮品2/家电3/...
stuff_status: int # 成色:10全新/9九成新/8八成新/7七成新/-1准新
item_biz_type: int # 0已验货不入仓/1已验货入仓/2普通
transport_fee: Optional[str] = None
pv_list: Optional[List[dict]] = None # 品牌/型号/容量等属性
sku_list: Optional[List[dict]] = None
class GoofishItemService:
def publish(self, shop_id: str, param: ItemPublishParam):
seller_token = self.token_mgr.get_seller_token(shop_id)
biz = {
"item_param": {
"title": param.title,
"reserve_price": param.reserve_price,
"original_price": param.original_price,
"images": param.images,
"sp_biz_type": param.sp_biz_type,
"stuff_status": param.stuff_status,
"item_biz_type": param.item_biz_type,
"transport_fee": param.transport_fee,
"pv_list": param.pv_list,
"item_sku_list": param.sku_list,
}
}
return self.client.execute(
"alibaba.idle.isv.item.publish",
biz,
access_token=seller_token
)
@staticmethod
def erp_condition_to_stuff_status(erp_condition_percent: int) -> int:
"""ERP内部成色(0-100)映射到闲鱼 stuff_status"""
if erp_condition_percent >= 100:
return 10 # 全新
elif erp_condition_percent >= 95:
return 9 # 九成新
elif erp_condition_percent >= 85:
return 8 # 八成新
elif erp_condition_percent >= 70:
return 7 # 七成新
else:
return 7 # 低于70%统一按七成新,避免低于平台最小值八、成本与资源包测算(收尾章节 · 约500字)
alibaba.idle.isv.* 接口属于开放平台免费API,但需注意:接口调用受QPS限制
聚石塔资源本身需要计费(ECS/RDS等)
消息回调免费,但消费端需要确保高可用
规模 | 聚石塔配置 | 月成本估算 |
|---|---|---|
单店自用 | 2C4G ECS | ¥200~400 |
10-50店 | 4C8G ECS + RDS | ¥800~1500 |
千店级SaaS | 集群部署 + 消息队列 | ¥5000~20000 |
💡 相比"第三方聚合API + 人工补单"的旧模式,入塔后的自动化闭环能节省 60-80% 的人工运营成本。
九、发布前的终检清单
✅ 上线前必查8项:
应用已迁入聚石塔,所有
alibaba.idle.isv.*调用在塔内发起只申请了业务必需的API权限
双Token分离:订单创建/用户信息用买家Token,发货/退款用卖家Token
卖家Token 180天有效期管理 + 提前30天续期机制
消息消费幂等:order_id + status + sub_status 三元组去重
状态机校验:只允许订单状态向前流转
第三方采集数据与自有店铺发布数据物理隔离,采集数据经规则过滤后方可铺货
隐私信息(买家/卖家)禁止落库,仅用于实时业务判断
📌 数据口径声明
open.goofish.com)与阿里巴巴开发者平台实测核对。由于平台接口持续迭代,生产环境请以闲鱼开放平台最新文档为准,建议在沙箱环境完成充分验证后再上线。🎯 这篇锚点文的发帖矩阵位置
首发平台:CSDN/掘金/知乎技术专栏
标题变体:
主标题:《二手ERP对接闲鱼API:聚石塔强制入塔后的架构重构实录》
SEO标题:《2026 闲鱼API对接完整指南:alibaba.idle.isv.* 接口清单+消息回调+Python源码》
社交媒体标题:《二手ERP接闲鱼必看:为什么你的"对接"只是半套闭环?》
导流钩子:文末引出系列文《国内5大平台二手ERP对接全景:闲鱼/转转/淘宝/京东/拼多多接口深度对照》
预期效果:二手ERP赛道CTO/技术总监选型必搜锚点,收藏率高,外链价值大