概述
基于网络货运平台沉淀下来的中台能力,面向外部的企业、应用系统提供一套标准的接口服务,根据不同的客户需求,客户可以自主选择需要使用的接口。
适用对象
本文档适用于期望接入网络货运平台做各种数据业务应用的相关工作人员。
接口联调流程
API接口通过https方式对外提供接口服务,遵循API接口规范,发送https请求,支持POST,数据交换接口将验证 API 用户的合法性和安全性,然后提供接口服务,接口数据采用 UTF-8 格式编码。
获取密钥
由接入方向我方人员发起申请,申请成功后将为您提供调用接口所需的appId、appSercet等信息。
编写接口
编写调用API接口的客户端代码,按照接口定义的https调用方式及传输转换后的参数进行调用。
联调测试
完成接口开发后,与网络货运平台进行联调测试,确保接口功能正常。
安全验证
接口验签
申请获取appId和appSecret,每次请求要根据AppId和AppSecret按照一定的规则来生成一套签名,当请求方带着签名值去请求提供方时,提供方就会验证这个签名,只有验证通过才会继续处理。具体的生成规则参照 接口定义 中sign字段生成规则。
IP白名单
申请到appId和appSecrete以后,需要补充接口调用者的外网ip,调用接口时,如果不在指定的IP范围内发起的请求将会被屏蔽。
IP黑名单
如果系统已经识别出某些非法的IP,会把它设置为IP黑名单,在黑名单范围里的IP请求将会被屏蔽。
接口定义
通用接口参数
字段名称 | 参数类型 | 描述 |
---|---|---|
kctrace | String | 随机的32位字符串,每次请求的kctrace值不重复 |
appId | String | 应用id,由网络货运平台分配 |
secretId | String | 密钥id,由网络货运平台获取 |
timestamp | String | 当前系统时间戳,单位为毫秒 |
sign | String | 签名信息,生成规则见下方说明 |
content | FormData | 所有参数统一加密,通过encryptionData进行传输 |
sign生成规则:
- 对参数先按如下顺序拼接字符串(signStr):
apiPath=xxxx&appId=xxx&content=xxx&kctrace=xxx&secret=xxx×tamp=xxx
- 用MD5对signStr生成签名信息:
signInfo = MD5(signStr)
content加密规则:
- 对业务参数data转换成Json串
- 使用AES(AES/CBC/PKCS5Padding)算法对原始的数据整体加密
- 把加密后的二进制转成16进制格式的字符串
请求示例
import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
*
* OpenApi 调用方示例
*
*/
public class OpenApiDemo {
public static void main(String[] args) throws Exception {
OpenApiDemo openApiDemo = new OpenApiDemo();
String response = openApiDemo.callOpenApiTest();
System.out.println("调用OpenApi结果:" + response);
}
/**
* 调用OpenAPi主函数
*
* @return Response Body
* @throws Exception 构造请求密文抛出的异常
*/
public String callOpenApiTest() throws Exception {
long timeStamp = System.currentTimeMillis();
// 1. kctrace生成: 随机的32位字符串,每次请求的kctrace值不重复
String kctrace = UUID.randomUUID().toString().substring(0, 32);
// 2. 请求参数 加密
Map param = MapUtil.builder("key", (Object) "value").build();
String content = OpenApiDemo.encryptAES(JSONUtil.toJsonStr(param), AppInfoConfig.appSecret);
// 3. sign生成
String signOriginStr = String.format("apiPath=%s&appId=%s&content=%s&kctrace=%s&secret=%s×tamp=%s",
this.apiUrlDemo, AppInfoConfig.appId, content, kctrace, AppInfoConfig.appSecret, timeStamp);
// MD5 工具使用了Hutool库提供的
String sign = MD5.create().digestHex(signOriginStr);
String queryParam = String.format("?kctrace=%s&appId=%s&secretId=%s×tamp=%s&sign=%s",
kctrace, AppInfoConfig.appId, AppInfoConfig.secretId, Objects.toString(timeStamp), sign); // 4. 构造请求 并发起调用,这里用了Hutool 库的 Http工具
String url = String.format("%s://%s:%s%s%s", this.protocol, this.host, this.port, this.apiUrlDemo, queryParam);
String responseBody = HttpRequest.post(url).header(Header.USER_AGENT, "Hutool http")//头信息,多个头信息多次调用此方法即可
.header(Header.ACCEPT, "application/json;charset=UTF-8")//头信息
.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded")//头信息
.form("content", content)
.execute()
.body();
System.out.println(responseBody);
return responseBody;
}
/**
* 获取请求参数密文
*
* @param cleartext 原文
* @param dataPassword 秘钥
* @return 密文
*/
private static String encryptAES(String cleartext, String dataPassword) throws Exception {
final byte[] KEY_VI = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
IvParameterSpec zeroIv = new IvParameterSpec(KEY_VI);
SecretKeySpec key = new SecretKeySpec(dataPassword.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, key, zeroIv);
byte[] encryptedData = cipher.doFinal(cleartext.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < encryptedData.length; ++i) {
String hex = Integer.toHexString(encryptedData[i] & 255);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return new String(sb.toString());
}
/**
* APP 相关配置
*/
private static class AppInfoConfig {
/**
* 应用ID
*/
private static String appId = "250409105367890";
/**
* 应用秘钥
*/
private static String appSecret = "3d210f51d9bb2db86f56091b5e883796";
/**
* 秘钥 ID
*/
private static String secretId = "22";
}
}
具体接口
派车单状态回传
接口地址:
/partner/orderVehicle/circulation
请求方式:
POST
请求参数:
字段名称 | 字段类型 | 是否必填 | 描述 |
---|---|---|---|
status | String | 是 | 状态(0-入门禁、1-一次过磅、2-二次过磅、3-出门禁) |
buttId | String | 是 | 蒙马运单号 例如:YD2161023111517075600h |
返回参数:
字段名称 | 字段类型 | 描述 |
---|---|---|
code | String | 编码 |
message | String | 返回信息描述 |
返回示例:
{
"code": "000000200",
"message": "请求成功"
}
(装)卸车单接口
接口地址:
/partner/thirdCommon/reportGrossWeight
请求方式:
POST
请求参数:
字段名称 | 字段类型 | 是否必填 | 描述 |
---|---|---|---|
id | String | 是 | 现场系统提供 |
orderNo | String | 是 | 派车单主键 运单号 例如:YD2161023111517075600h |
plateNo | String | 是 | 车牌号 |
weighTempTime | String | 是 | 毛重时间 格式:2025-01-01 12:00:00 |
weighTempTon | String | 是 | 毛重 |
返回参数:
字段名称 | 字段类型 | 描述 |
---|---|---|
code | String | 编码 |
message | String | 返回信息描述 |
返回示例:
{
"code": "000000200",
"message": "请求成功"
}
完整磅单回传及修改后回传
接口地址:
/partner/thirdCommon/reportTareWeight
请求方式:
POST
请求参数:
字段名称 | 字段类型 | 是否必填 | 描述 |
---|---|---|---|
poundNo | String | 是 | 磅单号 |
grossTime | String | 是 | 毛重时间 |
tareTime | String | 是 | 皮重时间 |
tareWeight | String | 是 | 皮重 |
reportWeight | String | 是 | 净重 |
orderNo | String | 是 | 派车单主键id(运单号) |
weightType | int | 是 | 1: 装车 ; 2:卸车 |
返回参数:
字段名称 | 字段类型 | 描述 |
---|---|---|
code | String | 编码 |
message | String | 返回信息描述 |
返回示例:
{
"code": "000000200",
"message": "请求成功"
}