开发者对接文档

**平台入驻:** 1.使用米合的管理员账户,可以在米合开放平台获取开发对接的应用。包括开放平台的clientId与clientSecret,accessToken。accessToken基于oauth2.0颁布,具有一定的使用时效,过期后需要重新获取。用户可以通过第3步的步骤,使用接口调用获取,也可以在开放平台的控制台的***刷新token***按钮进行刷新操作。附米合开放平台地址:[米合开放平台](http://openapi.ruwii.com/api/system) 2.已经获取到上述clientId与clientSecret后,用户可以获取到访问资源服务器的 accessToken,accessToken有一定的有效期,过期后需要重新调用接口获取新的accessToken。接口地址:https://easydoc.net/doc/46112299/BtJWN82i/7A0WVwrx **开发流程:** 1.使用米合的管理员账户登录[米合开放平台](http://openapi.ruwii.com/api/system) 2.选择控制台,首次登录用户选择创建应用,已经申请过的用户直接可以看到应用信息。每个企业只能申请一个应用。 3.根据第二步拿到的应用Id,应用secret和accessToken,即可调用米合开放平台接口 4.接口调用成功,即返回业务数据,后续按需使用即可。 **API调用文档:** 1.Get请求可以在url拼接参数,==注意:入参包含:[]{}等特殊符号,需要转义后传输,否则无法获取返回。== 2.签名的非字符串参数,在计算签名的时候需要转为字符串加签。以下单接口为例,入参包含ConsigneeInfo对象,需要将ConsigneeInfo的值转为字符串。 3.用户需要按照米合的协议规范拼装一个正确的URL,通过Http请求到米合即能够获取到所需数据。 主要流程包含:填写参数、生成签名、拼装HTTPS请求、发起请求、得到响应结果、解析结果。 4.公共参数 |参数名|必选|类型|说明| |:---- |:---|:----- |----- | |accessToken |是 |String | 采用OAuth授权方式获取的accessToken授权码 | |clientId |是 |String |API应用的clientId | |sign |是 |String | 签名(算法请看下面的签名详解) | |timestamp |是 |Long|时间戳,毫秒 | |v |是 |String | 开放平台的API版本,目前是2.0 | 5.签名算法 为了防止API在调用过程中被恶意者拦截随意篡改,调用API是需要传入签名参数,服务端会根据请求参数对签名进行验证,判断请求参数是否合法。签名规则过程如下: 将所有请求参数(包括公共参数和业务参数)按照字母先后顺序排列,例如:accessToken,clientId,timestamp,v ,这些参数 排序为accessToken,clientId,timestamp,v 把所有参数名和参数值进行拼接,例如(x代表参数的值): accessTokenxxxclientIdxxxtimestampxxxxxxvx 把clientSecret夹在字符串(上一步拼接串)的两端,例如:clientSecret+XXXX+clientSecret 使用MD5进行加密,再转化成大写,即为签名sign。 **签名算法的Java代码实现:** ```java import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import com.alibaba.fastjson.JSON; public class SignUtil { public static void main(String[] args) { String clientId = "通过开放平台获取的clientId"; String clientSecret = "通过开放平台获取的clientSecret"; String accessToken = "通过开放平台授权获取到的accessToken"; //以工具API,获取获取市级列表为例 String v = "2.0"; Long timestamp = System.currentTimeMillis(); //公共参数 Map<String, Object> params = new HashMap<>(); params.put("clientId", clientId); params.put("accessToken", accessToken); params.put("timestamp", timestamp.toString()); params.put("v", v); //业务参数 params.put("provinceCode", 210000); String buildSign = buildSign(clientSecret, params); System.out.println("签名为:" + buildSign); } //生成签名 public static String buildSign(String clientSecret, Map<String, Object> params) { // 生成待签字串 和 sign String preStrNew = buildSignString(params); String preStrNew_md5Key = clientSecret+ preStrNew + clientSecret; System.out.println("生成待签字串为:" + preStrNew_md5Key); String sign = DigestUtils.md5Hex(getContentBytes(preStrNew_md5Key)).toUpperCase(); // 返回结果 return sign; } // 构建签名字符串 public static String buildSignString(Map<String, Object> params) { if (params == null || params.size() == 0) { return ""; } List<String> keys = new ArrayList<>(params.size()); for (Entry<String, Object> entry : params.entrySet()) { if ("sign".equals(entry.getKey())) continue; if (StringUtils.isEmpty(entry.getValue() instanceof String? entry.getValue().toString(): JSON.toJSONString(entry.getValue()))) continue; keys.add(entry.getKey()); } // 字段排序 Collections.sort(keys); StringBuilder buf = new StringBuilder(); for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key) instanceof String? params.get(key).toString(): JSON.toJSONString(params.get(key)); buf.append(key + value); } return buf.toString(); } // 根据编码类型获得签名内容byte[] public static byte[] getContentBytes(String content) { try { return content.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("签名过程中出现错误"); } } } ``` 6.接口HTTP请求的Java示例代码 本请求示例整合了签名算法,以HttpClient为基础调用接口的示例。 ```java import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.alibaba.fastjson.JSON; public class HttpRequestDemo { public static void main(String[] args) { String url = "https://openapi.ruwii.com/api/logistics/cityGet"; String clientId = "10000001"; String clientSecret = "GL2zo1DtpRPHrsQu1uEnG4kul6Thh0ZZ"; String accessToken = "f0fdc75c-4a12-4cba-879f-c037906faf8d"; String v = "2.0"; Long timestamp = System.currentTimeMillis(); // 公共参数 Map<String, Object> params = new HashMap<>(); params.put("clientId", clientId); params.put("accessToken", accessToken); params.put("timestamp", timestamp.toString()); params.put("v", v); // 业务参数 params.put("provinceCode", 210000); //计算签名 String sign = buildSign(clientSecret, params); System.out.println("签名为:" + sign); params.put("sign", sign); StringBuffer paramStringBuffer = new StringBuffer(""); for (Map.Entry<String, Object> m : params.entrySet()) { paramStringBuffer.append(m.getKey()).append("=").append(m.getValue()).append("&"); } paramStringBuffer = paramStringBuffer.deleteCharAt(paramStringBuffer.length() - 1); String paramString = paramStringBuffer.toString(); String result = doGet(url,paramString); System.out.println("接口返回结果:" + result); } // 生成签名 public static String buildSign(String clientSecret, Map<String, Object> params) { // 生成待签字串 和 sign String preStrNew = buildSignString(params); String preStrNew_md5Key = clientSecret + preStrNew + clientSecret; System.out.println("生成待签字串为:" + preStrNew_md5Key); String sign = DigestUtils.md5Hex(getContentBytes(preStrNew_md5Key)).toUpperCase(); // 返回结果 return sign; } // 构建签名字符串 public static String buildSignString(Map<String, Object> params) { if (params == null || params.size() == 0) { return ""; } List<String> keys = new ArrayList<>(params.size()); for (Entry<String, Object> entry : params.entrySet()) { if ("sign".equals(entry.getKey())) continue; if (StringUtils.isEmpty(entry.getValue() instanceof String ? entry.getValue().toString() : JSON.toJSONString(entry.getValue()))) continue; keys.add(entry.getKey()); } // 字段排序 Collections.sort(keys); StringBuilder buf = new StringBuilder(); for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key) instanceof String ? params.get(key).toString() : JSON.toJSONString(params.get(key)); buf.append(key + value); } return buf.toString(); } // 根据编码类型获得签名内容byte[] public static byte[] getContentBytes(String content) { try { return content.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("签名过程中出现错误"); } } public static String doGet(String url, String paramString) { try { String openapiUrl = url + "?" + paramString; System.out.println(openapiUrl); // 建立连接请求 CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建get请求 HttpGet httpGet = new HttpGet(openapiUrl); // 设置请求超时时间 RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(1000) .setSocketTimeout(2000).setConnectTimeout(1000).build(); httpGet.setConfig(requestConfig); // 发送请求 CloseableHttpResponse response = httpClient.execute(httpGet); HttpEntity entity = response.getEntity(); if (entity != null) { String responseString = EntityUtils.toString(entity, "UTF-8"); return responseString; } // 资源释放 response.close(); httpClient.close(); } catch (Exception e) { e.printStackTrace(); } return null; } } ``` 7.沙箱 详情请参见:https://easydoc.net/doc/46112299/BtJWN82i/k5qP7Hpd