一、获取顾客编码及校验码

   https://qiao.sf-express.com/index.html 丰桥系统中申请api接口,获得url(调用地址)、clientCode(顾客编码)、checkword(校验码)。


    url:http://bsp-oisp.sf-express.com/bsp-oisp/sfexpressService


二、XML报文说明

    1.请求XML报文:

    service 属性定义“服务名”;Head元素定义“顾客编码”


<Request service="服务名">

<Head>顾客编码</Head>

<Body>请求数据XML</Body>

    2.响应XML报文:

     Head元素值为“OK”或“ERR”;OK代表交易成功,ERR代表发生系统或业务异常,交易失败;对于批量交易场景,只能为成功/失败,无部分成功/部分失败


<Response service="服务名">

<Head>OK|ERR</HEAD>

<Body>正常响应数据XML</Body>

<ERROR code="NNNN">错误详细信息</ERROR>

三、API接口详情

    1.下订单接口(OrderService):

    ①客户系统向顺丰下发订单 ②为订单分配运单号


/**

 * 获取顺丰下订单接口xml

 * @param params

 * @return

 */

private String getOrderServiceRequestXml(Map<String,String> params){

    //订单号

    String orderNo = params.get("orderNo");

    //收件人

    String receiverName = params.get("receiverName");

    //收件人电话

    String receiverMobile = params.get("receiverMobile");

    //收件人详细地址

    String receiverAddress = params.get("address");

    //商品名称

    String commodityName = params.get("commodityName");

    //商品数量

    String orderNum = params.get("orderNum");



    StringBuilder strBuilder = new StringBuilder();

    strBuilder.append("<Request service='OrderService'>");

    strBuilder.append("<Head>" + clientCode + "</Head>");

    strBuilder.append("<Body>");

    strBuilder.append("<Order").append(" ");

    strBuilder.append("orderid='" + orderNo.toString().trim() + "" + "'").append(" ");

    //返回顺丰运单号

    strBuilder.append("is_gen_bill_no='1'").append(" ");

    //寄件方信息

    strBuilder.append("j_company='" + j_company + "'").append(" ");

    strBuilder.append("j_contact='" + j_contact + "'").append(" ");

    strBuilder.append("j_tel='" + j_tel + "'").append(" ");

    strBuilder.append("j_address='" + j_province+j_city+j_county+j_address + "'").append(" ");

    //收件方信息

    strBuilder.append("d_company='" + d_company + "'").append(" ");

    strBuilder.append("d_contact='" + receiverName.toString().trim() + "'").append(" ");

    strBuilder.append("d_tel='" + receiverMobile.toString().trim() + "'").append(" ");

    strBuilder.append("d_address='" + receiverAddress.toString().trim() + "'").append(" ");

    strBuilder.append(" > ");

    //货物信息

    strBuilder.append("<Cargo").append(" ");

    strBuilder.append("name='" + commodityName + "'").append(" ");

    strBuilder.append("count='" + orderNum.toString() + "'").append(" ");

    strBuilder.append("unit='双'").append(">");

    strBuilder.append("</Cargo>");


    strBuilder.append("</Order>");

    strBuilder.append("</Body>");

    strBuilder.append("</Request>");


    return strBuilder.toString();

}

    2.订单结果查询接口(OrderSearchService):

      因Internet环境下,网络不是绝对可靠,用户系统下订单到顺丰后,不一定可以收到顺丰系统返回的数据,此接口用于在未收到返回数据时,查询下订单(含筛选)接口客户订单当前的处理情况。重复下单时可调用此接口查询下单顺丰单号


/**

 * 获取顺丰订单结果查询接口xml

 * @param params

 * @return

 */

private String getOrderSearchServiceRequestXml(Map<String,String> params){

    String orderNo = params.get("orderNo");

    StringBuilder strBuilder = new StringBuilder();

    strBuilder.append("<Request service='OrderSearchService'>");

    strBuilder.append("<Head>" + clientCode + "</Head>");

    strBuilder.append("<Body>");

    strBuilder.append("<OrderSearch").append(" ");

    strBuilder.append("orderid='" + orderNo.toString().trim() + "" + "'").append(" > ");

    strBuilder.append("</OrderSearch>");

    strBuilder.append("</Body>");

    strBuilder.append("</Request>");

    return strBuilder.toString();

}

    3.路由查询接口(RouteService):

      支持两类查询方式:①根据顺丰运单号查询 ②根据客户订单号查询


/**

 * 获取顺丰路由查询接口xml

 * @param params

 * @return

 */

private String getRouteServiceRequestXml(Map<String,String> params){

    String orderNo = params.get("orderNo");

    StringBuilder strBuilder = new StringBuilder();

    strBuilder.append("<Request service='RouteService'>");

    strBuilder.append("<Head>" + clientCode + "</Head>");

    strBuilder.append("<Body>");

    strBuilder.append("<RouteRequest").append(" ");

    strBuilder.append("tracking_type='2'").append(" ");

    strBuilder.append("tracking_number='" + orderNo.toString().trim() + "" + "'").append(" >");

    strBuilder.append("</RouteRequest>");

    strBuilder.append("</Body>");

    strBuilder.append("</Request>");

    return strBuilder.toString();

}

三、API接口调用

    ①把XML报文与checkword前后连接


    ②把连接后的字符串做MD5编码


    ③把MD5编码后的数据进行Base64编码,此时编码后的字符串即为校验码


//接口url

@Value("${express.sf.url}")

private String url = "http://bsp-oisp.sf-express.com/bsp-oisp/sfexpressService"

/**

 * 顺丰接口

 * @param params

 * @param type  1-下订单接口  2-订单结果查询接口 3-路由查询接口

 * @return

 */

@Override

public SfExpressResponse sfExpressMethod(Map<String,String> params, int type) throws Exception{

    logger.info("进入顺丰接口:params={},type={}", JSONObject.toJSONString(params),type);

    if (type < 1){

        logger.warn("调用接口类型传错");

        return null;

    }

    String requestXml = "";

    String methodName = "";

    if (type == 1){

        //1.获取下单xml

        requestXml = getOrderServiceRequestXml(params);

        methodName = "orderSerivce";

    } else if (type == 2){

        //1.获取订单结果查询xml

        requestXml = getOrderSearchServiceRequestXml(params);

        methodName = "orderSearchService";

    } else if (type == 3){

        //1.获取订单物流路由

        requestXml = getRouteServiceRequestXml(params);

        methodName = "routeService";

    }

    //2.xml+checkword

    String verifyCode = requestXml + checkword;

    //3.MD5加密 + Base64编码

    MessageDigest md5 = MessageDigest.getInstance("MD5");

    md5.update(verifyCode.getBytes("utf8"));

    verifyCode = Base64Util.encode(md5.digest());

    //4.post 请求

    Map<String, Object> toHttpParams = new HashMap<>();

    toHttpParams.put("xml",requestXml);

    toHttpParams.put("verifyCode",verifyCode);

    logger.info("请求顺丰接口"+methodName, toHttpParams.toString());

    String resultStr = HttpExtUtil.doPost(url, toHttpParams);

    logger.info("请求顺丰接口"+methodName+",result="+resultStr);

    SfExpressResponse response = (SfExpressResponse) 

                                               XMLUtil.convertToObject(SfExpressResponse.class,resultStr);

    return response;

}

四、响应XML解析

    综合三个接口的response xml,将SfExpressResponse定义如下(树状结构对象):


/**

 * 顺丰接口response对象

 * author: jing.mn

 * date: 2018/4/9 16:17

 * copyright wonhigh.net.cn

 */

@XmlAccessorType(XmlAccessType.FIELD)

@XmlRootElement(name = "Response")

public class SfExpressResponse implements Serializable {


    private static final long serialVersionUID = 1L;

    //响应状态

    @XmlElement(name = "Head")

    private String Head;

    //响应失败原因

    @XmlElement(name = "ERROR")

    private ERROR ERROR;

    //响应结果

    @XmlElement(name = "Body")

    private Body Body;



    @XmlAccessorType(XmlAccessType.NONE)

    public static class ERROR {

        @XmlAttribute(name = "code")

        private String code;

        @XmlValue

        private String text;


        public String getCode() {

            return code;

        }


        public void setCode(String code) {

            this.code = code;

        }


        public String getText() {

            return text;

        }


        public void setText(String text) {

            this.text = text;

        }

    }


    @XmlAccessorType(XmlAccessType.NONE)

    public static class Body {

        @XmlElement(name = "OrderResponse")

        private OrderResponse OrderResponse;


        @XmlElement(name = "RouteResponse")

        private RouteResponse RouteResponse;


        public SfExpressResponse.OrderResponse getOrderResponse() {

            return OrderResponse;

        }


        public void setOrderResponse(SfExpressResponse.OrderResponse orderResponse) {

            OrderResponse = orderResponse;

        }


        public SfExpressResponse.RouteResponse getRouteResponse() {

            return RouteResponse;

        }


        public void setRouteResponse(SfExpressResponse.RouteResponse routeResponse) {

            RouteResponse = routeResponse;

        }


    }


    @XmlRootElement(name="OrderResponse")

    @XmlAccessorType(XmlAccessType.NONE)

    public static class OrderResponse {

        //订单号

        @XmlAttribute(name = "orderid")

        private String orderId;

        //运单号

        @XmlAttribute(name = "mailno")

        private String mailNo;

        //原寄地区域代码(可用于顺丰电子运单标签打印)

        @XmlAttribute(name = "origincode")

        private String originCode;

        //目的地区域代码(可用于顺丰电子运单标签打印)

        @XmlAttribute(name = "destcode")

        private String destCode;

        //筛单结果:1:人工确认 2:可收派 3:不可以收派

        @XmlAttribute(name = "filter_result")

        private String filterResult;



        public String getOrderId() {

            return orderId;

        }


        public void setOrderId(String orderId) {

            this.orderId = orderId;

        }


        public String getMailNo() {

            return mailNo;

        }


        public void setMailNo(String mailNo) {

            this.mailNo = mailNo;

        }


        public String getOriginCode() {

            return originCode;

        }


        public void setOriginCode(String originCode) {

            this.originCode = originCode;

        }


        public String getDestCode() {

            return destCode;

        }


        public void setDestCode(String destCode) {

            this.destCode = destCode;

        }


        public String getFilterResult() {

            return filterResult;

        }


        public void setFilterResult(String filterResult) {

            this.filterResult = filterResult;

        }

    }


    @XmlRootElement(name="RouteResponse")

    @XmlAccessorType(XmlAccessType.NONE)

    public static class RouteResponse {

        //运单号

        @XmlAttribute(name = "mailno")

        private String mailNo;

        //路由

        @XmlElement(name = "Route")

        private List<Route> Route ;


        public String getMailNo() {

            return mailNo;

        }


        public void setMailNo(String mailNo) {

            this.mailNo = mailNo;

        }


        public List<SfExpressResponse.Route> getRoute() {

            return Route;

        }


        public void setRoute(List<SfExpressResponse.Route> route) {

            Route = route;

        }

    }


    @XmlRootElement(name="Route")

    @XmlAccessorType(XmlAccessType.NONE)

    public static class Route {

        //路由节点发生的时间

        @XmlAttribute(name = "accept_time")

        private String acceptTime;


        //路由节点具体描述

        @XmlAttribute(name = "remark")

        private String remark;


        //路由节点操作码

        @XmlAttribute(name = "opcode")

        private String opcode;


        public String getAcceptTime() {

            return acceptTime;

        }


        public void setAcceptTime(String acceptTime) {

            this.acceptTime = acceptTime;

        }


        public String getRemark() {

            return remark;

        }


        public void setRemark(String remark) {

            this.remark = remark;

        }


        public String getOpcode() {

            return opcode;

        }


        public void setOpcode(String opcode) {

            this.opcode = opcode;

        }

    }


    public String getHead() {

        return Head;

    }


    public void setHead(String head) {

        Head = head;

    }


    public SfExpressResponse.ERROR getERROR() {

        return ERROR;

    }


    public void setERROR(SfExpressResponse.ERROR ERROR) {

        this.ERROR = ERROR;

    }


    public SfExpressResponse.Body getBody() {

        return Body;

    }


    public void setBody(SfExpressResponse.Body body) {

        Body = body;

    }

}