银行卡三要素详版接口文档
更新时间:2023-11-13
接口描述
本文档为银行卡三要素详版接口文档
接口地址
见已购列表
返回格式:json
请求方式:post
接口签名说明
准备签名参数字符串str_for_sign:
请求参数按参数名字典排序(字典序)后,按name1=value1&name2=value2&… 字典序:在英文字典中,排列单词的顺序是先按照第一个字母以升序排列(即a、b、c……z 的顺序);如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,sigh 和 sight),那么把短者排在前。
准备签名参数字符串paramEncrypt:
该字符串是加密所得,比如reqEncryptType为1时,意味着AES加密(目前只支持AES加密)
使用secretKey作为加密密钥,加密前字符串为parameters中的所需参数,如"{"mobile":"xxxxxxxx"}" (根据接口不一样,参数也不一样)
AES加密的密钥长度:32位字符串,对应128位二进制数据;
AES加密的加密模式:aes-128-ecb
JAVA AES加密示例:
                Plain Text
                
            
            1Map<String, Object> paramMap = new HashMap<>();
2paramMap.put("mobile", mobile);
3String s = JSON.toJSONString(paramMap);
4String encrypt = AesEncryptUtil.encrypt(s, {secretKey});
5
6public static String encrypt(String content, String encryptKey) {
7    String key = encryptKey;
8    if (encryptKey.length() > 64) {
9        key = encryptKey.substring(0, DEFAULT_KEY_LEN);
10    }
11    try {
12        Cipher cipher = Cipher.getInstance("AES");
13        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES"));
14        byte[] b = cipher.doFinal(content.getBytes("utf-8"));
15        return Base64.encodeBase64String(b);
16    } catch (Exception e) {
17        log.info("AesEncryptUtil encrypt catch e: {}", e);
18        return null;
19    }
20
21}signature= Md5((str_for_sign+'&')+(paramEncrypt+'&')+timestamp+'&'+secretKey),其中secretKey 是系统获取的sk 注意:参数值为空不需要参与签名,如str_for_sign为空字符串,则直接省去(str_for_sign+'&') 如果采用aes加密,则不需要(str_for_sign+'&'),如果不采用aes加密,则不需要(paramEncrypt+'&')
请求头Header参数
| 参数名称 | 类型 | 是否必选 | 参数描述 | 示例 | 
|---|---|---|---|---|
| appkey | String | 是 | 应用key,在控制台中查看,不可以直接用示例 | 207xxx | 
| timestamp | String | 是 | 时间戳,单位秒 | 1667810816 | 
| signature | String | 是 | 签名 | e06fa22f57fd5c9e9138f183d59b18a | 
请求体Body参数
| 参数名称 | 类型 | 是否必选 | 参数描述 | 示例 | 
|---|---|---|---|---|
| parameters | JSON | 否 | 如果新增paramEncrypt、reqEncryptType参数,则不需要传该参数 | "parameters": {"name":"xxx","idNum":"xxx","mobile":"xxx"} | 
| paramEncrypt | String | 否 | 加密后的请求参数(就是将就是将上述的{"name":"xxx","idNum":"xxx","mobile":"xxx"}进行一次加密,并放入paramEncrypt属性中,详见上文准备签名参数字符串paramEncrypt) | Ht8YMQtfxHplmfPn2BcXXZtE0lN2NGHKEgQrHO5W1/qREKpHOmj21N8uInLRDLxx | 
| reqEncryptType | String | 否 | 入参加密类型 默认为0-不加密;1-AES加密 | 0 | 
注意事项
请注意,以上参数,假设paramEncrypt+reqEncryptType为A组,parameters为B组,A B组必须存在其一,且不可同时存在
- 如果采取入参aes加密,则reqEncryptType、paramEncrypt都需要传递,且reqEncryptType为1;parameters参数不需要传递
- 如果不需要采用入参加密,则传递parameters参数,reqEncryptType、paramEncrypt不需要传递
请求体中parameters参数
| 参数名称 | 类型 | 是否必选 | 参数描述 | 示例 | 
|---|---|---|---|---|
| name | String | 是 | 姓名 | 王xx | 
| idNum | String | 是 | 身份证号码,限单个 | xxxxxxxxx | 
| cardNo | String | 是 | 银行卡号,限单个 | xxxxxxx | 
请求体示例
                Plain Text
                
            
            1#明文请求
2{
3     "parameters": {"name":"{姓名}","idNum":"{身份证号}","cardNo":"{银行卡号}"}
4}
5#AES加密请求(就是将上述的 {"name":"{姓名}","idNum":"{身份证号}","cardNo":"{银行卡号}"}进行一次加密,并放入paramEncrypt属性中,详见上文 准备签名参数字符串paramEncrypt 章节)
6{
7 "paramEncrypt":"Ht8YMQtfxHplmfPn2BcXXZtE0lN2NGHKEgQrHO5W1/qREKpHOmj21N8uInLRDLeO",
8 "reqEncryptType":1
9}响应参数
| 参数名称 | 类型 | 是否存在 | 参数描述 | 示例 | 
|---|---|---|---|---|
| code | String | 是 | 响应码 | 01 | 
| data | json | 是 | 响应数据 | xxxxxxxxx | 
| message | String | 是 | 响应详细 | 成功 | 
data参数
| 参数名称 | 类型 | 是否存在 | 参数描述 | 示例 | 
|---|---|---|---|---|
| id | String | 是 | pnvs响应id | 971961d8ffce4669b75c35fb46738843 | 
| responseParams | json | 是 | 响应参数(非必传,字段可能会变化,建议不要使用) | |
| statusType | String | 是 | 业务状态 | “业务成功” | 
响应体示例
                Plain Text
                
            
            1{
2   "code":"01",
3   "data":{
4               "id":"896487b984e14a9382b384a5d37f7fa5",
5               "statusType":"业务成功"
6          },
7   "message":"验证成功,一致"
8}调用示例(AES版)
                Plain Text
                
            
            1@Test
2public void testApi() {
3    String localUrl = "见已购列表";
4    InvokeRequest invokeRequest = new InvokeRequest();
5    // appKey
6    Long timestamp = System.currentTimeMillis() / 1000;
7    Map<String, Object> paramMap = new HashMap<>();
8
9    
10    String name = "姓名";
11    String idNum = "身份证号";
12    String cardNo = "银行卡号";
13    
14    paramMap.put("name", name);
15    paramMap.put("idNum", idNum);
16    paramMap.put("cardNo", cardNo);
17
18    String encrypt = AesEncryptUtil.encrypt(JSON.toJSONString(paramMap), "sk");
19    invokeRequest.setParamEncrypt(encrypt);
20    invokeRequest.setReqEncryptType(1);
21    String secretKey = "sk";
22    StringBuilder assemble = AssembleUtil.assembleParameters(new HashMap<>());
23    if (StringUtils.isNotEmpty(invokeRequest.getParamEncrypt())) {
24        assemble.append(invokeRequest.getParamEncrypt()).append("&");
25    }
26    assemble.append(timestamp).append("&").append(secretKey);
27    System.out.println("assemble:" + assemble.toString());
28    String signature = MD5Util.md5Encode(assemble.toString());
29    String bodyJson = JSON.toJSONString(invokeRequest);
30    List<Header> headers = new ArrayList<>();
31    Header appKeyHeader = new BasicHeader("appkey", "ak");
32    Header timeStampHeader = new BasicHeader("timestamp",
33            String.valueOf(timestamp));
34    Header msgEncryptionHeader = new BasicHeader("signature", signature);
35    headers.add(appKeyHeader);
36    headers.add(timeStampHeader);
37    headers.add(msgEncryptionHeader);
38    System.out.println(bodyJson);
39    System.out.println(HttpsUtil.post(localUrl, headers.toArray(new Header[]{}), bodyJson,
40            2000, 30000));
41}
42
43
44
45// assemble方法
46public class AssembleUtil {
47    public static <T> StringBuilder assembleParameters(Map<String, T> paramMap) {
48        StringBuilder result = new StringBuilder();
49        if (paramMap == null || paramMap.isEmpty()) {
50            return result;
51        }
52
53        paramMap.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(entry ->
54                result.append(entry.getKey()).append("=").append(entry.getValue()).append("&"));
55        return result;
56    }
57}
58
59// md5方法
60public static String md5Encode(String plainText) {
61    byte[] secretBytes;
62    try {
63        secretBytes = MessageDigest.getInstance("md5").digest(plainText
64                .getBytes());
65    } catch (NoSuchAlgorithmException e) {
66        throw new RuntimeException("无此md5算法");
67    }
68    String md5code = (new BigInteger(1, secretBytes)).toString(16);
69    return StringUtils.leftPad(md5code, 32, "0");
70}