概述
最近接到一个需求,需要和顺丰接口对接。由于是第一次对接,就需要把所有的流程全部走一遍,从 注册
到 关联API
以及代码测试
,电子面单审核
,上线
,下面就分开来说明把。本来是想着偷懒来着,作为专业的程序员,能Ctrl + C
加上 Ctrl + V
的,绝对不会有多余的动作,但是发现这个能找到的文章都上年纪了,不适合当下了。
顺丰速运接入
接入肯定是需要看官方的文档的,点击这里看官方的详细接入指引,官网上面说的还是比较详细的,另外还有一个2分钟的视频说明还算是贴心的了。我这边就简单的梳理下,大伙按照官网的来就好。
第一步:注册
登录LaaS开放平台
如果您已有开放平台账号,可直接使用账号密码登录。如果您已有月结管家/速打平台/数据灯塔账号,可选择对应登录方式直接登录。如果您是顺丰内部员工,可选择顺丰工号登录方式完成域账号登录。
如果您是首次与顺丰合作,可通过注册平台账号后登录
第二步:认证
进入“控制台”,在控制台首页点击【立即认证】,根据实际情况选择“个人认证”或“企业认证”。这里我就不赘述了,大家看下官网就好,我是用的个人认证的,先测试通过再说。点击这里看官方的详细接入指引
第三步:创建应用
在“控制台-业务中心”选择“业务对接-开发者对接”,点击【新建应用】按钮创建应用。每个应用对应您需要接入顺丰服务的单个系统,平台将为每个应用分配独立的对接账号(顾客编码)及密钥(校验码)。
创建应用
创建成功
关联API
这里是我自己关联的API
这个云打印面单转PDF的接口这里先有个印象,后面回用到
到这里,我们的第一部分认证以及关联就算是搞完了,后面我们就开始上代码了。
SDK下载与说明
丰桥API-SDK下载
点击这里看官方连接,很重要,由于不知道什么时候我这里的文档也过时,看下官方的最保险。
下载完成之后,我们看下对应的包
CallExpressServiceTools
是使用 sdk-java
的入口,下载的zip包内json文件夹对应的是丰桥服务接口对应的报文,如下订单使用的报文是\json\EPSJson\EPS.1.COM_RECE_EPS_ORDER.json
,请求对应的接口使用对应的请求报文(可以将请求报文的文件放置在 java 工程的目录内,方便测试调用。
下载的zip包 java-demo 目录还有个java版的测试类(TestCallExpressNewAPIService.java
),可以在java工程中新建个测试目录如com.sf.test,完整的测试接口调用环境如下截图:
完整的客户端调用代码如下:
package com.sf.csim.express.test;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.sf.csim.express.service.CallExpressServiceTools;
import com.sf.csim.express.service.HttpClientUtil;
import com.sf.csim.express.service.IServiceCodeStandard;
import com.sf.csim.express.service.code.ExpressServiceCodeEnum;
public class TestCallExpressNewAPIService {
private static final String CLIENT_CODE = ""; //此处替换为您在丰桥平台获取的顾客编码
private static final String CHECK_WORD = "";//此处替换为您在丰桥平台获取的校验码
//沙箱环境的地址
private static final String CALL_URL_BOX = "https://sfapi-sbox.sf-express.com/std/service";
//生产环境的地址
private static final String CALL_URL_PROD = "https://sfapi.sf-express.com/std/service";
public static void main(String[] args) throws UnsupportedEncodingException {
/**ExpressServiceCodeEnum 对应速运类-快递APIs
POSTServiceCodeEnum 对应速运类-驿站APIs
YJTServiceCodeEnum 对应解决方案-医寄通APIs
EPSServiceCodeEnum 对应解决方案-快递管家APIs
详情见code目录下枚举类,客户可自行修改引用的该类
**/
IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_CREATE_ORDER; //下订单
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_SEARCH_ORDER_RESP; //查订单
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_UPDATE_ORDER;//订单取消
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_FILTER_ORDER_BSP;//订单筛选
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_SEARCH_ROUTES;//查路由
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_GET_SUB_MAILNO;//子单号
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_QUERY_SFWAYBILL;//查运费
// IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_REGISTER_ROUTE;//注册路由
// IServiceCodeStandard standardService =
CallExpressServiceTools tools=CallExpressServiceTools.getInstance();
// set common header
Map<String, String> params = new HashMap<String, String>();
String timeStamp = String.valueOf(System.currentTimeMillis());
String msgData =tools.packageMsgData(standardService);
params.put("partnerID", CLIENT_CODE); // 顾客编码
params.put("requestID", UUID.randomUUID().toString().replace("-", ""));
params.put("serviceCode",standardService.getCode());// 接口服务码
params.put("timestamp", timeStamp);
params.put("msgData", msgData);
params.put("msgDigest", tools.getMsgDigest(msgData,timeStamp,CHECK_WORD));
String result = HttpClientUtil.post(CALL_URL_BOX, params);
System.out.println("===调用地址 ==="+CALL_URL_BOX);
System.out.println("===顾客编码 ==="+CLIENT_CODE);
System.out.println("===返回结果:" +result);
}
}
代码测试
工程搭建
在项目工程中引入顺丰的SDK
Pom 文件
<dependency>
<groupId>com.sf</groupId>
<artifactId>SF-CSIM-EXPRESS-SDK</artifactId>
<version>2.1.7</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/SF-CSIM-EXPRESS-SDK-V2.1.7.jar</systemPath>
</dependency>
对应的调用代码示例
package com.demo.express.sf.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sf.csim.express.service.CallExpressServiceTools;
import com.sf.csim.express.service.HttpClientUtil;
import com.sf.csim.express.service.IServiceCodeStandard;
import com.sf.csim.express.service.code.ExpressServiceCodeEnum;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class CallExpressApiService {
private static final String CLIENT_CODE = ""; //此处替换为您在丰桥平台获取的顾客编码
private static final String CHECK_WORD = "";//此处替换为您在丰桥平台获取的校验码
//沙箱环境的地址 -PRO
private static final String CALL_URL_BOX = "https://sfapi-sbox.sf-express.com/std/service";
//生产环境的地址 -PRO
private static final String CALL_URL_PROD = "https://sfapi.sf-express.com/std/service";
/**
* 调用参数
* <pre>
* String msgData = "{" +
* " "cargoDetails":[" +
* " { " +
* " "count":2.365," +
* " "unit":"个"," +
* " "weight":6.1," +
* " "amount":100.5111," +
* " "currency":"HKD"," +
* " "name":"护肤品1", " +
* " "sourceArea":"CHN" " +
* " }]," +
* " "contactInfoList":[" +
* " {" +
* " "address":"广东省深圳市南山区软件产业基地11栋"," +
* " "contact":"小曾"," +
* " "contactType":1," +
* " "country":"CN"," +
* " "postCode":"580058"," +
* " "tel":"4006789888"" +
* " }," +
* " {" +
* " "address":"广东省广州市白云区湖北大厦"," +
* " "company":"顺丰速运"," +
* " "contact":"小邱"," +
* " "contactType":2," +
* " "country":"CN"," +
* " "postCode":"580058"," +
* " "tel":"18688806057"" +
* " }]," +
* " "language":"zh_CN"," +
* " "orderId":"OrderNum20240612223"" +
* "}";
* </pre>
* @param msgData
* @return
* @throws UnsupportedEncodingException
*/
public static String createOrder(String msgData) throws UnsupportedEncodingException {
IServiceCodeStandard standardService = ExpressServiceCodeEnum.EXP_RECE_CREATE_ORDER; //下订单
CallExpressServiceTools tools = CallExpressServiceTools.getInstance();
// set common header
Map<String, String> params = new HashMap<>();
String timeStamp = String.valueOf(System.currentTimeMillis());
params.put("partnerID", CLIENT_CODE); // 顾客编码 ,对应丰桥上获取的clientCode
params.put("requestID", UUID.randomUUID().toString().replace("-", ""));
params.put("serviceCode", standardService.getCode());// 接口服务码
params.put("timestamp", timeStamp);
params.put("msgData", msgData);
params.put("msgDigest", tools.getMsgDigest(msgData, timeStamp, CHECK_WORD));
System.out.println("====调用实际请求:" + params);
String result = HttpClientUtil.post(CALL_URL_BOX, params);
System.out.println("====调用丰桥的接口服务代码:" + standardService.getCode() + "====");
System.out.println("===调用地址 ===" + CALL_URL_BOX);
System.out.println("===顾客编码 ===" + CLIENT_CODE);
System.out.println("===返回结果:" + result);
return result;
}
}
这样子就可以测试了,几乎是按照官方的来的,还是比较简单。
下单接口文档
详细的接口文档地址 ,点击下面的 “下订单接口”
说明下:我这个接口后面有个 “上线” 的操作,这个是因为我这边已经审核通过了,最开始这里是有一个测试操作的。
查询接口测试记录
电子面单上传
在下单的接口处,我们会看到有这么一个地方,就是需要上传电子面单,这个东西我也是找了好久,最后发现这个其实是需要我们去调用面单打印的接口,由于我们是第一次调用,就需要去对接下这个接口
配置云打印面单接口
这里我们需要在 基础通用API
关联 云打印面单转PDF接口
,这里我是为了测试面单上传,就选择了 “同步” 方式,具体可以看下官方的说明,异步的话,需要自己给一个回调地址。
这里可以看到对应的模板名称
这个时候,我们就需要去关联云打印的接口了,基于上面的代码,我们增加两个函数:
- printWayBills 云打印面单打印
- 获取PDF接口
/**
* 调用参数
* <pre>
* {
* "templateCode": "fm_150_standard_YJ3CB3FX", 这里是配置的那个打印模板
* "version":"2.0",
* "fileType":"pdf",
* "sync":true,
* "documents": [{
* "masterWaybillNo": "SF7444480501251"
* }]
* }
*/
public static void printWayBills(String msgData) throws UnsupportedEncodingException {
IServiceCodeStandard standardService = ExpressServiceCodeEnum.COM_RECE_CLOUD_PRINT_WAYBILLS; //云打印面单打印2.0接口
CallExpressServiceTools tools = CallExpressServiceTools.getInstance();
// set common header
Map<String, String> params = new HashMap<>();
String timeStamp = String.valueOf(System.currentTimeMillis());
params.put("partnerID", CLIENT_CODE); // 顾客编码 ,对应丰桥上获取的clientCode
params.put("requestID", UUID.randomUUID().toString().replace("-", ""));
params.put("serviceCode", standardService.getCode());// 接口服务码
params.put("timestamp", timeStamp);
params.put("msgData", msgData);
params.put("msgDigest", tools.getMsgDigest(msgData, timeStamp, CHECK_WORD));
System.out.println("====调用实际请求:" + params);
String result = HttpClientUtil.post(CALL_URL_BOX, params);
System.out.println("====调用丰桥的接口服务代码:" + standardService.getCode() + "====");
System.out.println("===调用地址 ===" + CALL_URL_BOX);
System.out.println("===顾客编码 ===" + CLIENT_CODE);
System.out.println("===返回结果:" + result);
}
根据上面 printWayBills 返回值,我们可以拿到对应的pdf文件地址,返回的数据如下:
{"apiErrorMsg":"","apiResponseID":"00018E79F0AAEE3FE755F4D40823D73F","apiResultCode":"A1000","apiResultData":"{\"obj\":{\"clientCode\":\"YJ3CB3FX\",\"fileType\":\"pdf\",\"files\":[{\"areaNo\":1,\"documentSize\":0,\"pageCount\":0,\"pageNo\":1,\"seqNo\":1,\"token\":\"AUTH_tkv12_f146d1855480549d262b5c46ab0ab597ff20a97d9d0db45c16bedeb4fabd112b012deadd477ee524b1d690ce01baa3cdffbb125a6ccf69b73778dba2eb5157eb73eb03e946a2c01352db378fe2bdea7c95c535a186cf195dc290be8fb7d1e7064e80fa12c5e7757aff35d31ff59b7f55832b73ef3f6a4397c071ef11cba0f8623abd7a376adcd85a3c8e3e8c9b64f903a7d5c55353003625d76f23480fd915464d767f73ba97048cd4aef655f4d970ba\",\"url\":\"https://eos-scp-core-shenzhen-futian1-oss.sf-express.com:443/v1.2/AUTH_EOS-SCP-CORE/print-file-sbox/AAABjnnwq4I40xFIfoVMAJQtjYTrUht8_SF7444480501251_fm_150_standard_YJ3CB3FX_1_1.pdf\",\"waybillNo\":\"SF7444480501251\"}],\"templateCode\":\"fm_150_standard_YJ3CB3FX\"},\"requestId\":\"9c772eb846124800a15edcaf9e0cfea4\",\"success\":true}"}
调用代码,这里需要使用自己的请求参数,用我的是不行的哈
public static void getPDF(String result){
JSONObject resultJson = JSON.parseObject(result).getJSONObject("apiResultData").getJSONObject("obj");
JSONObject fileInfo = resultJson.getJSONArray("files").getJSONObject(0);
try {
URL noodleUrl = new URL(fileInfo.getString("url"));
URLConnection connection = noodleUrl.openConnection(); //创建连接
//设置请求头(下载文件时需要的token,设置在请求头的 X-Auth-token 字段,有效期 24h)
connection.setRequestProperty("X-Auth-token", fileInfo.getString("token"));
try ( InputStream in = connection.getInputStream()){
File outputFile = new File("D:\\test\\"+resultJson.getString("templateCode")+".pdf");
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
IOUtils.copy(in, outputStream);
System.out.println("内容已成功写入到文件中。");
}
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
然后在我本地就会有这个文件在,把这个文件转为png,在下单接口上传就好:
最后审核通过的截图
上线
API状态根据上线流程可分为:
待配置 :根据实际情况完成接口关键属性配置,对于不需要配置的接口,平台默认不显示此状态,直接切换为“测试中”。
测试中 :在沙箱环境自助联调接口。点击【测试】按钮可选择下载API-SDK或使用在线测试工具联调。点击【配置】按钮修改接口属 性,点击【查看】按钮查询已配置的接口属性。
待上线 :平台检测到API已具备切入生产环境条件(接口7天内测试成功3次以上,若为下单接口,需通过面单审核或配置免面单,具体参考面单部分说明)。点击【上线】按钮可完成接口上线。
已上线 :API已切入生产环境条件,可正常调用生产环境接口地址。
注 :新用户需对接使用云打印接口,上传云打印面单进行审核,审核通过后才能完成下单接口上线操作。