
| 序号 | 接口名称 | 版本号 | 发布日期 |
|---|---|---|---|
| 1 | 下单接口 | 1.0 | 2021-07-01 |
| 2 | 打印面单接口 | 1.0 | 2021-07-01 |
| 3 | 轨迹订阅回调接口 | 1.0 | 2021-07-01 |
| 4 | 轨迹实时查询接口 | 1.0 | 2021-07-01 |
| 5 | 国际一票多件&速达非专线&海外仓头程下单接口 | 1.0 | 2025-08-22 |
| 6 | 三段码查询接口 | 1.0 | 2021-07-01 |
| 7 | 省市区查询接口 | 1.0 | 2021-07-01 |
| 8 | 推送集包数据接口 | 1.0 | 2021-08-12 |
| 9 | 第三方轨迹推送接口 | 1.0 | 2021-08-17 |
| 10 | 取消订单接口 | 1.0 | 2021-08-26 |
| 11 | 更新订单接口 | 1.0 | 2021-08-30 |
| 12 | 新-省市区查询接口 | 1.0 | 2022-10-26 |
| 13 | 新-公布价查询接口 | 1.0 | 2022-10-26 |
| 14 | 新-运单查询接口 | 1.0 | 2023-08-28 |
协议: HTTP1.1
字符集: UTF-8
请求方式: POST
Content-Type: text/plain
| 参数名称 | 类型 | 用途 |
|---|---|---|
| appCode | String | 用于调用接口时进行验证,需要找速达非对接人获取 |
| secretKey | String | 用于数据加密、解密,生成签名(sgin),需要找速达非对接人获取 |
| timestamp | Long | 由当前时间(精确到毫秒ms)生成的时间戳, 示例:北京时间:2022-04-12 17:48:07,对应的1649756887081 可参考:https://tool.lu/timestamp/,查看当前时间戳; |
| data | String | Body中的传参,是base64转码得到的最终报文 |
注:上面所有接口都需要安全校验,可参考2、3点的示例;
appCode:11111111
secretKey:uYMGr8eU
timestamp:1649823785567
以测试环境“轨迹实时查询接口“为例
注:appCode、secretKey 需要找速达非对接人获取
生成sign(字段签名),签名使用MD5方式对业务data的内容进行签名,原理:md5 (timestamp+secretKey+data),详细解释如下;
假设json格式请求参数:{"mailNoList":["47234208672823"]},secretKey:uYMGr8eU,timestamp:1649823785567,
经过md5加密,得出sign为9a032a6baf9a6884b695382eb1d1e79f
将post请求中Body数据进行加密,原理:采用 DES 加密 CBC 模式 PKCS5Padding 填充及 BASE64 转码。
其中向量字节数组为 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF 。
data为业务请求数据,需要对整个BODY加密后传输。BODY:
{ "data": {"mailNoList":["47234208672823"]}, "sign": "9a032a6baf9a6884b695382eb1d1e79f"}xxxxxxxxxx3x/fO6064nGfCBwgmTnQm6Yr1WIlve2ZjhgaQQuldzUosXxmc4d33vPChswe2j3Deuz6lm++llQ+EjAZ81ThgOD26k3UvYjyz2/hgPVxqjONPI58/MTuQwqoVAGNxg8T调用接口:
POST请求
请求URL:https://uat-api.speedaf.com/open-api/express/track/query?appCode=11111111×tamp=1649823785567
设置Body 为row属性,选择Text,将最终报文传入,如图:

注:可以将以下curl地址复制到postman工具查看上图效果
curl --location --request POST 'https://uat-api.speedaf.com/open-api/express/track/query?appCode=11111111×tamp=1649823785567' \
--header 'Content-Type: text/plain' \
--data-raw '3x/fO6064nGfCBwgmTnQm6Yr1WIlve2ZjhgaQQuldzUosXxmc4d33vPChswe2j3Deuz6lm++llQ+EjAZ81ThgOD26k3UvYjyz2/hgPVxqjONPI58/MTuQwqoVAGNxg8T'
服务器对时间戳和签名进行比对:
- 60秒之前的报文直接丢弃
- 不支持的appCode直接丢弃
- Sign不对的报文直接丢弃
- 请求端的IP如果不在白名单里,直接丢弃。
返回参数统一放入data中,返回对象 data 为加密内容,需解密。
xxxxxxxxxx{ "success":true, "error":null, "data":"YGhaWcpzzQuSWDw1Y8zvOolO8MAggOULZ43B6z8/+uw3CwHB7ehj9EOfszr2xlsuf7cP6eElqFVHPnJvKPWAGrach6JxGduG9pggtk5CjUxqbsie5VZRC5s+h2YQgtSdHhnNyONfsigPVdYcw+AUvWion4gyC++6MzzfMEHjgrLtP87iSBf5YBElO6wIDp9z+UfD/xCxQauWPbLXYkz/uf55DyENxprAO91WifwLQcrDwv6Km3YVQZTZrLSxR4IVnuE+AnavpX+uDhyWQZcJM+CfCS+roZ/jEtrxkQLOwxWl7UayLq0630ssbpb/7RCxCwoi3Gktqfui8j9XWDFKJpWto3j4w5VwDAugO155DYnnOtdbzDGbkbmP0FeldJlCIdWjA6jKB0X+yZatLCIv8FZL71uGxBQX0ZjMdpHMuYbsQEB3uWeWMRxgoF1deyzbHjXIx7UmNlC+sdmJ0C/ycgIer3EHkuj/zPHdr+PSaRcKodo76SHSLhcjj2+yYtqlZfWZwjDxfIKiYKVsQt6fRwXY7tWZfcOuU2S/ijYyQ1mfhwFMzfrxe0rKDdyBZtLqpGHD7NXKTmK4nV40rTznsVb7JHR/7fDajCthsvN2pDjO8wyma6ZVlYI/8Wzb75dTmmiyx6PATMKBZb4BncYIaw7MRXulLGsD1VknK4TWtQlT+NqM3kbbZwr+cVOL8TxvtKav0SBJJR7cU7hcVgJH8BHTdT5G1/IT2Cls9mw2hf5H396qEHlGHYyehOwiP88U3/3iCDcL/Ed/0zSuByV2gMogXSpCWvAhgGEh1ZwXbGIO8oxmTha8/rHb8A9XZ43AcGLtJQeZejvF5FTI+AJkw2Rw+rxuaJPXByTJA0IGpWm8eXo27LEoHGV5jEE4vCGNq56x0f7RkERnJxjUiRno3/OPgLpcxrjZkanCvr3wXaFpjoOWPAd/1Ekx+BDKlAb5cYtZmc4pipg="}data解密,格式如下:
原理:采用 DES 解密 CBC 模式 PKCS5Padding 填充(cipher.DES(secretKey, data))及 BASE64 解码(默认UTF-8编码)
xxxxxxxxxx{ "success":true, "error":null, "data":[ { "mailNo":"754100001", "tracks":[ { "action":"1", "actionName":"Picked Up", "mailNo":"754100001", "message":"【一级网点01】已做收件扫描,收件员是【测试-lqw】", "msgArab":"Received in 【一级网点01】,Pick-Up Courier is【测试-lqw】", "msgBd":"[一级网点01] রসিদটি স্ক্যান করা হয়েছে, এবং প্রাপক হলেন [测试-lqw]", "msgEng":"Received in 【一级网点01】,Pick-Up Courier is【测试-lqw】", "msgFre":"Ramassé par【一级网点01】, Le courrier est 【测试-lqw】", "msgLoc":"Received in 【一级网点01】,Pick-Up Courier is【测试-lqw】", "receiverCountryCode":"BD", "time":"2022-04-12 12:50:52", "timezone":6 } ] } ]}
xxxxxxxxxx { "error": { "code": "code", "message": "msg" }, "success": false }| 参数 | 中文描述 | 类型 | 是否必填 | 备注 |
|---|---|---|---|---|
| data | 出参对象 | String | N | 需解密 |
| success | 是否成功 | boolean | Y | true |
| 参数 | 中文描述 | 类型 | 是否必填 | 备注 |
|---|---|---|---|---|
| error | 错误信息 | Object | Y | 错误对象 |
| success | 是否成功 | boolean | Y | false |
| 参数 | 中文描述 | 类型 | 是否必填 | 备注 |
|---|---|---|---|---|
| code | 错误代码 | String | Y | |
| message | 错误信息 | String | Y |
此为一般错误定义,各接口错误码会在后续接口错误码中体现,若未写则无需关注。
| 响应代码 | 消息 | 备注说明 |
|---|---|---|
| 60001 | request timeout | 请求超时 |
| 60002 | api unauthorized | 接口未授权 |
| 60003 | invalid appCode | 无效appcode |
| 60004 | signature error | 签名错误 |
| 60005 | md5 encryption failed | md5加密失败 |
| 60006 | des initialize failed | des初始化失败 |
| 60007 | des encryption failed | des加密失败 |
| 60008 | des decryption failed | des解密失败 |
| 60009 | method request error | 方法调用失败 |
| 60010 | request data error | 数据请求错误 |
| 60011 | signature is null | 签名不能为空 |
| 10005 | ip address is not in the whitelist | IP不在白名单内 |
xpublic class DesUtils {
/** * 设置向量 */ private static final byte[] DES_IV = {0x12, 0x34, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF}; /** * 加密算法的参数接口,IvParameterSpec是它的一个实现 */ private static AlgorithmParameterSpec iv; private Key key; private final String DES = "DES/CBC/PKCS5Padding"; private DesUtils(String secretKey) throws Exception { byte[] desKey = secretKey.getBytes(StandardCharsets.UTF_8); // 设置密钥参数 DESKeySpec keySpec = new DESKeySpec(desKey); // 设置向量 iv = new IvParameterSpec(DES_IV); // 获得密钥工厂 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); // 得到密钥对象 key = keyFactory.generateSecret(keySpec); } /** * secretKey即为速达非颁发的secretKey */ public static DesUtils setSecretKey(String secretKey) throws Exception { return new DesUtils(secretKey); } /** * 将原始数据转为string之后返回加密结果 */ public String encode(String data) throws Exception { // 得到加密对象Cipher Cipher enCipher = Cipher.getInstance(DES); // 设置工作模式为加密模式,给出密钥和向量 enCipher.init(Cipher.ENCRYPT_MODE, key, iv); byte[] pasByte = enCipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return encodeBase64(pasByte); } /** * 将加密结果解析 */ public String decode(String data) throws Exception { Cipher deCipher = Cipher.getInstance(DES); deCipher.init(Cipher.DECRYPT_MODE, key, iv); byte[] pasByte = deCipher.doFinal(decodeBase64(data)); return new String(pasByte, StandardCharsets.UTF_8); } // base64加密方法 public static String encodeBase64(byte[] bytes) { return Base64.encodeBase64String(bytes); } // base64解密方法 public static byte[] decodeBase64(String base64String) { return Base64.decodeBase64(base64String); }
}xxxxxxxxxx /// <summary /// 进行DES解密。 /// </summary /// <param name="pToDecrypt"要解密的以Base64</param /// <param name="sKey"密钥,且必须为8位。</param /// <returns已解密的字符串。</returns public static string DesDecrypt(string pToDecrypt, byte[] sKey, byte[] siv) { byte[] inputByteArray = Convert.FromBase64String(pToDecrypt); using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) { des.Key = sKey; des.IV = siv; System.IO.MemoryStream ms = new System.IO.MemoryStream(); using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); cs.Close(); } string str = Encoding.UTF8.GetString(ms.ToArray()); ms.Close(); return str; } }
/// <summary /// 进行DES加密。 /// </summary /// <param name="pToEncrypt"要加密的字符串。</param /// <param name="sKey"密钥</param /// <returns以Base64格式返回的加密字符串。</returns public static string DesEncrypt(string pToEncrypt, byte[] sKey, byte[] siv) { using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) { byte[] inputByteArray = Encoding.UTF8.GetBytes(pToEncrypt); des.Key = sKey; des.IV = siv; System.IO.MemoryStream ms = new System.IO.MemoryStream(); using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); cs.Close(); } string str = Convert.ToBase64String(ms.ToArray()); ms.Close(); return str; } } /// <summary /// 获取时间戳 /// </summary /// <returns</returns public static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds).ToString(); } public static string MD5Encrypt(string input) { using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) { byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input); byte[] hashBytes = md5.ComputeHash(inputBytes); StringBuilder sBuilder = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { sBuilder.Append(hashBytes[i].ToString("X2")); } return sBuilder.ToString(); } } 使用参考: byte[] key = Encoding.UTF8.GetBytes(secretKey); byte[] iv = { 0x12, 0x34, 0x56, 0x78, (byte)0x90, (byte)0xAB, (byte)0xCD, (byte)0xEF }; JObject data = new JObject { ["data"] = json, ["sign"] = MD5Encrypt(timestamp + secretKey + json) }; var body = DesEncrypt("data的json字符串报文", key, iv); using (WebClient web = new WebClient()) { web.BaseAddress = reqUrl; web.Encoding = Encoding.UTF8; resStr = web.UploadString(reqUrl, body); } 解密示例: DesDecrypt(res.data, key, iv)xxxxxxxxxx<?php class DesComponent { // 密钥secretkey由速达非提供 var $secretkey = "secretkey"; function encrypt($string) { $ivArray=array(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF); $iv=null; foreach ($ivArray as $element) $iv.=CHR($element);
$size = mcrypt_get_block_size ( MCRYPT_DES, MCRYPT_MODE_CBC ); $string = $this-pkcs5Pad ( $string, $size ); $data = mcrypt_encrypt(MCRYPT_DES, $this-secretkey, $string, MCRYPT_MODE_CBC, $iv);
$data = base64_encode($data); return $data; }
function decrypt($string) {
$ivArray=array(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF); $iv=null; foreach ($ivArray as $element) $iv.=CHR($element);
$string = base64_decode($string); //echo("****"); //echo($string); //echo("****"); $result = mcrypt_decrypt(MCRYPT_DES, $this-secretkey, $string, MCRYPT_MODE_CBC, $iv); $result = $this-pkcs5Unpad( $result ); return $result; }
function pkcs5Pad($text, $blocksize) { $pad = $blocksize - (strlen ( $text ) % $blocksize); return $text . str_repeat ( chr ( $pad ), $pad ); }
function pkcs5Unpad($text) {
$pad = ord ( $text {strlen ( $text ) - 1} );
if ($pad strlen ( $text )) return false;
if (strspn ( $text, chr ( $pad ), strlen ( $text ) - $pad ) != $pad) return false;
return substr ( $text, 0, - 1 * $pad );
}
}
function is_utf8($string) { return preg_match('%^(?: [\x09\x0A\x0D\x20-\x7E] # ASCII | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 )*$%xs', $string); }?
xxxxxxxxxx<?php
require './des.php';
header("Content-Type: text/html;charset=utf-8");
$str_one = <<<HTML { "id" : 123, "name" : "zhangsan", "phone" : "12343232" } HTML;
// 密钥secretkey和客户编码appCode由速达非提供
$appCode = "appCode";
$secretkey = "secretkey";
$timeline = time()."000";
$url = 'https://apis.speedaf.com/open-api/example/getData?timestamp='.$timeline.'&appCode='.$appCode;
$des = new DesComponent();
$des-key = $secretkey;
$arr = array(
"sign"=md5($timeline.$secretkey.$str_one,false),
"data"= $str_one
);
echo json_encode($arr, JSON_UNESCAPED_UNICODE)."<br";
$str_u = $des->encrypt(json_encode($arr, JSON_UNESCAPED_UNICODE));
echo $str_u."<br";
$ch = curl_init();
$header = array('Content-Type: application/json','Content-Length: '.strlen($str_u));
curl_setopt($ch, CURLOPT_URL, $url); //设置url
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); //不验证HTTPS证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //不验证HTTPS HOST
//curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //设置开启重定向支持
//curl_redir_exec($ch);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取数据返回
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); // 在启用 CURLOPT_RETURNTRANSFER 时候将获取数据返回
curl_setopt($ch, CURLOPT_POSTFIELDS, $str_u);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($str_u)));
$output = curl_exec($ch); //执行
curl_close($ch);
echo $output;?