Initial commit: xian-guanjia API client
This commit is contained in:
commit
3891554e79
6 files changed
+536
No files matched your search
+10
@@ -0,0 +1,10 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.env
|
||||
.vscode/
|
||||
.idea/
|
||||
*.log
|
||||
config.py
|
||||
credentials.json
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 kingofmarketing
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,89 @@
|
||||
# xian-guanjia
|
||||
|
||||
闲管家 API Python 客户端 - 闲鱼自动化发货、订单管理工具
|
||||
|
||||
## 简介
|
||||
|
||||
这是一个闲管家开放平台的 Python API 客户端,支持通过 API 进行:
|
||||
|
||||
- 订单查询和管理
|
||||
- 自动发货
|
||||
- 商品管理
|
||||
- 余额查询
|
||||
- 货源对接下单
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kingofmarketing/xian-guanjia.git
|
||||
cd xian-guanjia
|
||||
pip install requests
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### Python 代码调用
|
||||
|
||||
```python
|
||||
from xian_guanjia import XianGuanjiaClient
|
||||
|
||||
client = XianGuanjiaClient(
|
||||
app_id="your_app_id",
|
||||
app_secret="your_app_secret",
|
||||
mch_id="your_mch_id",
|
||||
mch_secret="your_mch_secret"
|
||||
)
|
||||
|
||||
# 查询订单列表
|
||||
result = client.get_order_list(page=1, page_size=20)
|
||||
print(result)
|
||||
|
||||
# 订单发货
|
||||
result = client.ship_order(
|
||||
order_id=12345,
|
||||
logistics_no="7894561230",
|
||||
logistics_company="顺丰速运"
|
||||
)
|
||||
|
||||
# 查询余额
|
||||
result = client.query_balance()
|
||||
print(result)
|
||||
```
|
||||
|
||||
### 命令行使用
|
||||
|
||||
```bash
|
||||
python scripts/xian_guanjia.py \
|
||||
--app-id YOUR_APP_ID \
|
||||
--app-secret YOUR_APP_SECRET \
|
||||
--mch-id YOUR_MCH_ID \
|
||||
--mch-secret YOUR_MCH_SECRET
|
||||
```
|
||||
|
||||
## 签名算法
|
||||
|
||||
闲管家使用 MD5 签名认证,本客户端已经自动处理签名,无需手动计算。
|
||||
|
||||
详细的签名规则参见 [SKILL.md](SKILL.md) 或 [references/api-docs.md](references/api-docs.md)
|
||||
|
||||
## 错误码
|
||||
|
||||
| 错误码 | 错误描述 |
|
||||
| --- | --- |
|
||||
| 0 | 请求成功 |
|
||||
| 401 | 签名错误 |
|
||||
| 403 | IP不在白名单 |
|
||||
| 408 | 时间戳过期 |
|
||||
| 1000 | 商户不存在 |
|
||||
| 1002 | 余额不足 |
|
||||
|
||||
完整错误码参见 [references/api-docs.md](references/api-docs.md)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
## 相关链接
|
||||
|
||||
- 闲管家官网:https://goofish.pro
|
||||
- API 文档:https://apifox.com/apidoc/shared-cf4d53fd-8bb2-41c5-8da3-371ba9a38956/doc-4985015
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
name: xian-guanjia
|
||||
description: 闲管家 API 集成,用于闲鱼自动化发货、订单管理、商品发布。支持通过 API 批量处理订单、自动发货。Use when: (1) 需要操作闲管家 API,(2) 帮用户处理闲鱼订单发货,(3) 查询订单列表和余额,(4) 用户提到"闲管家"、"闲鱼自动发货"。
|
||||
---
|
||||
|
||||
# 闲管家
|
||||
|
||||
闲管家 API 客户端,用于闲鱼自动化发货、订单管理和商品发布。
|
||||
|
||||
## API 文档
|
||||
|
||||
完整的 API 签名规则和错误码参见 [references/api-docs.md](references/api-docs.md)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 初始化客户端
|
||||
|
||||
```python
|
||||
from scripts.xian_guanjia import XianGuanjiaClient
|
||||
|
||||
client = XianGuanjiaClient(
|
||||
app_id="your_app_id",
|
||||
app_secret="your_app_secret",
|
||||
mch_id="your_mch_id",
|
||||
mch_secret="your_mch_secret"
|
||||
)
|
||||
```
|
||||
|
||||
### 常用操作
|
||||
|
||||
**查询订单列表**
|
||||
```python
|
||||
result = client.get_order_list(page=1, page_size=20)
|
||||
```
|
||||
|
||||
**查询订单详情**
|
||||
```python
|
||||
result = client.get_order_detail(order_id=12345)
|
||||
```
|
||||
|
||||
**订单发货**
|
||||
```python
|
||||
result = client.ship_order(
|
||||
order_id=12345,
|
||||
logistics_no="物流单号",
|
||||
logistics_company="快递公司"
|
||||
)
|
||||
```
|
||||
|
||||
**下单(货源对接)**
|
||||
```python
|
||||
result = client.place_order(
|
||||
goods_no="商品编号",
|
||||
goods_type=1, # 1-普通 2-虚拟
|
||||
buyer_nick="买家昵称",
|
||||
buyer_id="买家ID",
|
||||
order_no="管家订单号",
|
||||
price=29.90
|
||||
)
|
||||
```
|
||||
|
||||
**查询余额**
|
||||
```python
|
||||
result = client.query_balance()
|
||||
```
|
||||
|
||||
## 响应格式
|
||||
|
||||
所有请求返回格式:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "OK",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
- `code = 0` 表示成功
|
||||
- 其他值表示失败,错误信息在 `msg`
|
||||
|
||||
## 错误码
|
||||
|
||||
详见 [references/api-docs.md](references/api-docs.md) 中的全局错误码说明。
|
||||
|
||||
## 命令行使用
|
||||
|
||||
```bash
|
||||
python scripts/xian_guanjia.py \
|
||||
--app-id YOUR_APP_ID \
|
||||
--app-secret YOUR_APP_SECRET \
|
||||
--mch-id YOUR_MCH_ID \
|
||||
--mch-secret YOUR_MCH_SECRET
|
||||
```
|
||||
|
||||
## 依赖
|
||||
|
||||
需要安装 requests:
|
||||
```bash
|
||||
pip install requests
|
||||
```
|
||||
@@ -0,0 +1,88 @@
|
||||
# 闲管家 API 文档
|
||||
|
||||
## 接入说明
|
||||
|
||||
生产环境地址:https://open.goofish.pro
|
||||
|
||||
## 相关下载
|
||||
|
||||
- 闲管家省市区:
|
||||
- https://file.goofish.pro/doc/闲管家省市区.xlsx
|
||||
- https://file.goofish.pro/doc/闲管家省市区.sql
|
||||
|
||||
- 商品发布异常状态码及处理建议:
|
||||
- https://file.goofish.pro/doc/商品异常状态码.xlsx
|
||||
- https://file.goofish.pro/doc/商品异常状态码.sql
|
||||
|
||||
- 闲管家虚拟货源标准接口:
|
||||
- https://apifox.com/apidoc/shared-cf4d53fd-8bb2-41c5-8da3-371ba9a38956/doc-4985015
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 闲管家所有参数都是强校验,需要严格按照文档的字段类型传参+返回
|
||||
2. 返回结果 `code = 0` 才是成功,其他值表示失败
|
||||
|
||||
## 签名规则说明
|
||||
|
||||
### 签名参数说明
|
||||
|
||||
```
|
||||
app_id:管家平台提供的对接应用ID
|
||||
app_secret:管家平台提供的对接密钥
|
||||
mch_id:货源平台提供的对接商户ID
|
||||
mch_secret:货源平台提供的对接密钥
|
||||
timestamp:发起请求时的时间戳(秒)
|
||||
bodyString:POST原文字符串
|
||||
```
|
||||
|
||||
### 签名生成示例
|
||||
|
||||
```python
|
||||
app_id = 677859093659717
|
||||
app_secret = wK63PxlOBaY9NoqMksLeZySzGIW25ifA
|
||||
mch_id = 100
|
||||
mch_secret = o9wl81dncmvby3ijpq7eur456zhgtaxs
|
||||
timestamp = 1724414553
|
||||
body = {"goods_type":1,"goods_no":"12344532"}
|
||||
```
|
||||
|
||||
### 拼接签名参数
|
||||
|
||||
```python
|
||||
// 接口采用MD5加密算法生成签名值
|
||||
|
||||
// 报文值 = md5(Json格式的Body报文)
|
||||
bodyMd5 = md5({"goods_type":1,"goods_no":"12344532"})
|
||||
// output:9b8030f5097d5ed123405b003dd247c5
|
||||
|
||||
// 接口无body参数时签名字符串(请求时body也需要传入相同的值)
|
||||
// bodyMd5 = md5("{}") 或者 md5("")
|
||||
|
||||
// 签名值 = md5("管家应用ID,管家对接密钥,Body报文Md5值,时间戳,货源商户ID,货源对接密钥")
|
||||
signMd5 = md5("677859093659717,wK63PxlOBaY9NoqMksLeZySzGIW25ifA,9b8030f5097d5ed123405b003dd247c5,1724414553,1001,o9wl81dncmvby3ijpq7eur456zhgtaxs")
|
||||
// output:fda5954f48ab32b3b271c84450440ead
|
||||
```
|
||||
|
||||
最终的签名值:`sign = fda5954f48ab32b3b271c84450440ead`
|
||||
|
||||
## 全局错误码说明
|
||||
|
||||
| 错误码 | 错误描述 |
|
||||
| --- | --- |
|
||||
| 0 | 请求成功 |
|
||||
| 1 | 请求错误(不在定义范围内的错误) |
|
||||
| 401 | 签名错误 |
|
||||
| 403 | 访问IP不在IP白名单内 |
|
||||
| 408 | 时间戳已超过有效期 |
|
||||
| 500 | 系统异常,请重试 |
|
||||
| 1000 | 商户不存在 |
|
||||
| 1001 | 商户不可用 |
|
||||
| 1002 | 商户余额不足 |
|
||||
| 1100 | 商品不存在 |
|
||||
| 1101 | 商品不可用 |
|
||||
| 1102 | 商品库存不足 |
|
||||
| 1200 | 订单不存在 |
|
||||
| 1201 | 下单参数错误 |
|
||||
| 1202 | 下单金额低于成本价 |
|
||||
| 1203 | 下单管家订单号已存在 |
|
||||
| 1209 | 下单超时,请查单确认下单结果 |
|
||||
Executable
+228
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
闲管家 API 客户端
|
||||
用于通过 API 操作闲鱼自动发货、商品发布等功能
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import time
|
||||
import json
|
||||
import requests
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
|
||||
class XianGuanjiaClient:
|
||||
"""闲管家 API 客户端"""
|
||||
|
||||
def __init__(self, app_id: str, app_secret: str, mch_id: str, mch_secret: str, base_url: str = "https://open.goofish.pro"):
|
||||
"""
|
||||
初始化客户端
|
||||
|
||||
Args:
|
||||
app_id: 管家平台提供的对接应用ID
|
||||
app_secret: 管家平台提供的对接密钥
|
||||
mch_id: 货源平台提供的对接商户ID
|
||||
mch_secret: 货源平台提供的对接密钥
|
||||
base_url: API 基础地址,默认为生产环境
|
||||
"""
|
||||
self.app_id = app_id
|
||||
self.app_secret = app_secret
|
||||
self.mch_id = mch_id
|
||||
self.mch_secret = mch_secret
|
||||
self.base_url = base_url.rstrip('/')
|
||||
|
||||
def _generate_sign(self, body: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
生成签名
|
||||
|
||||
Args:
|
||||
body: 请求体字典
|
||||
|
||||
Returns:
|
||||
包含 timestamp 和 sign 的字典
|
||||
"""
|
||||
timestamp = int(time.time())
|
||||
body_str = json.dumps(body, separators=(',', ':')) if body else "{}"
|
||||
|
||||
# 计算 body MD5
|
||||
body_md5 = hashlib.md5(body_str.encode('utf-8')).hexdigest()
|
||||
|
||||
# 拼接签名字符串
|
||||
sign_str = f"{self.app_id},{self.app_secret},{body_md5},{timestamp},{self.mch_id},{self.mch_secret}"
|
||||
|
||||
# 计算最终签名
|
||||
sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
|
||||
|
||||
return {
|
||||
'timestamp': timestamp,
|
||||
'sign': sign
|
||||
}
|
||||
|
||||
def request(self, method: str, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
发送 API 请求
|
||||
|
||||
Args:
|
||||
method: HTTP 方法 (GET, POST, etc.)
|
||||
path: API 路径
|
||||
body: 请求体数据
|
||||
|
||||
Returns:
|
||||
API 响应 JSON
|
||||
"""
|
||||
if body is None:
|
||||
body = {}
|
||||
|
||||
url = f"{self.base_url}{path}"
|
||||
|
||||
# 生成签名
|
||||
sign_info = self._generate_sign(body)
|
||||
|
||||
# 将签名和时间戳添加到请求头
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-App-Id': str(self.app_id),
|
||||
'X-Mch-Id': str(self.mch_id),
|
||||
'X-Timestamp': str(sign_info['timestamp']),
|
||||
'X-Sign': sign_info['sign']
|
||||
}
|
||||
|
||||
response = requests.request(method, url, json=body, headers=headers, timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_order_list(self, page: int = 1, page_size: int = 20) -> Dict[str, Any]:
|
||||
"""
|
||||
获取订单列表
|
||||
|
||||
Args:
|
||||
page: 页码
|
||||
page_size: 每页数量
|
||||
|
||||
Returns:
|
||||
订单列表响应
|
||||
"""
|
||||
return self.request('POST', '/api/order/list', {
|
||||
'page': page,
|
||||
'page_size': page_size
|
||||
})
|
||||
|
||||
def get_order_detail(self, order_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
获取订单详情
|
||||
|
||||
Args:
|
||||
order_id: 订单ID
|
||||
|
||||
Returns:
|
||||
订单详情响应
|
||||
"""
|
||||
return self.request('POST', '/api/order/detail', {
|
||||
'order_id': order_id
|
||||
})
|
||||
|
||||
def ship_order(self, order_id: int, logistics_no: str, logistics_company: str) -> Dict[str, Any]:
|
||||
"""
|
||||
订单发货
|
||||
|
||||
Args:
|
||||
order_id: 订单ID
|
||||
logistics_no: 物流单号
|
||||
logistics_company: 物流公司
|
||||
|
||||
Returns:
|
||||
发货响应
|
||||
"""
|
||||
return self.request('POST', '/api/order/ship', {
|
||||
'order_id': order_id,
|
||||
'logistics_no': logistics_no,
|
||||
'logistics_company': logistics_company
|
||||
})
|
||||
|
||||
def get_product_list(self, page: int = 1, page_size: int = 20) -> Dict[str, Any]:
|
||||
"""
|
||||
获取商品列表
|
||||
|
||||
Args:
|
||||
page: 页码
|
||||
page_size: 每页数量
|
||||
|
||||
Returns:
|
||||
商品列表响应
|
||||
"""
|
||||
return self.request('POST', '/api/product/list', {
|
||||
'page': page,
|
||||
'page_size': page_size
|
||||
})
|
||||
|
||||
def place_order(self, goods_no: str, goods_type: int, buyer_nick: str,
|
||||
buyer_id: str, order_no: str, price: float) -> Dict[str, Any]:
|
||||
"""
|
||||
下单(对接收货源)
|
||||
|
||||
Args:
|
||||
goods_no: 商品编号
|
||||
goods_type: 商品类型 1-普通 2-虚拟
|
||||
buyer_nick: 买家昵称
|
||||
buyer_id: 买家ID
|
||||
order_no: 管家订单号
|
||||
price: 下单价格
|
||||
|
||||
Returns:
|
||||
下单响应
|
||||
"""
|
||||
return self.request('POST', '/api/order/place', {
|
||||
'goods_no': goods_no,
|
||||
'goods_type': goods_type,
|
||||
'buyer_nick': buyer_nick,
|
||||
'buyer_id': buyer_id,
|
||||
'order_no': order_no,
|
||||
'price': price
|
||||
})
|
||||
|
||||
def query_balance(self) -> Dict[str, Any]:
|
||||
"""
|
||||
查询商户余额
|
||||
|
||||
Returns:
|
||||
余额信息响应
|
||||
"""
|
||||
return self.request('POST', '/api/mch/balance', {})
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
命令行交互示例
|
||||
"""
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='闲管家 API 客户端')
|
||||
parser.add_argument('--app-id', required=True, help='应用ID')
|
||||
parser.add_argument('--app-secret', required=True, help='应用密钥')
|
||||
parser.add_argument('--mch-id', required=True, help='商户ID')
|
||||
parser.add_argument('--mch-secret', required=True, help='商户密钥')
|
||||
parser.add_argument('--base-url', default='https://open.goofish.pro', help='API 地址')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
client = XianGuanjiaClient(
|
||||
app_id=args.app_id,
|
||||
app_secret=args.app_secret,
|
||||
mch_id=args.mch_id,
|
||||
mch_secret=args.mch_secret,
|
||||
base_url=args.base_url
|
||||
)
|
||||
|
||||
# 测试查询余额
|
||||
try:
|
||||
result = client.query_balance()
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user