快速入门¶
基本概念¶
client_id是对接入餐链开放平台的基本凭证,它代表的是一个第三方开发者对接实体。所有接入餐链开放平台的第三方应用开发商必须要申请。
accessToken是平台商户对第三方应用开发商的授权访问令牌,有此访问令牌才能访问商户的开放资源。
注解
- accessToken访问令牌默认有效期为一年。
- client_secret同client_id是配套的,为了保障安全,请不要泄漏client_secret,如有泄漏,请联系餐链服务人员及时更换。
系统级输入参数¶
API参数共由两部分组成,系统级参数指所有由 餐链 提供的API所必须的参数,应用级参数是指对应API所需的参数。所有编码统一采用UTF-8
参数名 | 类型 | 描述 |
---|---|---|
timestamp | long | 进行接口调用时的时间戳,即当前时间戳,北京时间,单位为秒(s) |
client_id | string | 餐链分配给第三方应用开发商的client_id |
nonce | string | 随机字符串,能代表唯一的一次访问,最好加入client_id和timestamp来生成一个随机字符串 |
accessToken | string | 平台商户对第三方应用开发商授权后获取的访问令牌 |
sign | string | 输入参数计算后的签名结果 |
注解
不论接口是GET、POST、PUT、DELETE等HTTP方法,系统级参数都是以URL参数的方式传递的。
接口调用流程¶
针对每个接口的请求流程如下:
- 根据功能需求确认接口。
- 确认请求参数。
- 计算签名。
- 拼接请求地址。
- 针对不同接口,指定
request_body
、content_type
和请求方法(GET/POST/DELETE/PUT)。
注解
关于request_body的说明:
- 在某些POST或PUT等请求中,需要把json格式的参数体放在请求体(request_body)中。request_body不直接参与签名(sign)计算,但是这时候要先根据request_body生成bodySign参数,生成算法:Hex(SHA-256(request_body+client_secret)),Hex字符大写,得到bodySign。而bodySign需要参与签名(sign)的计算。
- “Content-Type”为”application/json;charset:utf-8;”
签名计算¶
签名计算规则¶
- 对参数名称进行排序得到
字符串1
将所有参数(sign除外)按照参数名的字母顺序排序,并用 &
连接:
accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=6B8C311E537B7C5B0D5E5EECEFE0BF941A262AC24A3744C249D9994DD55A3120×tamp=1497583267
注解
计算sign时需要注意的以下几点:
除特别申明不需要计算的参数之外(如包含在请求体中的参数),其他参数都需要包含在
字符串1
中在某些POST或PUT等请求中,需要把json格式的参数体放在请求体(request_body)中。request_body不直接参与签名(sign)计算,但是这时候要先根据request_body生成bodySign参数,生成算法:Hex(SHA-256(request_body+client_secret)),Hex字符大写,得到bodySign。而bodySign需要参与签名(sign)的计算。所以
字符串1
应该为:accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&bodySign=013A660AC36F14BAE42EC538F7E42E7E7DCEBD1C383F380D6A9C132098C2188F&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=6B8C311E537B7C5B0D5E5EECEFE0BF941A262AC24A3744C249D9994DD55A3120×tamp=1497583267
“Content-Type”为”application/json;charset:utf-8;”
- 计算
字符串2
按照请求url + ? + 字符串1
+ client_secret的顺序进行连接,得到 字符串2
/stores?accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=6B8C311E537B7C5B0D5E5EECEFE0BF941A262AC24A3744C249D9994DD55A3120×tamp=14975832675ea0ac4f-90f5-4136-81ab-615cbca49f34
- 计算签名
对 字符串2
计算SHA-256哈希,得到签名(把字节转换成Hex,签名必须大写):
0B79D9513EB643B678607D7DC1B1676E2EA8B6D177C664F1B21A1D5ABF25EEEA
- 拼接请求URL
将得到的签名赋给sign作为HTTP请求的URL参数:
http://openapi.mealcome.cn/stores?accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=0CA432789B15DEDF98431DE829B1A15BB64CA05AFCCFED17A994D975E92AB43A×tamp=1497583611&sign=0B79D9513EB643B678607D7DC1B1676E2EA8B6D177C664F1B21A1D5ABF25EEEA
- 签名示例
接下来将使用例子示例签名的计算过程。
查询商户的所有门店基础信息(数据均为假数据,只为演示签名过程,以实际调用结果为准):
url: /store
client_id: ae674b84-f266-4785-b37b-228c044be967
client_secret: 5ea0ac4f-90f5-4136-81ab-615cbca49f34
nonce: 6B8C311E537B7C5B0D5E5EECEFE0BF941A262AC24A3744C249D9994DD55A3120
timestamp: 1497583267
accessToken: 93f37d1bcf5b2f022cebc0bc3efd9342
用于计算签名的字符串为:
/store?accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=6B8C311E537B7C5B0D5E5EECEFE0BF941A262AC24A3744C249D9994DD55A3120×tamp=1497583267
请求url为:
http://openapi.mealcome.com/store?accessToken=93f37d1bcf5b2f022cebc0bc3efd9342&client_id=ae674b84-f266-4785-b37b-228c044be967&nonce=0CA432789B15DEDF98431DE829B1A15BB64CA05AFCCFED17A994D975E92AB43A×tamp=1497583611&sign=40EE0C1E5C6D267B1FBD25904652D5AE25C80D7A83B032570FB57632D4D99D9F
response body里的数据为(此为演示数据,实际调用结果不同):
{
"code": 200,
"data": {
"id": 1,
"name": "格林店",
"code": "1",
"pinyin": null,
"address": null,
"longitude": null,
"latitude": null,
"note": null,
"cityName": "",
"proviceName": "",
"tenantId": 1
},
"message": null,
"error": null
}
注解
- URL路径中的参数作为url一部分已参与签名计算,为了避免冗余,不再作为request params的一部分进行签名计算。
名称 | 类型 | 可空 | 描述 |
---|---|---|---|
code | int | 非空 | 返回的调用状态码 |
data | object | 可空 | 返回的业务数据 |
message | string | 可空 | 描述业务非正常处理原因 |
error | string | 可空 | 描述业务非正常处理的错误码 |
注解
string的类型为字符类型(包含所有中英文字符,utf8编码),单个字符其长度都统一为1,字节数以实际存储为准。
注解
目前签名验证比较常见的问题:
- 生成的时间戳在请求当前的时间前后15分钟范围之外
- 签名重复使用
- 时间戳是以秒为单位,也就是10位的时间戳。容易弄错成13位的时间戳
- POST方式的请求需要考虑中文乱码的情况,可能我方接到body中的中文是乱码的,从而导致签名计算跟实际情况不匹配
code码¶
状态码含义:
具体详情见下表。
code | 类型 |
---|---|
200 | 业务正常处理 |
400 | 参数异常 |
500 | 业务异常 |
600 | 未知异常,未检查异常 |
700 | 应用程序错误,已检查异常 |
800 | 框架调用异常 |
访问异常错误返回¶
{
"timestamp": 1495094151006,
"status": 500,
"error": "Internal Server Error",
"exception": "BusinessException",
"message": "javax.servlet.ServletException: BusinessException{errorCode='50003001', errorMessage='该随机字符串无效!'}",
"path": "/units"
}
接口测试信息¶
餐链开放平台请求的基础地址为:
http://120.26.164.191:8092
商家测试¶
餐链提供了以下体验店信息以供接口开发测试
python测试程序¶
# python 3.0以上版本
import http.client
import urllib.parse
import time
import uuid
import hashlib
headers = {"Content-type": "application/json;charset:utf-8;", "Content-Encoding": "UTF-8"}
host = "openapi.mealcome.cn"
clientId = "8fa2bc6957b24a8b8bf3dfcbe3a34c81"
clientSecret = "79753124b23147fb82309b0ed9e14533"
accessToken = "7c89d63940654f93988639532ce2c447"
def base():
mytime = int(time.time())
base_params = {"client_id": clientId, "accessToken": accessToken,
"timestamp": str(mytime), "nonce": uuid.uuid1()}
return base_params
def get(url, params):
base_params = base()
if params is not None:
urlParams = dict(base_params, **params)
else:
urlParams = base_params
# 排序
urlParams = sorted(urlParams.items(), key=lambda dict: dict[0])
# 创建签名字符串并生成签名
url = url + "?" + urllib.parse.urlencode(urlParams, False, "", "utf-8", None, urllib.parse.quote)
md = (urllib.parse.unquote(url) + clientSecret).encode("utf-8")
sign = hashlib.sha256(md).hexdigest()
# 请求接口
request = url + "&sign=" + str(sign).upper()
print("http://" + host + request)
httpClient = http.client.HTTPConnection(host)
httpClient.request("GET", request)
response = httpClient.getresponse()
print(response.read())
if __name__ == '__main__':
get("/store", None)
params = {"begin": "2017-11-04 10:00:00", "end": "2017-11-05 11:00:00"}
get("/material/changes", params)
接口特别说明¶
- 所有涉及到地址位置坐标的,均使用的是百度坐标系