开发指南
一:请求协议说明:
调用流程说明
合作方调用配送开放平台API,需要按照以下步骤:
填充参数>生成签名>拼装HTTP请求>发起HTTP请求>得到HTTP响应>解析json
二:接口协议
规则 | 说明 |
---|---|
测试环境请求地址 | http://open.s.bingex.com |
生产环境请求地址 | https://open.ishansong.com |
传输协议 | http |
参数格式 | application/x-www-form-urlencoded |
字符编码 | utf-8 |
请求方式 | POST |
三:返回值规则
规则 | 说明 |
---|---|
数据格式 | application/json |
字符编码 | utf-8 |
数据结构 | {"status": 状态码,"msg": 错误信息, "data": 数据} |
状态码为200代表正常,其它代表不正常。
四:接口入参介绍
闪送开放平台接口参数分为系统参数和业务参数两种,其中系统参数每个接口均需传入,业务参数是否传入取决于具体接口。
1:系统参数:clientId,accessToken,timestamp,sign
2:业务参数:data
请求闪送开放平台接口时,系统参数、业务参数均在body请求体中传入
闪送开放平台接口参数列表:
参数名称 | 参数类型 | 是否必传 | 参数描述 |
---|---|---|---|
clientId | String | yes | App Key(去账户中心->应用信息查看) |
accessToken | String | yes | 授权成功获取 |
timestamp | String | yes | 毫秒级时间戳 |
data | String(必须是json串) | 根据具体业务接口传递 | 业务入参 |
sign | String | yes | 签名 |
五:商户授权
商户授权请参考商户授权
六:接口签名
签名说明
1:为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带签名,开放平台服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。
2:将所有参数按照字典顺序进行排序,排除值为空以及sign字段。
3:将参数按照(App_secret的值+accessToken+"accessToken的值"+"clientId”+App ID的值+"data”+data的值+"timestamp”+timestamp的值)顺序拼接,参数对应的值不存在,参数和值都不进行拼接,参数使用utf-8编码。
4: 将拼接好的字符串进行MD5加密(32位大写加密),得到的MD5加密值最后转为"大写"赋给sign作为请求的参数。
参考方法:
一:accessToken参数为空
appSecret:bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW
clientId=ss6DzHy9GvHB46Jgo
data={"refreshToken":"10cab61c-5bcf-43f9-a120-feee9a24683d"}
timestamp=1631862240982
拼接成的加密串为:bnRtI4HQQB3JsAAcL5kizXBmzQA3iurWclientIdss6DzHy9GvHB46Jgodata{"refreshToken":"10cab61c-5bcf-43f9-a120-feee9a24683d"}timestamp1631862240982
得到的MD5加密值(32位大写加密)为:6966F7A42902CFFB96C49CF496A9CFC6
二:data参数为空
appSecret:bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW
accessToken=70882965-5e51-463f-a746-7bcf1b3f1c34
clientId=ss6DzHy9GvHB46Jgo
timestamp=1631862240982
拼接成的加密串为:bnRtI4HQQB3JsAAcL5kizXBmzQA3iurWaccessToken70882965-5e51-463f-a746-7bcf1b3f1c34clientIdss6DzHy9GvHB46Jgotimestamp1631862240982
得到的MD5加密值(32位大写加密)为:955E0C0DDAF87B9D26E0171BEF65C0D8
三:data参数不为空
appSecret:qBfAr1fAAFRw2Jat4i4yUaqE3Nh3NSw0
accessToken=70882965-5e51-463f-a746-7bcf1b3f1c34
clientId=ss6DzHy9GvHB46Jgo
timestamp=1631862240982
data={"cityId":1101}
拼接成的加密串为:bnRtI4HQQB3JsAAcL5kizXBmzQA3iurWaccessToken70882965-5e51-463f-a746-7bcf1b3f1c34clientIdss6DzHy9GvHB46Jgodata{"cityId":1101}timestamp1631862240982
得到的MD5加密值(32位大写加密)为:sign:56F220DAFDACCD1827544D75EEDEC086
七:代码示例
1、引入类
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.message.BasicNameValuePair;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
2、签名sign参数计算函数
//把字节数组转换成大写md5
public static String bytesToMD5(byte[] input) {
String md5str = null;
try {
//创建一个提供信息摘要算法的对象,初始化为md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
//计算后获得字节数组
byte[] buff = md.digest(input);
//把数组每一字节换成16进制连成md5字符串
md5str = bytesToHex(buff);
} catch (Exception e) {
e.printStackTrace();
}
return md5str.toUpperCase();
}
//把字节数组转成16进位制数
public static String bytesToHex(byte[] bytes) {
StringBuffer md5str = new StringBuffer();
//把数组每一字节换成16进制连成md5字符串
int digital;
for (int i = 0; i < bytes.length; i++) {
digital = bytes[i];
if(digital < 0) {
digital += 256;
}
if(digital < 16){
md5str.append("0");
}
md5str.append(Integer.toHexString(digital));
}
return md5str.toString();
}
3、post请求函数
public static String sendPost(String url, Map<String,Object> params) {
String response = null;
try {
List<NameValuePair> pairs = null;
if (params != null && !params.isEmpty()) {
pairs = new ArrayList<NameValuePair>(params.size());
for (String key : params.keySet()) {
pairs.add(new BasicNameValuePair(key, params.get(key).toString()));
}
}
CloseableHttpClient httpClient = null;
CloseableHttpResponse httpResponse = null;
try {
httpClient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(url);
if (pairs != null && pairs.size() > 0) {
httppost.setEntity(new UrlEncodedFormEntity(pairs, "UTF-8"));
}
httpResponse = httpClient.execute(httppost);
response = EntityUtils
.toString(httpResponse.getEntity());
System.out.println(response);
} finally {
if (httpClient != null) {
httpClient.close();
}
if (httpResponse != null) {
httpResponse.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
4、accessToken参数为空请求接口的代码示例(RefreshToken刷新AccessToken接口为例)
public static void main(String[] args) {
String appSecret = "bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW";
String clientId = "ss6DzHy9GvHB46Jgo";
String data = "{\"refreshToken\":\"10cab61c-5bcf-43f9-a120-feee9a24683d\"}";
Long timestamp = System.currentTimeMillis();
StringBuffer sb = new StringBuffer(appSecret)
.append("clientId").append(clientId)
.append("data").append(data)
.append("timestamp").append(timestamp);
//计算签名
String sign = bytesToMD5(sb.toString().getBytes());
Map<String,Object> map = new HashMap<String,Object>();
map.put("clientId",clientId);
map.put("data", data);
map.put("timestamp",timestamp);
map.put("sign",sign);
//请求接口获取的结果
String res = sendPost("http://open.s.bingex.com/openapi/oauth/refresh_token",map);
}
5、data参数为空请求接口的代码示例(查询开通城市接口为例)
public static void main(String[] args) {
String appSecret = "bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW";
String clientId = "ss6DzHy9GvHB46Jgo";
String accessToken = "70882965-5e51-463f-a746-7bcf1b3f1c34";
Long timestamp = System.currentTimeMillis();
StringBuffer sb = new StringBuffer(appSecret)
.append("accessToken").append(accessToken)
.append("clientId").append(clientId)
.append("timestamp").append(timestamp);
//计算签名
String sign = bytesToMD5(sb.toString().getBytes());
Map<String,Object> map = new HashMap<String,Object>();
map.put("clientId",clientId);
map.put("accessToken", accessToken);
map.put("timestamp",timestamp);
map.put("sign",sign);
//请求接口获取的结果
String res = sendPost("http://open.s.bingex.com/openapi/developer/v5/openCitiesLists",map);
}
6、data参数不为空请求接口的代码示例(查询保单产品接口为例)
public static void main(String[] args) {
String appSecret = "bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW";
String accessToken = "70882965-5e51-463f-a746-7bcf1b3f1c34";
String clientId = "ss6DzHy9GvHB46Jgo";
String data = "{\"cityId\":1101}";
Long timestamp = System.currentTimeMillis();
StringBuffer sb = new StringBuffer(appSecret)
.append("accessToken").append(accessToken)
.append("clientId").append(clientId)
.append("data").append(data)
.append("timestamp").append(timestamp);
//计算签名
String sign = bytesToMD5(sb.toString().getBytes());
Map<String,Object> map = new HashMap<String,Object>();
map.put("accessToken", accessToken);
map.put("clientId",clientId);
map.put("data",data);
map.put("timestamp",timestamp);
map.put("sign",sign);
//请求接口获取的结果
String res = sendPost("http://open.s.bingex.com/openapi/developer/v5/insuranceProductsList",map);
}
7、accessToken和data参数都为空请求接口的代码示例(上传图片接口为例)
public static void main(String[] args) throws IOException {
String appSecret = "bnRtI4HQQB3JsAAcL5kizXBmzQA3iurW";
String clientId = "ss6DzHy9GvHB46Jgo";
Long timestamp = System.currentTimeMillis();
StringBuffer sb = new StringBuffer(appSecret)
.append("clientId").append(clientId)
.append("timestamp").append(timestamp);
//计算签名
String sign = bytesToMD5(sb.toString().getBytes());
//图片文件的路径
File file = new File("/Users/Desktop/WX20211108-164848.png");
FileItem fileItem = new DiskFileItem(
"iss",
Files.probeContentType(file.toPath()),
false,
file.getName(),
(int) file.length(),
file.getParentFile());
IOUtils.copy(new FileInputStream(file), fileItem.getOutputStream());
MultipartFile cMultiFile = new CommonsMultipartFile(fileItem);
System.out.println(cMultiFile.getOriginalFilename());
String fileName = cMultiFile.getOriginalFilename();
CloseableHttpClient client = HttpClients.createDefault();
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("sign", sign, ContentType.create(ContentType.MULTIPART_FORM_DATA.getMimeType(), Charset.defaultCharset()));
builder.addTextBody("clientId", clientId, ContentType.create(ContentType.MULTIPART_FORM_DATA.getMimeType(), "utf-8"));
builder.addTextBody("timestamp", "" + timestamp, ContentType.create(ContentType.MULTIPART_FORM_DATA.getMimeType(), "utf-8"));
builder.addBinaryBody("file", cMultiFile.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);
HttpEntity build = builder.build();
HttpPost httpPost = new HttpPost("http://open.s.bingex.com/openapi/file/v5/fileUpload");
httpPost.setEntity(build);
HttpResponse httpResponse = null;
try {
httpResponse = client.execute(httpPost);
String response = EntityUtils
.toString(httpResponse.getEntity());
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}