快递100接口demo改良版Kuaidi100Api V6.0 (导入、打印...)

文档说明

原v6版本demo代码文件由快递100的qq技术群提供,本人在搬运之后做了适当的修改并贡献给大家。

需求说明

  1. 用户在公司的平台上 下寄件单,平台分为2个,小程序创建和后台系统上传;
  2. 在系统为其默认推荐一个快递公司、并且创建好寄件单之后,需同步(上传)至快递100,然后可以在快递100的系统上查看到我们的寄件单;
  3. 在公司系统上,可以查看到用户所下的寄件单以及同步状态,可为寄件单更换快递公司,并向快递100申请打印。

改良说明

  1. 本文针对的版本为API_V6.0 ,demo也是再次基础上修改的,语言是java;
  2. 原API直接将参数(如appKey、appSecret、redirectUri等等)写死,我是将其保存为快递账号的表存到数据库中的(这样可以同时使用多个账号,不同用户可归类至不同账号);
表名表含义包含信息
Kuaidi100快递100账号表appKey、appSecret、redirectUri等
SendExpressList寄件单表寄件人、收件人等信息
SendExpressListDetail寄件单详情表多个物品的重量、价格、名称等
ExpressCompany快递公司表公司名称、公司编码等

Kuaidi100Api

package ***.util;
    
import ***.entity.Kuaidi100;
import ***.entity.SendExpressList;
import ***.service.ExpressCompanyService;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.annotation.Resource;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 快递100 Api
 * @author sgambler
 *
 */
public class Kuaidi100Api {
	
	
	@Resource
	private static ExpressCompanyService expressCompanyService;

    static {
        try {
            SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
            sslcontext.init(null, new TrustManager[]{new MyX509TrustManager()}, new java.security.SecureRandom());
            HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
                public boolean verify(String s, SSLSession sslsession) {
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 转换寄件单信息为传入快递100的data
     * @param se 寄件单
     * @return
     */
    public static String parseSendExpressListToOrderData(SendExpressList se){
    	String orderData = "{";
		orderData += "\"recMobile\":\"" + se.getReceiverPhone() + "\",";
		orderData += "\"recName\":\"" + se.getReceiverName() + "\",";
		orderData += "\"recAddr\":\"" + se.getReceiverFullAddress() + "\",";
		orderData += "\"sendTel\":\"" + se.getSendPhone() + "\",";
		orderData += "\"sendName\":\"" + se.getSendName() + "\",";
		orderData += "\"sendAddr\":\"" + se.getSendAddress() + "\",";
		orderData += "\"orderNum\":\"" + se.getSendExpressListSn() + "\",";
		orderData += "\"weight\":\"" + se.getTotleWeight() + "\",";
		orderData += "\"cargo\":\"" + se.getSendExpressListDetails().iterator().next().getItemName() + "\",";
		orderData += "\"kuaidiCom\":\"" + se.getExpressCompany().getKuaidiCom() + "\"";	//公司编码
		orderData += "}";
		return orderData;
    }


    /**
     * 校验回调信息的sign
     *
     * @param callbackData 回调的参数数据
     * @return 校验是否通过
     */
    public static boolean callbackVerify(Map<String, String> callbackData, Kuaidi100 kuaidi100) {
    	String appSecret = kuaidi100.getAppSecret();
        callbackData = transformToTreeMap(callbackData);
        StringBuilder sbd = new StringBuilder(appSecret);
        for (Map.Entry<String, String> entry : callbackData.entrySet()) {
            if (!"sign".equals(entry.getKey())) {
                sbd.append(entry.getKey()).append(entry.getKey());
            }
        }
        sbd.append(appSecret);
        return new MD5().encode(sbd.toString()).equals(callbackData.get("sign"));
    }

    /**
     * 生成授权地址
     *
     * @param state state
     * @return 授权地址
     * @throws UnsupportedEncodingException
     */
    public static String authorize(String state, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
    	String redirectUri = kuaidi100.getRedirectUri();
        String timestamp = String.valueOf(System.currentTimeMillis());
        state = null == state ? "" : state;
        Map<String, String> data = new TreeMap<>();
        data.put("client_id", appKey);
        data.put("response_type", "code");
        data.put("redirect_uri", redirectUri);
        data.put("state", state);
        data.put("timestamp", timestamp);
        String sign = generateSign(data, kuaidi100);
        return "https://b.kuaidi100.com/open/oauth/authorize?response_type=code&client_id=" + appKey + "&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8") + "&state=" + state + "&timestamp=" + timestamp + "&sign=" + sign;
    }

    /**
     * 用授权得到的code换取accessToken
     *
     * @param code 临时凭证
     * @return
     */
    public static String accessToken(String code, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
    	String appSecret = kuaidi100.getAppSecret();
    	String redirectUri = kuaidi100.getRedirectUri();
        if (null == code || code.length() < 1) {
            throw new RuntimeException("无效参数");
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("client_id", appKey);
        data.put("client_secret", appSecret);
        data.put("grant_type", "authorization_code");
        data.put("code", code);
        data.put("redirect_uri", redirectUri);
        data.put("timestamp", timestamp);
        data.put("sign", generateSign(data, kuaidi100));
        System.out.println(data);
        return httpsRequest("https://b.kuaidi100.com/open/oauth/accessToken", data);
    }

    /**
     * 刷新accessToken
     *
     * @param refreshToken 刷新token
     * @return
     */
    public static String refreshToken(String refreshToken, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
    	String appSecret = kuaidi100.getAppSecret();
        if (null == refreshToken || refreshToken.length() < 1) {
            throw new RuntimeException("refreshToken为无效参数");
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("client_id", appKey);
        data.put("client_secret", appSecret);
        data.put("grant_type", "refresh_token");
        data.put("refresh_token", refreshToken);
        data.put("timestamp", timestamp);
        data.put("sign", generateSign(data, kuaidi100));
        return httpsRequest("https://b.kuaidi100.com/open/oauth/refreshToken", data);
    }

    /**
     * 导入订单
     *
     * @param accessToken 身份凭证
     * @param orderData   订单信息
     * @return
     */
    public static String send(String accessToken, String orderData, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
        if (null == accessToken || accessToken.length() < 1
                || null == orderData || orderData.length() < 1) {
            throw new RuntimeException("无效参数");
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("appid", appKey);
        data.put("access_token", accessToken);
        data.put("data", orderData);
        data.put("timestamp", timestamp);
        data.put("sign", generateSign(data, kuaidi100));
        return httpsRequest("https://b.kuaidi100.com/v6/open/api/send", data);
    }

    /**
     * 修改订单
     *
     * @param accessToken 身份凭证
     * @param orderData   订单信息
     * @return
     */
    public static String update(String accessToken, String orderData, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
        if (null == accessToken || accessToken.length() < 1
                || null == orderData || orderData.length() < 1) {
            throw new RuntimeException("无效参数");
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("appid", appKey);
        data.put("access_token", accessToken);
        data.put("data", orderData);
        data.put("timestamp", timestamp);
        data.put("sign", generateSign(data, kuaidi100));
        return httpsRequest("https://b.kuaidi100.com/v6/open/api/update", data);
    }

    /**
     * 快速打印
     *
     * @param accessToken 身份凭证
     * @param printList   订单号列表
     * @return 打印订单的地址
     */
    public static String quickPrint(String accessToken, String printList, Kuaidi100 kuaidi100) {
    	String appKey = kuaidi100.getAppKey();
        if (null == accessToken || accessToken.length() < 1
                || null == printList || printList.length() < 1) {
            throw new RuntimeException("无效参数");
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("appid", appKey);
        data.put("access_token", accessToken);
        data.put("printlist", printList);
        data.put("timestamp", timestamp);
        String sign = generateSign(data, kuaidi100);
        return "https://b.kuaidi100.com/v6/open/api/print?appid=" + appKey + "&access_token=" + accessToken + "&printlist=" + printList + "&timestamp=" + timestamp + "&sign=" + sign;
    }

    /**
     * 自动打印
     *
     * @param accessToken 身份凭证
     * @param printList   订单号列表
     * @return
     */
    public static String autoPrint(String accessToken, String printList, Kuaidi100 kuaidi100) throws Exception {
    	String appKey = kuaidi100.getAppKey();
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> data = new TreeMap<>();
        data.put("appid", appKey);
        data.put("access_token", accessToken);
        data.put("printlist", printList);
        data.put("timestamp", timestamp);
        data.put("sign", generateSign(data, kuaidi100));
        return httpsRequest("https://b.kuaidi100.com/v6/open/api/autoPrint", data);
    }

    /**
     * 生成签名
     *
     * @param data 请求数据
     * @return 签名
     */
    private static String generateSign(Map<String, String> data, Kuaidi100 kuaidi100) {
    	String appSecret = kuaidi100.getAppSecret();
        data = transformToTreeMap(data);
        StringBuilder sbd = new StringBuilder(appSecret);
        for (Map.Entry<String, String> entry : data.entrySet()) {
            sbd.append(entry.getKey()).append(entry.getValue());
        }
        sbd.append(appSecret);
        return new MD5().encode(sbd.toString());
    }

    /**
     * 把map转换为treeMap
     *
     * @param map 任意类型的map
     * @return treeMap
     */
    private static Map<String, String> transformToTreeMap(Map<String, String> map) {
        return map instanceof TreeMap ? map : new TreeMap<>(map);
    }

    /**
     * 发送http请求
     *
     * @param url  请求地址
     * @param data 请求参数
     * @return 接口返回的结果
     */
    private static String httpsRequest(String url, Map<String, String> data) throws Exception {
        URL apiUrl = new URL(url);
        StringBuilder paramSbd = new StringBuilder();
        if (null != data) {
            for (Map.Entry<String, String> entry : data.entrySet()) {
                paramSbd.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        String param = paramSbd.length() > 0 ? paramSbd.substring(0, paramSbd.length() - 1) : "";
        try {
            HttpURLConnection httpConn = (HttpURLConnection) apiUrl.openConnection();
            //设置参数
            httpConn.setDoOutput(true);
            httpConn.setDoInput(true);
            httpConn.setUseCaches(false);
            httpConn.setRequestMethod("POST");

            //设置请求属性
            httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            httpConn.setRequestProperty("Charset", "UTF-8");
            httpConn.setRequestProperty("Accept", "application/json;charset=UTF-8");


            //建立输入流,向指向的URL传入参数
            OutputStream out = httpConn.getOutputStream();
            out.write(param.getBytes("UTF-8"));
            out.flush();
            out.close();

            //获得响应状态
            int resultCode = httpConn.getResponseCode();
            if (HttpURLConnection.HTTP_OK == resultCode) {
                StringBuilder sbd = new StringBuilder();
                String readLine;
                BufferedReader responseReader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
                while ((readLine = responseReader.readLine()) != null) {
                    sbd.append(readLine).append("\n");
                }
                responseReader.close();
                System.out.println(sbd.toString());
                return sbd.toString();
            } else {
                throw new RuntimeException("http请求失败:" + httpConn.getResponseCode());
            }
        } catch (Exception e) {
            throw new RuntimeException("http请求失败", e);
        }
    }

    static class MD5 {
        // 获得MD5摘要算法的 MessageDigest 对象
        private MessageDigest _mdInst = null;
        private char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

        private MessageDigest getMdInst() {
            if (_mdInst == null) {
                try {
                    _mdInst = MessageDigest.getInstance("MD5");
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
            return _mdInst;
        }

        String encode(String s) {
            try {
                byte[] btInput = s.getBytes();
                // 使用指定的字节更新摘要
                getMdInst().update(btInput);
                // 获得密文
                byte[] md = getMdInst().digest();
                // 把密文转换成十六进制的字符串形式
                int j = md.length;
                char str[] = new char[j * 2];
                int k = 0;
                for (byte byte0 : md) {
                    str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                    str[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(str);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }

    static class MyX509TrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate certificates[], String authType) {
        }
        @Override
        public void checkServerTrusted(X509Certificate[] ax509certificate, String s) {
        }
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }
    
    /**
     * 日志
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("Kuaidi100 java sdk");
        return logger;
    }
    
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);
        return documentBuilderFactory.newDocumentBuilder();
    }
}

总结

此篇只有直接对接快递100的接口api,关于具体业务调用以及回调接口见下章。

如有侵扰,请私信。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值