一、POS对外接口技术方案
<h5>对接POS openapi步骤</h5>
<ul>
<li>1.对接者先向POS申请serviceName和serviceSecret
serviceName: 调用接口服务的身份标识,为固定字符串常量
serviceSecret: 调用接口服务的身份密钥,为确保安全,请妥善保管,不可泄露。</li>
<li>2.对接者根据API定义编写客户端程序,可以使用任何技术语言编写。</li>
<li>3.对接者与POS数据服务端进行接口联调,直至调试通过。</li>
</ul>
<h5>接口设计</h5>
<ul>
<li>接口url: /{contextPath}/{version}{requestPath}
其中:
1) contextPath 是被调用服务的名称;
2) version 是被调用服务的版本信息;
3) requestPath是调用服务的请求路径。
示例如下:
url: xxxx/openapi/v1/demo/postorder?shopcode=12345678&productcode=12345
contextPath: openapi
version: v1
requestPath: /demo/postorder</li>
</ul>
<h5>公共请求消息头</h5>
<ul>
<li>1.所有请求头都必须包含 X-Caller-Service、 X-Caller-Timestamp、 X-Caller-Sign、 Content-Type</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">请求头(Header)</th>
<th style="text-align: left;">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">X-Caller-Service</td>
<td style="text-align: left;">对应上文中的serviceName</td>
</tr>
<tr>
<td style="text-align: left;">X-Caller-Timestamp</td>
<td style="text-align: left;">时间戳.格式:yyyy-MM-dd HH:mm:ss,时区为GMT+8.如:2021-04-01 12:20:11</td>
</tr>
<tr>
<td style="text-align: left;">X-Caller-Sign</td>
<td style="text-align: left;">签名. 参考sign安全校验机制</td>
</tr>
<tr>
<td style="text-align: left;">Content-Type</td>
<td style="text-align: left;">只支持JSON格式. application/json; charset=utf-8</td>
</tr>
</tbody>
</table>
<h5>sign安全校验机制</h5>
<ul>
<li>1.各个服务通过gateway进行调用的时候,需要增加三个自定义的头信息:X-Caller-Service、XCaller-Timestamp、X-Caller-Sign.</li>
<li>2.X-CallerSign是通过callerService、timestamp、contextPath、version、serviceSecret、restPath,按首字母排序后,进行md5加密获得的值,只需要将对应的名字做修改即可.
Java版本代码如下:
<pre><code>import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class SignConvertUtil {
public static String generateSign(String callerService, String contextPath, String version,String timestamp, String serviceSecret, String requestPath) {
String sign = "";
if (callerService == null || callerService.equals("") || contextPath == null ||contextPath.equals("") ||timestamp == null || timestamp.equals("") || serviceSecret == null || serviceSecret.equals("")) {
return sign;
}
Map<String, String> map = new LinkedHashMap<>();
map.put("callerService", callerService);
map.put("contextPath", contextPath);
try {
if (requestPath != null) {
StringBuilder sb = new StringBuilder();
for(String part : requestPath.split("/")) {
sb.append("/").append(URLEncoder.encode(part,"utf-8"));
}
map.put("requestPath", sb.toString().substring(1));
}
map.put("timestamp", timestamp);
map.put("v", version);
sign = generateMD5Sign(serviceSecret, map);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
return "";
}
return sign;
}
private static String generateMD5Sign(String secret, Map<String, String> parameters) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[]bytes=md5.digest(generateConcatSign(secret,parameters).getBytes("utf-
8"));
return byteToHex(bytes);
}
private static String generateConcatSign(String secret, Map<String, String>parameters) {
StringBuilder sb = new StringBuilder().append(secret);
Set<String> keys = parameters.keySet();
for (String key : keys) {
sb.append(key).append(parameters.get(key));
}
return sb.append(secret).toString();
}
private static String byteToHex(byte[] bytesIn) {
StringBuilder sb = new StringBuilder();
for (byte byteIn : bytesIn) {
String bt = Integer.toHexString(byteIn & 0xff);
if (bt.length() == 1)
sb.append(0).append(bt);
else
sb.append(bt);
}
return sb.toString().toUpperCase();
}
}</code></pre></li>
</ul>
<h5>返回参数</h5>
<table>
<thead>
<tr>
<th style="text-align: left;">参数名</th>
<th style="text-align: left;">类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">result</td>
<td style="text-align: left;">string</td>
<td>1-成功,0-失败</td>
</tr>
<tr>
<td style="text-align: left;">msg</td>
<td style="text-align: left;">string</td>
<td>描述信息</td>
</tr>
<tr>
<td style="text-align: left;">totalcount</td>
<td style="text-align: left;">int</td>
<td>记录总数</td>
</tr>
<tr>
<td style="text-align: left;">data</td>
<td style="text-align: left;">array</td>
<td>数据信息</td>
</tr>
</tbody>
</table>
<h5>返回示例</h5>
<pre><code> {
"result": 1,
"msg": "处理成功",
"totalcount": 1,
"data": {
"ShopCode": "00000000",
"ProdCode": "12345",
"CounterStock": 5,
"DepotStock": 20,
"TotalStock": 25
}
}</code></pre>