最近项目中需要用到查询快递信息的功能,像各个快递公司查询快递一样,显示时间节点和地点,即路由信息(轨迹)。最简单省事的方法自然是云市场买接口次数,但本着能白嫖就白嫖的原则,自然是要使用免费的。
一、准备工作
1. 进入[顺丰开放平台](https://open.sf-express.com/)注册账号并登录。
2. 进入控制台——开发者对接——认证(**第一次使用需要认证**,个人认证的话只需要身份证即可)
3. 认证完成后进入控制台——开发者对接——新建应用,新建后点击关联API,选择路由查询接口

4. 保存**顾客编码**、**沙箱校验码**、**生产校验码**,后续会使用,若忘了保存可在开发者对接中点击应用详情查看
二、测试
1. 进入沙箱工具——API测试工具,选择**沙箱环境**,输入**沙箱校验码**,选择**API名称**(即刚刚关联的路由API)
2. 提交测试
3. 滚动屏幕,下面有API请求结果,出现以下结果,说明测试成功

4. 回到顶部,点击接口文档,找到[路由查询接口接口-速运类API](https://open.sf-express.com/Api/ApiDetails?level3=397&interName=%E8%B7%AF%E7%94%B1%E6%9F%A5%E8%AF%A2%E6%8E%A5%E5%8F%A3-EXP_RECE_SEARCH_ROUTES)
5. 根据这些参数,我们来组装POST请求,可以使用SDK也可不使用
非SDK方式
通过HuTool工具类发送请求,其中的ExpressConstants是自己建的常量类,里面定义了顺丰的顾客编码、校验码之类的。若是只是在自己的项目中使用顺丰,则没必要另外建了,直接用字符串即可。
若不使用HuTool工具类也可,根据以下代码自行修改
沙箱环境地址和生产环境地址在API-SDK的开发文档
1 2 3 4
| private static final String CALL_URL_BOX = "https://sfapi-sbox.sfexpress.com/std/service";
private static final String CALL_URL_PROD = "https://sfapi.sfexpress.com/std/service";
|
引入HuTool工具类
1 2 3 4 5 6
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.11</version> </dependency>
|
编写实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| import cn.hutool.core.lang.UUID; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSONObject; import com.erp.admin.service.ExpresService; import com.erp.common.constant.ExpressConstants; import com.erp.common.core.domain.AjaxResult; import com.erp.common.exception.ServiceException; import com.erp.common.utils.DateUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern;
@Slf4j @Service public class ExpresServiceImpl implements ExpresService {
@Override public AjaxResult queryRoutes(String expressNum,String phoneNumber) throws IOException, GeneralSecurityException { if (expressNum == null || expressNum.isEmpty() || phoneNumber== null || phoneNumber.isEmpty()){ throw new ServiceException("运单号/手机号不能为空!"); } String result; String sfRegex = ExpressConstants.SF_CODE_RULE; Pattern sfPattern = Pattern.compile(sfRegex, Pattern.CASE_INSENSITIVE); Matcher sfMatcher = sfPattern.matcher(expressNum); if(sfMatcher.matches()){ result = querySFExpress(expressNum, phoneNumber); Map routeRespsMap = (Map) JSONObject.parseObject(result).getJSONArray("routeResps").get(0); List<Map> routesList = (List<Map>)routeRespsMap.get("routes"); if (routesList == null || routesList.isEmpty()){ throw new ServiceException("运单号或手机号不正确!"); } routesList.sort(Comparator.comparing(route -> String.valueOf(route.getOrDefault("acceptTime", 0).toString()), Comparator.reverseOrder())); return AjaxResult.success("SF",routesList); }else { throw new ServiceException("运单号或手机号不正确!"); } }
private String querySFExpress(String expressNum,String phoneNumber) throws NoSuchAlgorithmException { String timeStamp = DateUtils.parseDateToStr("yyyyMMddHHmmssSSS",new Date()); JSONObject msgDataJson = new JSONObject(); msgDataJson.put("language","0"); msgDataJson.put("trackingType","1"); msgDataJson.put("trackingNumber",expressNum); msgDataJson.put("methodType","1"); msgDataJson.put("checkPhoneNo",phoneNumber); String msgData = JSONUtil.toJsonStr(msgDataJson); String encryptStr; try { encryptStr = URLEncoder.encode(msgData + timeStamp + ExpressConstants.SF_CHECK_WORD_PROD, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(encryptStr.getBytes(StandardCharsets.UTF_8)); String msgDigest = (new Base64()).encodeAsString(md5.digest());
HttpRequest get = HttpUtil.createPost(ExpressConstants.SF_CALL_URL_BOX); Map<String,String> headers = new HashMap<>(); headers.put("appCode", ExpressConstants.SF_CLIENT_CODE); headers.put("timestamp", timeStamp); headers.put("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); get.addHeaders(headers); get.form("partnerID",ExpressConstants.SF_CLIENT_CODE) .form("requestID", UUID.randomUUID().toString().replace("-", "")) .form("serviceCode", "EXP_RECE_SEARCH_ROUTES") .form("timestamp", timeStamp) .form("msgData", msgData) .form("msgDigest",msgDigest); String body = get.execute().body();
JSONObject msgDataResult = JSONObject.parseObject(body) .getJSONObject("apiResultData") .getJSONObject("msgData"); return msgDataResult.toString(); } }
|
常量类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class ExpressConstants { public static final String SF_CLIENT_CODE = "******"; public static final String SF_CHECK_WORD_PROD = "**********"; public static final String SF_CHECK_WORD_DEV = "***********"; public static final String SF_CALL_URL_BOX = "*********************"; public static final String SF_CALL_URL_PROD = "********************"; public static final String SF_CODE_RULE = "^[A-Za-z0-9-]{4,35}$"; }
|
SDK方式
1. 进入[丰桥API-SDK(JAVA)使用说明](https://open.sf-express.com/developSupport/976720?activeIndex=399112)
2. 下载SDK
3. 根据PDF文档编写代码
4. 主要问题是jar包不好引入,我是用的是idea,每次引入完之后刷新maven就需要重新引入,打包时也打不进去,网上方法找遍了,都不好使,只能[手动将外部jar包引入本地仓库](https://blog.csdn.net/qq_40093289/article/details/110822523)
三、上线
API需要先完成接口配置后才可以联调测试,API在沙箱环境7天内成功调用三次以上才可申请上线
在沙箱调用成功三次之后就可以点击开发者对接—查看API,点击后面的上线,即可完成上线。
四、Tips
- 顺丰API可以查询任何人的快递信息,另外有调用次数,生产环境500000次/日,测试环境2000次/日
- 手机号是收件人或寄件人的后四位,两个手机号都可以查到
- 若公司有月结卡号的话,可以绑定月结卡号,这样就只需要物流单号就可以查询了,但必须是用月结下的单才可以。