├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── README.md ├── README.rst ├── asset └── menduo_kdniao_py.png ├── bin ├── __init__.py └── kdniao ├── docs ├── README.md ├── README.rst ├── index.rst └── requirements.txt ├── examples └── tornado_handler.py ├── index.rst ├── kdniao ├── __init__.py ├── api.py ├── client.py ├── client_aio.py ├── conf.py └── mdutils.py ├── makefile ├── requirements.txt ├── setup.py └── test ├── test_client.py ├── test_recognise.py ├── test_track.py └── test_users.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .settings/org.eclipse.core.resources.prefs 3 | .pydevproject 4 | .swp 5 | .DS_Store 6 | .project 7 | .idea/ 8 | *~ 9 | *# 10 | .idea/* 11 | *.swp 12 | .tags 13 | .tags_sorted_by_file 14 | .pylint 15 | .vscode 16 | _amds 17 | conf/settings_local.py 18 | conf/settings_local*.py 19 | .python-version 20 | _ignored/ 21 | kdniao.egg-info 22 | build/ 23 | dist 24 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.11" 12 | # You can also specify other tool versions: 13 | # nodejs: "19" 14 | # rust: "1.64" 15 | # golang: "1.19" 16 | 17 | # Build documentation in the "docs/" directory with Sphinx 18 | sphinx: 19 | builder: html 20 | 21 | # Optionally build your docs in additional formats such as PDF and ePub 22 | # formats: 23 | # - pdf 24 | # - epub 25 | 26 | # Optional but recommended, declare the Python requirements required 27 | # to build your documentation 28 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 29 | # python: 30 | # install: 31 | # - requirements: docs/requirements.txt 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.1-beta-2017-03-05 2 | init -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menduo/kdniao_python/444944c5e5c9ad652c64da8cf8c3ad3f69504153/CONTRIBUTING.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kdniao_python 2 | 3 | 快递鸟 kdniao python sdk, with tornado async http client support. 4 | 5 | - github: [https://github.com/menduo/kdniao_python](https://github.com/menduo/kdniao_python) 6 | - oschina: [https://git.oschina.net/menduo/kdniao_python](https://git.oschina.net/menduo/kdniao_python) 7 | 8 | > version: 0.1.2 9 | 10 | 非官方。无利益关系。 11 | 12 | # Screenshot(terminal) 13 | ![](https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png) 14 | 15 | # TODO 16 | - doc, more doc 17 | - test, more test 18 | 19 | # Support API 支持的快递鸟 API 20 | 21 | - [x] 即时查询 [http://www.kdniao.com/api-track](http://www.kdniao.com/api-track) 22 | - [x] 物流跟踪 [http://www.kdniao.com/api-follow](http://www.kdniao.com/api-follow) 23 | - [x] 电子面单 [http://www.kdniao.com/api-eorder](http://www.kdniao.com/api-eorder) 24 | - [x] 单号识别 [http://www.kdniao.com/api-recognise](http://www.kdniao.com/api-recognise) 25 | - [x] 预约取件 [http://www.kdniao.com/api-order](http://www.kdniao.com/api-order) 26 | - [x] 在途监控 [http://www.kdniao.com/api-monitor](http://www.kdniao.com/api-monitor) 27 | - [x] 隐私快递 [http://www.kdniao.com/api-safemail](http://www.kdniao.com/api-safemail) 28 | - [ ] 代收货款 [http://www.kdniao.com/CollectionMoneyAPI.aspx](http://www.kdniao.com/CollectionMoneyAPI.aspx) 29 | - [x] 用户信息类 30 | - [x] 注册 9001 31 | - [x] 更新 CMD1002 32 | - [x] 查询 cmd1003 33 | - [x] 提交返款银行信息 CMD1009 34 | - [x] 查询返款银行信息 CMD1008 35 | - [x] 查询用户额度 CMD1014 36 | - [x] 服务申请类 37 | - [x] 垫付业务申请 CMD1004 38 | - [x] 直退业务申请 CMD1005 39 | - [x] 普通代收货款申请 CMD1006 40 | - [x] 查询服务申请状态 CMD1007 41 | - [x] 订单类 42 | - [x] 获取订单货款状态 CMD1010 43 | 44 | 所有 API 见 [http://www.kdniao.com/api-all](http://www.kdniao.com/api-all),快递鸟可能会随时推出新的 API。 45 | 46 | # Install 安装 47 | 48 | `pip install -u kdniao` 49 | 50 | # Usage 使用 51 | ## 依赖 52 | 53 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app id 及 app key。可在 `http://www.kdniao.com/reg` 注册获取。 54 | 55 | 在命令行运行 `kdniao` 命令时,需要在命令行参数中指定 id 与key,或者预先在环境变量中指定 `KDNIAO_APP_ID` 及 `KDNIAO_APP_KEY`。如: 56 | 57 | 1. `KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}`,或: 58 | 2. 在 `~/.bash_profile` 中设置变量,并重新打开 shell 执行: `kdniao {运单号}`,或: 59 | 3. `kdniao {运单号} --ik={APP_ID},{APP_KEY}` 60 | 61 | ## Command Line 命令行 62 | 63 | ```bash 64 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 65 | 66 | # 如: 67 | # $ kdniao 12345678 --s YTO 68 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 69 | ``` 70 | 71 | ## Sync 同步客户端 72 | 73 | ```python 74 | from kdniao.client import KdNiaoClient 75 | app_id = 12345678 76 | app_key = "YOUR_APP_KEY" 77 | is_prod = True 78 | 79 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 80 | 81 | client = KdNiaoClient(app_id, app_key, is_prod) 82 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 83 | 84 | # Your logic code here 85 | ``` 86 | 87 | ## Tornado Async Client 异步客户端 88 | ```python 89 | from kdniao.client import KdNiaoAsyncClient 90 | app_id = 12345678 91 | app_key = "YOUR_APP_KEY" 92 | is_prod = True 93 | 94 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 95 | 96 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 97 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 98 | 99 | # Your logic code here 100 | ``` 101 | 102 | # 贡献 103 | 104 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 105 | 106 | # 免责声明 107 | 108 | 1. 快递鸟官方 可能会随时推出新的 API,`kdniao_python` 未必会及时支持。 109 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 110 | 111 | # 相关链接 112 | - 快递鸟官网:[http://www.kdniao.com/](http://www.kdniao.com/) 113 | - 快递鸟官网 API 列表:[http://www.kdniao.com/api-all](http://www.kdniao.com/api-all) 114 | 115 | # 联系 116 | - `shimenduo AT gmail DOT com` 117 | - github: [https://github.com/menduo/kdniao_python](https://github.com/menduo/kdniao_python) 118 | - oschina: [https://git.oschina.net/menduo/kdniao_python](https://git.oschina.net/menduo/kdniao_python) 119 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | kdniao\_python 2 | ============== 3 | 4 | 快递鸟 kdniao python sdk, with tornado async http client support. 5 | 6 | - github: https://github.com/menduo/kdniao_python 7 | - oschina: https://git.oschina.net/menduo/kdniao_python 8 | 9 | version: 0.1.2 10 | 11 | 非官方。无利益关系。 12 | 13 | Screenshot(terminal) 14 | ==================== 15 | 16 | .. figure:: https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png 17 | :alt: 18 | 19 | TODO 20 | ==== 21 | 22 | - doc, more doc 23 | - test, more test 24 | 25 | Support API 支持的快递鸟 API 26 | ============================ 27 | 28 | - [x] 即时查询 http://www.kdniao.com/api-track 29 | - [x] 物流跟踪 http://www.kdniao.com/api-follow 30 | - [x] 电子面单 http://www.kdniao.com/api-eorder 31 | - [x] 单号识别 http://www.kdniao.com/api-recognise 32 | - [x] 预约取件 http://www.kdniao.com/api-order 33 | - [x] 在途监控 http://www.kdniao.com/api-monitor 34 | - [x] 隐私快递 http://www.kdniao.com/api-safemail 35 | - [ ] 代收货款 http://www.kdniao.com/CollectionMoneyAPI.aspx 36 | 37 | - [x] 用户信息类 38 | 39 | - [x] 注册 9001 40 | - [x] 更新 CMD1002 41 | - [x] 查询 cmd1003 42 | - [x] 提交返款银行信息 CMD1009 43 | - [x] 查询返款银行信息 CMD1008 44 | - [x] 查询用户额度 CMD1014 45 | 46 | - [x] 服务申请类 47 | 48 | - [x] 垫付业务申请 CMD1004 49 | - [x] 直退业务申请 CMD1005 50 | - [x] 普通代收货款申请 CMD1006 51 | - [x] 查询服务申请状态 CMD1007 52 | 53 | - [x] 订单类 54 | 55 | - [x] 获取订单货款状态 CMD1010 56 | 57 | 所有 API 见 http://www.kdniao.com/api-all\ ,快递鸟可能会随时推出新的 58 | API。 59 | 60 | Install 安装 61 | ============ 62 | 63 | ``pip install -u kdniao`` 64 | 65 | Usage 使用 66 | ========== 67 | 68 | 依赖 69 | ---- 70 | 71 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app 72 | id 及 app key。可在 ``http://www.kdniao.com/reg`` 注册获取。 73 | 74 | 在命令行运行 ``kdniao`` 命令时,需要在命令行参数中指定 id 75 | 与key,或者预先在环境变量中指定 ``KDNIAO_APP_ID`` 及 76 | ``KDNIAO_APP_KEY``\ 。如: 77 | 78 | 1. ``KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}``\ ,或: 79 | 2. 在 ``~/.bash_profile`` 中设置变量,并重新打开 shell 执行: 80 | ``kdniao {运单号}``\ ,或: 81 | 3. ``kdniao {运单号} --ik={APP_ID},{APP_KEY}`` 82 | 83 | Command Line 命令行 84 | ------------------- 85 | 86 | .. code:: bash 87 | 88 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 89 | 90 | # 如: 91 | # $ kdniao 12345678 --s YTO 92 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 93 | 94 | Sync 同步客户端 95 | --------------- 96 | 97 | .. code:: python 98 | 99 | from kdniao.client import KdNiaoClient 100 | app_id = 12345678 101 | app_key = "YOUR_APP_KEY" 102 | is_prod = True 103 | 104 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 105 | 106 | client = KdNiaoClient(app_id, app_key, is_prod) 107 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 108 | 109 | # Your logic code here 110 | 111 | Tornado Async Client 异步客户端 112 | ------------------------------- 113 | 114 | .. code:: python 115 | 116 | from kdniao.client import KdNiaoAsyncClient 117 | app_id = 12345678 118 | app_key = "YOUR_APP_KEY" 119 | is_prod = True 120 | 121 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 122 | 123 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 124 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 125 | 126 | # Your logic code here 127 | 128 | 贡献 129 | ==== 130 | 131 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 132 | 133 | 免责声明 134 | ======== 135 | 136 | 1. 快递鸟官方 可能会随时推出新的 API,\ ``kdniao_python`` 137 | 未必会及时支持。 138 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 139 | 140 | 相关链接 141 | ======== 142 | 143 | - 快递鸟官网:\ http://www.kdniao.com/ 144 | - 快递鸟官网 API 列表:\ http://www.kdniao.com/api-all 145 | 146 | 联系 147 | ==== 148 | 149 | - ``shimenduo AT gmail DOT com`` 150 | - github: https://github.com/menduo/kdniao_python 151 | - oschina: https://git.oschina.net/menduo/kdniao_python 152 | -------------------------------------------------------------------------------- /asset/menduo_kdniao_py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menduo/kdniao_python/444944c5e5c9ad652c64da8cf8c3ad3f69504153/asset/menduo_kdniao_py.png -------------------------------------------------------------------------------- /bin/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | -------------------------------------------------------------------------------- /bin/kdniao: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ 4 | Command line for kdniao https://github.com/menduo/kdniao_python 5 | 6 | install 7 | pip install kdniao 8 | 9 | usage: 10 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 11 | 12 | 如: 13 | $ kdniao 12345678 --s YTO 14 | $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 15 | 16 | """ 17 | import argparse 18 | import sys, os 19 | from os.path import join 20 | 21 | uppath = lambda _path, n: os.sep.join(_path.split(os.sep)[:-n]) 22 | _p_parent_dir = uppath(os.path.abspath(__file__), 2) 23 | _path_of_kdniao_dir = join(uppath(os.path.abspath(__file__), 2), "kdniao") 24 | 25 | if os.path.exists(join(_path_of_kdniao_dir, "client.py")): 26 | sys.path.insert(0, _p_parent_dir) 27 | 28 | from kdniao.client import KdNiaoClient 29 | from kdniao import __version__ 30 | 31 | CLIENT = None 32 | 33 | 34 | def get_env_data(): 35 | """ 36 | get env data 37 | :return: 38 | """ 39 | env = os.environ 40 | app_id = env.get("KDNIAO_APP_ID") 41 | app_key = env.get("KDNIAO_APP_KEY") 42 | env_type = env.get("KDNIAO_ENV_TYPE", "prod") 43 | return app_id, app_key, env_type 44 | 45 | 46 | def parse_args(): 47 | """ 48 | Define and parse `optparse` options for command-line usage. 49 | """ 50 | usage = """kdniao 运单号 [--s=快递公司编号 --ik=AppId,AppKey]""" 51 | desc = "快递鸟 ~~~ %s" % __version__ 52 | 53 | parser = argparse.ArgumentParser(usage=usage, description=desc) 54 | parser.add_argument("logistic_code", type=str, help="logistic code 运单号") 55 | parser.add_argument("--s", dest="shipper_code", type=str, default="", 56 | help="[可选]shipper code 快递公司编码(快递鸟官方编码)") 57 | parser.add_argument("--o", dest="order_code", type=str, default="", 58 | help="[可选]order code 订单号") 59 | 60 | parser.add_argument("--ik", dest="ik", type=str, default="", 61 | help="app_id,app_key, .eg: --ik=1,2") 62 | 63 | return parser.parse_args() 64 | 65 | 66 | def track(logistic_code, shipper_code="", order_code=""): 67 | """ 68 | 即时查询 69 | :param str logistic_code: 运单编号 70 | :param str shipper_code: 快递公司编码,快递鸟版 71 | :param str order_code: 订单编号 72 | :return: 73 | """ 74 | 75 | if shipper_code: 76 | resp = CLIENT.track(logistic_code, shipper_code, order_code) 77 | else: 78 | res = CLIENT.recognise(logistic_code) 79 | shipper_list = res.get("data", {}).get("Shippers", []) 80 | shipper_code = None 81 | 82 | if shipper_list: 83 | shipper_code = shipper_list[0]["ShipperCode"] 84 | if not shipper_code: 85 | return 86 | resp = CLIENT.track(logistic_code, shipper_code, order_code) 87 | 88 | return resp 89 | 90 | 91 | def main(logistic_code, shipper_code="", order_code=""): 92 | """ 93 | 入口 94 | :param str logistic_code: 运单编号 95 | :param str shipper_code: 快递公司编码,快递鸟版 96 | :param str order_code: 订单编号 97 | :return: 98 | """ 99 | r = track(logistic_code, shipper_code, order_code) or {} 100 | 101 | r_data = r.get("data", {}) 102 | trace_list = r_data.get("Traces", []) 103 | 104 | if trace_list: 105 | msg_temp = "%s - %s" 106 | for i in trace_list: 107 | accept_time = i["AcceptTime"] 108 | accept_station = i["AcceptStation"] 109 | print(msg_temp % (accept_time, accept_station)) 110 | else: 111 | print("没有找到路由信息") 112 | 113 | 114 | if __name__ == '__main__': 115 | args = parse_args() 116 | 117 | _e = [app_id, app_key, env_type] = get_env_data() 118 | if not all(_e): 119 | _ik = [str(i).strip() for i in args.ik.split(",") if str(i).strip()] 120 | if not _ik: 121 | _ik = ["", "", ""] 122 | else: 123 | _ik = _ik + ["prod"] 124 | _e = [app_id, app_key, env_type] = _ik 125 | 126 | if not all(_e): 127 | print("\n") 128 | print("\tboth app_id and app_key are required!!!") 129 | print("\n") 130 | sys.exit(1) 131 | 132 | CLIENT = KdNiaoClient(app_id, app_key, env_type) 133 | 134 | main(args.logistic_code, args.shipper_code, args.order_code) 135 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # kdniao_python 2 | 3 | 快递鸟 kdniao python sdk, with tornado async http client support. 4 | 5 | - github: [https://github.com/menduo/kdniao_python](https://github.com/menduo/kdniao_python) 6 | - oschina: [https://git.oschina.net/menduo/kdniao_python](https://git.oschina.net/menduo/kdniao_python) 7 | 8 | > version: 0.1.2 9 | 10 | 非官方。无利益关系。 11 | 12 | # Screenshot(terminal) 13 | ![](https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png) 14 | 15 | # TODO 16 | - doc, more doc 17 | - test, more test 18 | 19 | # Support API 支持的快递鸟 API 20 | 21 | - [x] 即时查询 [http://www.kdniao.com/api-track](http://www.kdniao.com/api-track) 22 | - [x] 物流跟踪 [http://www.kdniao.com/api-follow](http://www.kdniao.com/api-follow) 23 | - [x] 电子面单 [http://www.kdniao.com/api-eorder](http://www.kdniao.com/api-eorder) 24 | - [x] 单号识别 [http://www.kdniao.com/api-recognise](http://www.kdniao.com/api-recognise) 25 | - [x] 预约取件 [http://www.kdniao.com/api-order](http://www.kdniao.com/api-order) 26 | - [x] 在途监控 [http://www.kdniao.com/api-monitor](http://www.kdniao.com/api-monitor) 27 | - [x] 隐私快递 [http://www.kdniao.com/api-safemail](http://www.kdniao.com/api-safemail) 28 | - [ ] 代收货款 [http://www.kdniao.com/CollectionMoneyAPI.aspx](http://www.kdniao.com/CollectionMoneyAPI.aspx) 29 | - [x] 用户信息类 30 | - [x] 注册 9001 31 | - [x] 更新 CMD1002 32 | - [x] 查询 cmd1003 33 | - [x] 提交返款银行信息 CMD1009 34 | - [x] 查询返款银行信息 CMD1008 35 | - [x] 查询用户额度 CMD1014 36 | - [x] 服务申请类 37 | - [x] 垫付业务申请 CMD1004 38 | - [x] 直退业务申请 CMD1005 39 | - [x] 普通代收货款申请 CMD1006 40 | - [x] 查询服务申请状态 CMD1007 41 | - [x] 订单类 42 | - [x] 获取订单货款状态 CMD1010 43 | 44 | 所有 API 见 [http://www.kdniao.com/api-all](http://www.kdniao.com/api-all),快递鸟可能会随时推出新的 API。 45 | 46 | # Install 安装 47 | 48 | `pip install -u kdniao` 49 | 50 | # Usage 使用 51 | ## 依赖 52 | 53 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app id 及 app key。可在 `http://www.kdniao.com/reg` 注册获取。 54 | 55 | 在命令行运行 `kdniao` 命令时,需要在命令行参数中指定 id 与key,或者预先在环境变量中指定 `KDNIAO_APP_ID` 及 `KDNIAO_APP_KEY`。如: 56 | 57 | 1. `KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}`,或: 58 | 2. 在 `~/.bash_profile` 中设置变量,并重新打开 shell 执行: `kdniao {运单号}`,或: 59 | 3. `kdniao {运单号} --ik={APP_ID},{APP_KEY}` 60 | 61 | ## Command Line 命令行 62 | 63 | ```bash 64 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 65 | 66 | # 如: 67 | # $ kdniao 12345678 --s YTO 68 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 69 | ``` 70 | 71 | ## Sync 同步客户端 72 | 73 | ```python 74 | from kdniao.client import KdNiaoClient 75 | app_id = 12345678 76 | app_key = "YOUR_APP_KEY" 77 | is_prod = True 78 | 79 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 80 | 81 | client = KdNiaoClient(app_id, app_key, is_prod) 82 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 83 | 84 | # Your logic code here 85 | ``` 86 | 87 | ## Tornado Async Client 异步客户端 88 | ```python 89 | from kdniao.client import KdNiaoAsyncClient 90 | app_id = 12345678 91 | app_key = "YOUR_APP_KEY" 92 | is_prod = True 93 | 94 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 95 | 96 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 97 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 98 | 99 | # Your logic code here 100 | ``` 101 | 102 | # 贡献 103 | 104 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 105 | 106 | # 免责声明 107 | 108 | 1. 快递鸟官方 可能会随时推出新的 API,`kdniao_python` 未必会及时支持。 109 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 110 | 111 | # 相关链接 112 | - 快递鸟官网:[http://www.kdniao.com/](http://www.kdniao.com/) 113 | - 快递鸟官网 API 列表:[http://www.kdniao.com/api-all](http://www.kdniao.com/api-all) 114 | 115 | # 联系 116 | - `shimenduo AT gmail DOT com` 117 | - github: [https://github.com/menduo/kdniao_python](https://github.com/menduo/kdniao_python) 118 | - oschina: [https://git.oschina.net/menduo/kdniao_python](https://git.oschina.net/menduo/kdniao_python) 119 | -------------------------------------------------------------------------------- /docs/README.rst: -------------------------------------------------------------------------------- 1 | kdniao\_python 2 | ============== 3 | 4 | 快递鸟 kdniao python sdk, with tornado async http client support. 5 | 6 | - github: https://github.com/menduo/kdniao_python 7 | - oschina: https://git.oschina.net/menduo/kdniao_python 8 | 9 | version: 0.1.2 10 | 11 | 非官方。无利益关系。 12 | 13 | Screenshot(terminal) 14 | ==================== 15 | 16 | .. figure:: https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png 17 | :alt: 18 | 19 | TODO 20 | ==== 21 | 22 | - doc, more doc 23 | - test, more test 24 | 25 | Support API 支持的快递鸟 API 26 | ============================ 27 | 28 | - [x] 即时查询 http://www.kdniao.com/api-track 29 | - [x] 物流跟踪 http://www.kdniao.com/api-follow 30 | - [x] 电子面单 http://www.kdniao.com/api-eorder 31 | - [x] 单号识别 http://www.kdniao.com/api-recognise 32 | - [x] 预约取件 http://www.kdniao.com/api-order 33 | - [x] 在途监控 http://www.kdniao.com/api-monitor 34 | - [x] 隐私快递 http://www.kdniao.com/api-safemail 35 | - [ ] 代收货款 http://www.kdniao.com/CollectionMoneyAPI.aspx 36 | 37 | - [x] 用户信息类 38 | 39 | - [x] 注册 9001 40 | - [x] 更新 CMD1002 41 | - [x] 查询 cmd1003 42 | - [x] 提交返款银行信息 CMD1009 43 | - [x] 查询返款银行信息 CMD1008 44 | - [x] 查询用户额度 CMD1014 45 | 46 | - [x] 服务申请类 47 | 48 | - [x] 垫付业务申请 CMD1004 49 | - [x] 直退业务申请 CMD1005 50 | - [x] 普通代收货款申请 CMD1006 51 | - [x] 查询服务申请状态 CMD1007 52 | 53 | - [x] 订单类 54 | 55 | - [x] 获取订单货款状态 CMD1010 56 | 57 | 所有 API 见 http://www.kdniao.com/api-all\ ,快递鸟可能会随时推出新的 58 | API。 59 | 60 | Install 安装 61 | ============ 62 | 63 | ``pip install -u kdniao`` 64 | 65 | Usage 使用 66 | ========== 67 | 68 | 依赖 69 | ---- 70 | 71 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app 72 | id 及 app key。可在 ``http://www.kdniao.com/reg`` 注册获取。 73 | 74 | 在命令行运行 ``kdniao`` 命令时,需要在命令行参数中指定 id 75 | 与key,或者预先在环境变量中指定 ``KDNIAO_APP_ID`` 及 76 | ``KDNIAO_APP_KEY``\ 。如: 77 | 78 | 1. ``KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}``\ ,或: 79 | 2. 在 ``~/.bash_profile`` 中设置变量,并重新打开 shell 执行: 80 | ``kdniao {运单号}``\ ,或: 81 | 3. ``kdniao {运单号} --ik={APP_ID},{APP_KEY}`` 82 | 83 | Command Line 命令行 84 | ------------------- 85 | 86 | .. code:: bash 87 | 88 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 89 | 90 | # 如: 91 | # $ kdniao 12345678 --s YTO 92 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 93 | 94 | Sync 同步客户端 95 | --------------- 96 | 97 | .. code:: python 98 | 99 | from kdniao.client import KdNiaoClient 100 | app_id = 12345678 101 | app_key = "YOUR_APP_KEY" 102 | is_prod = True 103 | 104 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 105 | 106 | client = KdNiaoClient(app_id, app_key, is_prod) 107 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 108 | 109 | # Your logic code here 110 | 111 | Tornado Async Client 异步客户端 112 | ------------------------------- 113 | 114 | .. code:: python 115 | 116 | from kdniao.client import KdNiaoAsyncClient 117 | app_id = 12345678 118 | app_key = "YOUR_APP_KEY" 119 | is_prod = True 120 | 121 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 122 | 123 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 124 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 125 | 126 | # Your logic code here 127 | 128 | 贡献 129 | ==== 130 | 131 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 132 | 133 | 免责声明 134 | ======== 135 | 136 | 1. 快递鸟官方 可能会随时推出新的 API,\ ``kdniao_python`` 137 | 未必会及时支持。 138 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 139 | 140 | 相关链接 141 | ======== 142 | 143 | - 快递鸟官网:\ http://www.kdniao.com/ 144 | - 快递鸟官网 API 列表:\ http://www.kdniao.com/api-all 145 | 146 | 联系 147 | ==== 148 | 149 | - ``shimenduo AT gmail DOT com`` 150 | - github: https://github.com/menduo/kdniao_python 151 | - oschina: https://git.oschina.net/menduo/kdniao_python 152 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | kdniao\_python 2 | ============== 3 | 4 | 快递鸟 kdniao python sdk, with tornado async http client support. 5 | 6 | - github: https://github.com/menduo/kdniao_python 7 | - oschina: https://git.oschina.net/menduo/kdniao_python 8 | 9 | version: 0.1.2 10 | 11 | 非官方。无利益关系。 12 | 13 | Screenshot(terminal) 14 | ==================== 15 | 16 | .. figure:: https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png 17 | :alt: 18 | 19 | TODO 20 | ==== 21 | 22 | - doc, more doc 23 | - test, more test 24 | 25 | Support API 支持的快递鸟 API 26 | ============================ 27 | 28 | - [x] 即时查询 http://www.kdniao.com/api-track 29 | - [x] 物流跟踪 http://www.kdniao.com/api-follow 30 | - [x] 电子面单 http://www.kdniao.com/api-eorder 31 | - [x] 单号识别 http://www.kdniao.com/api-recognise 32 | - [x] 预约取件 http://www.kdniao.com/api-order 33 | - [x] 在途监控 http://www.kdniao.com/api-monitor 34 | - [x] 隐私快递 http://www.kdniao.com/api-safemail 35 | - [ ] 代收货款 http://www.kdniao.com/CollectionMoneyAPI.aspx 36 | 37 | - [x] 用户信息类 38 | 39 | - [x] 注册 9001 40 | - [x] 更新 CMD1002 41 | - [x] 查询 cmd1003 42 | - [x] 提交返款银行信息 CMD1009 43 | - [x] 查询返款银行信息 CMD1008 44 | - [x] 查询用户额度 CMD1014 45 | 46 | - [x] 服务申请类 47 | 48 | - [x] 垫付业务申请 CMD1004 49 | - [x] 直退业务申请 CMD1005 50 | - [x] 普通代收货款申请 CMD1006 51 | - [x] 查询服务申请状态 CMD1007 52 | 53 | - [x] 订单类 54 | 55 | - [x] 获取订单货款状态 CMD1010 56 | 57 | 所有 API 见 http://www.kdniao.com/api-all\ ,快递鸟可能会随时推出新的 58 | API。 59 | 60 | Install 安装 61 | ============ 62 | 63 | ``pip install -u kdniao`` 64 | 65 | Usage 使用 66 | ========== 67 | 68 | 依赖 69 | ---- 70 | 71 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app 72 | id 及 app key。可在 ``http://www.kdniao.com/reg`` 注册获取。 73 | 74 | 在命令行运行 ``kdniao`` 命令时,需要在命令行参数中指定 id 75 | 与key,或者预先在环境变量中指定 ``KDNIAO_APP_ID`` 及 76 | ``KDNIAO_APP_KEY``\ 。如: 77 | 78 | 1. ``KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}``\ ,或: 79 | 2. 在 ``~/.bash_profile`` 中设置变量,并重新打开 shell 执行: 80 | ``kdniao {运单号}``\ ,或: 81 | 3. ``kdniao {运单号} --ik={APP_ID},{APP_KEY}`` 82 | 83 | Command Line 命令行 84 | ------------------- 85 | 86 | .. code:: bash 87 | 88 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 89 | 90 | # 如: 91 | # $ kdniao 12345678 --s YTO 92 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 93 | 94 | Sync 同步客户端 95 | --------------- 96 | 97 | .. code:: python 98 | 99 | from kdniao.client import KdNiaoClient 100 | app_id = 12345678 101 | app_key = "YOUR_APP_KEY" 102 | is_prod = True 103 | 104 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 105 | 106 | client = KdNiaoClient(app_id, app_key, is_prod) 107 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 108 | 109 | # Your logic code here 110 | 111 | Tornado Async Client 异步客户端 112 | ------------------------------- 113 | 114 | .. code:: python 115 | 116 | from kdniao.client import KdNiaoAsyncClient 117 | app_id = 12345678 118 | app_key = "YOUR_APP_KEY" 119 | is_prod = True 120 | 121 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 122 | 123 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 124 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 125 | 126 | # Your logic code here 127 | 128 | 贡献 129 | ==== 130 | 131 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 132 | 133 | 免责声明 134 | ======== 135 | 136 | 1. 快递鸟官方 可能会随时推出新的 API,\ ``kdniao_python`` 137 | 未必会及时支持。 138 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 139 | 140 | 相关链接 141 | ======== 142 | 143 | - 快递鸟官网:\ http://www.kdniao.com/ 144 | - 快递鸟官网 API 列表:\ http://www.kdniao.com/api-all 145 | 146 | 联系 147 | ==== 148 | 149 | - ``shimenduo AT gmail DOT com`` 150 | - github: https://github.com/menduo/kdniao_python 151 | - oschina: https://git.oschina.net/menduo/kdniao_python 152 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | tornado>=4.0.2 3 | kdniao 4 | -------------------------------------------------------------------------------- /examples/tornado_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | __doc__ = """ 4 | Example for using kdniao async clinet in tornado web app 5 | 6 | run 7 | 8 | $ python tornado_handler.py --app_id={YourAppId} --app_key={YourAppKey} 9 | 10 | then visit the links below: 11 | 12 | http://127.0.0.1:19890/track?shipper_code=YTO&logistic_code=12345678 13 | 14 | http://127.0.0.1:19890/recognise?shipper_code=YTO&logistic_code=12345678 15 | 16 | http://127.0.0.1:19890/track_by_recognise?shipper_code=YTO&logistic_code=12345678 17 | http://127.0.0.1:19890/track_by_recognise?shipper_code=YTO&logistic_code=A__12345678b__B 18 | http://127.0.0.1:19890/track_by_recognise?shipper_code=YTO&logistic_code=a 19 | """ 20 | 21 | import sys 22 | 23 | sys.path.insert(0, "..") 24 | 25 | import os 26 | import json 27 | import tornado.web 28 | import tornado.gen 29 | import tornado.ioloop 30 | from tornado.options import define, options, parse_command_line 31 | 32 | define("app_id", type=int) 33 | define("app_key", type=str) 34 | define("port", type=int, default=19890) 35 | define("debug", type=bool, default=True) 36 | parse_command_line() 37 | 38 | from kdniao import KdNiaoAsyncClient 39 | 40 | 41 | class Env(object): 42 | API_ID = None 43 | API_KEY = None 44 | 45 | 46 | class BaseHandler(tornado.web.RequestHandler): 47 | def initialize(self): 48 | self.async_client = KdNiaoAsyncClient(Env.API_ID, Env.API_KEY) 49 | 50 | def send_json(self, data): 51 | """ 52 | send json data and set header 53 | """ 54 | self.set_header("Content-Type", "application/json") 55 | self.write(json.dumps(data)) 56 | self.finish() 57 | 58 | 59 | class IndexHandler(BaseHandler): 60 | def get(self, *args, **kwargs): 61 | res = __doc__ 62 | res = res.replace("\n", "
") 63 | return self.write(res) 64 | 65 | 66 | class TrackHandler(BaseHandler): 67 | @tornado.gen.coroutine 68 | def get(self): 69 | shipper_code = self.get_argument("shipper_code") 70 | logistic_code = self.get_argument("logistic_code") 71 | order_code = self.get_argument("order_code", "") 72 | resp = yield self.async_client.api_track.track(logistic_code, shipper_code, order_code) 73 | self.send_json(resp) 74 | 75 | 76 | class TrackByRecogniseHandler(BaseHandler): 77 | @tornado.gen.coroutine 78 | def get(self): 79 | logistic_code = self.get_argument("logistic_code") 80 | order_code = self.get_argument("order_code", "") 81 | 82 | res = yield self.async_client.api_recognise.recognise(logistic_code) 83 | shipper_list = res.get("data", {}).get("Shippers", []) 84 | 85 | shipper_code = None 86 | 87 | if shipper_list: 88 | shipper_code = shipper_list[0]["ShipperCode"] 89 | if not shipper_code: 90 | self.send_json({"data": {}}) 91 | raise tornado.gen.Return() 92 | 93 | resp = yield self.async_client.api_track.track(logistic_code, shipper_code, order_code) 94 | self.send_json(resp) 95 | 96 | 97 | class RecogniseHandler(BaseHandler): 98 | @tornado.gen.coroutine 99 | def get(self): 100 | logistic_code = self.get_argument("logistic_code") 101 | resp = yield self.async_client.api_recognise.recognise(logistic_code) 102 | self.send_json(resp) 103 | 104 | 105 | app = tornado.web.Application( 106 | handlers=[ 107 | ('/', IndexHandler), 108 | ('/track', TrackHandler), 109 | ('/track_by_recognise', TrackByRecogniseHandler), 110 | ('/recognise', RecogniseHandler), 111 | 112 | ], 113 | debug=options.debug, 114 | ) 115 | 116 | if __name__ == '__main__': 117 | # Parse API id and key from the env and command line: 118 | _e = [app_id, app_key] = [ 119 | os.environ.get("KDNIAO_APP_ID", ""), 120 | os.environ.get("KDNIAO_APP_KEY", "") 121 | ] 122 | 123 | if not all(_e): 124 | _e = [app_id, app_key] = [options.app_id, options.app_key] 125 | 126 | if not all(_e): 127 | raise ValueError("both app_id and app_key are required") 128 | 129 | Env.API_ID = app_id 130 | Env.API_KEY = app_key 131 | 132 | # Start our app: 133 | 134 | app.listen(options.port) 135 | print("\n\t Example for KdNiao Python SDK: please check http://127.0.0.1:%s\n" % options.port) 136 | tornado.ioloop.IOLoop.instance().start() 137 | -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | kdniao\_python 2 | ============== 3 | 4 | 快递鸟 kdniao python sdk, with tornado async http client support. 5 | 6 | - github: https://github.com/menduo/kdniao_python 7 | - oschina: https://git.oschina.net/menduo/kdniao_python 8 | 9 | version: 0.1.2 10 | 11 | 非官方。无利益关系。 12 | 13 | Screenshot(terminal) 14 | ==================== 15 | 16 | .. figure:: https://raw.githubusercontent.com/menduo/kdniao_python/master/asset/menduo_kdniao_py.png 17 | :alt: 18 | 19 | TODO 20 | ==== 21 | 22 | - doc, more doc 23 | - test, more test 24 | 25 | Support API 支持的快递鸟 API 26 | ============================ 27 | 28 | - [x] 即时查询 http://www.kdniao.com/api-track 29 | - [x] 物流跟踪 http://www.kdniao.com/api-follow 30 | - [x] 电子面单 http://www.kdniao.com/api-eorder 31 | - [x] 单号识别 http://www.kdniao.com/api-recognise 32 | - [x] 预约取件 http://www.kdniao.com/api-order 33 | - [x] 在途监控 http://www.kdniao.com/api-monitor 34 | - [x] 隐私快递 http://www.kdniao.com/api-safemail 35 | - [ ] 代收货款 http://www.kdniao.com/CollectionMoneyAPI.aspx 36 | 37 | - [x] 用户信息类 38 | 39 | - [x] 注册 9001 40 | - [x] 更新 CMD1002 41 | - [x] 查询 cmd1003 42 | - [x] 提交返款银行信息 CMD1009 43 | - [x] 查询返款银行信息 CMD1008 44 | - [x] 查询用户额度 CMD1014 45 | 46 | - [x] 服务申请类 47 | 48 | - [x] 垫付业务申请 CMD1004 49 | - [x] 直退业务申请 CMD1005 50 | - [x] 普通代收货款申请 CMD1006 51 | - [x] 查询服务申请状态 CMD1007 52 | 53 | - [x] 订单类 54 | 55 | - [x] 获取订单货款状态 CMD1010 56 | 57 | 所有 API 见 http://www.kdniao.com/api-all\ ,快递鸟可能会随时推出新的 58 | API。 59 | 60 | Install 安装 61 | ============ 62 | 63 | ``pip install -u kdniao`` 64 | 65 | Usage 使用 66 | ========== 67 | 68 | 依赖 69 | ---- 70 | 71 | 无论是在程序上,还是在命令行中,你都必须先获得快递鸟官方分配给你的 app 72 | id 及 app key。可在 ``http://www.kdniao.com/reg`` 注册获取。 73 | 74 | 在命令行运行 ``kdniao`` 命令时,需要在命令行参数中指定 id 75 | 与key,或者预先在环境变量中指定 ``KDNIAO_APP_ID`` 及 76 | ``KDNIAO_APP_KEY``\ 。如: 77 | 78 | 1. ``KDNIAO_APP_ID={你的ID} KDNIAO_APP_KEY={你的Key} kdniao {运单号}``\ ,或: 79 | 2. 在 ``~/.bash_profile`` 中设置变量,并重新打开 shell 执行: 80 | ``kdniao {运单号}``\ ,或: 81 | 3. ``kdniao {运单号} --ik={APP_ID},{APP_KEY}`` 82 | 83 | Command Line 命令行 84 | ------------------- 85 | 86 | .. code:: bash 87 | 88 | $ kdniao {运单号} --s=快递公司编码 --o=订单号 --ik={APP_ID},{APP_KEY} 89 | 90 | # 如: 91 | # $ kdniao 12345678 --s YTO 92 | # $ kdniao 12345678 --ik={APP_ID},{APP_KEY} 93 | 94 | Sync 同步客户端 95 | --------------- 96 | 97 | .. code:: python 98 | 99 | from kdniao.client import KdNiaoClient 100 | app_id = 12345678 101 | app_key = "YOUR_APP_KEY" 102 | is_prod = True 103 | 104 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 105 | 106 | client = KdNiaoClient(app_id, app_key, is_prod) 107 | trace_res = client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 108 | 109 | # Your logic code here 110 | 111 | Tornado Async Client 异步客户端 112 | ------------------------------- 113 | 114 | .. code:: python 115 | 116 | from kdniao.client import KdNiaoAsyncClient 117 | app_id = 12345678 118 | app_key = "YOUR_APP_KEY" 119 | is_prod = True 120 | 121 | logistic_code, shipper_code, order_code = 12345678, "SF", "" 122 | 123 | async_client = KdNiaoAsyncClient(app_id, app_key, is_prod) 124 | trace_res = yield async_client.api_track.track(logistic_code, shipper_code, order_code, timeout=(10, 10)) 125 | 126 | # Your logic code here 127 | 128 | 贡献 129 | ==== 130 | 131 | 欢迎 start、fork 并贡献代码。也欢迎讨论交流、指正。 132 | 133 | 免责声明 134 | ======== 135 | 136 | 1. 快递鸟官方 可能会随时推出新的 API,\ ``kdniao_python`` 137 | 未必会及时支持。 138 | 2. 快递鸟官方 可能会随时变动 API 协议,包括 API 网址、参数、签名算法等。 139 | 140 | 相关链接 141 | ======== 142 | 143 | - 快递鸟官网:\ http://www.kdniao.com/ 144 | - 快递鸟官网 API 列表:\ http://www.kdniao.com/api-all 145 | 146 | 联系 147 | ==== 148 | 149 | - ``shimenduo AT gmail DOT com`` 150 | - github: https://github.com/menduo/kdniao_python 151 | - oschina: https://git.oschina.net/menduo/kdniao_python 152 | -------------------------------------------------------------------------------- /kdniao/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.2" 2 | __author__ = "menduo" 3 | __author_email__ = "shimenduo@gmail.com" 4 | __repo__ = "https://github.com/menduo/kdniao_python" 5 | from .client import KdNiaoClient, KdNiaoAsyncClient 6 | -------------------------------------------------------------------------------- /kdniao/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ 4 | API 封装 5 | """ 6 | import json 7 | import hashlib 8 | import base64 9 | from kdniao.mdutils import utf8 10 | from kdniao.conf import APIPathBuilder 11 | 12 | 13 | class ClientMixin(object): 14 | """ 15 | client mixin 16 | """ 17 | 18 | def __init__(self, client): 19 | """ 20 | :param client: 21 | """ 22 | self._client = client 23 | self._name = type(self).__name__.lower() 24 | 25 | def _send(self, req_type, data, **kwargs): 26 | """ 27 | :param str req_type: 指令类型 28 | :param dict data: 数据 29 | :param dict kwargs: 其他参数,如 timeout=(10, 10) 30 | :return: 31 | """ 32 | return self._client._request(req_type, data, **kwargs) 33 | 34 | 35 | class API(object): 36 | """ 37 | API Wrapper for http://www.kdniao.com/api-all 38 | """ 39 | REQ_TYPE = None 40 | 41 | def __init__(self, app_id, app_key, env_type="prod"): 42 | """ 43 | init 44 | :param int app_id: app id 45 | :param str app_key: app key 46 | :param str env_type: env type, prod or test, default is prod 47 | """ 48 | 49 | # config 50 | self._app_id = app_id 51 | self._app_key = app_key 52 | self._env_type = env_type 53 | 54 | # API group exported API 分组 55 | self.api_track = APITrack(self) 56 | self.api_follow = APIFollow(self) 57 | self.api_recognise = APIRecognise(self) 58 | self.api_eorder = APIEOrder(self) 59 | self.api_order = APIOrder(self) 60 | self.api_monitor = APIMonitor(self) 61 | self.api_user = APIUser(self) 62 | self.api_collect = APICollectionMoney(self) 63 | 64 | # Method exported 常用接口导出 65 | self.track = self.api_track.track 66 | self.recognise = self.api_recognise.recognise 67 | self.follow = self.api_follow.follow 68 | self.create_eorder = self.api_eorder.create 69 | 70 | @classmethod 71 | def __sign(cls, data, app_key): 72 | """ 73 | sign 74 | b64(md5(REQUEST_DATA)) 75 | """ 76 | m = hashlib.md5() 77 | s = utf8(data + app_key) 78 | m.update(s) 79 | digest = utf8(m.hexdigest()) 80 | b64_str = base64.b64encode(digest).decode() 81 | return b64_str 82 | 83 | def _prepare_req_params(self, req_type, data): 84 | """ 85 | :param str req_type: 86 | :param dict data: 87 | :return: 88 | """ 89 | json_data = json.dumps(data, sort_keys=True) 90 | sign = self.__sign(json_data, self._app_key) 91 | req_params = { 92 | "RequestData": json_data, 93 | "EBusinessID": str(self._app_id), 94 | "RequestType": str(req_type), # "1002" 95 | "DataSign": sign, 96 | "DataType": str(2), 97 | } 98 | return req_params 99 | 100 | def valid_sign(self, sign, data): 101 | """ 102 | :param str sign: 签名 103 | :param str data: 数据 104 | :return bool: 105 | """ 106 | return sign == self.__sign(data, self._app_key) 107 | 108 | def _gen_api_url(self, req_type): 109 | """ 110 | generate api url 111 | :param tr req_type: request type 112 | :return str: url 113 | """ 114 | return APIPathBuilder.gen_api_url(self._env_type, req_type) 115 | 116 | 117 | class APITrack(ClientMixin): 118 | """ 119 | 即时查询 路由查询 轨迹跟踪 120 | http://www.kdniao.com/api-track 121 | """ 122 | 123 | def track(self, logistic_code, shipper_code, order_code="", **kwargs): 124 | """ 125 | 即时查询 路由查询 轨迹跟踪 126 | :param str logistic_code: 运单编号 127 | :param str shipper_code: 物流公司编号 128 | :param str order_code: 订单编号 129 | :return dict: 130 | """ 131 | rtype = "1002" 132 | params = { 133 | "LogisticCode": logistic_code, 134 | "ShipperCode": shipper_code, 135 | "OrderCode": order_code, 136 | } 137 | return self._send(rtype, params, **kwargs) 138 | 139 | 140 | class APIFollow(ClientMixin): 141 | """ 142 | 物流跟踪 http://www.kdniao.com/api-follow 143 | """ 144 | 145 | def follow(self, logistic_code, shipper_code, params=None, **kwargs): 146 | """ 147 | 物流跟踪 148 | :param str logistic_code: 运单编号 149 | :param str shipper_code: 物流公司编号 150 | :param dict params: 其他选填参数,见 http://www.kdniao.com/api-follow 二、接口参数 订阅接口 请求内容字段定义: 151 | :return dict: 152 | """ 153 | rtype = "1008" 154 | params = params or {} 155 | params.update( 156 | {"LogisticCode": logistic_code, 157 | "ShipperCode": shipper_code} 158 | ) 159 | return self._send(rtype, params, **kwargs) 160 | 161 | 162 | class APIEOrder(ClientMixin): 163 | """ 164 | 电子面单 http://www.kdniao.com/api-eorder 165 | """ 166 | 167 | def create(self, params, **kwargs): 168 | """ 169 | 电子面单下单 170 | :param dict params: 下单参数 171 | :param dict kwargs: 其他可选参数 172 | :return: 173 | """ 174 | rtype = "1007" 175 | return self._send(rtype, params, **kwargs) 176 | 177 | 178 | class APIOrder(ClientMixin): 179 | """ 180 | 预约取件接口 http://www.kdniao.com/api-order 181 | """ 182 | 183 | def create(self, params, **kwargs): 184 | """ 185 | 预约取件 186 | :param params: 187 | :param kwargs: 188 | :return: 189 | """ 190 | rtype = "1001" 191 | return self._send(rtype, params, **kwargs) 192 | 193 | def cancle(self, logistic_code, shipper_code, order_code, **kwargs): 194 | """ 195 | 取消订单 196 | :param str logistic_code: 运单编号 197 | :param str shipper_code: 物流公司编号 198 | :param str order_code: 订单编号 199 | :return dict: 200 | """ 201 | rtype = "1004" 202 | params = { 203 | "LogisticCode": logistic_code, 204 | "ShipperCode": shipper_code, 205 | "OrderCode": order_code, 206 | } 207 | return self._send(rtype, params, **kwargs) 208 | 209 | 210 | class APIRecognise(ClientMixin): 211 | """ 212 | 单号识别 http://www.kdniao.com/api-recognise 213 | """ 214 | 215 | def recognise(self, logistic_code, **kwargs): 216 | """ 217 | 单号识别 http://www.kdniao.com/api-recognise 218 | :return: 219 | """ 220 | rtype = "2002" 221 | params = { 222 | "LogisticCode": logistic_code, 223 | } 224 | return self._send(rtype, params, **kwargs) 225 | 226 | 227 | class APIMonitor(ClientMixin): 228 | """ 229 | 在途监控 http://www.kdniao.com/api-monitor 230 | """ 231 | 232 | def track(self, logistic_code, shipper_code, order_code, **kwargs): 233 | """ 234 | 即时查询(增值版)接口 http://www.kdniao.com/api-monitor 235 | :param str logistic_code: 运单编号 236 | :param str shipper_code: 物流公司编号 237 | :param str order_code: 订单编号 238 | :return dict: 239 | """ 240 | rtype = "8001" 241 | params = { 242 | "LogisticCode": logistic_code, 243 | "ShipperCode": shipper_code, 244 | "OrderCode": order_code, 245 | } 246 | return self._send(rtype, params, **kwargs) 247 | 248 | def subscribe(self, params, **kwargs): 249 | """ 250 | 订阅(增值版)接口 http://www.kdniao.com/api-monitor 251 | :return: 252 | """ 253 | rtype = "8008" 254 | return self._send(rtype, params, **kwargs) 255 | 256 | 257 | class APISafeMail(ClientMixin): 258 | """ 259 | 隐私快递 http://www.kdniao.com/api-safemail 260 | """ 261 | 262 | def safe_number(self, params, **kwargs): 263 | """ 264 | 安全号码 http://www.kdniao.com/api-safemail 265 | :param dict params: 接口请求参数 266 | :param dict kwargs: 其他参数 267 | :return dict: 268 | """ 269 | rtype = "3001" 270 | return self._send(rtype, params, **kwargs) 271 | 272 | def safe_order(self, params, **kwargs): 273 | """ 274 | 隐私电子面单 http://www.kdniao.com/api-safemail 275 | :param dict params: 接口请求参数 276 | :param dict kwargs: 其他参数 277 | :return: 278 | """ 279 | rtype = "1007" 280 | return self._send(rtype, params, **kwargs) 281 | 282 | 283 | class APIUser(ClientMixin): 284 | """ 285 | 用户类 286 | """ 287 | 288 | def reg(self, data, **kwargs): 289 | """ 290 | 注册用户 291 | :param data: 292 | :param kwargs: 293 | :return: 294 | """ 295 | rtype = "9001" 296 | return self._send(rtype, data, **kwargs) 297 | 298 | def update(self, params, **kwargs): 299 | """ 300 | 更新用户信息 301 | 详细参数见 http://www.kdniao.com/CollectionMoneyAPI.aspx 1.2.6 JSON请求示例 302 | :return: 303 | """ 304 | rtype = "cmd1002" 305 | return self._send(rtype, params, **kwargs) 306 | 307 | def get(self, **kwargs): 308 | """ 309 | 获取用户信息 310 | :param kwargs: 311 | :return: 312 | """ 313 | rtype = "cmd1003" 314 | return self._send(rtype, {}, **kwargs) 315 | 316 | 317 | class APICollectionMoney(ClientMixin): 318 | """ 319 | 代收货款 320 | """ 321 | 322 | def submit_bank(self, params, **kwargs): 323 | """ 324 | 提交返款银行信息 - CMD1009。设置用户代收货款订单的银行返款信息。 325 | :param dict params: 接口参数, 如:{ 326 | "BankType": "0", 327 | "BankAccountNo": "62266226622662266226", 328 | "BankAccountName": "hoo", 329 | "BankName": "招商银行", 330 | "BankBranch": "", 331 | "BankCardPicA": "", 332 | "BankCardPicB": "" 333 | } 334 | :param dict kwargs: 335 | :return: 336 | """ 337 | rtype = "CMD1009" 338 | return self._send(rtype, params, **kwargs) 339 | 340 | def get_order_bank(self, bank_type, **kwargs): 341 | """ 342 | 查询代收货款订单的银行返款信息 - CMD1008。 343 | :param str bank_type: 信息类型:0-直退,1-垫付 344 | :return: 345 | """ 346 | rtype = "CMD1008" 347 | params = { 348 | "BankType": bank_type 349 | } 350 | return self._send(rtype, params, **kwargs) 351 | 352 | def get_credit(self, services_code, **kwargs): 353 | """ 354 | 查询用户额度 CMD1014 - 查询用户的代收货款的额度限制和当前可用额度。 355 | :param str services_code: 服务代码:普通代收货款 COD,货款直退 CODBACK,货款垫付 CODPAY 网点速退 CODFAST 356 | :param dict kwargs: 357 | :return: 358 | """ 359 | rtype = "CMD1014" 360 | params = { 361 | "ServicesCode": services_code 362 | } 363 | return self._send(rtype, params, **kwargs) 364 | 365 | def request_advance(self, params, **kwargs): 366 | """ 367 | 垫付业务申请 CMD1004 368 | :param dict params: 接口参数,如: { 369 | "BankAccountNo": "62266226622662266226", 370 | "BankAccountName": "hoo", 371 | "BankName": "招商银行", 372 | "BankBranch": "深圳深圳支行", 373 | "BankCardPicA": "", 374 | "BankCardPicB": "" 375 | } 376 | :param dict kwargs: 377 | :return: 378 | """ 379 | rtype = "CMD1004" 380 | return self._send(rtype, params, **kwargs) 381 | 382 | def request_direct_refund(self, params, **kwargs): 383 | """ 384 | 直退业务申请 CMD1005 - 申请直退业务的权限。 385 | :param dict params: 接口参数,如: { 386 | "BankAccountNo": "62266226622662266226", 387 | "BankAccountName": "hoo", 388 | "BankName": "招商银行", 389 | "BankBranch": "深圳深圳支行", 390 | "BankCardPicA": "", 391 | "BankCardPicB": "" 392 | } 393 | :param dict kwargs: 394 | :return: 395 | """ 396 | rtype = "CMD1005" 397 | return self._send(rtype, params, **kwargs) 398 | 399 | def request_normal_ollection(self, **kwargs): 400 | """ 401 | 普通代收货款申请 CMD1006 - 申请普通代收货款业务的权限。 402 | :param dict kwargs: 403 | :return: 404 | """ 405 | rtype = "CMD1006" 406 | return self._send(rtype, {}, **kwargs) 407 | 408 | def get_srv_status(self, services_code, **kwargs): 409 | """ 410 | 查询服务申请状态 CMD1007 - 查询某用户的服务开通情况。 411 | :param str services_code: 服务代码:普通代收货款 COD,货款直退 CODBACK,货款垫付 CODPAY 网点速退 CODFAST 412 | :param dict kwargs: 413 | :return: 414 | """ 415 | rtype = "CMD1007" 416 | params = { 417 | "ServicesCode": services_code 418 | } 419 | return self._send(rtype, params, **kwargs) 420 | 421 | def get_payment_status(self, params, **kwargs): 422 | """ 423 | 获取订单货款状态 CMD1010 - 获取订单货款状态。 424 | :param dict params: 接口参数,如: { 425 | "OrderNos": "", 426 | "BeginTime": "", 427 | "EndTime": "", 428 | "PageIndex": "1", 429 | "PageSize": "10" 430 | } 431 | :param dict kwargs: 432 | :return: 433 | """ 434 | rtype = "CMD1010" 435 | return self._send(rtype, params, **kwargs) 436 | -------------------------------------------------------------------------------- /kdniao/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ 4 | client, 同步、异步 5 | """ 6 | import logging 7 | import json 8 | import tornado.gen 9 | import requests 10 | from tornado.httpclient import HTTPRequest, AsyncHTTPClient 11 | from kdniao.api import API 12 | from kdniao.mdutils import utf8, urlencode 13 | 14 | from kdniao.conf import HEADERS, KdNiaoConfig, APIErrorCode, BizRespCode, DEFAULT_HTTP_RESP 15 | 16 | 17 | class KdNiaoClient(API): 18 | """ 19 | Blocking Client 20 | """ 21 | 22 | def __init__(self, app_id, app_key, is_prod=True): 23 | """ 24 | init 25 | :param int app_id: app id 26 | :param str app_key: app key 27 | :param bool is_prod: env type, prod or test, default is True 28 | """ 29 | env_type = "prod" if is_prod else "test" 30 | super(KdNiaoClient, self).__init__(app_id, app_key, env_type=env_type) 31 | 32 | def _request(self, rtype, data, **kwargs): 33 | """ 34 | prepare request 35 | :param str rtype: 36 | :param dict data: 37 | :param kwargs: 38 | :return: 39 | """ 40 | timeout = kwargs.get("timeout") 41 | timeout = self._parese_time_out(timeout) 42 | 43 | url = self._gen_api_url(rtype) 44 | rdata = self._prepare_req_params(rtype, data) 45 | 46 | try: 47 | res = self._post(url, rdata, timeout) 48 | setattr(res, "body", res.text) 49 | except requests.exceptions.Timeout as exc: 50 | logging.error("timeout while request kdniao API: %s", exc, exc_info=True) 51 | res = DEFAULT_HTTP_RESP 52 | res.status_code = 599 53 | res._content = "" 54 | res.encoding = "utf-8" 55 | res.url = url 56 | res.reason = "timeout" 57 | 58 | resp = self._parse_http_resp(res.status_code, res.reason, res.body, res.url) 59 | res = self._parse_api_resp(resp) 60 | 61 | return res 62 | 63 | @classmethod 64 | def _post(cls, url, data, timeout=(10, 10)): 65 | """ 66 | send post request 67 | :param str url: 68 | :param dict data: 69 | :param object timeout: tuple or an int object 70 | :return: 71 | """ 72 | r = requests.post(url, data, headers=HEADERS, timeout=timeout) 73 | r.encoding = "utf-8" 74 | return r 75 | 76 | @classmethod 77 | def _parese_time_out(cls, timeout): 78 | """ 79 | parse timeout value 80 | :param object timeout: 81 | :return: 82 | """ 83 | res = KdNiaoConfig.DEFAULT_TIMEOUT # seconds 84 | is_valid = True 85 | if isinstance(timeout, type(None)): 86 | pass 87 | elif isinstance(timeout, (int, float)): 88 | res = (timeout, timeout) 89 | elif isinstance(timeout, tuple): 90 | for i in timeout: 91 | if not isinstance(i, (int, float)): 92 | is_valid = False 93 | res = timeout 94 | else: 95 | is_valid = False 96 | if not is_valid: 97 | raise ValueError("timeout must be an int or a tuple include int object") 98 | return res 99 | 100 | def _parse_http_resp(self, code, reason, body, url): 101 | """ 102 | parse http response 103 | :param int code: http resp code 104 | :param str reason: http reason 105 | :param str body: resp body 106 | :param str url: request url 107 | :return dict: { 108 | "code_http": http code, 109 | "Message": error message, 110 | "data": origin dict 111 | } 112 | """ 113 | 114 | resp = { 115 | "code_http": code, 116 | } 117 | 118 | if code != 200: # if not success 119 | if code == 404: # may be not found, get kdniao's json data or other 120 | try: 121 | resp.update(json.loads(body)) 122 | except ValueError: 123 | resp["_msg"] = "API Not Found: %s " % url 124 | elif code == 599: 125 | resp["_msg"] = "HTTP Timeout" 126 | else: 127 | resp["_msg"] = "API ERROR: %s" % code 128 | else: 129 | resp.update(json.loads(body)) 130 | return resp 131 | 132 | def _parse_api_resp(self, data): 133 | """ 134 | parse resp data 135 | :param dict data: 136 | :return: 137 | """ 138 | api_ecode = APIErrorCode.Success 139 | biz_ecode = BizRespCode.Success 140 | 141 | http_code = data["code_http"] 142 | 143 | msg = "Success" 144 | 145 | # API Error 146 | if http_code != 200: 147 | api_ecode = APIErrorCode.Failed 148 | biz_ecode = BizRespCode.Failed 149 | msg = data.pop("_msg", "Unknow Error") 150 | 151 | data_keys = list(data.keys()) 152 | data_keys.remove("code_http") 153 | if data_keys == 1 and data_keys[0] == "Message": 154 | api_ecode = APIErrorCode.Failed 155 | biz_ecode = BizRespCode.Failed 156 | msg = data["Message"] 157 | 158 | # Biz Error 159 | if "Reason" in data: 160 | msg = data["Reason"] 161 | if "Success" in data: 162 | biz_ecode = {True: BizRespCode.Success, False: BizRespCode.Failed}[data["Success"]] 163 | 164 | res = { 165 | "code_api": api_ecode, 166 | "code_biz": biz_ecode, 167 | "code_http": data.pop("code_http"), 168 | "msg": msg, 169 | "data": data 170 | } 171 | 172 | return res 173 | 174 | 175 | class KdNiaoAsyncClient(KdNiaoClient): 176 | """ 177 | Non-Blocking Client by Tornado HTTPclient 178 | """ 179 | 180 | def __init__(self, app_id, app_key, is_prod=True): 181 | """ 182 | init 183 | :param int app_id: app id 184 | :param str app_key: app key 185 | :param bool is_prod: env type, prod or test, default is True 186 | """ 187 | super(KdNiaoAsyncClient, self).__init__(app_id, app_key, is_prod=is_prod) 188 | 189 | @tornado.gen.coroutine 190 | def _request(self, rtype, data, **kwargs): 191 | """ 192 | prepare and send request 193 | :param str rtype: 194 | :param data: 195 | :param timeout: 196 | :return: 197 | """ 198 | timeout = kwargs.pop("timeout", None) 199 | timeout = self._parese_time_out(timeout) 200 | 201 | callback = kwargs.get("callback") 202 | 203 | url = self._gen_api_url(rtype) 204 | rdata = self._prepare_req_params(rtype, data) 205 | 206 | res = yield self._post(url, rdata, timeout, callback) 207 | resp = self._parse_http_resp(res.code, res.reason, res.body, res.request.url) 208 | res = self._parse_api_resp(resp) 209 | 210 | raise tornado.gen.Return(res) 211 | 212 | @classmethod 213 | @tornado.gen.coroutine 214 | def _post(cls, url, data, timeout=(10, 10), callback=None): 215 | """ 216 | :param str url: URL 217 | :param dict data: request parms 218 | :param tuple timeout: tuple or an int object 219 | :param function callback: callback function 220 | :return: 221 | """ 222 | _timeout = { 223 | "connect_timeout": timeout[0], 224 | "request_timeout": timeout[1], 225 | } 226 | 227 | if isinstance(data, dict): 228 | data = urlencode(data) 229 | 230 | req = HTTPRequest(url, "POST", HEADERS, utf8(data), **_timeout) 231 | resp = yield AsyncHTTPClient().fetch(req, callback, raise_error=False) 232 | raise tornado.gen.Return(resp) 233 | 234 | 235 | __all__ = ["KdNiaoClient", "KdNiaoAsyncClient"] 236 | -------------------------------------------------------------------------------- /kdniao/client_aio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ 4 | client, 同步、异步 5 | """ 6 | import logging 7 | import asyncio 8 | import aiohttp 9 | from asyncio import TimeoutError 10 | from kdniao.client import KdNiaoClient 11 | from kdniao.conf import HEADERS, DEFAULT_HTTP_RESP 12 | 13 | 14 | class KdNiaoAsyncIOClient(KdNiaoClient): 15 | """ 16 | Non-Blocking Client by asyncio 17 | 18 | Warning::: A ValueError will be raised if you are calling this in python < 3.4 19 | 20 | """ 21 | 22 | def __init__(self, app_id, app_key, is_prod=True): 23 | """ 24 | init 25 | :param int app_id: app id 26 | :param str app_key: app key 27 | :param bool is_prod: env type, prod or test, default is True 28 | """ 29 | super(KdNiaoAsyncIOClient, self).__init__(app_id, app_key, is_prod=is_prod) 30 | 31 | @asyncio.coroutine 32 | def _request(self, rtype, data, **kwargs): 33 | """ 34 | prepare and send request 35 | :param str rtype: 36 | :param data: 37 | :param timeout: 38 | :return: 39 | """ 40 | timeout = kwargs.get("timeout") 41 | timeout = self._parese_time_out(timeout) 42 | if isinstance(timeout, (list, set, tuple)): 43 | timeout = timeout[0] 44 | 45 | url = self._gen_api_url(rtype) 46 | rdata = self._prepare_req_params(rtype, data) 47 | 48 | try: 49 | res = yield from self._post(url, rdata, timeout) 50 | except TimeoutError as exc: 51 | logging.error("timeout while request kdniao API: %s", exc, exc_info=True) 52 | res = DEFAULT_HTTP_RESP() 53 | res.url = url 54 | res.body = "" 55 | 56 | resp = self._parse_http_resp(res.status, res.reason, res.body, res.url) 57 | res = self._parse_api_resp(resp) 58 | return res 59 | 60 | @classmethod 61 | @asyncio.coroutine 62 | def _post(cls, url, data, timeout=None, callback=None): 63 | """ 64 | :param str url: URL 65 | :param dict data: request parms 66 | :param tuple timeout: tuple or an int object 67 | :param function callback: callback function 68 | :return: 69 | """ 70 | with aiohttp.ClientSession() as session: 71 | res = yield from session.post(url, data=data, headers=HEADERS, timeout=timeout) 72 | body = yield from res.read() 73 | setattr(res, "body", body) 74 | return res 75 | 76 | 77 | __all__ = ["KdNiaoAsyncIOClient"] 78 | -------------------------------------------------------------------------------- /kdniao/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ 4 | API 相关配置 5 | """ 6 | from kdniao.mdutils import urljoin 7 | from kdniao import __version__ 8 | 9 | 10 | class KdNiaoConfig(object): 11 | DEFAULT_TIMEOUT = (10, 10) # seconds 12 | 13 | 14 | HEADERS = { 15 | "Accept": "application/x-www-form-urlencoded;charset=utf-8", 16 | "Accept-Encoding": "utf-8", 17 | "'User-Agent'": "github.com/menduo/kdniao_python %s" % __version__, 18 | } 19 | 20 | 21 | class DEFAULT_HTTP_RESP(object): 22 | code = 599 23 | status = 599 24 | url = None 25 | reason = "timeout" 26 | body = "" 27 | 28 | 29 | class APIPath(object): 30 | """ 31 | api path 32 | """ 33 | OrderCreate = "/api/oorderservice" # 预约取件接口 1001 34 | EOrderService = "/api/EOrderService" # 电子面单 1007 35 | EbusinessOrderHandle = "/Ebusiness/EbusinessOrderHandle.aspx" # 物流轨迹(即时查询)1002, 单号识别 2002 36 | DIST = "/api/dist" # 物流轨迹(订阅查询) 1008 37 | ExRecommend = "/api/dist" # 智选物流 2001 38 | SafeNumber = "/api/apiservice" # 安全号码接口 3001, 39 | UserReg = "/api/reg" # 用户注册 9001, 40 | Agency = "/api/agencyfund" # 代理商接口 41 | UserUpdate = "/api/agencyfund" # 更新用户 cmd1002, 42 | UserGet = "/api/agencyfund" # 更新用户 cmd1003, 43 | 44 | 45 | # 指令类型与 API 网址映射 46 | RequestTypeAPIPathMap = { 47 | "1001": APIPath.OrderCreate, # 预约取件接口 1001 48 | "1002": APIPath.EbusinessOrderHandle, # 物流轨迹(即时查询)1002 49 | "1007": APIPath.EOrderService, # 电子面单 1007 50 | "1008": APIPath.DIST, # 物流轨迹(订阅查询) 1008 51 | "2001": APIPath.EbusinessOrderHandle, # 智选物流 2001 52 | "2002": APIPath.EbusinessOrderHandle, # 单号识别 2002 53 | "8001": APIPath.EbusinessOrderHandle, # 在途监控 8001 54 | "8008": APIPath.ExRecommend, # 订阅(增值版)接口 8008 55 | "3001": APIPath.SafeNumber, # 安全号码接口 3001, 56 | "9001": APIPath.UserReg, # 用户注册 9001, 57 | "cmd1002": APIPath.UserUpdate, # 更新用户 CMD1002, 58 | "cmd1003": APIPath.UserGet, # 查询用户信息 cmd1003, 59 | "cmd1004": APIPath.Agency, # 垫付业务申请CMD1004 60 | "cmd1005": APIPath.Agency, # 直退业务申请CMD1005 61 | "cmd1006": APIPath.Agency, # 普通代收货款申请CMD1006 62 | "cmd1007": APIPath.Agency, # 查询服务申请状态CMD1007, 63 | "cmd1008": APIPath.Agency, # 查询返款银行信息CMD1008 64 | "cmd1009": APIPath.Agency, # 提交返款银行信息CMD1009 65 | "cmd1010": APIPath.Agency, # 获取订单货款状态CMD1010 66 | } 67 | 68 | # 部分接口的API 网址,生产环境和测试环境是一样的 69 | RequestTypeUsingProdHost = { 70 | "1002", # 即时查询 71 | "8001", # 在途监控 72 | } 73 | 74 | 75 | class APIErrorCode(object): 76 | """ 77 | API Reuqest Error Code 78 | """ 79 | Success = 0 80 | Failed = 0 81 | 82 | 83 | class BizRespCode(object): 84 | """ 85 | Biz Response Code 86 | """ 87 | Success = 100 88 | Failed = -100 89 | 90 | 91 | # 生产与测试环境相关配置 92 | class APIPathBuilder(object): 93 | """ 94 | env type 95 | """ 96 | PROD = "api.kdniao.cc" 97 | TEST = "testapi.kdniao.cc:8081" 98 | 99 | @classmethod 100 | def env_list(cls): 101 | """ 102 | env type list 103 | """ 104 | return ["prod", "test"] 105 | 106 | @classmethod 107 | def get_host(cls, env_type): 108 | """ 109 | get host by env type 110 | :param env_type: 111 | :return: 112 | """ 113 | assert env_type in cls.env_list() 114 | return getattr(cls, env_type.upper()) 115 | 116 | @classmethod 117 | def gen_api_url(cls, env_type, req_type): 118 | """ 119 | gen api url by env type and req type 120 | :param str env_type: 121 | :param str req_type: 122 | :return: 123 | """ 124 | if req_type in RequestTypeUsingProdHost: 125 | env_type = "prod" 126 | 127 | host = cls.get_host(env_type) 128 | api_prefix = "http://%s" % host 129 | 130 | api_path = RequestTypeAPIPathMap[req_type] 131 | url = urljoin(api_prefix, api_path) 132 | 133 | return url 134 | -------------------------------------------------------------------------------- /kdniao/mdutils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | import sys 4 | 5 | 6 | def _clean_list(alist): 7 | """ 8 | clean items 9 | :param list alist: a list 10 | :return: 11 | """ 12 | return [str(i).strip() for i in alist if str(i).strip()] 13 | 14 | 15 | # Code from tornado.escape 16 | PY3 = sys.version_info.major == 3 17 | PY34PLUS = PY3 and sys.version_info.minor >= 4 and sys.version_info.micro >= 2 18 | bytes_type = bytes 19 | if PY3: 20 | unicode_type = str 21 | basestring_type = str 22 | from urllib.parse import urlencode, urljoin 23 | else: 24 | unicode_type = unicode 25 | basestring_type = basestring 26 | from urllib import urlencode 27 | from urlparse import urljoin 28 | 29 | _UTF8_TYPES = (bytes, type(None)) 30 | 31 | 32 | def utf8(value): 33 | # type: (typing.Union[bytes,unicode_type,None])->typing.Union[bytes,None] 34 | """Converts a string argument to a byte string. 35 | 36 | If the argument is already a byte string or None, it is returned unchanged. 37 | Otherwise it must be a unicode string and is encoded as utf8. 38 | """ 39 | if isinstance(value, _UTF8_TYPES): 40 | return value 41 | if not isinstance(value, unicode_type): 42 | raise TypeError( 43 | "Expected bytes, unicode, or None; got %r" % type(value) 44 | ) 45 | return value.encode("utf-8") 46 | 47 | 48 | _TO_UNICODE_TYPES = (unicode_type, type(None)) 49 | 50 | 51 | def to_unicode(value): 52 | """Converts a string argument to a unicode string. 53 | 54 | If the argument is already a unicode string or None, it is returned 55 | unchanged. Otherwise it must be a byte string and is decoded as utf8. 56 | """ 57 | if isinstance(value, _TO_UNICODE_TYPES): 58 | return value 59 | if not isinstance(value, bytes): 60 | raise TypeError( 61 | "Expected bytes, unicode, or None; got %r" % type(value) 62 | ) 63 | return value.decode("utf-8") 64 | 65 | 66 | # to_unicode was previously named _unicode not because it was private, 67 | # but to avoid conflicts with the built-in unicode() function/type 68 | _unicode = to_unicode 69 | 70 | # When dealing with the standard library across python 2 and 3 it is 71 | # sometimes useful to have a direct conversion to the native string type 72 | if str is unicode_type: 73 | to_native_str = to_unicode 74 | else: 75 | to_native_str = utf8 76 | 77 | to_str = to_native_str 78 | 79 | _BASESTRING_TYPES = (basestring_type, type(None)) 80 | 81 | 82 | def to_basestring(value): 83 | """Converts a string argument to a subclass of basestring. 84 | 85 | In python2, byte and unicode strings are mostly interchangeable, 86 | so functions that deal with a user-supplied argument in combination 87 | with ascii string constants can use either and should return the type 88 | the user supplied. In python3, the two types are not interchangeable, 89 | so this method is needed to convert byte strings to unicode. 90 | """ 91 | if isinstance(value, _BASESTRING_TYPES): 92 | return value 93 | if not isinstance(value, bytes): 94 | raise TypeError( 95 | "Expected bytes, unicode, or None; got %r" % type(value) 96 | ) 97 | return value.decode("utf-8") 98 | 99 | 100 | def recursive_unicode(obj): 101 | """Walks a simple data structure, converting byte strings to unicode. 102 | 103 | Supports lists, tuples, and dictionaries. 104 | """ 105 | if isinstance(obj, dict): 106 | return dict((recursive_unicode(k), recursive_unicode(v)) for (k, v) in obj.items()) 107 | elif isinstance(obj, list): 108 | return list(recursive_unicode(i) for i in obj) 109 | elif isinstance(obj, tuple): 110 | return tuple(recursive_unicode(i) for i in obj) 111 | elif isinstance(obj, bytes): 112 | return to_unicode(obj) 113 | else: 114 | return obj 115 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | clean: 4 | find . -name '*.pyc' -delete 5 | find . -name '*.py~' -delete 6 | find . -name '*.pyo' -delete 7 | 8 | all: 9 | $(shell echo "what" ) 10 | 11 | default: 12 | $(ls;pwd;) 13 | 14 | register: 15 | python setup.py register -r pypi 16 | 17 | publish: 18 | python setup.py sdist upload -r pypi 19 | 20 | testregister: 21 | python setup.py register -r pypitest 22 | 23 | testpublish: 24 | python setup.py sdist upload -r pypitest 25 | 26 | .PHONY : deploy 27 | deploy: 28 | python setup.py register -r pypi 29 | python setup.py sdist --manifest-only -r pypi 30 | python setup.py sdist --formats zip,gztar upload -r pypi 31 | 32 | md2rst: 33 | pandoc README.md -f markdown -t rst -o README.rst 34 | cp README.rst index.rst 35 | cp README.md docs/README.md 36 | cp README.rst docs/README.rst 37 | cp README.rst docs/index.rst -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | tornado>=4.0.2 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | 快递鸟 Python SDK 5 | """ 6 | from setuptools import setup, find_packages 7 | 8 | repo_url = "https://github.com/menduo/kdniao_python" 9 | __version__ = "0.1.3" 10 | 11 | setup( 12 | name="kdniao", 13 | version=__version__, 14 | keywords=("kdniao", "express", "Express inquiry"), 15 | description="Python SDK for Kdniao", 16 | long_description="see more at:\n%s\n" % repo_url, 17 | license="MIT", 18 | url=repo_url, 19 | author="menduo", 20 | author_email="shimenduo@gmail.com", 21 | packages=find_packages(), 22 | scripts=["bin/kdniao"], 23 | platforms="any", 24 | classifiers=[ 25 | "Intended Audience :: Developers", 26 | "License :: OSI Approved :: MIT License", 27 | "Operating System :: OS Independent", 28 | "Programming Language :: Python", 29 | "Programming Language :: Python :: 2", 30 | "Programming Language :: Python :: 2.6", 31 | "Programming Language :: Python :: 2.7", 32 | "Programming Language :: Python :: 3", 33 | "Programming Language :: Python :: 3.3", 34 | "Programming Language :: Python :: 3.4", 35 | "Programming Language :: Python :: 3.5", 36 | "Programming Language :: Python :: 3.6", 37 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 38 | "Topic :: Software Development :: Libraries :: Python Modules" 39 | ], 40 | install_requires=["requests", "tornado>=4.0.2"], 41 | ) 42 | -------------------------------------------------------------------------------- /test/test_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | -------------------------------------------------------------------------------- /test/test_recognise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | -------------------------------------------------------------------------------- /test/test_track.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | -------------------------------------------------------------------------------- /test/test_users.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | --------------------------------------------------------------------------------