调用第三方快递查询接口实现快递查询

简介: 前两天需要做一个快递查询的需求,由于也是第一次做,完全不懂,但是还是明白这必定是需要调用外部接口来实现,并且应该不是调用快递公司所给的接口,那样一家家对接很麻烦,于是便上网查询了能够提供快递查询接口的公司,国内主流的快递物流查询API接口服务商目前只有三家,分别是菜鸟、快递鸟、快递100,这三家都分别获得了融资,菜鸟是阿里投资的(http://newseed.pedaily.cn/data/invest/40084),为淘系(淘宝和天猫)服务。

前两天需要做一个快递查询的需求,由于也是第一次做,完全不懂,但是还是明白这必定是需要调用外部接口来实现,并且应该不是调用快递公司所给的接口,那样一家家对接很麻烦,于是便上网查询了能够提供快递查询接口的公司,国内主流的快递物流查询API接口服务商目前只有三家,分别是菜鸟、快递鸟、快递100,这三家都分别获得了融资,菜鸟是阿里投资的(http://newseed.pedaily.cn/data/invest/40084),为淘系(淘宝和天猫)服务。快递鸟是中银粤财、东方富海、启赋资本投资的(http://newseed.pedaily.cn/data/invest/41050),为非淘系(拼多多、蘑菇街、网易考拉等)服务。快递100是京东、国信弘盛投资的( https://zdb.pedaily.cn/inv/show12486 ),为京东、百度等服务。类似阿里云这类应用市场平台,入驻这类的平台的很多服务商都是用的快递鸟免费物流接口,快递鸟除快递查询API接口之外,还提供了其他的服务接口(快递鸟API接口-为电商提供最全面的物流api服务),也更加完善,如果想在将来对自己产品的物流模块全流程业务进行功能完善,个人推荐选择快递鸟,快递鸟是全球最大的第三方快递物流接口服务商, 目前已经集成了418家快递单号查询接口,31家电子面单接口。高实时、高稳定、高并发,支持快递单号自动识别。本人公司选择的是快递鸟(https://www.kdniao.com),所以就以快递鸟作为案例进行解说吧。

快递鸟第三方快递查询接口很好用,关键免费,使用的用户很多,有十几个千人技术QQ群为用户免费的技术支持接入服务,整个对接非常简单,到快递鸟网站免费注册申请KEY和ID,下载调用Demo,修改下参数对接后就可以实现快递查询了。

【注册快递鸟账号】快递单号查询接口_电子面单_APIKey授权申请-快递鸟账号注册

快递鸟API key免费注册

注册完成后登入,进入到个人中心,这里需要进行一个实名认证,按公司产品的类型进行一个选择,展示页面如下图

快递鸟实名认证
认证完成后在我的产品服务中选择自己所需要的服务,当然对于初创型公司来说选择免费版就够了,等到业务做大时再选择其他产品服务

还有就是在个人中心中如下图所示,我们需要记住的是用户ID,和API key(不要泄露),这将是我们进行接口调用时需要用到的,

【调用接口文档】快递物流api接口文档下载_电子面单接口文档-快递鸟code接口文档

说完了上面的,接下来我们就开始调用API进行开发吧,打开首页中API文档,选择即时查询,我们可以看到它对改接口的介绍,我们来看看对于参数的说明,下图是在发送请求时所需的参数

快递鸟API参数说明

下图是进行接口请求时需要用的参数,这里需要说明下,每个所提供接口的公司他们对物流公司的编码都不同,可以下载他们提供的编码文档进行查询(http://www.kdniao.com/file/2019快递鸟接口支持快递公司编码.xlsx),避免出现错误,当然快递鸟也有提供根据物流单号查询智能查询快递公司以及编码,下面我会一起详细介绍

下图是请求成功后返回的数据

我们可以下载他们写好的分别是即时查询接口demo(快递鸟「即时查询DEMO」让对接更简单)和单号识别接口demo进行更改,这里就不打开demo了,我直接讲解我修改后的,并且也不讲解前端代码,大家只要根据文档来进行数据传输就可以了,后端代码这里建议大家将以下数据抽取出来单独建一个类,方便以后信息修改:

公共信息

/快递鸟查询快递信息/
public class ShoppingCricleLogisticKey {

//电商ID
public static final String EBusinessID="***";
//电商加密私钥,快递鸟提供,注意保管,不要泄漏
public static final String AppKey="*****";
//根据单号和快递公司编码来查询物流信息接口
public static final String ReqURL="http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
//通过单号识别来查询快递公司接口
public static final String OrderReqURL="http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";

}
controller层

@RestController
@RequestMapping("/api/logistics")
public class LogisticController {

@Autowired
private LogisticService logisticService;//及时查询
@Autowired
private OrderDistinguishService orderDistinguishService;//单号识别

// String shipperCode//物流单号(必须),String logisticCode//快递公司编码(必须)

@PostMapping("/findOne")
public Object findOne(@RequestBody Map<String,Object> params){
    String shipperCode =String.valueOf(params.get("shipperCode"));
    String logisticCode =String.valueOf(params.get("logisticCode"));
    try {
        return JSON.parse(logisticService.getOrderTracesByJson(shipperCode,logisticCode));

    } catch (Exception e) {
        throw new ServiceException("未查到该单号信息,请确认单号信息是否准确");
    }
}
//根据快递单号查询快递公司以及快递公司编码
@GetMapping("/findCompany/{logisticCode}")
public Object findCompany(@PathVariable("logisticCode")String logisticCode){
    try {
        return JSON.parse(orderDistinguishService.getOrderTracesByJson(logisticCode));
    } catch (Exception e) {
        throw new ServiceException("未查到该单号信息,请确认单号信息是否准确");
    }
}

}
由于即时查询接口demo和单号识别接口demo它们中的部分方法有所重复,故对它们重复的方法进行抽取,作为公共的方法:

公共方法类

public class LogisticsUntil {

public static String MD5(String str, String charset) throws Exception {
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(str.getBytes(charset));
    byte[] result = md.digest();
    StringBuffer sb = new StringBuffer(32);
    for (int i = 0; i < result.length; i++) {
        int val = result[i] & 0xff;
        if (val <= 0xf) {
            sb.append("0");
        }
        sb.append(Integer.toHexString(val));
    }
    return sb.toString().toLowerCase();
}
public static String sendPost(String url, Map<String, String> params) {
    OutputStreamWriter out = null;
    BufferedReader in = null;
    StringBuilder result = new StringBuilder();
    try {
        URL realUrl = new URL(url);
        HttpURLConnection conn =(HttpURLConnection) realUrl.openConnection();
        // 发送POST请求必须设置如下两行
        conn.setDoOutput(true);
        conn.setDoInput(true);
        // POST方法
        conn.setRequestMethod("POST");
        // 设置通用的请求属性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("user-agent",
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.connect();
        // 获取URLConnection对象对应的输出流
        out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
        // 发送请求参数
        if (params != null) {
            StringBuilder param = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if(param.length()>0){
                    param.append("&");
                }
                param.append(entry.getKey());
                param.append("=");
                param.append(entry.getValue());
            }
            out.write(param.toString());
        }
        // flush输出流的缓冲
        out.flush();
        // 定义BufferedReader输入流来读取URL的响应
        in = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String line;
        while ((line = in.readLine()) != null) {
            result.append(line);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    //使用finally块来关闭输出流、输入流
    finally{
        try{
            if(out!=null){
                out.close();
            }
            if(in!=null){
                in.close();
            }
        }
        catch(IOException ex){
            ex.printStackTrace();
        }
    }
    return result.toString();
}

public static char[] base64EncodeChars = new char[] {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
        'w', 'x', 'y', 'z', '0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9', '+', '/' };

public static String base64Encode(byte[] data) {
    StringBuffer sb = new StringBuffer();
    int len = data.length;
    int i = 0;
    int b1, b2, b3;
    while (i < len) {
        b1 = data[i++] & 0xff;
        if (i == len)
        {
            sb.append(base64EncodeChars[b1 >>> 2]);
            sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
            sb.append("==");
            break;
        }
        b2 = data[i++] & 0xff;
        if (i == len)
        {
            sb.append(base64EncodeChars[b1 >>> 2]);
            sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
            sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
            sb.append("=");
            break;
        }
        b3 = data[i++] & 0xff;
        sb.append(base64EncodeChars[b1 >>> 2]);
        sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
        sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
        sb.append(base64EncodeChars[b3 & 0x3f]);
    }
    return sb.toString();
}

}
service层

@Service
public class LogisticService {

/**
 * Json方式 查询订单物流轨迹
 * @throws Exception
 */
public String getOrderTracesByJson(String shipperCode, String logisticCode) throws Exception{
    String requestData= "{'OrderCode':'','ShipperCode':'" + shipperCode + "','LogisticCode':'" + logisticCode + "'}";

    Map<String, String> params = new HashMap<String, String>();
    params.put("RequestData", urlEncoder(requestData, "UTF-8"));
    params.put("EBusinessID", ShoppingCricleLogisticKey.EBusinessID);
    params.put("RequestType", "1002");
    String dataSign=encrypt(requestData, ShoppingCricleLogisticKey.AppKey, "UTF-8");
    params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
    params.put("DataType", "2");

    String result=sendPost(ShoppingCricleLogisticKey.ReqURL, params);

    //根据公司业务处理返回的信息......

    return result;
}

/**
 * MD5加密
 * @param str 内容
 * @param charset 编码方式
 * @throws Exception
 */
@SuppressWarnings("unused")
private String MD5(String str, String charset) throws Exception {
    return LogisticsUntil.MD5(str,charset);
}

/**
 * base64编码
 * @param str 内容
 * @param charset 编码方式
 * @throws UnsupportedEncodingException
 */
private String base64(String str, String charset) throws UnsupportedEncodingException{
    String encoded = base64Encode(str.getBytes(charset));
    return encoded;
}

@SuppressWarnings("unused")
private String urlEncoder(String str, String charset) throws UnsupportedEncodingException{
    String result = URLEncoder.encode(str, charset);
    return result;
}

/**
 * 电商Sign签名生成
 * @param content 内容
 * @param keyValue Appkey
 * @param charset 编码方式
 * @throws UnsupportedEncodingException ,Exception
 * @return DataSign签名
 */
@SuppressWarnings("unused")
private String encrypt (String content, String keyValue, String charset) throws UnsupportedEncodingException, Exception
{
    if (keyValue != null)
    {
        return base64(MD5(content + keyValue, charset), charset);
    }
    return base64(MD5(content, charset), charset);
}

/**
 * 向指定 URL 发送POST方法的请求
 * @param url 发送请求的 URL
 * @param params 请求的参数集合
 * @return 远程资源的响应结果
 */
@SuppressWarnings("unused")
public String sendPost(String url, Map<String, String> params) {
    return LogisticsUntil.sendPost(url,params);
}

public static String base64Encode(byte[] data) {
    return LogisticsUntil.base64Encode(data);
}

}

@Service
public class OrderDistinguishService {

public String getOrderTracesByJson(String expNo) throws Exception{
    String requestData= "{'LogisticCode':'" + expNo + "'}";

    Map<String, String> params = new HashMap<String, String>();
    params.put("RequestData", urlEncoder(requestData, "UTF-8"));
    params.put("EBusinessID", ShoppingCricleLogisticKey.EBusinessID);
    params.put("RequestType", "2002");
    String dataSign=encrypt(requestData, ShoppingCricleLogisticKey.AppKey, "UTF-8");
    params.put("DataSign", urlEncoder(dataSign, "UTF-8"));
    params.put("DataType", "2");

    String result=sendPost(ShoppingCricleLogisticKey.OrderReqURL, params);

    //根据公司业务处理返回的信息......

    return result;
}

/**
 * MD5加密
 * @param str 内容
 * @param charset 编码方式
 * @throws Exception
 */
@SuppressWarnings("unused")
private String MD5(String str, String charset) throws Exception {
  return LogisticsUntil.MD5(str,charset);
}

/**
 * base64编码
 * @param str 内容
 * @param charset 编码方式
 * @throws UnsupportedEncodingException
 */
private String base64(String str, String charset) throws UnsupportedEncodingException{
    String encoded = base64Encode(str.getBytes(charset));
    return encoded;
}

@SuppressWarnings("unused")
private String urlEncoder(String str, String charset) throws UnsupportedEncodingException{
    String result = URLEncoder.encode(str, charset);
    return result;
}

/**
 * 电商Sign签名生成
 * @param content 内容
 * @param keyValue Appkey
 * @param charset 编码方式
 * @throws UnsupportedEncodingException ,Exception
 * @return DataSign签名
 */
@SuppressWarnings("unused")
private String encrypt (String content, String keyValue, String charset) throws UnsupportedEncodingException, Exception
{
    if (keyValue != null)
    {
        return base64(MD5(content + keyValue, charset), charset);
    }
    return base64(MD5(content, charset), charset);
}

/**
 * 向指定 URL 发送POST方法的请求
 * @param url 发送请求的 URL
 * @param params 请求的参数集合
 * @return 远程资源的响应结果
 */
@SuppressWarnings("unused")
private String sendPost(String url, Map<String, String> params) {
   return LogisticsUntil.sendPost(url,params);
}

public static String base64Encode(byte[] data) {
    return LogisticsUntil.base64Encode(data);
}

}

这里测试返回的数据大家可以参考API所提供的数据,形式就跟我们在支付宝上查询的物流信息一样,美化的话就需要靠前端了,这里就不给大家展示了。

快递鸟API推送的物流状态

快递鸟API查询的物流动态

目录
相关文章
|
12月前
|
机器学习/深度学习 人工智能 算法
从申请到调用:全国快递物流查询 API 使用教程
面对越来越多的快递需求和快递公司的日益增多,手动查询快递状态的工作变得愈发繁琐。此时,一个全国快递物流查询 API 的出现能够极大地提高查询的效率和准确性,解决人工查询的问题,为用户提供更加便捷的服务体验。全国快递物流查询 API 可以通过接口自动查询快递状态并返回相应信息,同时还支持自动识别快递公司,方便用户快速查询到自己的快递信息。
1277 0
快速简单对接【身份证二要素接口】API接口
很多同学课程中都需要练习API接口对接,这里告知一个免费获取实名认证API接口的途径,也提供简单对接的使用方法。
快速简单对接【身份证二要素接口】API接口
申通快递单号查询api接口免费对接调用
申通物流轨迹查询-使用的物流单号和快递单号即可实现查询物流信息。 目前提供的快递查询接口有免费版和收费版,目前比较常用的是菜鸟和快递鸟接口。 快递鸟接口免费不限量对接 接口规则 (1)、查询接口支持按照运单号查询(单个查询,并发不超过10个/S)。
|
1月前
|
API
1688接口推荐:1688跨境属性数据接口
1688接口推荐:1688跨境属性数据接口
25 0
|
JSON Java API
Java快递单号查询接口怎么接入物流API
Java怎么写物流接口,怎么接入物流接口,如何根据单号查询物流跟踪的详细信息 需求 根据用户输入的订单号,我们的后台识别订单号并根据快递鸟查询快递Api接口,实现自动查询的功能 demo实例 本人自己运行过的Demo —> 点我下载 应用场景(下图) 实现步骤 4.
10369 1
|
10月前
|
JSON 安全 API
【淘宝/天猫】商品快递费用调用API接口封装简介
淘宝/天猫是国内最大的电商平台之一,每天都有数以亿计的人通过淘宝/天猫购买商品,而随着电商业务的不断发展,物流也成为了电商平台中不可或缺的一环。物流的速度和成本直接影响到消费者购物体验以及商家的盈利能力。为了让消费者和商家更好地掌握物流成本,淘宝/天猫提供了淘宝商品快递费用 API调用接口,方便开发者和商家获取淘宝商品的快递费用。
|
11月前
|
数据采集 算法 Java
全国快递物流 API 实现快递单号自动识别的原理解析
全国快递物流 API 实现快递单号自动识别的原理解析
409 0
|
大数据 API
快递物流查询API有什么作用?
随着电商的发展,如今网上购物的人越来越多,频率越来越高,不用出门就能买到自己想要的东西。商品下了单之后商品怎么到自己的手上呢?这就离不开快递和物流了,商家把商品给到快递和物流服务商,快递和物流服务商则把商品运输并配送到我们的手上。除了电商行业之外,在我们生活、工作中处处也离不开快递物流。爸妈给出门在外的子女寄东西需要快递物流;去某个地方,东西太重了不好随身携带,可以寄快递物流送过去;两个公司之间纸质合同、文件、发票寄送也需要走快递物流等等,很多很多地方我们都需要用到快递物流,它也方便了我们的生活、工作。
288 0
快递物流查询API有什么作用?
|
存储 API PHP
PHP快递单号查询接口源码指导 (快递鸟API)
背景 最近进行网站后台开发,需要实现物流的即时查询, 为了方便以后的业务需求,经过比较,最后选择使用第三方快递查询接口服务商 ——【快递鸟】同时,在此进行整理,希望能帮到有需求的道友们,谢谢 ...官方文档: 快递鸟 —— 即时查询API开发框架:ThinkPHP5.1.2 开发步骤 ①. 前期准备首先要进行快递鸟账号的注册 快递单号查询接口_电子面单_APIKey授权申请-快递鸟账号注册 快递鸟API key免费注册 并根据对方的要求,完善用户申请。
3597 0
|
API 数据安全/隐私保护 Python
快递鸟API单号查询接口功能之python
之前写快递的时候,官网上只有php和.net的示例,而我使用python封装的相关功能踩了许多坑,才搞出来,分享一下我的方法。先上图,看结果: 技术文档请参考快递鸟官网api:免费查询快递接口_100%安全保障_物流即时查询API-快递鸟 快递公司编码链接:https://www.
2454 0