顺丰java_Java顺丰同城接口开发
顺丰同城接口
阅读官方文档
首先必须仔细阅读好官方文档
先注册成为开发者,成功后会获得开发者ID(devId)和开发者密钥(devKey)
这里比较重要的是sign签名的生成
签名生成方式如下:
将POST发送的字符串内容进行JSON序列化
使用&连接JSON的数据及dev_id、dev_key
对上步得到的字符串用MD5加密然后Base64计算得到签名密钥
请求的应答为JSON,其中字段可能随未来版本迭代而增加,请Java用户在映射为对象时,若使用Jackson库解析JSON,务必添加 @JsonIgnoreProperties 注解
请求内容必须为UTF-8编码,否则结构解析会失败【下单会报push_time字段错误】
注意:由于JSON没有明确的跨语言规范,因此对JSON解析之后再进行重新编码,并不一定能还原原始JSON字符串。所以请严格按照原始报文进行签名和验签!
文档中还注明了回调接口访问和签名方式,请求为POST方式
工具类的介绍
这里我使用了spring-boot-sfcity项目中的工具类
SignUtil类
首先是SignUtil类的介绍,这个工具类其实实现的功能就是第一步中将生成签名的文字转换为代码。
String toSign = content + "&" + devId + "&" + devKey; // 拼接加密前的字符串
String md5Result = md5(toSign.getBytes(StandardCharsets.UTF_8)); // 进行md5加密
String finalResult = base64Encode(md5Result.getBytes(StandardCharsets.UTF_8)); // 再进行base64计算
最后得到的finalResult就是我们加密后的签名sign
JsonUtil类
ObjectMapper:实现json反序列化为java对象
toObject方法:用于将字符串转换为指定的类型
该工具类中还有其他的方法,但是目前我做的任务中还没有用到,所以暂时就不介绍了。
HttpUtil类
Post方法
post返回值的是Response对象;
需要使用JsonUtil将请求发送的参数对象转为String类型;
HttpHeaders用于生成请求头,HttpEntity用于将headers和请求数据(body)进行整合,用于post请求;
postForEntity:进行post请求,参数是链接、请求体;
返回的结果是ResponseEntity,再使用JsonUtil.toObject方法进行转换成Response类
JsonUtil.toObject(httpResponse.getBody(), Response.class); // 转换后的Response对象
DateUtil类
这个类主要解决之后我们会遇到的一个问题,获取秒级时间戳,其实也很简单,只是下面一句话
System.currentTimeMillis() / 1000 // 获取秒级时间戳
UrlGenerateUtil类
这个类的主要目的是根据用户的不同的请求生成对应的Url,查看文档可以知道,每个功能的接口Url是不同的,所以需要相应的生成对应的Url。
这里举个例子,加入我们想要创建订单,则对应创建订单地址应该是
https://commit-openic.sf-express.com/open/api/external/createorder?sign=$sign
这个地址我们拆分成三个部分,首先是hostServer,其次是Url,最后是签名;由于是测试环境,所以hostServer = "https://commit-openic.sf-express.com"
现在是url,根据创建订单页面中的post请求地址我们可以知道,这里的
url = "/open/api/external/createorder"
sign由我们后台生成。最后的结果就是上面的地址了。
这里我们举一个接下来会用到的例子,创建订单
private static final String CREATE_ORDER_URL = "/open/api/external/createorder";
public static String getCreateOrderUrl(String sfLogisticsHost) {
return sfLogisticsHost + CREATE_ORDER_URL;
}
public static String getOrderStatusUrl(String sfLogisticsHost){ return sfLogisticsHost + ORDER_STATUS_URL;}
这里还有一个小问题,getCreateOrderUrl方法中需要传递的sfLogisticsHost是什么?从哪儿来呢?
这里根据拼接规则可以发现这是官网文档中给的接口地址,这些我们都可以写在配置文件中
配置文件编写
创建logistics.properties文件,将配置信息编写进去
# 顺丰物流的配置信息
logistics.developer-id= 1578751034
logistics.developer-key= 4fb6604b07f7ca1df68efdd435674f61
logistics.shop-id= 3243279847393
logistics.api-url= https://commit-openic.sf-express.com
将这些信息封装
@Data
@Component
@ConfigurationProperties(prefix = "logistics")
public class LogisticsProperties {
@Value("${logistics.developer-id}")
private Integer developerId;
@Value("${logistics.developer-key}")
private String developerKey;
@Value("${logistics.api-url}")
private String apiUrl;
@Value("${logistics.shop-id}")
private String shopId;
}
工具我们介绍的差不多了,接下来我们进行下一步。相信各位小伙伴看了文档会发现,我们在发送请求时需要设定一些参数,那这就涉及到我们需要为这些参数设计一个能够对他们进行get/set的类。
我把所有的request请求需要用到的参数类全部放到了request文件夹下。这里举一个例子:订单创建
Request类编写
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class CreateOrderRequest{
/**
* 是同城开发者ID
*/
@JsonProperty("dev_id")
private Integer devId;
/**
* 0是店铺ID
*/
@JsonProperty("shop_id")
private String shopId;
/**
* 店铺ID类型 否
* 1:顺丰店铺ID ;2:接入方店铺ID
*/
@JsonProperty("shop_type")
private Integer shopType;
/**
* 商家订单号 是
* 不允许重复
*/
@JsonProperty("shop_order_id")
private String shopOrderId;
/**
* 订单接入来源 是
* 1:美团;2:饿了么;3:百度;4:口碑;其他请直接填写中文字符串值
*/
@JsonProperty("order_source")
private String orderSource;
/**
* 取货序号 否
* 与order_source配合使用
* 如:饿了么10号单,表示如下:
* order_source=2;order_sequence=10。
* 用于骑士快速寻找配送物
*/
@JsonProperty("order_sequence")
private String orderSequence;
/**
* 坐标类型 否
* 1:百度坐标,2:高德坐标
*/
@JsonProperty("lbs_type")
private Integer lbsType;
/**
* 用户支付方式 是
* 1:已付款 0:货到付款
*/
@JsonProperty("pay_type")
private Integer payType;
/**
* 代收金额 否
* 单位:分
*/
@JsonProperty("receive_user_money")
private Integer receiveUserMoney;
/**
* 用户下单时间 是
* 秒级时间戳
*/
@JsonProperty("order_time")
private Integer orderTime;
/**
* 是否是预约单 是
* 0:非预约单;1:预约单
*/
@JsonProperty("is_appoint")
private Integer isAppoint;
/**
* 用户期望送达时间 否
* 预约单需必传,秒级时间戳
*/
@JsonProperty("expect_time")
private Integer expectTime;
/**
* 是否保价 是
* 0:非保价;1:保价
*/
@JsonProperty("is_insured")
private Integer isInsured;
/**
* 保价金额 否
* 单位:分
*/
@JsonProperty("declared_value")
private Integer declaredValue;
/**
* 订单备注 否
*/
@JsonProperty("remark")
private String remark;
/**
* 物流流向 否
* 1:从门店取件送至用户;
* 2:从用户取件送至门店
*/
@JsonProperty("rider_pick_method")
private Integer riderPickMethod;
/**
* 返回字段控制标志位(二进制) 否
* 1:价格,2:距离,4:重量,组合条件请相加
* 例如全部返回为填入7
*/
@JsonProperty("return_flag")
private Integer returnFlag;
/**
* 推单时间 是
* 秒级时间戳
*/
@JsonProperty("push_time")
private Integer pushTime;
/**
* 版本号 是
* 参照文档主版本号填写
* 如:文档版本号1.7,version=17
*/
@JsonProperty("version")
private Integer version;
/**
* 收货人信息 是
* Obj,详见receive结构
*/
@JsonProperty("receive")
private Receive receive;
/**
* 发货店铺信息 否
* Obj,详见shop结构,
* 平台级开发者(如饿了么)需传入
* 如无特殊说明此字段可忽略
*/
@JsonProperty("shop")
private Shop shop;
/**
* 订单详情 是
* Obj,详见order_detail结构
*/
@JsonProperty("order_detail")
private OrderDetail orderDetail;
}
这里的Shop和OrderDetail在文档中又有多个属性,所以这里也当成一个类,同样的根据文档的参数进行类的构建
import lombok.Builder;
import lombok.Data;
/**
* 店铺信息
*/
@Data
@Builder
public class Shop {
/**
* 店铺名称 是
*/
private String shopName;
/**
* 店铺电话 是
*/
private String shopPhone;
/**
* 店铺地址 是
*/
private String shopAddress;
/**
* 店铺经度 否
*/
private String shopLng;
/**
* 店铺纬度 否
*/
private String shopLat;
}
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class OrderDetail {
/**
* 用户订单总金额(单位:分) 是
*/
@JsonProperty("total_price")
private Integer totalPrice;
/**
* 物品类型 是
* 1快餐;2送药;3百货;
* 4脏衣服收;5干净衣服派;6生鲜;
* 7保单;8饮品;9现场勘查;
* 10快递;12文件证照;13蛋糕;
* 14鲜花;15电子数码;16服装鞋帽;
* 17汽车配件;18珠宝;20披萨;
* 21中餐;99其他
*/
@JsonProperty("product_type")
private Integer productType;
/**
* 实收用户金额(单位:分)否
*/
@JsonProperty("user_money")
private Integer userMoney;
/**
* 实付商户金额(单位:分) 否
*/
@JsonProperty("shop_money")
private Integer shopMoney;
/**
* 物品重量(单位:克)是
*/
@JsonProperty("weight_gram")
private Integer weightGram;
/**
* 物品体积(单位:升) 否
*/
@JsonProperty("volume_litre")
private Integer volumeLitre;
/**
* 商户收取的配送费(单位:分) 否
*/
@JsonProperty("delivery_money")
private Integer deliveryMoney;
/**
* 物品个数 是
*/
@JsonProperty("product_num")
private Integer productNum;
/**
* 物品种类个数 是
*/
@JsonProperty("product_type_num")
private Integer productTypeNum;
/**
* 物品详情 是
*/
@JsonProperty("product_detail")
private List productDetail;
}
有了request,当然还得有response,根据文档中的返回值,编写好response类。
首先是response类,这个是文档中规定的返回请求。
{
"error_code": 0,
"error_msg": "",
"error_data": null,//详细报错信息(报错的时候非空)
"result": {}
}
Response类编写
@Data
public class Response {
@JsonProperty("error_code")
private Integer errorCode;
@JsonProperty("error_msg")
private String errorMsg;
/**
* 详细报错信息(报错的时候非空)
*/
@JsonProperty("error_data")
private Object errorData;
@JsonProperty("result")
private Object result;
}
但其实result才是我们想要的返回的内容,所以我们要根据不同的请求编写不同的result类,这里我们编写的类是CreateOrderResponse类。
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class CreateOrderResponse extends Response{
/**
* 3165848793513984",//顺丰订单号(标准默认为int,可以设置为string)
*/
@JsonProperty("sf_order_id")
private Long sfOrderId;
/**
* 509008343346",//顺丰运单号(需要设置)
*/
@JsonProperty("sf_bill_id")
private String sfBillId;
/**
* 15104092022333",//商家订单号
*/
@JsonProperty("shop_order_id")
private String shopOrderId;
/**
* 1510680568" //推送时间
*/
@JsonProperty("push_time")
private Integer pushTime;
/**
* 以下字段受请求参数中 return_flag 控制:return_flag中未包含的,此字段将不存在,请注意!
* 1300, //配送费价格,当return_flag中包含1时返回,单位分(值为计算出来此单总价)
*/
@JsonProperty("total_price")
private Integer totalPrice;
/**
* 1234, //配送距离,当return_flag中包含2时返回,单位米(值为计算出来实际配送距离)
*/
@JsonProperty("delivery_distance_meter")
private Integer deliveryDistanceMeter;
/**
* 1000, //商品重量,当return_flag中包含4时返回,单位克(值为下单传入参数回传)
*/
@JsonProperty("weight_gram")
private Integer weightGram;
}
好了,我们现在把所有要用到的实体类都已经编写完成了。接下来还有两个问题,第一是发送请求前需要规定好request中的一些参数,第二就是发送post请求。我们一步一步解决。
这里我们先收集一些需要的参数,在官网的开发者中心可以看到必要的参数
点击店铺可以看到我们测试需要的一些店铺信息
初始化各类信息
首先给CreateOrderRequest附上请求必要的一些参数,由于之后我们肯定不是以写死的方式来赋值(废话),所以我们应该在调用的时候传递一个符合条件的参数,但是这里是测试,我这里给出一个初始化各项参数的方法,仅供测试使用。
private CreateOrderRequest initCreatePreOrderRequest() {
PreCreateOrderRequest preCreateOrderRequest = new PreCreateOrderRequest();
preCreateOrderRequest.setProductType(1);
preCreateOrderRequest.setUserLng("116.3534196");
preCreateOrderRequest.setUserLat("40.0159778");
preCreateOrderRequest.setUserAddress("123456");
preCreateOrderRequest.setShopOrderId(System.currentTimeMillis() + "");
preCreateOrderRequest.setOrderSource("测试");
preCreateOrderRequest.setPayType(1);
preCreateOrderRequest.setOrderTime(DateUtil.currentSecond().intValue());
preCreateOrderRequest.setIsAppoint(0);
preCreateOrderRequest.setIsInsured(0);
preCreateOrderRequest.setRiderPickMethod(1);
preCreateOrderRequest.setPushTime(DateUtil.currentSecond().intValue());
preCreateOrderRequest.setVersion(17);
preCreateOrderRequest.setShop(
Shop.builder()
.shopName("店铺名")
.shopPhone("13266666666")
.shopAddress("朝阳区高碑店镇四惠大厦F1-008")
.shopLng("116.514236")
.shopLat("39.905328")
.build()
);
preCreateOrderRequest.setReceive(
Receive.builder()
.userName("小明")
.userPhone("13288888888")
.userLng("116.3534196")
.userLat("40.0159778")
.userAddress("朝阳区高碑店镇四惠大厦F1-008")
.cityName("北京市")
.build()
);
preCreateOrderRequest.setOrderDetail(
OrderDetail.builder()
.totalPrice(100)
.productType(1)
.weightGram(500)
.productNum(1)
.productTypeNum(1)
.productDetail(
Stream.of(
ProductDetail.builder()
.productName("小炒肉")
.productNum(1)
.build()
).collect(Collectors.toList())
)
.build()
);
return preCreateOrderRequest;
}
接下来是Service,这里就只展示实现类的代码,接口就由各位自己去实现了。
@Service
@Slf4j
public class SfServiceImpl implements SfService {
// 这里写请求不同功能的方法
@Autowired
private LogisticsProperties logisticsProperties;
/**
* 创建订单
* @param createOrderRequest
* @return CreateOrderResponse
*/
@Override
public CreateOrderResponse createOrder(CreateOrderRequest createOrderRequest){
// 开发者ID赋值
createOrderRequest.setDevId(logisticsProperties.getDeveloperId());
createOrderRequest.setShopId(logisticsProperties.getShopId()); // shopId需要传递进来
String content = JsonUtil.toJsonString(createOrderRequest); // 将请求对象转换为String
log.info("正在向顺丰接口进行请求......");
// 发送POST请求
Response response = HttpUtil.post(
logisticsProperties.getDeveloperId(),
logisticsProperties.getDeveloperKey(),
UrlGenerateUtil.getCreateOrderUrl(logisticsProperties.getApiUrl()),
content
);
if(StrUtil.isNotEmpty(response.getErrorMsg())){
log.error("顺丰接口返回的错误信息:" + response.getErrorMsg());
}
return JsonUtil.toObject(response.getResult(), CreateOrderResponse.class);
}
}
ok,我们所有的准备已经完成了,现在可以开始给顺丰发请求了。
我们在需要使用到功能的地方添加上相应的代码
@Autowired
private SfService sfService
public void test(){
...
/**
* 构建一个对象,符合CreateOrderRequest,对象为createOrderRequest
*/
System.out.println(sfService.createOrder(createOrderRequest));
}
我们的调用createOrder方法,传递相应的参数后,观察返回的结果。
结果分析
我这里直接输出返回结果,也就是之前提到的result对象。
可以看到这里有返回的顺丰订单号、顺丰运单号等。现在我们就可以利用这些信息给回到我们想要显示的地方了。
当然,如果我们传入的参数有缺失,返回的结果中error_msg和error_data会有详细的信息。根据返回的结果进行修改即可。
顺丰java_Java顺丰同城接口开发相关推荐
- Java顺丰同城接口开发
顺丰同城接口 顺丰同城接口 阅读官方文档 工具类的介绍 SignUtil类 JsonUtil类 HttpUtil类 Post方法 DateUtil类 UrlGenerateUtil类 配置文件编写 R ...
- php接口 汉字出错 空,php接口开发时,数据解析失败问题,字符转义,编码问题(示例代码)...
php接口开发时,数据解析失败问题,字符转义,编码问题 情景: A平台--->向接口请求数据---->接口向B平台请求数据---->B平台返回数据给接口---->接口返回数据给 ...
- 《Web接口开发与自动化测试 -- 基于Python语言》 ---前言
前 言 本书的原型是我整理一份Django学习文档,从事软件测试工作的这六.七年来,一直有整理学习资料的习惯,这种学习理解再输出的方式对我非常受用,博客和文档是我主要的输出形式,这些输出同时也帮 ...
- 一周的微信公众平台接口开发总结
2019独角兽企业重金招聘Python工程师标准>>> 在公司弄了一个星期的微信公众平台的接口开发,由于之前没弄过,开发的过程遇到了很多的小问题.下面我就总结一下我在开发过程中遇到了 ...
- php限制接口访问次数_PhalApi专业版 v2.4 版本发布!接口开发,快人一步
dogstar 小白开放平台 今天 PhalApi专业版介绍 PhalApi Pro 是PhalApi开源接口框架的专业版, 基于主流的PHP+MySQL,是一款开发和管理接口的超级框架. 从接口设计 ...
- php 接口 2.0,YII 2.0 API接口开发
YII2.0 API接口开发 首先先安装 YII2.0 高级模板(安装请参考其他教程) 准备 添加数据库配置 common/config/main-local.php image 把backend目录 ...
- php 动态彩码辨色 接口的调用_好用的云函数!后端低代码接口开发,零基础编写API接口...
前言 在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作. 不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人, ...
- 浅谈 PHP 与手机 APP 开发(API 接口开发)
http://www.thinkphp.cn/topic/5023.html 推荐阅读:RESTful 是什么?一起来理解 RESTful 架构 更深入了解API开发 这个帖子写给不太了解PHP与AP ...
- 微信JS-SDK之图像接口开发详解
2019独角兽企业重金招聘Python工程师标准>>> 参考: 微信JS-SDK之图像接口开发详解 http://www.cnblogs.com/it-cen/p/4587812.h ...
最新文章
- django之Ajax
- JSONEasy的用法(JSONDateHandler)
- 【swift3.0】【枚举定义的不同方式】
- Angular module加载的原理研究
- org manual翻译--2.1 大纲
- Postgresql多线程hashjoin(inner join)
- thinkphp仿素材火教程_国外都用古风效果图获奖了,为什么你连素材都没有?
- 互联网晚报 | 3月25日 星期五 |​ ​​私募大佬但斌疑似空仓;蔚来和小米汽车拟采用比亚迪电池...
- cad缩放工具怎么用_小米电视怎么投屏?这个投屏工具真的太好用啦!
- 【Spring】Spring中BeanPostProcessor
- 为提升 DCP 传输效率,阿里工程师竟然这样做!
- php的正则匹配方法preg_match_all问题
- JAVA:json解析
- 【Redis笔记】简单动态字符串(SDS)
- 根据文件大小搜索电脑文件
- AD10操作技巧及参数
- Oracle 异常处理函数SQLCODE和SQLERRM
- RuoYi-Vue Spring Security 密码加密
- 嵌入式开发培训学哪些?嵌入式培训课程好学吗
- win10系统hp笔记本开机黑屏一段时间解决方法
热门文章
- android接口的作用是什么意思,Android开发中接口的用处
- crontab 每天凌晨12点定时器_Linux下使用crontab实现mysql数据库自动备份
- 生日祝福卡片 html,暖心的卡片生日祝福语
- 感知机学习:鸢尾花二分类
- c语言中指数函数fabs,高一指数函数公式,高一指数函数
- VC6.0(VC++6.0)使用教程(使用VC6.0编写C语言程序)
- 牛牛找工作--网易2019实习生招聘编程题
- 黑苹果 macOS 无法修复磁盘 XXX 已修复
- laravel短信验证接口思路
- 将硬盘转换成GPT分区格式