# 鉴权

ShipOut API鉴权分为 开发者鉴权 和 用户鉴权 两部分

请求ShipOut-Openapi时,需要在http请求的header部分,加入【如何获得API Key】中 第一和第二步,生成的鉴权信息,详见下图请求事例

图中appKey:是获取API Key中生成的开发者鉴权信息中的appKey。

图中Authorization: 是获取API Key中生成的商家鉴权信息。

图中version:版本,当前为1.0.0。

图中sign 和 timestamp:是获取API Key中生成的开发者鉴权信息中的securetKey,经过ShipOut提供的签名工具类生成(https://cdn.ezeeshipcn.cn/OpenAPI/openapi-sdk-1.0.0-RELEASE.jar)。

如果技术栈不支持使用jar包(如使用.Net技术等)可以参照以下方法生成

package com;

import com.google.common.collect.Maps;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class SignUtils {
    public static Map<String, String> generateSign(String path, String secretKey, String version, String timeStampStr) {
        assert !StringUtils.isEmpty(path);
        assert !StringUtils.isEmpty(secretKey);
        assert !StringUtils.isEmpty(version);
        assert !StringUtils.isEmpty(timeStampStr);

        Map<String, String> resultMap = Maps.newHashMapWithExpectedSize(3);
        Map<String, String> map = Maps.newHashMapWithExpectedSize(3);
        map.put("timestamp", timeStampStr);
        map.put("path", path);
        map.put("version", version);

        List<String> storedKeys = Arrays.stream(map.keySet()
                        .toArray(new String[]{}))
                .sorted(Comparator.naturalOrder())
                .collect(Collectors.toList());

        final String sign = storedKeys.stream()
                .map(key -> String.join("", key, map.get(key)))
                .collect(Collectors.joining())
                .concat(secretKey);

        System.out.println("加密原始串:" + sign);
        String md5Str = DigestUtils.md5Hex(sign).toUpperCase();

        resultMap.put("timestamp", timeStampStr);
        resultMap.put("sign", md5Str);
        resultMap.put("version", version);

        return resultMap;
    }

    public static void main(String[] args) {
        String path = "/http/order/findById";
        String secretKey = "FC300595425B48C39FA3F56F3787D9CA";
        String version = "1.0.0";
        String timeStampStr = "1639795057257";//String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        Map<String, String> resultMap = generateSign(path, secretKey, version, timeStampStr);
        resultMap.forEach((k, v) -> System.out.println(k + " = " + v));
    }

}

输出结果:
加密原始串:path/http/order/findByIdtimestamp1639795057257version1.0.0FC300595425B48C39FA3F56F3787D9CA
sign = C9A285632306F49A2C9CBF7DCFE67231
version = 1.0.0
timestamp = 1639795057257


特别注意:
path参数为您调用接口的具体的url,比如您调用https://opendev.shipout.com/api/open-api/oms/info/warehouse/list接口,那么path是"/open-api/oms/info/warehouse/list"
timeStampStr参数为调用当时的时间戳,见上面样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# Examples

curl --location --request GET 'https://opendev.shipout.com/api/open-api/oms/info/warehouse/list' \
--header 'Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcmdUeXBlIjoxLCJvcmdOYW1lIjoiT3BlbkFwaSIsImZ1bGxOYW1lIjoib3BlbiBhcGkiLCJzY29wZSI6WyJvcGVuLWFwaSJdLCJ0aW1lWm9uZSI6IkFtZXJpY2EvTmV3X1lvcmsiLCJ1c2VyTmFtZSI6IjQzNTk2MDA3NEBxcS5jb20iLCJ1c2VySWQiOiIxNTMxNTgxNzAwMjI3NTc1ODEwIiwianRpIjoiNmUwN2FlMGQtNDViMS00M2MxLWFlNmYtZjI0MGU3NTJlMGY1Iiwib3JnSWQiOiIyNjEzNyIsImNsaWVudF9pZCI6Im9wZW4tYXBpLXVpIiwiZXhwIjoxNzEyMTMwMDg4LCJlemVlc2hpcFVzZXJJZCI6MTEwMDB9.OOOEqqzMn1wYUC_ZPplhTmQqZ2qKa9EV_SJhfLEfY5w' \
--header 'timestamp: 1642668055502' \
--header 'appKey: B5CABC8551C349B9A5500E3DC4F55170' \
--header 'sign: C9A285632306F49A2C9CBF7DCFE67231' \
--header 'version: 1.0.0' \
--header 'User-Agent: Apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Accept: */*' \
--header 'Host: opendev.shipout.com' \
--header 'Connection: keep-alive'
1
2
3
4
5
6
7
8
9
10

# API环境说明

ShipOut OpenAPI分为两套环境,正式环境与沙盒环境

开发联调期间建议使用沙盒环境,请先使用沙盒环境进行数据对接并调试成功以后再进行正式环境的数据对接,沙盒环境的账号创建流程如下:

沙盒环境API访问前缀:https://opendev.shipout.com/api/

正式环境是客户处理真实订单的环境,需严格按照文档步骤对接

正式环境API访问前缀:https://open.shipout.com/api/

# 异步回调接口实现示例

(需要调用方实现 在订单处理结束后通知调用) 该接口地址作为订单异步提交中callBackUrl参数传入 由调用方提供实现 请确保传入的地址是真实可靠的 不然无法通知到

请求示例:
curl --location --request POST 'https://www.test.com/test/notification' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: www.test.com' \
--header 'Connection: keep-alive' \
--data-raw '{"orderId":"test1","orderNo":"111","status":"SUCCESS","message":"1"}'

通知内容如下:
{
    "orderId":"test1",
    "orderNo":"111",
    "status":"SUCCESS",
    "message":"1"
}
其中status定义如下:
enum class Status {
        UNKNOWN, // 暂没实现 失败会主动取消订单 订单状态会变成6
        FAILURE,
        SUCCESS
    }  
返回结果建议如下(ShipOut不对返回结果进行处理,只负责通知调用):
{
    "result": "OK",
    "errorCode": null,
    "message": null,
    "zhMessage": null,
    "errorType": null,
    "data": {
        "orderId": "test1",
        "orderNo": "111",
        "status": "SUCCESS",
        "message": "1"
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37