因为公司已经支持了支付宝支付和微信支付,所以需要加上一个银联的云闪付,主要是二维码支付。
银联商务的官方文档其实已经很清楚了,但是因为之前对接支付宝和微信,有一个保存商户信息的表,表结构已经确定了,再更改的话会比较麻烦易生支付怎么对接云闪付,但是银联的商户信息和支付宝微信又不太相同,支付宝的签名验签主要依靠公钥和秘钥,微信主要有证书,银联却是证书和公钥,而且银联的不同版本会出现不同的验签,5.1.0需要多个证书,demo里的逻辑是证书保存在本地的文件夹,验签时从本地读取,事实上我们需要的是证书保存在数据库,这样就需要进行部分改造对接,这一期的主要对接版本是5.0.0,就是需要一个证书,一个公钥的版本
就拿下单来说,组装参数调用util的地方基本一样,只是有些值的获取变成了从数据库读取
Map contentData = new HashMap();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
contentData.put("version", payAccountInfoDO.getVersion()); //版本号 全渠道默认值
contentData.put("encoding", UnionPayConfig.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
contentData.put("signMethod", UnionPayConfig.signMethod); //签名方法
contentData.put("txnType", "01"); //交易类型 01:消费
contentData.put("txnSu*ype", "07"); //交易子类 07:申请消费二维码
contentData.put("bizType", UnionPayConfig.bizType); //填写000000
contentData.put("channelType", UnionPayConfig.channelType); //渠道类型 08手机
/***商户接入参数***/
contentData.put("merId", payAccountInfoDO.getMchId()); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
contentData.put("accessType", UnionPayConfig.accessType); //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
contentData.put("orderId",transOrderDO.getOutTradeNo()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
contentData.put("txnTime",UnionPayConfig.getCurrentTime()); //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
contentData.put("txnAmt",transOrderDO.getFee().toString()); //交易金额 单位为分,不能带小数点
contentData.put("currencyCode", UnionPayConfig.currencyCode);
contentData.put("backUrl", UnionPayConfig.BACKURL);
byte[] certBytes = payAccountInfoDO.getCertBytes();
String keypwd = payAccountInfoDO.getCertPassword();
String type = payAccountInfoDO.getSignType(); //证书类型
String publicRsa = payAccountInfoDO.getPublicRsa();
UnionUnifiedOrderResult unifiedOrderResult = UnionpayApiUtil.orderResult(logId,contentData,certBytes,keypwd,type,publicRsa);
有看到除了银联原本下单需要的参数外,我还获取到了证书易生支付怎么对接云闪付,证书密码,签名方式和公钥,这样就需要修改银联的签名和验签方法了,一步步的传进去处理一下签名在sdkutil里面,从里签名是在certutil里,有一个方法是将签名私钥证书文件读取为证书对象的,因为我没有保存在文件夹,所以证书是传进去从数据库读取成文件的
/**
* 将签名私钥证书文件读取为证书存储对象
*
* 证书文件名
* @param keypwd
* 证书密码
* @param type
* 证书类型
* @return 证书对象
* @throws IOException
*/
private static KeyStore getKeyInfo(byte[] certBytes, String keypwd, String type) throws IOException {
LogUtil.writeLog("加载签名证书==>" + certBytes);
ByteArrayInputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type, "BC");
LogUtil.writeLog("Load RSA CertPath=[" + certBytes + "],Pwd=["+ keypwd + "],type=["+type+"]");
fis = new ByteArrayInputStream(certBytes);
char[] nPassword = null;
nPassword = null == keypwd || "".equals(keypwd.trim()) ? null: keypwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
return ks;
} catch (Exception e) {
LogUtil.writeErrorLog("getKeyInfo Error", e);
return null;
} finally {
if(null!=fis)
fis.close();
}
}
可以看到原本的证书地址被直接改成byte类型的证书字节流了,再修改一下读取方式变成ByteArrayInputStream的方式读取就可以了,在sdkutil里会设置一个证书的序列号对应一个证书文件,后面基本不用改变
签名完成后继续执行银联下单的post请求,会返回一个请求结果,得到结果后,进行验签,验签时会先判断版本号,因为我们只对接5.0.0,所以不考虑5.1.0时验证签名书链的情况,就是需要多个证书的情况。前面已经把需要的验签公钥传进来了,所以在验签方法中进行修改就可以,在certutil里
/**
* 用配置文件acp_sdk.properties配置路径 加载验证签名证书
*/
private static void initValidateCertFromDir(String publicRsa) {
if(!"01".equals(SDKConfig.getConfig().getSignMethod())){
LogUtil.writeLog("非rsa签名方式,不加载验签证书。");
return;
}
certMap.clear();
CertificateFactory cf = null;
ByteArrayInputStream in = null;
try {
cf = CertificateFactory.getInstance("X.509", "BC");
}catch (NoSuchProviderException e) {
LogUtil.writeErrorLog("LoadVerifyCert Error: No BC Provider", e);
return ;
} catch (CertificateException e) {
LogUtil.writeErrorLog("LoadVerifyCert Error", e);
return ;
}
try {
in = new ByteArrayInputStream(publicRsa.getBytes());
validateCert = (X509Certificate) cf.generateCertificate(in);
if(validateCert == null) {
LogUtil.writeErrorLog("Load verify cert error, " + publicRsa + " has error cert content.");
return ;
}
certMap.put(validateCert.getSerialNumber().toString(),
validateCert);
// 打印证书加载信息,供测试阶段调试
LogUtil.writeLog("[" + publicRsa + "][CertId="
+ encryptCert.getSerialNumber().toString() + "]");
} catch (CertificateException e) {
LogUtil.writeErrorLog("LoadVerifyCert Error", e);
}catch (Exception e) {
LogUtil.writeErrorLog("LoadVerifyCert Error File Not Found", e);
}finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
LogUtil.writeErrorLog(e.toString());
}
}
}
LogUtil.writeLog("LoadVerifyCert Finish");
}
还是把路径变成了ByteArrayInputStream这样的读取,后面的内容基本不用改变
需要注意的是银联的公钥从**下载下来全部保存在数据库,包括前面的begin和后面的end,因为支付宝的没有,所以一开始没有保存那个,一直报错,验签失败,找了好久才发现是因为公钥保存的问题,真的很坑
开发基本就这样,条码、查询什么的基本一样,根据银联的官方文档来就可以了主要是签名和验签的问题
测试的话比较讨厌,不能用云闪付***测试,只能在银联的**有一个二维码仿真,用那个来测试还挺麻烦的
大概就长这个样子
以上
最后共勉
I don't want to be someone that you're settling for. I don't want to be someone that anyone settles for.我不想要你将就,我也不想成为将就的对象。
Tag: 证书 银联 签名 读取 对接