消息类接口开发规范

一、调用介绍

到家通过HTTP协议POST方式调用商家消息接收接口,实时为商家推送通知消息。推荐商家在接收到消息且进行基础校验后,后续逻辑采用异步方式处理。商家提供的消息接收接口性能要求: TP99 <200ms, TPS>1000, 最长响应时间超时阀值为3s。
由于网络原因、接口响应超时或要求业务重试(code返回-10000),平台会自动重新推送消息:重试最大有效期为4小时,每次重试间隔最大为5分钟,4小时内间隔性重试,要求接口具备消息排重能力。

二、调用环境入口

环境入口释义:推送消息时,需要调用商家侧开发的消息接收接口,接收相应订单状态下的数据,接口命名规则如下:

http调用入口地址:http://xxx.xxx.xxx/xxx/djsw/接口名,(其中红色部分商家自定义)。

三、调用参数

商家侧开发消息接收接口,须严格按照开放平台签名规则开发,以下签名示例,仅供参考。
调用API时,必须传入系统级参数和对应的应用级参数。各个API的输入参数和返回结果详见API文档。

①系统级参数

字段

类型

是否必须

描述

token

String

采用OAuth授权方式为必填参数。

app_key

String

应用的app_key

sign

String

签名

timestamp

String

时间戳,格式为yyyy-MM-dd HH:mm:ss,例如:2011-06-16 13:23:30。

format

String

暂时只支持json

v

String

API协议版本,可选值:1.0.

②如下为应用级参数拼装(拼装示例 ,仅供参考)

字段

描述

示例值

jd_param_json

String

标准json 类型

{
"billId":"10003129",
"statusId":"33060",
"timestamp":"2015-10-16 13:23:30",
}


encrypt_jd_param_json

String

密文字符串

vADmYTpJtnUjzVWBTnGB9BubRu9CsgO41oBBFC3yhKWFOd5x1/lh5vpOtWHnO2JRoIr6pvFklvdGuAYwyo54Y
KilGDTwGNU6fP44iK4f/lf4XLi+jxrqA1ytlVsdjqcISYld4ZQLA/WbigFggCCzhA==


四、API签名

调用API时需要对请求参数进行签名,服务器端会验证请求参数是否合法。

加密规则

① 所有请求参数按照字母先后顺序排列

例如:将token,app_key,timestamp,format,v 排序为app_key,format,timestamp,token,v

② 把所有参数名和参数值进行拼装

例如:app_keyxxxformatxxxxtimestampxxxxxxtokenxxxvx

③ 把appSecret夹在字符串的两端

例如:appSecret+XXXX+appSecret

④ 使用MD5进行加密,再转化成大写


示例(以下示例只体现逻辑)

调用http://xxxx.xxx.xxx/xxx/djsw/接口名 ,假设app_key=yourappkey、appSecret=yourappSecret、token=yourtoken

1) 输入参数

token=yourtoken
app_key=yourappkey
timestamp=2015-10-16  13:23:30
format=json
v=1.0
jd_param_json=
{
"billId":"10003129",
"statusId":"33060",
"timestamp": "2015-10-16 13:23:30",
}

2) 把参数按照字母顺序排列

app_key=yourappkey
format=json
jd_param_json=
{
"billId":"10003129",
"statusId":"33060",
"timestamp": "2015-10-16 13:23:30",
}
timestamp=2016-07-20 11:10:04
token=yourtoken
v=1.0

3) 连接参数名与参数值,并在首尾加上appSecret

3) 对于加密的消息,jd_param_json没有值的情况下,一定要将encrypt_jd_param_json字段中的值解密,然后赋到jd_param_json字段中,然后根据以上规则参与签名,encrypt_jd_param_json本身并不直接参与签名

授权接口:(示例)

yourappSecretapp_keyyourappkeyformatjsonjd_param_json{"billId":"10003129","statusId":"33060","timestamp":"2015-10-16 13:23:30"

}timestamp2016-07-20 11:10:04tokenyourtokenv1.0yourappSecret

注:

①以上字符串不允许有跨行,粘贴时请特别注意,除日期和时间中间的空格之外,不允许在其它位置出现空格。


4) MD5加密后转成大写:EDBE87FB9640EDD681FEBC79D858AAC0

注:

①以上字符串不允许有跨行,粘贴时请特别注意,除日期和时间中间的空格之外,不允许在其它位置出现空格。

②反向签名验证时,在接收到的jd_param_json值,当成普通字符串,不要解析成array,然后直接参与签名运算


五、数据加密规则

1、加密算法采取AES(AES/CBC/NoPadding,即AES-128-CBC加密的NoPadding模式)算法。通知参数增加新字段encrypt_jd_param_json,此字段是对原字段jd_param_json的整体加密,加密的接口在API文档列表上会打上密标
2、数据解析规则:

1)、对于无需加密的消息,数据内容不变

2)、对于需要加密的消息,明文密文并行期,两个字段jd_param_json(明文),encrypt_jd_param_json(密文)均有值

3)、并行期过后,仅encrypt_jd_param_json(密文)有值

4)、具体处理逻辑(参考):

所有消息,优先判断密文字段encrypt_jd_param_json内容是否为空,

如果为空,仍然解析明文字段jd_param_json中的业务数据

如果不为空的,获取密文字段encrypt_jd_param_json内容并解密,解密后获取业务数据(解密后的数据等效于明文字段jd_param_json中的数据)

一定不要将需要解密的消息限制到一个固定范围,只针对现有加密接口进行解密。请参考以上逻辑,一旦有非加密接口变成加密接口时,仍然能够正常解析

在消息仅发送密文的情况下,jd_param_json的值为空,此时请注意签名问题,签名规则不变,所以需要将解密后的内容放到jd_param_json下,来参与签名,encrypt_jd_param_json字段并不直接参与签名运算

5)、AES算法需要的key和偏移量采用appSecret的前16位和后16位

以java版SDK为例,使用方法

String key = appSecret.substring(0, 16);
String iv = appSecret.substring(16, 32);
String json = AESUtils.decryptAES(encrypt_jd_param_json, key, iv);

3、参考示例(可用于验证算法):

密文:8FvHJcQmVojAIU61SNaS1ermHN2UVWknueRHFSNf2q5EbxNNmznoTYpRu7ySc/8CuU+QGZ9UIBMCyTuFafY3PuszEokEKc8M1Qfv/+o15h5bIU8LXfwRKOCm3JYzZtTOvJVU0hk/USvtDgraToszFl2hQZjZN5gGH1af0X8vopo=
key:0bcbe9d6e6124cf2
iv:aef2856a540f1326
对应明文:{"billId":"232219501234567","outBillId":"12345678901","statusId":"150","storeId":"11912345","timestamp":"2022-08-14 17:24:44"}

4、具体示例代码(JAVA):

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @Classname AESUtils
 */
public class AESUtils {

    /**
     * @Description AES算法加密明文,加密用的Key 可以用26个字母和数字组成 使用AES-128-CBC加密模式,key需要为16位。
     * key 密钥,长度16
     * iv 偏移量,长度16
     * @param data 明文
     * @return 密文
     */
    public static String encryptAES(String data,String key,String iv) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;

            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());  // CBC模式,需要一个向量iv,可增加加密算法的强度

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return AESUtils.encode(encrypted).trim(); // BASE64做转码。

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Description AES算法解密密文
     * @param data 密文
     * @return 明文
     */
    public static String decryptAES(String data,String key,String iv) throws Exception {
        try
        {
            byte[] encrypted1 = AESUtils.decode(data);//先用base64解密

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString.trim();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 编码
     * @param byteArray
     * @return
     */
    public static String encode(byte[] byteArray) {
        return new String(new Base64().encode(byteArray));
    }

    /**
     * 解码
     * @param base64EncodedString
     * @return
     */
    public static byte[] decode(String base64EncodedString) {
        return new Base64().decode(base64EncodedString);
    }

}

				    

六、API调用注意事项

• 所有的API请求和响应数据编码皆为UTF-8格式(切勿转成unicode编码),仅支持Post表单请求(Content-Type是application/x-www-form-urlencoded),请求里的所有参数值在发送请求时会URLEncoder,接收请求时需做URLDecoder处理

API请求的应用级参数需要转换成json格式,例如:jd_param_json={"return_id":"20032","trade_no":"20032"}

• json格式应为标准的json

• 目前接口返回结果格式只支持json格式,具体格式示例:{"code":"0","msg":"success","data":""}

• 所有API请求和响应内的日期格式都为yyyy-MM-dd HH:mm:ss,注意小时格式是24小时制,例如:2011-07-21 20:23:30

• 签名方式为 md5(appsecret + key+ value .... key + value+appsecret)然后转大写字母,其中key、value对是除签名所有请求参数按key做的升序排列,value无需编码。

七、错误码

错误码 描述
0 操作成功
-1 操作失败
-10000 业务重试
10005 必填项参数未填
10013 无效Token令牌
10014 无效Sign签名
10015 API参数异常
10018 不存在的方法名