银行卡四要素简版接口文档
更新时间: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加密示例:
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("mobile", mobile);
String s = JSON.toJSONString(paramMap);
String encrypt = AesEncryptUtil.encrypt(s, {secretKey});
public static String encrypt(String content, String encryptKey) {
String key = encryptKey;
if (encryptKey.length() > 64) {
key = encryptKey.substring(0, DEFAULT_KEY_LEN);
}
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES"));
byte[] b = cipher.doFinal(content.getBytes("utf-8"));
return Base64.encodeBase64String(b);
} catch (Exception e) {
log.info("AesEncryptUtil encrypt catch e: {}", e);
return null;
}
}
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 |
mobile | String | 是 | 银行预留手机号,仅支持国内11位号码 | 188xxxxxxx |
cardNo | String | 是 | 银行卡号,限单个 | xxxxxxx |
请求体示例
#明文请求
{
"parameters": {"name":"{姓名}","idNum":"{身份证号}","cardNo":"{银行卡号}","mobile":"{手机号}"}
}
#AES加密请求(就是将上述的就是将上述的{"name":"{姓名}","idNum":"{身份证号}","cardNo":"{银行卡号}","mobile":"{手机号}"}进行一次加密,并放入paramEncrypt属性中,详见上文 准备签名参数字符串paramEncrypt 章节)
{
"paramEncrypt":"Ht8YMQtfxHplmfPn2BcXXZtE0lN2NGHKEgQrHO5W1/qREKpHOmj21N8uInLRDLeO",
"reqEncryptType":1
}
响应参数
参数名称 | 类型 | 是否存在 | 参数描述 | 示例 |
---|---|---|---|---|
code | String | 是 | 响应码 | 01 |
data | json | 是 | 响应数据 | xxxxxxxxx |
message | String | 是 | 响应详细 | 成功 |
data参数
参数名称 | 类型 | 是否存在 | 参数描述 | 示例 |
---|---|---|---|---|
id | String | 是 | pnvs响应id | 971961d8ffce4669b75c35fb46738843 |
responseParams | json | 是 | 响应参数(非必传,字段可能会变化,建议不要使用) | |
statusType | String | 是 | 业务状态 | “业务成功” |
响应体示例
{
"code":"01",
"data":{
"id":"896487b984e14a9382b384a5d37f7fa5",
"statusType":"业务成功"
},
"message":"验证成功,一致"
}
调用示例(AES版)
@Test
public void testTelecomApi() {
String localUrl = "见已购列表";
InvokeRequest invokeRequest = new InvokeRequest();
// appKey
Long timestamp = System.currentTimeMillis() / 1000;
Map<String, Object> paramMap = new HashMap<>();
String mobile = "手机号";
String name = "姓名";
String idNum = "身份证号";
String cardNo = "银行卡号";
paramMap.put("mobile", mobile);
paramMap.put("name", name);
paramMap.put("idNum", idNum);
paramMap.put("cardNo", cardNo);
String encrypt = AesEncryptUtil.encrypt(JSON.toJSONString(paramMap), "sk");
invokeRequest.setParamEncrypt(encrypt);
invokeRequest.setReqEncryptType(1);
String secretKey = "sk";
StringBuilder assemble = AssembleUtil.assembleParameters(new HashMap<>());
if (StringUtils.isNotEmpty(invokeRequest.getParamEncrypt())) {
assemble.append(invokeRequest.getParamEncrypt()).append("&");
}
assemble.append(timestamp).append("&").append(secretKey);
System.out.println("assemble:" + assemble.toString());
String signature = MD5Util.md5Encode(assemble.toString());
String bodyJson = JSON.toJSONString(invokeRequest);
List<Header> headers = new ArrayList<>();
Header appKeyHeader = new BasicHeader("appkey", "ak");
Header timeStampHeader = new BasicHeader("timestamp",
String.valueOf(timestamp));
Header msgEncryptionHeader = new BasicHeader("signature", signature);
headers.add(appKeyHeader);
headers.add(timeStampHeader);
headers.add(msgEncryptionHeader);
System.out.println(bodyJson);
System.out.println(HttpsUtil.post(localUrl, headers.toArray(new Header[]{}), bodyJson,
2000, 30000));
}
// assemble方法
public class AssembleUtil {
public static <T> StringBuilder assembleParameters(Map<String, T> paramMap) {
StringBuilder result = new StringBuilder();
if (paramMap == null || paramMap.isEmpty()) {
return result;
}
paramMap.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(entry ->
result.append(entry.getKey()).append("=").append(entry.getValue()).append("&"));
return result;
}
}
// md5方法
public static String md5Encode(String plainText) {
byte[] secretBytes;
try {
secretBytes = MessageDigest.getInstance("md5").digest(plainText
.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("无此md5算法");
}
String md5code = (new BigInteger(1, secretBytes)).toString(16);
return StringUtils.leftPad(md5code, 32, "0");
}