java银联支付

article/2025/10/9 0:44:26

貌似最近银联出了什么活动,公司急需接入银联支付, 作为写代码的我们 , 只能服从命令,特地记录下接入银联的过程  ,后续还会说接入支付宝和微信的亲自体验历程....................................................................

1.申请商户

    首先得去银联那里开通商户,这个基本上是别人完成的 ,然后银联会给我们发一封邮件,里面包含商户号和生产环境的一些配置,这里有一个注意的就是得使用IE浏览器安装控件 , 谷歌是不行滴,,,,,,,附件里面也有操作文档 ,可以查看,就不多说了,补几张过程图吧

配置好后 下载一个.pfx的签名文件,后面用到

2. 下载官方DEMO

 去银联官网上下载官方的DEMO,解压后找到JAVA的SDK,文件如图

这里面含有一些配置文件,在assets文件夹下

3.测试环境配置

 下面进入代码阶段,先配置下测试环境,后面改成正式环境即可

文件的结构:

  

 先贴出配置文件代码,找到一个叫acp_sdk.properties的配置文件

 

##############SDK配置文件(证书方式签名)################
# 说明:
# 1. 使用时请删除后缀的“.证书”,并将此文件复制到src文件夹下替换原来的acp_sdk.properties。
# 2. 具体配置项请根据注释修改。
#
################################################
#商户号
acpsdk.merId=777290058110048##########################入网测试环境交易发送地址(线上测试需要使用生产环境交易请求地址)#############################
##交易请求地址#测试环境
acpsdk.frontTransUrl=https://gateway.test.95516.com/gateway/api/frontTransReq.do
acpsdk.backTransUrl=https://gateway.test.95516.com/gateway/api/backTransReq.do
acpsdk.singleQueryUrl=https://gateway.test.95516.com/gateway/api/queryTrans.do
acpsdk.batchTransUrl=https://gateway.test.95516.com/gateway/api/batchTrans.do
acpsdk.fileTransUrl=https://filedownload.test.95516.com/
acpsdk.appTransUrl=https://gateway.test.95516.com/gateway/api/appTransReq.do
acpsdk.cardTransUrl=https://gateway.test.95516.com/gateway/api/cardTransReq.do#以下缴费产品使用,其余产品用不到
#测试
acpsdk.jfFrontTransUrl=https://gateway.test.95516.com/jiaofei/api/frontTransReq.do
acpsdk.jfBackTransUrl=https://gateway.test.95516.com/jiaofei/api/backTransReq.do
acpsdk.jfSingleQueryUrl=https://gateway.test.95516.com/jiaofei/api/queryTrans.do
acpsdk.jfCardTransUrl=https://gateway.test.95516.com/jiaofei/api/cardTransReq.do
acpsdk.jfAppTransUrl=https://gateway.test.95516.com/jiaofei/api/appTransReq.do######################################################################### 报文版本号,固定5.1.0,请勿改动
acpsdk.version=5.1.0# 签名方式,证书方式固定01,请勿改动
acpsdk.signMethod=01# 是否验证验签证书的CN,测试环境请设置false,生产环境请设置true。非false的值默认都当true处理。
acpsdk.ifValidateCNName=false# 是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再false。非true的值默认都当false处理。
acpsdk.ifValidateRemoteCert=false#前台通知地址,填写后台接收银联前台通知的地址
acpsdk.frontUrl=https://XXXXXXXXx/union/successRedict#后台通知地址,填写后台接收银联后台通知的地址,必须外网能访问
#111.204.128.115:8929 ---> 192.168.0.54:8087
acpsdk.backUrl=https://XXXXXXXXx/union/backRcvResponse
#111.204.128.115:8917 ---> 192.168.0.54:8085
#acpsdk.backUrl=http://111.204.128.115:8917/garage/wx/pay/union/backRcvResponse
#111.204.128.115:8925 ---> 192.168.1.19:8087
#acpsdk.backUrl=http://111.204.128.115:8925/garageWechat/wx/pay/union/backRcvResponse#########################入网测试环境签名证书配置 ################################
# 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例:
acpsdk.signCert.path=cert/acp_test_sign.pfx
# linux样例(注意:在linux下读取证书需要保证证书有被应用读的权限)(后续其他路径配置也同此条说明)
#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/acp700000000000001.pfx# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
acpsdk.signCert.pwd=123456
# 签名证书类型,固定不需要修改
acpsdk.signCert.type=PKCS12##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
acpsdk.encryptCert.path=cert/acp_test_enc.cer##########################验签证书配置################################
# 验签中级证书路径(银联提供)
acpsdk.middleCert.path=cert/acp_test_middle.cer
# 验签根证书路径(银联提供)
acpsdk.rootCert.path=cert/acp_test_root.cer

其中测试环境的证书,在下载的DEMO中含有

配置文件到此基本上就已经完成了,下面贴出支付的代码

 @Overridepublic void pay(HttpServletRequest request, HttpServletResponse response) throws IOException {request.setCharacterEncoding(DemoBase.encoding);response.setContentType("text/html; charset="+ DemoBase.encoding);String orderId = String.valueOf(System.currentTimeMillis()); //实际上是orderSnString txnAmt = null;  //订单金额String txnTime = null; //订单发送时间//交易金额,单位分,不要带小数点BigDecimal amount = new BigDecimal("0.01");amount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);txnAmt = amount.toString();//订单发送时间用于查询用Date sendPaymentDate = new Date();txnTime = formatTime(sendPaymentDate);Map<String, String> requestData = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/requestData.put("version", DemoBase.version);   			  //版本号,全渠道默认值requestData.put("encoding", DemoBase.encoding); 			  //字符集编码,可以使用UTF-8,GBK两种方式requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法requestData.put("txnType", "01");               			  //交易类型 ,01:消费requestData.put("txnSubType", "01");            			  //交易子类型, 01:自助消费requestData.put("bizType", "000201");           			  //业务类型,B2C网关支付,手机wap支付requestData.put("channelType", "08");           			  //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板  08:手机/***商户接入参数***/requestData.put("merId", merId);    	          			  //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号requestData.put("accessType", "0");             			  //接入类型,0:直连商户requestData.put("orderId", String.valueOf(System.currentTimeMillis()));                          //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则requestData.put("txnTime", formatTime(new Date()));        //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效requestData.put("currencyCode", "156");         			  //交易币种(境内商户一般是156 人民币)requestData.put("txnAmt", "1");  //交易金额,单位分,不要带小数点requestData.put("accType",  "01");   //账号类型 01:银行卡02:存折03:IC卡帐号类型(卡介质)//前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址//如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限//异步通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知//  requestData.put("frontUrl", DemoBase.frontUrl);//后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知//后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知//注意:1.需设置为外网能访问,否则收不到通知    2.http https均可  3.收单后台通知后需要10秒内返回http200或302状态码//    4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。//    5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败requestData.put("backUrl", DemoBase.backUrl);// 订单超时时间。// 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。// 此时间建议取支付时的北京时间加15分钟。// 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。// requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));//////       报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt/////**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**//* Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding);  //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();  //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrlString html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding);   //生成自动跳转的Html表单*/Map<String, String> reqData = AcpService.sign(requestData,DemoBase.encoding);			 //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl();								 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrlMap<String, String> rspData = AcpService.post(reqData,requestAppUrl,DemoBase.encoding);  //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过// LogUtil.writeLog("打印请求HTML,此为请求报文,为联调排查问题的依据:"+html);//将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");String respCode = rspData.get("respCode") ;if(("00").equals(respCode)){//成功,获取tn号String tn = rspData.get("tn");System.out.println(tn);//TODO}else{//其他应答码为失败请排查原因或做失败处理//TODO}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");}String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);response.getWriter().write("请求报文:<br/>"+reqMessage+"<br/>" + "应答报文:</br>"+rspMessage+"");}

如果是手机控件支付的话 将获取到的银联的订单号tn之前返回给手机端即可,无需其他处理

 

测试商户号在官网申请支付测试,会找到测试商户号

也含有测试的银行卡和手机号

 

至此,测试环境即可完成,调用接口,返回如下信息

大功告成!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 

4.正式环境配置

1.将请求连接换成正式请求连接

2.将配置文件中的某些false改成true

3.证书的修改,在下载的DEMo中会找到三个证书,还缺啥呢? 对就是缺少前面下载的那个.pfx结尾的文件,将下载的证书copy进来,然后将配置文件中的证书修改成正式的即可

4. 注意   注意  注意   

  本人用的是springboot项目 ,发布时使用jar包发布的,那么用官方的包中的读取配置文件方式是有问题的,所以需要修改读取配置文件方式

修改后

这下问题是解决了,代码 代码   代码  一个个贴出来

 

package com.example.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** Created by wangfei on 2017/4/22.*/@Validated
@RestController
@RequestMapping("/union")
public class UnipayController {@Autowiredprivate UnionPaymentService service;/*** 支付* @param request* @param response* @throws IOException*/@RequestMapping(value = "/pay", method = {RequestMethod.POST, RequestMethod.GET})public void pay(HttpServletRequest request, HttpServletResponse response) throws IOException{service.pay(request, response);}/*** 后台回调* @param request* @param response* @throws IOException*/@RequestMapping(value = "/backRcvResponse", method = {RequestMethod.GET, RequestMethod.POST})public void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {service.backRcvResponse(request, response);}/*** 前台回调* @param request* @param response* @throws IOException*/@RequestMapping(value = "/frontRcvResponse", method = RequestMethod.POST)public void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {service.frontRcvResponse(request, response);}/*** 成功后跳转* @param request* @param response* @throws IOException*/@RequestMapping(value = "/successRedict", method = RequestMethod.POST)public void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException {service.successRedict(request, response);}/*** 查询、检查交易状态* @param request* @param response*/@RequestMapping(value = "/query", method = RequestMethod.POST)public void query(HttpServletRequest request, HttpServletResponse response) throws IOException {service.query(request, response);}/*** 交易状态查询* @param orderId*/@RequestMapping(value = "/check", method = RequestMethod.POST)public JsonResult check(Long orderId) {try {service.check(orderId);return JsonResult.resultSuccess("已支付", "");} catch (Exception e) {e.printStackTrace();return JsonResult.resultError("未发现支付信息");}}}

 

package com.example.demo;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public interface UnionPaymentService {/*** 支付* @param request* @param response*/void pay(HttpServletRequest request, HttpServletResponse response) throws IOException;/*** 后台回调* @param request* @param response*/void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException;/*** 前台回调* @param request* @param response* @throws IOException*/void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;/*** 交易状态查询* @param request* @param response*/void query(HttpServletRequest request, HttpServletResponse response) throws IOException;/*** 支付成功后的跳转* @param request* @param response*/void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException;/*** 检查支付结果* @param shopOrderId* @throws IOException*/void check(Long shopOrderId);
}
package com.example.demo;import com.example.demo.union.DemoBase;
import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.service.AcpService;
import com.example.demo.union.util.CertUtil;
import com.example.demo.union.util.LogUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;/*** Created by gangsun on 2017/4/12.*/
@Service
public class UnionPaymentServiceImpl implements UnionPaymentService {//先取商户号private String merId = "700000000000001";private String redictUrl = "http://www.baidu.com";@PostConstructpublic void  init(){System.out.println("银联支付初始化");SDKConfig.getConfig().loadPropertiesFromSrc(); //从classpath加载acp_sdk.properties文件CertUtil.init();}/*** 支付* @param request* @param response*/@Overridepublic void pay(HttpServletRequest request, HttpServletResponse response) throws IOException {request.setCharacterEncoding(DemoBase.encoding);response.setContentType("text/html; charset="+ DemoBase.encoding);String orderId = String.valueOf(System.currentTimeMillis()); //实际上是orderSnString txnAmt = null;  //订单金额String txnTime = null; //订单发送时间//交易金额,单位分,不要带小数点BigDecimal amount = new BigDecimal("0.01");amount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);txnAmt = amount.toString();//订单发送时间用于查询用Date sendPaymentDate = new Date();txnTime = formatTime(sendPaymentDate);Map<String, String> requestData = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/requestData.put("version", DemoBase.version);   			  //版本号,全渠道默认值requestData.put("encoding", DemoBase.encoding); 			  //字符集编码,可以使用UTF-8,GBK两种方式requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法requestData.put("txnType", "01");               			  //交易类型 ,01:消费requestData.put("txnSubType", "01");            			  //交易子类型, 01:自助消费requestData.put("bizType", "000201");           			  //业务类型,B2C网关支付,手机wap支付requestData.put("channelType", "08");           			  //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板  08:手机/***商户接入参数***/requestData.put("merId", merId);    	          			  //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号requestData.put("accessType", "0");             			  //接入类型,0:直连商户requestData.put("orderId", String.valueOf(System.currentTimeMillis()));                          //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则requestData.put("txnTime", formatTime(new Date()));        //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效requestData.put("currencyCode", "156");         			  //交易币种(境内商户一般是156 人民币)requestData.put("txnAmt", "1");  //交易金额,单位分,不要带小数点requestData.put("accType",  "01");   //账号类型 01:银行卡02:存折03:IC卡帐号类型(卡介质)//前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址//如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限//异步通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知//  requestData.put("frontUrl", DemoBase.frontUrl);//后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知//后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知//注意:1.需设置为外网能访问,否则收不到通知    2.http https均可  3.收单后台通知后需要10秒内返回http200或302状态码//    4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。//    5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败requestData.put("backUrl", DemoBase.backUrl);// 订单超时时间。// 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。// 此时间建议取支付时的北京时间加15分钟。// 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。// requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));//////       报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt/////**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**//* Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding);  //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();  //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrlString html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding);   //生成自动跳转的Html表单*/Map<String, String> reqData = AcpService.sign(requestData,DemoBase.encoding);			 //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl();								 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrlMap<String, String> rspData = AcpService.post(reqData,requestAppUrl,DemoBase.encoding);  //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过// LogUtil.writeLog("打印请求HTML,此为请求报文,为联调排查问题的依据:"+html);//将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");String respCode = rspData.get("respCode") ;if(("00").equals(respCode)){//成功,获取tn号String tn = rspData.get("tn");System.out.println(tn);//TODO}else{//其他应答码为失败请排查原因或做失败处理//TODO}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");}String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);response.getWriter().write("请求报文:<br/>"+reqMessage+"<br/>" + "应答报文:</br>"+rspMessage+"");}/*** 后台回调* @param request* @param response*/@Overridepublic void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {LogUtil.writeLog("BackRcvResponse接收后台通知开始");String encoding = request.getParameter(SDKConstants.param_encoding);// 获取银联通知服务器发送的后台通知参数Map<String, String> reqParam = getAllRequestParam(request);LogUtil.printRequestLog(reqParam);Map<String, String> valideData = null;if (null != reqParam && !reqParam.isEmpty()) {Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();valideData = new HashMap<String, String>(reqParam.size());while (it.hasNext()) {Map.Entry<String, String> e = it.next();String key = (String) e.getKey();String value = (String) e.getValue();value = new String(value.getBytes(encoding), encoding);valideData.put(key, value);}}//重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过if (!AcpService.validate(valideData, encoding)) {LogUtil.writeLog("验证签名结果[失败].");//验签失败,需解决验签问题} else {LogUtil.writeLog("验证签名结果[成功].");//【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态String orderId =valideData.get("orderId"); //获取后台通知的数据,其他字段也可用类似方式获取String orderSn =orderId; //orderId其实存的是SnString respCode = valideData.get("respCode");String txnAmt = valideData.get("txnAmt");BigDecimal txnAmount = (new BigDecimal(txnAmt)).multiply(new BigDecimal(0.01));String queryId = valideData.get("queryId");String traceTime = valideData.get("traceTime");String payCardNo = valideData.get("payCardNo");String payCardType = valideData.get("payCardType"); //支付卡类型String paymentMethodMethod; //PayPaymentMethod里面的method字段if(StringUtils.isEmpty(payCardType)){paymentMethodMethod = "UNION";  //对之前代码做兼容,如果没有支付卡类型的情况走默认}else{paymentMethodMethod = "UNION-" + payCardType;}//判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。if("00".equals(respCode)){  // 00 交易成功//todo 若交易成功}else if("A6".equals(respCode)){  // A6 部分成功}}LogUtil.writeLog("BackRcvResponse接收后台通知结束");//返回给银联服务器http 200  状态码response.getWriter().print("ok");}@Overridepublic void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {LogUtil.writeLog("FrontRcvResponse前台接收报文返回开始");String encoding = request.getParameter(SDKConstants.param_encoding);LogUtil.writeLog("返回报文中encoding=[" + encoding + "]");String pageResult = "";if (DemoBase.encoding.equalsIgnoreCase(encoding)) {pageResult = "/utf8_result.jsp";} else {pageResult = "/gbk_result.jsp";}Map<String, String> respParam = getAllRequestParam(request);// 打印请求报文LogUtil.printRequestLog(respParam);Map<String, String> valideData = null;StringBuffer page = new StringBuffer();if (null != respParam && !respParam.isEmpty()) {Iterator<Map.Entry<String, String>> it = respParam.entrySet().iterator();valideData = new HashMap<String, String>(respParam.size());while (it.hasNext()) {Map.Entry<String, String> e = it.next();String key = (String) e.getKey();String value = (String) e.getValue();value = new String(value.getBytes(encoding), encoding);page.append("<tr><td width=\"30%\" align=\"right\">" + key+ "(" + key + ")</td><td>" + value + "</td></tr>");valideData.put(key, value);}}if (!AcpService.validate(valideData, encoding)) {page.append("<tr><td width=\"30%\" align=\"right\">验证签名结果</td><td>失败</td></tr>");LogUtil.writeLog("验证签名结果[失败].");} else {page.append("<tr><td width=\"30%\" align=\"right\">验证签名结果</td><td>成功</td></tr>");LogUtil.writeLog("验证签名结果[成功].");System.out.println(valideData.get("orderId")); //其他字段也可用类似方式获取String respCode = valideData.get("respCode");//判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。}request.setAttribute("result", page.toString());request.getRequestDispatcher(pageResult).forward(request, response);LogUtil.writeLog("FrontRcvResponse前台接收报文返回结束");}@Overridepublic void query(HttpServletRequest request, HttpServletResponse response) throws IOException {String orderId = String.valueOf(System.currentTimeMillis()); //实际上是orderSnString txnTime = null; //订单发送时间//订单发送时间Map<String, String> data = new HashMap<String, String>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", DemoBase.version);                 //版本号data.put("encoding", DemoBase.encoding);               //字符集编码 可以使用UTF-8,GBK两种方式data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "00");                             //交易类型 00-默认data.put("txnSubType", "00");                          //交易子类型  默认00data.put("bizType", "000201");                         //业务类型 B2C网关支付,手机wap支付/***商户接入参数***/data.put("merId", merId);                  //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0");                           //接入类型,商户接入固定填0,不需修改/***要调通交易以下字段必须修改***/data.put("orderId", orderId);                 //****商户订单号,每次发交易测试需修改为被查询的交易的订单号data.put("txnTime", txnTime);                 //****订单发送时间,每次发交易测试需修改为被查询的交易的订单发送时间/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String url = SDKConfig.getConfig().getSingleQueryUrl();// 交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过Map<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");if("00".equals(rspData.get("respCode"))){//如果查询交易成功//处理被查询交易的应答码逻辑String origRespCode = rspData.get("origRespCode");if("00".equals(origRespCode)){//交易成功,更新商户订单状态//TODO}else if("03".equals(origRespCode) ||"04".equals(origRespCode) ||"05".equals(origRespCode)){//需再次发起交易状态查询交易//TODO}else{//其他应答码为失败请排查原因//TODO}}else{//查询交易本身失败,或者未查到原交易,检查查询交易报文要素//TODO}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");}String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);response.getWriter().write("</br>请求报文:<br/>"+reqMessage+"<br/>" + "应答报文:</br>"+rspMessage+"");}@Overridepublic void check(Long shopOrderId){String orderId = String.valueOf(System.currentTimeMillis()); //实际上是orderSnString txnTime = null; //订单发送时间Map<String, String> data = new HashMap<String, String>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", DemoBase.version);                 //版本号data.put("encoding", DemoBase.encoding);               //字符集编码 可以使用UTF-8,GBK两种方式data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "00");                             //交易类型 00-默认data.put("txnSubType", "00");                          //交易子类型  默认00data.put("bizType", "000201");                         //业务类型 B2C网关支付,手机wap支付/***商户接入参数***/data.put("merId", merId);                  //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0");                           //接入类型,商户接入固定填0,不需修改/***要调通交易以下字段必须修改***/data.put("orderId", orderId);                 //****商户订单号,每次发交易测试需修改为被查询的交易的订单号data.put("txnTime", txnTime);                 //****订单发送时间,每次发交易测试需修改为被查询的交易的订单发送时间/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。LogUtil.writeLog("查询请求数据: " + reqData.toString());String url = SDKConfig.getConfig().getSingleQueryUrl();// 交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过Map<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);LogUtil.writeLog("查询响应数据: " + rspData.toString());/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");if("00".equals(rspData.get("respCode"))){//如果查询交易成功//处理被查询交易的应答码逻辑String origRespCode = rspData.get("origRespCode");if("00".equals(origRespCode)){//交易成功,更新商户订单状态String txnAmt = rspData.get("txnAmt");BigDecimal txnAmount = null;if(!StringUtils.isEmpty(txnAmt)){txnAmount = (new BigDecimal(txnAmt)).multiply(new BigDecimal(0.01));  //分转换为元;}String queryId = rspData.get("queryId");String traceTime = rspData.get("traceTime");String payCardNo = rspData.get("payCardNo");String payCardType = rspData.get("payCardType"); //支付卡类型String paymentMethodMethod; //PayPaymentMethod里面的method字段if(StringUtils.isEmpty(payCardType)){paymentMethodMethod = "UNION";  //对之前代码做兼容,如果没有支付卡类型的情况走默认}else{paymentMethodMethod = "UNION-" + payCardType;}//更改支付状态//改变工单状态}else if("03".equals(origRespCode) ||"04".equals(origRespCode) ||"05".equals(origRespCode)){//需再次发起交易状态查询交易throw new RuntimeException("查询结果:订单号" + orderId + "交易失败,应答码为“" + origRespCode + "”");}else{//其他应答码为失败请排查原因throw new RuntimeException("查询结果:订单号" + orderId + "交易失败,应答码为“" + origRespCode + "”");}}else{//查询交易本身失败,或者未查到原交易,检查查询交易报文要素throw new RuntimeException("查询失败");}}else{LogUtil.writeErrorLog("验证签名失败");throw new RuntimeException("验证签名失败");//TODO 检查验证签名失败的原因}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");throw new RuntimeException("未获取到返回报文或返回http状态码非200");}}@Overridepublic void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException {response.sendRedirect(redictUrl);}//---------------------------------------------------- private -----------------------------------------------------/*** 获取请求参数中所有的信息** @param request* @return*/private static Map<String, String> getAllRequestParam(final HttpServletRequest request) {Map<String, String> res = new HashMap<String, String>();Enumeration<?> temp = request.getParameterNames();if (null != temp) {while (temp.hasMoreElements()) {String en = (String) temp.nextElement();String value = request.getParameter(en);res.put(en, value);//在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段>//System.out.println("ServletUtil类247行  temp数据的键=="+en+"     值==="+value);if (null == res.get(en) || "".equals(res.get(en))) {res.remove(en);}}}return res;}//收费比率 精确到分保留两位小数四舍五入private BigDecimal getFeeAmount(BigDecimal amount, BigDecimal feeRatio, BigDecimal feeMax) {BigDecimal fee = new BigDecimal(0);if(null == amount || null == feeRatio) return fee;//金额乘以费率 = 手续费fee = amount.multiply(feeRatio);//最大值为feeMaxif(null != feeMax && feeMax.compareTo(new BigDecimal("0")) >= 0) fee = fee.max(feeMax);//设置精确到分并四舍五入fee = fee.setScale(4, BigDecimal.ROUND_HALF_UP);return fee;}/*** 时间格式化* @param date* @return*/private static String formatTime(Date date){return new SimpleDateFormat("yyyyMMddHHmmss").format(date);}private static Date formatTime(String dateStr){if(null == dateStr) return null;if(dateStr.length() == 14){try {return new SimpleDateFormat("yyyyMMddHHmmss").parse(dateStr);} catch (ParseException e) {e.printStackTrace();return null;}}else{try {return new SimpleDateFormat("MMddHHmmss").parse(dateStr);} catch (ParseException e) {e.printStackTrace();return null;}}}}
package com.example.demo;import java.util.HashMap;
import java.util.Map;/*** Created by think on 2017/6/5.*/
public class JsonResult {private Boolean success = Boolean.valueOf(false);private String msg = "";private Object obj = null;private Integer type = Integer.valueOf(0);private Map<String, String> fieldErrors = new HashMap();public JsonResult() {}public JsonResult(Boolean success, String msg) {this.success = success;this.msg = msg;}public JsonResult(Boolean success, String msg, Integer type) {this.success = success;this.msg = msg;this.type = type;}public JsonResult(Boolean success, String msg, Object obj, Integer type) {this.success = success;this.msg = msg;this.obj = obj;this.type = type;}public Boolean getSuccess() {return Boolean.valueOf(this.success.booleanValue() && this.fieldErrors.size() == 0);}public void setSuccess(Boolean success) {this.success = success;}public String getMsg() {return this.msg;}public void setMsg(String msg) {this.msg = msg;}public Object getObj() {return this.obj;}public void setObj(Object obj) {this.obj = obj;}public Integer getType() {return this.type;}public void setType(Integer type) {this.type = type;}public void addFieldError(String field, String message) {this.fieldErrors.put(field, message);}public void addFieldErrors(Map<String, String> fieldErrors) {this.fieldErrors.putAll(fieldErrors);}public Map<String, String> getFieldErrors() {return this.fieldErrors;}public static JsonResult resultError(String message) {return resultError(message, 0);}public static JsonResult resultError(String message, int type) {return new JsonResult(Boolean.valueOf(false), message, Integer.valueOf(0));}public static JsonResult resultSuccess(Object obj) {return resultSuccess("", obj);}public static JsonResult resultSuccess(String message, Object obj) {return resultSuccess(message, obj, 0);}public static JsonResult resultSuccess(String message, Object obj, int type) {return new JsonResult(Boolean.valueOf(true), message, obj, Integer.valueOf(type));}
}
package com.example.demo.union;import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;/*** 名称: demo中用到的方法<br>* 日期: 2015-09<br>* 版本: 1.0.0 * 版权: 中国银联<br>* 说明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己需要,按照技术文档编写。该代码仅供参考。<br>*/
public class DemoBase {//默认配置的是UTF-8public static String encoding = "UTF-8";//全渠道固定值public static String version = SDKConfig.getConfig().getVersion();//后台服务对应的写法参照 FrontRcvResponse.javapublic static String frontUrl = SDKConfig.getConfig().getFrontUrl();//后台服务对应的写法参照 BackRcvResponse.javapublic static String backUrl = SDKConfig.getConfig().getBackUrl();//受理方和发卡方自选填写的域[O]--后台通知地址// 商户发送交易时间 格式:YYYYMMDDhhmmsspublic static String getCurrentTime() {return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());}// AN8..40 商户订单号,不能含"-"或"_"public static String getOrderId() {return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());}/*** 组装请求,返回报文字符串用于显示* @param data* @return*/public static String genHtmlResult(Map<String, String> data){TreeMap<String, String> tree = new TreeMap<String, String>();Iterator<Entry<String, String>> it = data.entrySet().iterator();while (it.hasNext()) {Entry<String, String> en = it.next();tree.put(en.getKey(), en.getValue());}it = tree.entrySet().iterator();StringBuffer sf = new StringBuffer();while (it.hasNext()) {Entry<String, String> en = it.next();String key = en.getKey(); String value =  en.getValue();if("respCode".equals(key)){sf.append("<b>"+key + SDKConstants.EQUAL + value+"</br></b>");}elsesf.append(key + SDKConstants.EQUAL + value+"</br>");}return sf.toString();}/*** 功能:解析全渠道商户对账文件中的ZM文件并以List<Map>方式返回* 适用交易:对账文件下载后对文件的查看* @param filePath ZM文件全路径* @return 包含每一笔交易中 序列号 和 值 的map序列*/public static List<Map> parseZMFile(String filePath){int lengthArray[] = {3,11,11,6,10,19,12,4,2,21,2,32,2,6,10,13,13,4,15,2,2,6,2,4,32,1,21,15,1,15,32,13,13,8,32,13,13,12,2,1,32,98};return parseFile(filePath,lengthArray);}/*** 功能:解析全渠道商户对账文件中的ZME文件并以List<Map>方式返回* 适用交易:对账文件下载后对文件的查看* @param filePath ZME文件全路径* @return 包含每一笔交易中 序列号 和 值 的map序列*/public static List<Map> parseZMEFile(String filePath){int lengthArray[] = {3,11,11,6,10,19,12,4,2,2,6,10,4,12,13,13,15,15,1,12,2,135};return parseFile(filePath,lengthArray);}/*** 功能:解析全渠道商户 ZM,ZME对账文件* @param filePath* @param lengthArray 参照《全渠道平台接入接口规范 第3部分 文件接口》 全渠道商户对账文件 6.1 ZM文件和6.2 ZME 文件 格式的类型长度组成int型数组* @return*/private static List<Map> parseFile(String filePath,int lengthArray[]){List<Map> ZmDataList = new ArrayList<Map>();try {String encoding="UTF-8";File file=new File(filePath);if(file.isFile() && file.exists()){ //判断文件是否存在InputStreamReader read = new InputStreamReader(new FileInputStream(file),encoding);//考虑到编码格式BufferedReader bufferedReader = new BufferedReader(read);String lineTxt = null;while((lineTxt = bufferedReader.readLine()) != null){//解析的结果MAP,key为对账文件列序号,value为解析的值Map<Integer,String> ZmDataMap = new LinkedHashMap<Integer,String>();//左侧游标int leftIndex = 0;//右侧游标int rightIndex = 0;for(int i=0;i<lengthArray.length;i++){rightIndex = leftIndex + lengthArray[i];String filed = lineTxt.substring(leftIndex,rightIndex);leftIndex = rightIndex+1;ZmDataMap.put(i, filed);}ZmDataList.add(ZmDataMap);}read.close();}else{System.out.println("找不到指定的文件");}} catch (Exception e) {System.out.println("读取文件内容出错");e.printStackTrace();}return ZmDataList;	}public static String getFileContentTable(List<Map> dataList,String file){StringBuffer  tableSb = new StringBuffer("对账文件的规范参考 https://open.unionpay.com/ajweb/help/file/ 产品接口规范->平台接口规范:文件接口</br> 文件【"+file + "】解析后内容如下:");tableSb.append("<table border=\"1\">");if(dataList.size() > 0){Map<Integer,String> dataMapTmp = dataList.get(0);tableSb.append("<tr>");for(Iterator<Integer> it = dataMapTmp.keySet().iterator();it.hasNext();){Integer key = it.next();String value = dataMapTmp.get(key);System.out.println("序号:"+ (key+1) + " 值: '"+ value +"'");tableSb.append("<td>序号"+(key+1)+"</td>");}tableSb.append("</tr>");}for(int i=0;i<dataList.size();i++){System.out.println("行数: "+ (i+1));Map<Integer,String> dataMapTmp = dataList.get(i);tableSb.append("<tr>");for(Iterator<Integer> it = dataMapTmp.keySet().iterator();it.hasNext();){Integer key = it.next();String value = dataMapTmp.get(key);System.out.println("序号:"+ (key+1) + " 值: '"+ value +"'");tableSb.append("<td>"+value+"</td>");}tableSb.append("</tr>");}tableSb.append("</table>");return tableSb.toString();}public static List<String> unzip(String zipFilePath,String outPutDirectory){List<String> fileList = new ArrayList<String>();try {ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFilePath));//输入源zip路径  BufferedInputStream bin = new BufferedInputStream(zin);BufferedOutputStream bout = null;File file=null;  ZipEntry entry;try {while((entry = zin.getNextEntry())!=null && !entry.isDirectory()){file = new File(outPutDirectory,entry.getName());  if(!file.exists()){  (new File(file.getParent())).mkdirs();  }bout = new BufferedOutputStream(new FileOutputStream(file));  int b;while((b=bin.read())!=-1){  bout.write(b);  }bout.flush();fileList.add(file.getAbsolutePath());System.out.println(file+"解压成功");}} catch (IOException e) {  e.printStackTrace();  }finally{try {bin.close();zin.close();if(bout!=null){bout.close();}} catch (IOException e) {e.printStackTrace();}  }} catch (FileNotFoundException e) {e.printStackTrace();  }return fileList;}}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28     报文加密解密等操作的工具类* =============================================================================*/
package com.example.demo.union.util;import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.digests.SM3Digest;import javax.crypto.Cipher;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;/*** * @ClassName SecureUtil* @Description acpsdk安全算法工具类* @date 2016-7-22 下午4:08:32**/
public class SecureUtil {/*** 算法常量: SHA1*/private static final String ALGORITHM_SHA1 = "SHA-1";/*** 算法常量: SHA256*/private static final String ALGORITHM_SHA256 = "SHA-256";/*** 算法常量:SHA1withRSA*/private static final String BC_PROV_ALGORITHM_SHA1RSA = "SHA1withRSA";/*** 算法常量:SHA256withRSA*/private static final String BC_PROV_ALGORITHM_SHA256RSA = "SHA256withRSA";/*** sm3计算后进行16进制转换* * @param data*            待计算的数据* @param encoding*            编码* @return 计算结果*/public static String sm3X16Str(String data, String encoding) {byte[] bytes = sm3(data, encoding);StringBuilder sm3StrBuff = new StringBuilder();for (int i = 0; i < bytes.length; i++) {if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {sm3StrBuff.append("0").append(Integer.toHexString(0xFF & bytes[i]));} else {sm3StrBuff.append(Integer.toHexString(0xFF & bytes[i]));}}return sm3StrBuff.toString();}/*** sha1计算后进行16进制转换* * @param data*            待计算的数据* @param encoding*            编码* @return 计算结果*/public static byte[] sha1X16(String data, String encoding) {byte[] bytes = sha1(data, encoding);StringBuilder sha1StrBuff = new StringBuilder();for (int i = 0; i < bytes.length; i++) {if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {sha1StrBuff.append("0").append(Integer.toHexString(0xFF & bytes[i]));} else {sha1StrBuff.append(Integer.toHexString(0xFF & bytes[i]));}}try {return sha1StrBuff.toString().getBytes(encoding);} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);return null;}}/*** sha256计算后进行16进制转换* * @param data*            待计算的数据* @param encoding*            编码* @return 计算结果*/public static String sha256X16Str(String data, String encoding) {byte[] bytes = sha256(data, encoding);StringBuilder sha256StrBuff = new StringBuilder();for (int i = 0; i < bytes.length; i++) {if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {sha256StrBuff.append("0").append(Integer.toHexString(0xFF & bytes[i]));} else {sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i]));}}return sha256StrBuff.toString();}/*** sha256计算后进行16进制转换* * @param data*            待计算的数据* @param encoding*            编码* @return 计算结果*/public static byte[] sha256X16(String data, String encoding) {byte[] bytes = sha256(data, encoding);StringBuilder sha256StrBuff = new StringBuilder();for (int i = 0; i < bytes.length; i++) {if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {sha256StrBuff.append("0").append(Integer.toHexString(0xFF & bytes[i]));} else {sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i]));}}try {return sha256StrBuff.toString().getBytes(encoding);} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);return null;}}/*** sha1计算.* * @param data*            待计算的数据* @return 计算结果*/private static byte[] sha1(byte[] data) {MessageDigest md = null;try {md = MessageDigest.getInstance(ALGORITHM_SHA1);md.reset();md.update(data);return md.digest();} catch (Exception e) {LogUtil.writeErrorLog("SHA1计算失败", e);return null;}}/*** sha256计算.* * @param data*            待计算的数据* @return 计算结果*/private static byte[] sha256(byte[] data) {MessageDigest md = null;try {md = MessageDigest.getInstance(ALGORITHM_SHA256);md.reset();md.update(data);return md.digest();} catch (Exception e) {LogUtil.writeErrorLog("SHA256计算失败", e);return null;}}/*** SM3计算.* * @param data*            待计算的数据* @return 计算结果*/private static byte[] sm3(byte[] data) {SM3Digest sm3 = new SM3Digest();sm3.update(data, 0, data.length);byte[] result = new byte[sm3.getDigestSize()];sm3.doFinal(result, 0);return result;}/*** sha1计算* * @param datas*            待计算的数据* @param encoding*            字符集编码* @return*/private static byte[] sha1(String datas, String encoding) {try {return sha1(datas.getBytes(encoding));} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog("SHA1计算失败", e);return null;}}/*** sha256计算* * @param datas*            待计算的数据* @param encoding*            字符集编码* @return*/private static byte[] sha256(String datas, String encoding) {try {return sha256(datas.getBytes(encoding));} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog("SHA256计算失败", e);return null;}}/*** sm3计算* * @param datas*            待计算的数据* @param encoding*            字符集编码* @return*/private static byte[] sm3(String datas, String encoding) {try {return sm3(datas.getBytes(encoding));} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog("SM3计算失败", e);return null;}}/*** * @param privateKey* @param data* @return* @throws Exception*/public static byte[] signBySoft(PrivateKey privateKey, byte[] data)throws Exception {byte[] result = null;Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA, "BC");st.initSign(privateKey);st.update(data);result = st.sign();return result;}/*** @param privateKey* @param data* @return* @throws Exception*/public static byte[] signBySoft256(PrivateKey privateKey, byte[] data)throws Exception {byte[] result = null;Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA256RSA, "BC");st.initSign(privateKey);st.update(data);result = st.sign();return result;}public static boolean validateSignBySoft(PublicKey publicKey,byte[] signData, byte[] srcData) throws Exception {Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA, "BC");st.initVerify(publicKey);st.update(srcData);return st.verify(signData);}public static boolean validateSignBySoft256(PublicKey publicKey,byte[] signData, byte[] srcData) throws Exception {Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA256RSA, "BC");st.initVerify(publicKey);st.update(srcData);return st.verify(signData);}/*** 对数据通过公钥进行加密,并进行base64计算* * @param dataString*            待处理数据* @param encoding*            字符编码* @param key*            公钥* @return*/public static String encryptData(String dataString, String encoding,PublicKey key) {/** 使用公钥对密码加密 **/byte[] data = null;try {data = encryptData(key, dataString.getBytes(encoding));return new String(SecureUtil.base64Encode(data), encoding);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);return "";}}/*** 对数据通过公钥进行加密,并进行base64计算* * @param pin*            待处理数据* @param encoding*            字符编码* @param key*            公钥* @return*/public static String encryptPin(String accNo, String pin, String encoding,PublicKey key) {/** 使用公钥对密码加密 **/byte[] data = null;try {data = pin2PinBlockWithCardNO(pin, accNo);data = encryptData(key, data);return new String(SecureUtil.base64Encode(data), encoding);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);return "";}}/*** 通过私钥解密* * @param dataString*            base64过的数据* @param encoding*            编码* @param key*            私钥* @return 解密后的数据*/public static String decryptData(String dataString, String encoding,PrivateKey key) {byte[] data = null;try {data = SecureUtil.base64Decode(dataString.getBytes(encoding));data = decryptData(key, data);return new String(data, encoding);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);return "";}}/*** BASE64解码* * @param inputByte*            待解码数据* @return 解码后的数据* @throws IOException*/public static byte[] base64Decode(byte[] inputByte) throws IOException {return Base64.decodeBase64(inputByte);}/*** BASE64编码* * @param inputByte*            待编码数据* @return 解码后的数据* @throws IOException*/public static byte[] base64Encode(byte[] inputByte) throws IOException {return Base64.encodeBase64(inputByte);}/*** 加密除pin之外的其他信息* * @param publicKey* @param plainData* @return* @throws Exception*/private static byte[] encryptData(PublicKey publicKey, byte[] plainData)throws Exception {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(plainData);} catch (Exception e) {throw new Exception(e.getMessage());}}/*** @param privateKey* @param data* @return* @throws Exception*/private static byte[] decryptData(PrivateKey privateKey, byte[] data)throws Exception {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(data);} catch (Exception e) {LogUtil.writeErrorLog("解密失败", e);}return null;}/*** * @param aPin* @return*/private static byte[] pin2PinBlock(String aPin) {int tTemp = 1;int tPinLen = aPin.length();byte[] tByte = new byte[8];try {/******************************************************************** if (tPinLen > 9) { tByte[0] = (byte) Integer.parseInt(new* Integer(tPinLen) .toString(), 16); } else { tByte[0] = (byte)* Integer.parseInt(new Integer(tPinLen) .toString(), 10); }******************************************************************/
//			tByte[0] = (byte) Integer.parseInt(new Integer(tPinLen).toString(),
//					10);tByte[0] = (byte) Integer.parseInt(Integer.toString(tPinLen), 10);if (tPinLen % 2 == 0) {for (int i = 0; i < tPinLen;) {String a = aPin.substring(i, i + 2);tByte[tTemp] = (byte) Integer.parseInt(a, 16);if (i == (tPinLen - 2)) {if (tTemp < 7) {for (int x = (tTemp + 1); x < 8; x++) {tByte[x] = (byte) 0xff;}}}tTemp++;i = i + 2;}} else {for (int i = 0; i < tPinLen - 1;) {String a;a = aPin.substring(i, i + 2);tByte[tTemp] = (byte) Integer.parseInt(a, 16);if (i == (tPinLen - 3)) {String b = aPin.substring(tPinLen - 1) + "F";tByte[tTemp + 1] = (byte) Integer.parseInt(b, 16);if ((tTemp + 1) < 7) {for (int x = (tTemp + 2); x < 8; x++) {tByte[x] = (byte) 0xff;}}}tTemp++;i = i + 2;}}} catch (Exception e) {}return tByte;}/*** * @param aPan* @return*/private static byte[] formatPan(String aPan) {int tPanLen = aPan.length();byte[] tByte = new byte[8];;int temp = tPanLen - 13;try {tByte[0] = (byte) 0x00;tByte[1] = (byte) 0x00;for (int i = 2; i < 8; i++) {String a = aPan.substring(temp, temp + 2);tByte[i] = (byte) Integer.parseInt(a, 16);temp = temp + 2;}} catch (Exception e) {}return tByte;}/*** * @param aPin* @param aCardNO* @return*/private static byte[] pin2PinBlockWithCardNO(String aPin, String aCardNO) {byte[] tPinByte = pin2PinBlock(aPin);if (aCardNO.length() == 11) {aCardNO = "00" + aCardNO;} else if (aCardNO.length() == 12) {aCardNO = "0" + aCardNO;}byte[] tPanByte = formatPan(aCardNO);byte[] tByte = new byte[8];for (int i = 0; i < 8; i++) {tByte[i] = (byte) (tPinByte[i] ^ tPanByte[i]);}return tByte;}/*** luhn算法* * @param number* @return*/public static int genLuhn(String number) {number = number + "0";int s1 = 0, s2 = 0;String reverse = new StringBuffer(number).reverse().toString();for (int i = 0; i < reverse.length(); i++) {int digit = Character.digit(reverse.charAt(i), 10);if (i % 2 == 0) {// this is for odd digits, they are 1-indexed in //// the algorithms1 += digit;} else {// add 2 * digit for 0-4, add 2 * digit - 9 for 5-9s2 += 2 * digit;if (digit >= 5) {s2 -= 9;}}}int check = 10 - ((s1 + s2) % 10);if (check == 10)check = 0;return check;}
}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28      MPI工具类* =============================================================================*/
package com.example.demo.union.util;import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;
import org.apache.commons.lang.StringUtils;import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;
import java.util.zip.Deflater;
import java.util.zip.Inflater;import static com.example.demo.union.constants.SDKConstants.*;/*** * @ClassName SDKUtil* @Description acpsdk工具类* @date 2016-7-22 下午4:06:18**/
public class SDKUtil {/*** 根据signMethod的值,提供三种计算签名的方法* * @param data*            待签名数据Map键值对形式* @param encoding*            编码* @return 签名是否成功*/public static boolean sign(Map<String, String> data, String encoding) {if (isEmpty(encoding)) {encoding = "UTF-8";}String signMethod = data.get(param_signMethod);String version = data.get(SDKConstants.param_version);if (!VERSION_1_0_0.equals(version) && !VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {LogUtil.writeErrorLog("signMethod must Not null");return false;}if (isEmpty(version)) {LogUtil.writeErrorLog("version must Not null");return false;}if (SIGNMETHOD_RSA.equals(signMethod)|| VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {if (VERSION_5_0_0.equals(version)|| VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {// 设置签名证书序列号data.put(SDKConstants.param_certId, CertUtil.getSignCertId());// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");byte[] byteSign = null;String stringSign = null;try {// 通过SHA1进行摘要并转16进制byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(CertUtil.getSignCertPrivateKey(), signDigest));stringSign = new String(byteSign);// 设置签名域值data.put(SDKConstants.param_signature, stringSign);return true;} catch (Exception e) {LogUtil.writeErrorLog("Sign Error", e);return false;}} else if (VERSION_5_1_0.equals(version)) {// 设置签名证书序列号data.put(SDKConstants.param_certId, CertUtil.getSignCertId());// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");byte[] byteSign = null;String stringSign = null;try {// 通过SHA256进行摘要并转16进制byte[] signDigest = SecureUtil.sha256X16(stringData, encoding);byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(CertUtil.getSignCertPrivateKey(), signDigest));stringSign = new String(byteSign);// 设置签名域值data.put(SDKConstants.param_signature, stringSign);return true;} catch (Exception e) {LogUtil.writeErrorLog("Sign Error", e);return false;}}} else if (SIGNMETHOD_SHA256.equals(signMethod)) {return signBySecureKey(data, SDKConfig.getConfig().getSecureKey(), encoding);} else if (SIGNMETHOD_SM3.equals(signMethod)) {return signBySecureKey(data, SDKConfig.getConfig().getSecureKey(), encoding);}return false;}/*** 通过传入的证书绝对路径和证书密码读取签名证书进行签名并返回签名值<br>* * @param data*            待签名数据Map键值对形式* @param encoding*            编码* @param secureKey*            证书绝对路径* @para certPwd*            证书密码* @return 签名值*/public static boolean signBySecureKey(Map<String, String> data, String secureKey,String encoding) {if (isEmpty(encoding)) {encoding = "UTF-8";}if (isEmpty(secureKey)) {LogUtil.writeErrorLog("secureKey is empty");return false;}String signMethod = data.get(param_signMethod);if (isEmpty(signMethod)) {LogUtil.writeErrorLog("signMethod must Not null");return false;}if (SIGNMETHOD_SHA256.equals(signMethod)) {// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");String strBeforeSha256 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sha256X16Str(secureKey, encoding);String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,encoding);// 设置签名域值data.put(SDKConstants.param_signature, strAfterSha256);return true;} else if (SIGNMETHOD_SM3.equals(signMethod)) {String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");String strBeforeSM3 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sm3X16Str(secureKey, encoding);String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);// 设置签名域值data.put(SDKConstants.param_signature, strAfterSM3);return true;}return false;}/*** 通过传入的签名密钥进行签名并返回签名值<br>* * @param data*            待签名数据Map键值对形式* @param encoding*            编码* @param certPath*            证书绝对路径* @param certPwd*            证书密码* @return 签名值*/public static boolean signByCertInfo(Map<String, String> data,String certPath, String certPwd, String encoding) {if (isEmpty(encoding)) {encoding = "UTF-8";}if (isEmpty(certPath) || isEmpty(certPwd)) {LogUtil.writeErrorLog("CertPath or CertPwd is empty");return false;}String signMethod = data.get(param_signMethod);String version = data.get(SDKConstants.param_version);if (!VERSION_1_0_0.equals(version) && !VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {LogUtil.writeErrorLog("signMethod must Not null");return false;}if (isEmpty(version)) {LogUtil.writeErrorLog("version must Not null");return false;}if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {// 设置签名证书序列号data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");byte[] byteSign = null;String stringSign = null;try {// 通过SHA1进行摘要并转16进制byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));stringSign = new String(byteSign);// 设置签名域值data.put(SDKConstants.param_signature, stringSign);return true;} catch (Exception e) {LogUtil.writeErrorLog("Sign Error", e);return false;}} else if (VERSION_5_1_0.equals(version)) {// 设置签名证书序列号data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(data);LogUtil.writeLog("待签名请求报文串:[" + stringData + "]");byte[] byteSign = null;String stringSign = null;try {// 通过SHA256进行摘要并转16进制byte[] signDigest = SecureUtil.sha256X16(stringData, encoding);byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));stringSign = new String(byteSign);// 设置签名域值data.put(SDKConstants.param_signature, stringSign);return true;} catch (Exception e) {LogUtil.writeErrorLog("Sign Error", e);return false;}}} return false;}/*** 验证签名* * @param resData*            返回报文数据* @param encoding*            编码格式* @return*/public static boolean validateBySecureKey(Map<String, String> resData, String secureKey, String encoding) {LogUtil.writeLog("验签处理开始");if (isEmpty(encoding)) {encoding = "UTF-8";}String signMethod = resData.get(SDKConstants.param_signMethod);if (SIGNMETHOD_SHA256.equals(signMethod)) {// 1.进行SHA256验证String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");String strBeforeSha256 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sha256X16Str(secureKey, encoding);String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,encoding);return stringSign.equals(strAfterSha256);} else if (SIGNMETHOD_SM3.equals(signMethod)) {// 1.进行SM3验证String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");String strBeforeSM3 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sm3X16Str(secureKey, encoding);String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);return stringSign.equals(strAfterSM3);}return false;}/*** 验证签名* * @param resData*            返回报文数据* @param encoding*            编码格式* @return*/public static boolean validate(Map<String, String> resData, String encoding) {LogUtil.writeLog("验签处理开始");if (isEmpty(encoding)) {encoding = "UTF-8";}String signMethod = resData.get(SDKConstants.param_signMethod);String version = resData.get(SDKConstants.param_version);if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {// 获取返回报文的版本号if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 从返回报文中获取certId ,然后去证书静态Map中查询对应验签证书对象String certId = resData.get(SDKConstants.param_certId);LogUtil.writeLog("对返回报文串验签使用的验签公钥序列号:["+certId+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");try {// 验证签名需要用银联发给商户的公钥证书.return SecureUtil.validateSignBySoft(CertUtil.getValidatePublicKey(certId), SecureUtil.base64Decode(stringSign.getBytes(encoding)),SecureUtil.sha1X16(stringData, encoding));} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);}} else if (VERSION_5_1_0.equals(version)) {// 1.从返回报文中获取公钥信息转换成公钥对象String strCert = resData.get(SDKConstants.param_signPubKeyCert);
//				LogUtil.writeLog("验签公钥证书:["+strCert+"]");X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);if(x509Cert == null) {LogUtil.writeErrorLog("convert signPubKeyCert failed");return false;}// 2.验证证书链if (!CertUtil.verifyCertificate(x509Cert)) {LogUtil.writeErrorLog("验证公钥证书失败,证书信息:["+strCert+"]");return false;}// 3.验签String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");try {// 验证签名需要用银联发给商户的公钥证书.boolean result = SecureUtil.validateSignBySoft256(x509Cert.getPublicKey(), SecureUtil.base64Decode(stringSign.getBytes(encoding)), SecureUtil.sha256X16(stringData, encoding));LogUtil.writeLog("验证签名" + (result? "成功":"失败"));return result;} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);}}} else if (SIGNMETHOD_SHA256.equals(signMethod)) {// 1.进行SHA256验证String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");String strBeforeSha256 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sha256X16Str(SDKConfig.getConfig().getSecureKey(), encoding);String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,encoding);boolean result =  stringSign.equals(strAfterSha256);LogUtil.writeLog("验证签名" + (result? "成功":"失败"));return result;} else if (SIGNMETHOD_SM3.equals(signMethod)) {// 1.进行SM3验证String stringSign = resData.get(SDKConstants.param_signature);LogUtil.writeLog("签名原文:["+stringSign+"]");// 将Map信息转换成key1=value1&key2=value2的形式String stringData = coverMap2String(resData);LogUtil.writeLog("待验签返回报文串:["+stringData+"]");String strBeforeSM3 = stringData+ SDKConstants.AMPERSAND+ SecureUtil.sm3X16Str(SDKConfig.getConfig().getSecureKey(), encoding);String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);boolean result =  stringSign.equals(strAfterSM3);LogUtil.writeLog("验证签名" + (result? "成功":"失败"));return result;}return false;}/*** 将Map中的数据转换成key1=value1&key2=value2的形式 不包含签名域signature* * @param data*            待拼接的Map数据* @return 拼接好后的字符串*/public static String coverMap2String(Map<String, String> data) {TreeMap<String, String> tree = new TreeMap<String, String>();Iterator<Entry<String, String>> it = data.entrySet().iterator();while (it.hasNext()) {Entry<String, String> en = it.next();if (SDKConstants.param_signature.equals(en.getKey().trim())) {continue;}tree.put(en.getKey(), en.getValue());}it = tree.entrySet().iterator();StringBuffer sf = new StringBuffer();while (it.hasNext()) {Entry<String, String> en = it.next();sf.append(en.getKey() + SDKConstants.EQUAL + en.getValue()+ SDKConstants.AMPERSAND);}return sf.substring(0, sf.length() - 1);}/*** 兼容老方法 将形如key=value&key=value的字符串转换为相应的Map对象* * @param result* @return*/public static Map<String, String> coverResultString2Map(String result) {return convertResultStringToMap(result);}/*** 将形如key=value&key=value的字符串转换为相应的Map对象* * @param result* @return*/public static Map<String, String> convertResultStringToMap(String result) {Map<String, String> map =null;try {if (StringUtils.isNotBlank(result)) {if (result.startsWith("{") && result.endsWith("}")) {result = result.substring(1, result.length() - 1);}map = parseQString(result);}} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return map;}/*** 解析应答字符串,生成应答要素* * @param str*            需要解析的字符串* @return 解析的结果map* @throws UnsupportedEncodingException*/public static Map<String, String> parseQString(String str)throws UnsupportedEncodingException {Map<String, String> map = new HashMap<String, String>();int len = str.length();StringBuilder temp = new StringBuilder();char curChar;String key = null;boolean isKey = true;boolean isOpen = false;//值里有嵌套char openName = 0;if(len>0){for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串curChar = str.charAt(i);// 取当前字符if (isKey) {// 如果当前生成的是keyif (curChar == '=') {// 如果读取到=分隔符 key = temp.toString();temp.setLength(0);isKey = false;} else {temp.append(curChar);}} else  {// 如果当前生成的是valueif(isOpen){if(curChar == openName){isOpen = false;}}else{//如果没开启嵌套if(curChar == '{'){//如果碰到,就开启嵌套isOpen = true;openName ='}';}if(curChar == '['){isOpen = true;openName =']';}}if (curChar == '&' && !isOpen) {// 如果读取到&分割符,同时这个分割符不是值域,这时将map里添加putKeyValueToMap(temp, isKey, key, map);temp.setLength(0);isKey = true;}else{temp.append(curChar);}}}putKeyValueToMap(temp, isKey, key, map);}return map;}private static void putKeyValueToMap(StringBuilder temp, boolean isKey,String key, Map<String, String> map)throws UnsupportedEncodingException {if (isKey) {key = temp.toString();if (key.length() == 0) {throw new RuntimeException("QString format illegal");}map.put(key, "");} else {if (key.length() == 0) {throw new RuntimeException("QString format illegal");}map.put(key, temp.toString());}}/*** * 获取应答报文中的加密公钥证书,并存储到本地,并备份原始证书<br>* 更新成功则返回1,无更新返回0,失败异常返回-1。* * @param resData* @param encoding* @return*/public static int getEncryptCert(Map<String, String> resData,String encoding) {String strCert = resData.get(SDKConstants.param_encryptPubKeyCert);String certType = resData.get(SDKConstants.param_certType);if (isEmpty(strCert) || isEmpty(certType))return -1;X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);if (CERTTYPE_01.equals(certType)) {// 更新敏感信息加密公钥if (!CertUtil.getEncryptCertId().equals(x509Cert.getSerialNumber().toString())) {// ID不同时进行本地证书更新操作String localCertPath = SDKConfig.getConfig().getEncryptCertPath();String newLocalCertPath = genBackupName(localCertPath);// 1.将本地证书进行备份存储if (!copyFile(localCertPath, newLocalCertPath))return -1;// 2.备份成功,进行新证书的存储if (!writeFile(localCertPath, strCert, encoding))return -1;LogUtil.writeLog("save new encryptPubKeyCert success");CertUtil.resetEncryptCertPublicKey();return 1;}else {return 0;}} else if (CERTTYPE_02.equals(certType)) {
//			// 更新磁道加密公钥
//			if (!CertUtil.getEncryptTrackCertId().equals(
//					x509Cert.getSerialNumber().toString())) {
//				// ID不同时进行本地证书更新操作
//				String localCertPath = SDKConfig.getConfig().getEncryptTrackCertPath();
//				String newLocalCertPath = genBackupName(localCertPath);
//				// 1.将本地证书进行备份存储
//				if (!copyFile(localCertPath, newLocalCertPath))
//					return -1;
//				// 2.备份成功,进行新证书的存储
//				if (!writeFile(localCertPath, strCert, encoding))
//					return -1;
//				LogUtil.writeLog("save new encryptPubKeyCert success");
//				CertUtil.resetEncryptTrackCertPublicKey();
//				return 1;
//			}else {return 0;
//			}}else {LogUtil.writeLog("unknown cerType:"+certType);return -1;}}/*** 文件拷贝方法* * @param srcFile*            源文件* @param destFile*            目标文件* @return* @throws IOException*/public static boolean copyFile(String srcFile, String destFile) {boolean flag = false;FileInputStream fin = null;FileOutputStream fout = null;FileChannel fcin = null;FileChannel fcout = null;try {// 获取源文件和目标文件的输入输出流fin = new FileInputStream(srcFile);fout = new FileOutputStream(destFile);// 获取输入输出通道fcin = fin.getChannel();fcout = fout.getChannel();// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) {// clear方法重设缓冲区,使它可以接受读入的数据buffer.clear();// 从输入通道中将数据读到缓冲区int r = fcin.read(buffer);// read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1if (r == -1) {flag = true;break;}// flip方法让缓冲区可以将新读入的数据写入另一个通道buffer.flip();// 从输出通道中将数据写入缓冲区fcout.write(buffer);}fout.flush();} catch (IOException e) {LogUtil.writeErrorLog("CopyFile fail", e);} finally {try {if (null != fin)fin.close();if (null != fout)fout.close();if (null != fcin)fcin.close();if (null != fcout)fcout.close();} catch (IOException ex) {LogUtil.writeErrorLog("Releases any system resources fail", ex);}}return flag;}/*** 写文件方法* * @param filePath*            文件路径* @param fileContent*            文件内容* @param encoding*            编码* @return*/public static boolean writeFile(String filePath, String fileContent,String encoding) {FileOutputStream fout = null;FileChannel fcout = null;File file = new File(filePath);if (file.exists()) {file.delete();}try {fout = new FileOutputStream(filePath);// 获取输出通道fcout = fout.getChannel();// 创建缓冲区// ByteBuffer buffer = ByteBuffer.allocate(1024);ByteBuffer buffer = ByteBuffer.wrap(fileContent.getBytes(encoding));fcout.write(buffer);fout.flush();} catch (FileNotFoundException e) {LogUtil.writeErrorLog("WriteFile fail", e);return false;} catch (IOException ex) {LogUtil.writeErrorLog("WriteFile fail", ex);return false;} finally {try {if (null != fout)fout.close();if (null != fcout)fcout.close();} catch (IOException ex) {LogUtil.writeErrorLog("Releases any system resources fail", ex);return false;}}return true;}/*** 将传入的文件名(xxx)改名 <br>* 结果为: xxx_backup.cer* * @param fileName* @return*/public static String genBackupName(String fileName) {if (isEmpty(fileName))return "";int i = fileName.lastIndexOf(POINT);String leftFileName = fileName.substring(0, i);String rightFileName = fileName.substring(i + 1);String newFileName = leftFileName + "_backup" + POINT + rightFileName;return newFileName;}public static byte[] readFileByNIO(String filePath) {FileInputStream in = null;FileChannel fc = null;ByteBuffer bf = null;try {in = new FileInputStream(filePath);fc = in.getChannel();bf = ByteBuffer.allocate((int) fc.size());fc.read(bf);return bf.array();} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage());return null;} finally {try {if (null != fc) {fc.close();}if (null != in) {in.close();}} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage());return null;}}}/*** 过滤请求报文中的空字符串或者空字符串* @param contentData* @return*/public static Map<String, String> filterBlank(Map<String, String> contentData){LogUtil.writeLog("打印请求报文域 :");Map<String, String> submitFromData = new HashMap<String, String>();Set<String> keyset = contentData.keySet();for(String key:keyset){String value = contentData.get(key);if (StringUtils.isNotBlank(value)) {// 对value值进行去除前后空处理submitFromData.put(key, value.trim());LogUtil.writeLog(key + "-->" + String.valueOf(value));}}return submitFromData;}/*** 解压缩.* * @param inputByte*            byte[]数组类型的数据* @return 解压缩后的数据* @throws IOException*/public static byte[] inflater(final byte[] inputByte) throws IOException {int compressedDataLength = 0;Inflater compresser = new Inflater(false);compresser.setInput(inputByte, 0, inputByte.length);ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);byte[] result = new byte[1024];try {while (!compresser.finished()) {compressedDataLength = compresser.inflate(result);if (compressedDataLength == 0) {break;}o.write(result, 0, compressedDataLength);}} catch (Exception ex) {System.err.println("Data format error!\n");ex.printStackTrace();} finally {o.close();}compresser.end();return o.toByteArray();}/*** 压缩.* * @param inputByte*            需要解压缩的byte[]数组* @return 压缩后的数据* @throws IOException*/public static byte[] deflater(final byte[] inputByte) throws IOException {int compressedDataLength = 0;Deflater compresser = new Deflater();compresser.setInput(inputByte);compresser.finish();ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);byte[] result = new byte[1024];try {while (!compresser.finished()) {compressedDataLength = compresser.deflate(result);o.write(result, 0, compressedDataLength);}} finally {o.close();}compresser.end();return o.toByteArray();}/*** 判断字符串是否为NULL或空* * @param s*            待判断的字符串数据* @return 判断结果 true-是 false-否*/public static boolean isEmpty(String s) {return null == s || "".equals(s.trim());}}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28       日志打印工具类* =============================================================================*/
package com.example.demo.union.util;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;/*** * @ClassName LogUtil* @Description acpsdk日志工具类* @date 2016-7-22 下午4:04:35**/
public class LogUtil {private final static Logger GATELOG = LoggerFactory.getLogger("ACP_SDK_LOG");private final static Logger GATELOG_ERROR = LoggerFactory.getLogger("SDK_ERR_LOG");private final static Logger GATELOG_MESSAGE = LoggerFactory.getLogger("SDK_MSG_LOG");final static String LOG_STRING_REQ_MSG_BEGIN = "============================== SDK REQ MSG BEGIN ==============================";final static String LOG_STRING_REQ_MSG_END = "==============================  SDK REQ MSG END  ==============================";final static String LOG_STRING_RSP_MSG_BEGIN = "============================== SDK RSP MSG BEGIN ==============================";final static String LOG_STRING_RSP_MSG_END = "==============================  SDK RSP MSG END  ==============================";/*** 记录普通日志* * @param cont*/public static void writeLog(String cont) {GATELOG.info(cont);}/*** 记录ERORR日志* * @param cont*/public static void writeErrorLog(String cont) {GATELOG_ERROR.error(cont);}/*** 记录ERROR日志* * @param cont* @param ex*/public static void writeErrorLog(String cont, Throwable ex) {GATELOG_ERROR.error(cont, ex);}/*** 记录通信报文* * @param msg*/public static void writeMessage(String msg) {GATELOG_MESSAGE.info(msg);}/*** 打印请求报文* * @param reqParam*/public static void printRequestLog(Map<String, String> reqParam) {writeMessage(LOG_STRING_REQ_MSG_BEGIN);Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();while (it.hasNext()) {Entry<String, String> en = it.next();writeMessage("[" + en.getKey() + "] = [" + en.getValue() + "]");}writeMessage(LOG_STRING_REQ_MSG_END);}/*** 打印响应报文.* * @param res*/public static void printResponseLog(String res) {writeMessage(LOG_STRING_RSP_MSG_BEGIN);writeMessage(res);writeMessage(LOG_STRING_RSP_MSG_END);}/*** debug方法* * @param cont*/public static void debug(String cont) {if (GATELOG.isDebugEnabled()) {GATELOG.debug(cont);}}
}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28       HTTP通信工具类* =============================================================================*/
package com.example.demo.union.util;import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.service.BaseHttpSSLSocketFactory;import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.*;
import java.util.Map;
import java.util.Map.Entry;/*** * @ClassName HttpClient* @Description acpsdk发送后台http请求类* @date 2016-7-22 下午4:03:25**/
public class HttpClient {/*** 目标地址*/private URL url;/*** 通信连接超时时间*/private int connectionTimeout;/*** 通信读超时时间*/private int readTimeOut;/*** 通信结果*/private String result;/*** 获取通信结果* @return*/public String getResult() {return result;}/*** 设置通信结果* @param result*/public void setResult(String result) {this.result = result;}/*** 构造函数* @param url 目标地址* @param connectionTimeout HTTP连接超时时间* @param readTimeOut HTTP读写超时时间*/public HttpClient(String url, int connectionTimeout, int readTimeOut) {try {this.url = new URL(url);this.connectionTimeout = connectionTimeout;this.readTimeOut = readTimeOut;} catch (MalformedURLException e) {LogUtil.writeErrorLog(e.getMessage(), e);}}/*** 发送信息到服务端* @param data* @param encoding* @return* @throws Exception*/public int send(Map<String, String> data, String encoding) throws Exception {try {HttpURLConnection httpURLConnection = createConnection(encoding);if (null == httpURLConnection) {throw new Exception("Create httpURLConnection Failure");}String sendData = this.getRequestParamString(data, encoding);LogUtil.writeLog("请求报文:[" + sendData + "]");this.requestServer(httpURLConnection, sendData,encoding);this.result = this.response(httpURLConnection, encoding);LogUtil.writeLog("Response message:[" + result + "]");return httpURLConnection.getResponseCode();} catch (Exception e) {throw e;}}/*** 发送信息到服务端 GET方式* @param encoding* @return* @throws Exception*/public int sendGet(String encoding) throws Exception {try {HttpURLConnection httpURLConnection = createConnectionGet(encoding);if(null == httpURLConnection){throw new Exception("创建联接失败");}this.result = this.response(httpURLConnection, encoding);LogUtil.writeLog("同步返回报文:[" + result + "]");return httpURLConnection.getResponseCode();} catch (Exception e) {throw e;}}/*** HTTP Post发送消息** @param connection* @param message* @throws IOException*/private void requestServer(final URLConnection connection, String message, String encoder)throws Exception {PrintStream out = null;try {connection.connect();out = new PrintStream(connection.getOutputStream(), false, encoder);out.print(message);out.flush();} catch (Exception e) {throw e;} finally {if (null != out) {out.close();}}}/*** 显示Response消息** @param connection* @param encoding* @return* @throws URISyntaxException* @throws IOException*/private String response(final HttpURLConnection connection, String encoding)throws URISyntaxException, IOException, Exception {InputStream in = null;StringBuilder sb = new StringBuilder(1024);BufferedReader br = null;try {if (200 == connection.getResponseCode()) {in = connection.getInputStream();sb.append(new String(read(in), encoding));} else {in = connection.getErrorStream();sb.append(new String(read(in), encoding));}LogUtil.writeLog("HTTP Return Status-Code:["+ connection.getResponseCode() + "]");return sb.toString();} catch (Exception e) {throw e;} finally {if (null != br) {br.close();}if (null != in) {in.close();}if (null != connection) {connection.disconnect();}}}public static byte[] read(InputStream in) throws IOException {byte[] buf = new byte[1024];int length = 0;ByteArrayOutputStream bout = new ByteArrayOutputStream();while ((length = in.read(buf, 0, buf.length)) > 0) {bout.write(buf, 0, length);}bout.flush();return bout.toByteArray();}/*** 创建连接** @return* @throws ProtocolException*/private HttpURLConnection createConnection(String encoding) throws ProtocolException {HttpURLConnection httpURLConnection = null;try {httpURLConnection = (HttpURLConnection) url.openConnection();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);return null;}httpURLConnection.setConnectTimeout(this.connectionTimeout);// 连接超时时间httpURLConnection.setReadTimeout(this.readTimeOut);// 读取结果超时时间httpURLConnection.setDoInput(true); // 可读httpURLConnection.setDoOutput(true); // 可写httpURLConnection.setUseCaches(false);// 取消缓存httpURLConnection.setRequestProperty("Content-type","application/x-www-form-urlencoded;charset=" + encoding);httpURLConnection.setRequestMethod("POST");if ("https".equalsIgnoreCase(url.getProtocol())) {HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;//是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再falseif(!SDKConfig.getConfig().isIfValidateRemoteCert()){husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory());husn.setHostnameVerifier(new BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier());//解决由于服务器证书问题导致HTTPS无法访问的情况}return husn;}return httpURLConnection;}/*** 创建连接** @return* @throws ProtocolException*/private HttpURLConnection createConnectionGet(String encoding) throws ProtocolException {HttpURLConnection httpURLConnection = null;try {httpURLConnection = (HttpURLConnection) url.openConnection();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);return null;}httpURLConnection.setConnectTimeout(this.connectionTimeout);// 连接超时时间httpURLConnection.setReadTimeout(this.readTimeOut);// 读取结果超时时间httpURLConnection.setUseCaches(false);// 取消缓存httpURLConnection.setRequestProperty("Content-type","application/x-www-form-urlencoded;charset=" + encoding);httpURLConnection.setRequestMethod("GET");if ("https".equalsIgnoreCase(url.getProtocol())) {HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;//是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再falseif(!SDKConfig.getConfig().isIfValidateRemoteCert()){husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory());husn.setHostnameVerifier(new BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier());//解决由于服务器证书问题导致HTTPS无法访问的情况}return husn;}return httpURLConnection;}/*** 将Map存储的对象,转换为key=value&key=value的字符** @param requestParam* @param coder* @return*/private String getRequestParamString(Map<String, String> requestParam, String coder) {if (null == coder || "".equals(coder)) {coder = "UTF-8";}StringBuffer sf = new StringBuffer("");String reqstr = "";if (null != requestParam && 0 != requestParam.size()) {for (Entry<String, String> en : requestParam.entrySet()) {try {sf.append(en.getKey()+ "="+ (null == en.getValue() || "".equals(en.getValue()) ? "" : URLEncoder.encode(en.getValue(), coder)) + "&");} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);return "";}}reqstr = sf.substring(0, sf.length() - 1);}LogUtil.writeLog("Request Message:[" + reqstr + "]");return reqstr;}}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28       证书工具类.* =============================================================================*/
package com.bt.star.util;import com.bt.star.config.SDKConfig;
import com.bt.star.properties.SDKConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** @ClassName: CertUtil* @Description: acpsdk证书工具类,主要用于对证书的加载和使用* @date 2016-7-22 下午2:46:20**/
@Slf4j
public class CertUtil {/** 证书容器,存储对商户请求报文签名私钥证书. */private static KeyStore keyStore = null;/** 敏感信息加密公钥证书 */private static X509Certificate encryptCert = null;/** 磁道加密公钥 */private static PublicKey encryptTrackKey = null;/** 验证银联返回报文签名证书. */private static X509Certificate validateCert = null;/** 验签中级证书 */private static X509Certificate middleCert = null;/** 验签根证书 */private static X509Certificate rootCert = null;/** 验证银联返回报文签名的公钥证书存储Map. */private static Map<String, X509Certificate> certMap = new HashMap<String, X509Certificate>();/** 商户私钥存储Map */private final static Map<String, KeyStore> keyStoreMap = new ConcurrentHashMap<String, KeyStore>();/*** 初始化所有证书.*/public static void init() {try {addProvider();//向系统添加BC providerinitSignCert();//初始化签名私钥证书initMiddleCert();//初始化验签证书的中级证书initRootCert();//初始化验签证书的根证书initEncryptCert();//初始化加密公钥initTrackKey();//构建磁道加密公钥initValidateCertFromDir();//初始化所有的验签证书} catch (Exception e) {log.error("init失败。(如果是用对称密钥签名的可无视此异常。)", e);}}/*** 添加签名,验签,加密算法提供者*/private static void addProvider(){if (Security.getProvider("BC") == null) {log.info("add BC provider");Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());} else {Security.removeProvider("BC"); //解决eclipse调试时tomcat自动重新加载时,BC存在不明原因异常的问题。Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());log.info("re-add BC provider");}printSysInfo();}/*** 用配置文件acp_sdk.properties中配置的私钥路径和密码 加载签名证书*/private static void initSignCert() {if(!"01".equals(SDKConfig.getConfig().getSignMethod())){log.info("非rsa签名方式,不加载签名证书。");return;}if (SDKConfig.getConfig().getSignCertPath() == null|| SDKConfig.getConfig().getSignCertPwd() == null|| SDKConfig.getConfig().getSignCertType() == null) {log.error("WARN: " + SDKConfig.SDK_SIGNCERT_PATH + "或" + SDKConfig.SDK_SIGNCERT_PWD + "或" + SDKConfig.SDK_SIGNCERT_TYPE + "为空。 停止加载签名证书。");return;}if (null != keyStore) {keyStore = null;}try {keyStore = getKeyInfo(SDKConfig.getConfig().getSignCertPath(),SDKConfig.getConfig().getSignCertPwd(), SDKConfig.getConfig().getSignCertType());log.info("InitSignCert Successful. CertId=["+ getSignCertId() + "]");} catch (IOException e) {log.error("InitSignCert Error", e);}}/*** 用配置文件acp_sdk.properties配置路径 加载敏感信息加密证书*/private static void initMiddleCert() {log.info("加载中级证书==>"+SDKConfig.getConfig().getMiddleCertPath());if (!SDKUtil.isEmpty(SDKConfig.getConfig().getMiddleCertPath())) {middleCert = initCert(SDKConfig.getConfig().getMiddleCertPath());log.info("Load MiddleCert Successful");} else {log.info("WARN: acpsdk.middle.path is empty");}}/*** 用配置文件acp_sdk.properties配置路径 加载敏感信息加密证书*/private static void initRootCert() {log.info("加载根证书==>"+SDKConfig.getConfig().getRootCertPath());if (!SDKUtil.isEmpty(SDKConfig.getConfig().getRootCertPath())) {rootCert = initCert(SDKConfig.getConfig().getRootCertPath());log.info("Load RootCert Successful");} else {log.info("WARN: acpsdk.rootCert.path is empty");}}/*** 用配置文件acp_sdk.properties配置路径 加载银联公钥上级证书(中级证书)*/private static void initEncryptCert() {log.info("加载敏感信息加密证书==>"+SDKConfig.getConfig().getEncryptCertPath());if (!SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptCertPath())) {encryptCert = initCert(SDKConfig.getConfig().getEncryptCertPath());log.info("Load EncryptCert Successful");} else {log.info("WARN: acpsdk.encryptCert.path is empty");}}/*** 用配置文件acp_sdk.properties配置路径 加载磁道公钥*/private static void initTrackKey() {if (!SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptTrackKeyModulus())&& !SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptTrackKeyExponent())) {encryptTrackKey = getPublicKey(SDKConfig.getConfig().getEncryptTrackKeyModulus(), SDKConfig.getConfig().getEncryptTrackKeyExponent());log.info("LoadEncryptTrackKey Successful");} else {log.info("WARN: acpsdk.encryptTrackKey.modulus or acpsdk.encryptTrackKey.exponent is empty");}}/*** 用配置文件acp_sdk.properties配置路径 加载验证签名证书*/private static void initValidateCertFromDir() {if(!"01".equals(SDKConfig.getConfig().getSignMethod())){log.info("非rsa签名方式,不加载验签证书。");return;}certMap.clear();String dir = SDKConfig.getConfig().getValidateCertDir();log.info("加载验证签名证书目录==>" + dir +" 注:如果请求报文中version=5.1.0那么此验签证书目录使用不到,可以不需要设置(version=5.0.0必须设置)。");if (SDKUtil.isEmpty(dir)) {log.error("WARN: acpsdk.validateCert.dir is empty");return;}CertificateFactory cf = null;FileInputStream in = null;try {cf = CertificateFactory.getInstance("X.509", "BC");File fileDir = new File(dir);File[] files = fileDir.listFiles(new CerFilter());for (int i = 0; i < files.length; i++) {File file = files[i];in = new FileInputStream(file.getAbsolutePath());validateCert = (X509Certificate) cf.generateCertificate(in);certMap.put(validateCert.getSerialNumber().toString(),validateCert);// 打印证书加载信息,供测试阶段调试log.info("[" + file.getAbsolutePath() + "][CertId="+ validateCert.getSerialNumber().toString() + "]");}log.info("LoadVerifyCert Successful");} catch (CertificateException e) {log.error("LoadVerifyCert Error", e);} catch (FileNotFoundException e) {log.error("LoadVerifyCert Error File Not Found", e);} catch (NoSuchProviderException e) {log.error("LoadVerifyCert Error No BC Provider", e);} finally {if (null != in) {try {in.close();} catch (IOException e) {log.error(e.toString());}}}}/*** 用给定的路径和密码 加载签名证书,并保存到certKeyStoreMap* * @param certFilePath* @param certPwd*/private static void loadSignCert(String certFilePath, String certPwd) {KeyStore keyStore = null;try {keyStore = getKeyInfo(certFilePath, certPwd, "PKCS12");keyStoreMap.put(certFilePath, keyStore);log.info("LoadRsaCert Successful");} catch (IOException e) {log.error("LoadRsaCert Error", e);}}/*** 通过证书路径初始化为公钥证书* @param path* @return*/private static X509Certificate initCert(String path) {X509Certificate encryptCertTemp = null;CertificateFactory cf = null;InputStream in = null;//相对路径变为绝对路径try {cf = CertificateFactory.getInstance("X.509", "BC");Resource resource = new ClassPathResource(path);in = resource.getInputStream();encryptCertTemp = (X509Certificate) cf.generateCertificate(in);// 打印证书加载信息,供测试阶段调试log.info("[" + path + "][CertId="+ encryptCertTemp.getSerialNumber().toString() + "]");} catch (CertificateException e) {log.error("InitCert Error", e);} catch (FileNotFoundException e) {log.error("InitCert Error File Not Found", e);} catch (NoSuchProviderException e) {log.error("LoadVerifyCert Error No BC Provider", e);} catch (IOException e) {e.printStackTrace();} finally {if (null != in) {try {in.close();} catch (IOException e) {log.error(e.toString());}}}return encryptCertTemp;}/*** 通过keyStore 获取私钥签名证书PrivateKey对象* * @return*/public static PrivateKey getSignCertPrivateKey() {try {Enumeration<String> aliasenum = keyStore.aliases();String keyAlias = null;if (aliasenum.hasMoreElements()) {keyAlias = aliasenum.nextElement();}PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias,SDKConfig.getConfig().getSignCertPwd().toCharArray());return privateKey;} catch (KeyStoreException e) {log.error("getSignCertPrivateKey Error", e);return null;} catch (UnrecoverableKeyException e) {log.error("getSignCertPrivateKey Error", e);return null;} catch (NoSuchAlgorithmException e) {log.error("getSignCertPrivateKey Error", e);return null;}}/*** 通过指定路径的私钥证书  获取PrivateKey对象* * @return*/public static PrivateKey getSignCertPrivateKeyByStoreMap(String certPath,String certPwd) {if (!keyStoreMap.containsKey(certPath)) {loadSignCert(certPath, certPwd);}try {Enumeration<String> aliasenum = keyStoreMap.get(certPath).aliases();String keyAlias = null;if (aliasenum.hasMoreElements()) {keyAlias = aliasenum.nextElement();}PrivateKey privateKey = (PrivateKey) keyStoreMap.get(certPath).getKey(keyAlias, certPwd.toCharArray());return privateKey;} catch (KeyStoreException e) {log.error("getSignCertPrivateKeyByStoreMap Error", e);return null;} catch (UnrecoverableKeyException e) {log.error("getSignCertPrivateKeyByStoreMap Error", e);return null;} catch (NoSuchAlgorithmException e) {log.error("getSignCertPrivateKeyByStoreMap Error", e);return null;}}/*** 获取敏感信息加密证书PublicKey* * @return*/public static PublicKey getEncryptCertPublicKey() {if (null == encryptCert) {String path = SDKConfig.getConfig().getEncryptCertPath();if (!SDKUtil.isEmpty(path)) {encryptCert = initCert(path);return encryptCert.getPublicKey();} else {log.error("acpsdk.encryptCert.path is empty");return null;}} else {return encryptCert.getPublicKey();}}/*** 重置敏感信息加密证书公钥*/public static void resetEncryptCertPublicKey() {encryptCert = null;}/*** 获取磁道加密证书PublicKey* * @return*/public static PublicKey getEncryptTrackPublicKey() {if (null == encryptTrackKey) {initTrackKey();}return encryptTrackKey;}/*** 通过certId获取验签证书Map中对应证书PublicKey* * @param certId 证书物理序号* @return 通过证书编号获取到的公钥*/public static PublicKey getValidatePublicKey(String certId) {X509Certificate cf = null;if (certMap.containsKey(certId)) {// 存在certId对应的证书对象cf = certMap.get(certId);return cf.getPublicKey();} else {// 不存在则重新Load证书文件目录initValidateCertFromDir();if (certMap.containsKey(certId)) {// 存在certId对应的证书对象cf = certMap.get(certId);return cf.getPublicKey();} else {log.error("缺少certId=[" + certId + "]对应的验签证书.");return null;}}}/*** 获取配置文件acp_sdk.properties中配置的签名私钥证书certId* * @return 证书的物理编号*/public static String getSignCertId() {try {Enumeration<String> aliasenum = keyStore.aliases();String keyAlias = null;if (aliasenum.hasMoreElements()) {keyAlias = aliasenum.nextElement();}X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias);return cert.getSerialNumber().toString();} catch (Exception e) {log.error("getSignCertId Error", e);return null;}}/*** 获取敏感信息加密证书的certId* * @return*/public static String getEncryptCertId() {if (null == encryptCert) {String path = SDKConfig.getConfig().getEncryptCertPath();if (!SDKUtil.isEmpty(path)) {encryptCert = initCert(path);return encryptCert.getSerialNumber().toString();} else {log.error("acpsdk.encryptCert.path is empty");return null;}} else {return encryptCert.getSerialNumber().toString();}}/*** 将签名私钥证书文件读取为证书存储对象* * @param pfxkeyfile*            证书文件名* @param keypwd*            证书密码* @param type*            证书类型* @return 证书对象* @throws IOException */private static KeyStore getKeyInfo(String pfxkeyfile, String keypwd,String type) throws IOException {log.info("加载签名证书==>" + pfxkeyfile);InputStream fis = null;try {KeyStore ks = KeyStore.getInstance(type, "BC");log.info("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=["+ keypwd + "],type=["+type+"]");//File file=new File(getRealCertPath() + pfxkeyfile);Resource resource = new ClassPathResource(pfxkeyfile);fis = resource.getInputStream();char[] nPassword = null;nPassword = null == keypwd || "".equals(keypwd.trim()) ? null: keypwd.toCharArray();if (null != ks) {ks.load(fis, nPassword);}return ks;} catch (Exception e) {log.error("getKeyInfo Error", e);return null;} finally {if(null!=fis)fis.close();}}/*** 通过签名私钥证书路径,密码获取私钥证书certId* @param certPath* @param certPwd* @return*/public static String getCertIdByKeyStoreMap(String certPath, String certPwd) {if (!keyStoreMap.containsKey(certPath)) {// 缓存中未查询到,则加载RSA证书loadSignCert(certPath, certPwd);}return getCertIdIdByStore(keyStoreMap.get(certPath));}/*** 通过keystore获取私钥证书的certId值* @param keyStore* @return*/private static String getCertIdIdByStore(KeyStore keyStore) {Enumeration<String> aliasenum = null;try {aliasenum = keyStore.aliases();String keyAlias = null;if (aliasenum.hasMoreElements()) {keyAlias = aliasenum.nextElement();}X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias);return cert.getSerialNumber().toString();} catch (KeyStoreException e) {log.error("getCertIdIdByStore Error", e);return null;}}/*** 使用模和指数生成RSA公钥 注意:此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同* * @param modulus*            模* @param exponent*            指数* @return*/private static PublicKey getPublicKey(String modulus, String exponent) {try {BigInteger b1 = new BigInteger(modulus);BigInteger b2 = new BigInteger(exponent);KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);return keyFactory.generatePublic(keySpec);} catch (Exception e) {log.error("构造RSA公钥失败:" + e);return null;}}/*** 将字符串转换为X509Certificate对象.* * @param x509CertString* @return*/public static X509Certificate genCertificateByStr(String x509CertString) {X509Certificate x509Cert = null;try {CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes("ISO-8859-1"));x509Cert = (X509Certificate) cf.generateCertificate(tIn);} catch (Exception e) {log.error("gen certificate error", e);			}return x509Cert;}/*** 从配置文件acp_sdk.properties中获取验签公钥使用的中级证书* @return*/public static X509Certificate getMiddleCert() {if (null == middleCert) {String path = SDKConfig.getConfig().getMiddleCertPath();if (!SDKUtil.isEmpty(path)) {initMiddleCert();} else {log.error(SDKConfig.SDK_MIDDLECERT_PATH + " not set in " + SDKConfig.FILE_NAME);return null;}}return middleCert;}/*** 从配置文件acp_sdk.properties中获取验签公钥使用的根证书* @return*/public static X509Certificate getRootCert() {if (null == rootCert) {String path = SDKConfig.getConfig().getRootCertPath();if (!SDKUtil.isEmpty(path)) {initRootCert();} else {log.error(SDKConfig.SDK_ROOTCERT_PATH + " not set in " + SDKConfig.FILE_NAME);return null;}}return rootCert;}/*** 获取证书的CN* @param aCert* @return*/private static String getIdentitiesFromCertficate(X509Certificate aCert) {String tDN = aCert.getSubjectDN().toString(); String tPart = "";if ((tDN != null)) {String tSplitStr[] = tDN.substring(tDN.indexOf("CN=")).split("@");if (tSplitStr != null && tSplitStr.length > 2&& tSplitStr[2] != null)tPart = tSplitStr[2];}return tPart;}/*** 验证书链。* @param cert* @return*/private static boolean verifyCertificateChain(X509Certificate cert){if ( null == cert) {log.error("cert must Not null");return false;}X509Certificate middleCert = CertUtil.getMiddleCert();if (null == middleCert) {log.error("middleCert must Not null");return false;}X509Certificate rootCert = CertUtil.getRootCert();if (null == rootCert) {log.error("rootCert or cert must Not null");return false;}try {X509CertSelector selector = new X509CertSelector();selector.setCertificate(cert);Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();trustAnchors.add(new TrustAnchor(rootCert, null));PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();intermediateCerts.add(rootCert);intermediateCerts.add(middleCert);intermediateCerts.add(cert);pkixParams.setRevocationEnabled(false);CertStore intermediateCertStore = CertStore.getInstance("Collection",new CollectionCertStoreParameters(intermediateCerts), "BC");pkixParams.addCertStore(intermediateCertStore);CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");@SuppressWarnings("unused")PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(pkixParams);log.info("verify certificate chain succeed.");return true;} catch (CertPathBuilderException e){log.error("verify certificate chain fail.", e);} catch (Exception e) {log.error("verify certificate chain exception: ", e);}return false;}/*** 检查证书链* *  rootCerts*  根证书* @param cert*            待验证的证书* @return*/public static boolean verifyCertificate(X509Certificate cert) {if ( null == cert) {log.error("cert must Not null");return false;}try {cert.checkValidity();//验证有效期
//			cert.verify(middleCert.getPublicKey());if(!verifyCertificateChain(cert)){return false;}} catch (Exception e) {log.error("verifyCertificate fail", e);return false;}if(SDKConfig.getConfig().isIfValidateCNName()){// 验证公钥是否属于银联if(!SDKConstants.UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))) {log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));return false;}} else {// 验证公钥是否属于银联if(!SDKConstants.UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))&& !"00040000:SIGN".equals(CertUtil.getIdentitiesFromCertficate(cert))) {log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));return false;}}return true;		}/*** 打印系统环境信息*/private static void printSysInfo() {log.info("================= SYS INFO begin====================");log.info("os_name:" + System.getProperty("os.name"));log.info("os_arch:" + System.getProperty("os.arch"));log.info("os_version:" + System.getProperty("os.version"));log.info("java_vm_specification_version:"+ System.getProperty("java.vm.specification.version"));log.info("java_vm_specification_vendor:"+ System.getProperty("java.vm.specification.vendor"));log.info("java_vm_specification_name:"+ System.getProperty("java.vm.specification.name"));log.info("java_vm_version:"+ System.getProperty("java.vm.version"));log.info("java_vm_name:" + System.getProperty("java.vm.name"));log.info("java.version:" + System.getProperty("java.version"));log.info("java.vm.vendor=[" + System.getProperty("java.vm.vendor") + "]");log.info("java.version=[" + System.getProperty("java.version") + "]");printProviders();log.info("================= SYS INFO end=====================");}/*** 打jre中印算法提供者列表*/private static void printProviders() {log.info("Providers List:");Provider[] providers = Security.getProviders();for (int i = 0; i < providers.length; i++) {log.info(i + 1 + "." + providers[i].getName());}}/*** 证书文件过滤器* */static class CerFilter implements FilenameFilter {public boolean isCer(String name) {if (name.toLowerCase().endsWith(".cer")) {return true;} else {return false;}}public boolean accept(File dir, String name) {return isCer(name);}}private static String getRealCertPath(){String clazzPath=Thread.currentThread().getContextClassLoader().getResource("").getPath();clazzPath=clazzPath.replace("test-classes","classes");clazzPath=clazzPath.replace("/classes/","/classes");return clazzPath;}
}
package com.example.demo.union.service;import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.util.*;import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @ClassName AcpService* @Description acpsdk接口服务类,接入商户集成请可以直接参考使用本类中的方法* @date 2016-7-22 下午2:44:37*/
public class AcpService {/*** 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>* @param reqData 请求报文map<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return 签名后的map对象<br>*/public static Map<String, String> sign(Map<String, String> reqData,String encoding) {reqData = SDKUtil.filterBlank(reqData);SDKUtil.sign(reqData, encoding);return reqData;}/*** 同signByCertInfo<br>* @param reqData* @param certPath* @param certPwd* @param encoding* @return* @deprecated*/public static Map<String, String> sign(Map<String, String> reqData, String certPath, String certPwd,String encoding) {reqData = SDKUtil.filterBlank(reqData);SDKUtil.signByCertInfo(reqData,certPath,certPwd,encoding);return reqData;}/*** 多证书签名(通过传入私钥证书路径和密码签名)<br>* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>* @param reqData 请求报文map<br>* @param certPath 签名私钥文件(带路径)<br>* @param certPwd 签名私钥密码<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return 签名后的map对象<br>*/public static Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath, String certPwd,String encoding) {reqData = SDKUtil.filterBlank(reqData);SDKUtil.signByCertInfo(reqData,certPath,certPwd,encoding);return reqData;}/*** 多密钥签名(通过传入密钥签名)<br>* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>* @param reqData 请求报文map<br>* @param secureKey 签名对称密钥<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return 签名后的map对象<br>*/public static Map<String, String> signBySecureKey(Map<String, String> reqData, String secureKey, String encoding) {reqData = SDKUtil.filterBlank(reqData);SDKUtil.signBySecureKey(reqData, secureKey, encoding);return reqData;}/*** 验证签名(SHA-1摘要算法)<br>* @param rspData 返回报文数据<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return true 通过 false 未通过<br>*/public static boolean validate(Map<String, String> rspData, String encoding) {return SDKUtil.validate(rspData, encoding);}/*** 多密钥验签(通过传入密钥签名)<br>* @param rspData 返回报文数据<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return true 通过 false 未通过<br>*/public static boolean validateBySecureKey(Map<String, String> rspData, String secureKey, String encoding) {return SDKUtil.validateBySecureKey(rspData, secureKey, encoding);}/*** @deprecated 5.1.0开发包已删除此方法,请直接参考5.1.0开发包中的VerifyAppData.java验签。* 对控件支付成功返回的结果信息中data域进行验签(控件端获取的应答信息)<br>* @param jsonData json格式数据,例如:{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==",  "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}* @return 是否成功*/public static boolean validateAppResponse(String jsonData, String encoding) {LogUtil.writeLog("控件应答信息验签处理开始:[" + jsonData + "]");if (SDKUtil.isEmpty(encoding)) {encoding = "UTF-8";}Pattern p = Pattern.compile("\\s*\"sign\"\\s*:\\s*\"([^\"]*)\"\\s*");Matcher m = p.matcher(jsonData);if(!m.find()) return false;String sign = m.group(1);p = Pattern.compile("\\s*\"data\"\\s*:\\s*\"([^\"]*)\"\\s*");m = p.matcher(jsonData);if(!m.find()) return false;String data = m.group(1);p = Pattern.compile("cert_id=(\\d*)");m = p.matcher(jsonData);if(!m.find()) return false;String certId = m.group(1);try {// 验证签名需要用银联发给商户的公钥证书.return SecureUtil.validateSignBySoft(CertUtil.getValidatePublicKey(certId), SecureUtil.base64Decode(sign.getBytes(encoding)), SecureUtil.sha1X16(data,encoding));} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);}return false;}/*** 功能:后台交易提交请求报文并接收同步应答报文<br>* @param reqData 请求报文<br>* @param reqUrl  请求地址<br>* @param encoding<br>* @return 应答http 200返回true ,其他false<br>*/public static Map<String,String> post(Map<String, String> reqData,String reqUrl,String encoding) {Map<String, String> rspData = new HashMap<String,String>();LogUtil.writeLog("请求银联地址:" + reqUrl);//发送后台请求数据HttpClient hc = new HttpClient(reqUrl, 30000, 30000);try {int status = hc.send(reqData, encoding);if (200 == status) {String resultString = hc.getResult();if (null != resultString && !"".equals(resultString)) {// 将返回结果转换为mapMap<String,String> tmpRspData  = SDKUtil.convertResultStringToMap(resultString);rspData.putAll(tmpRspData);}}else{LogUtil.writeLog("返回http状态码["+status+"],请检查请求报文或者请求地址是否正确");}} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);}return rspData;}/*** 功能:http Get方法 便民缴费产品中使用<br>* @param reqUrl 请求地址<br>* @param encoding<br>* @return*/public static String get(String reqUrl,String encoding) {LogUtil.writeLog("请求银联地址:" + reqUrl);//发送后台请求数据HttpClient hc = new HttpClient(reqUrl, 30000, 30000);try {int status = hc.sendGet(encoding);if (200 == status) {String resultString = hc.getResult();if (null != resultString && !"".equals(resultString)) {return resultString;}}else{LogUtil.writeLog("返回http状态码["+status+"],请检查请求报文或者请求地址是否正确");}} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);}return null;}/*** 功能:前台交易构造HTTP POST自动提交表单<br>* @param reqUrl 表单提交地址<br>* @param hiddens 以MAP形式存储的表单键值<br>* @param encoding 上送请求报文域encoding字段的值<br>* @return 构造好的HTTP POST交易表单<br>*/public static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens,String encoding) {StringBuffer sf = new StringBuffer();sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset="+encoding+"\"/></head><body>");sf.append("<form id = \"pay_form\" action=\"" + reqUrl+ "\" method=\"post\">");if (null != hiddens && 0 != hiddens.size()) {Set<Entry<String, String>> set = hiddens.entrySet();Iterator<Entry<String, String>> it = set.iterator();while (it.hasNext()) {Entry<String, String> ey = it.next();String key = ey.getKey();String value = ey.getValue();sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\""+ key + "\" value=\"" + value + "\"/>");}}sf.append("</form>");sf.append("</body>");sf.append("<script type=\"text/javascript\">");sf.append("document.all.pay_form.submit();");sf.append("</script>");sf.append("</html>");return sf.toString();}/*** 功能:将批量文件内容使用DEFLATE压缩算法压缩,Base64编码生成字符串并返回<br>* 适用到的交易:批量代付,批量代收,批量退货<br>* @param filePath 批量文件-全路径文件名<br>* @return*/public static String enCodeFileContent(String filePath,String encoding){String baseFileContent = "";File file = new File(filePath);if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}}InputStream in = null;try {in = new FileInputStream(file);int fl = in.available();if (null != in) {byte[] s = new byte[fl];in.read(s, 0, fl);// 压缩编码.baseFileContent = new String(SecureUtil.base64Encode(SDKUtil.deflater(s)),encoding);}} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);} finally {if (null != in) {try {in.close();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}}}return baseFileContent;}/*** 功能:解析交易返回的fileContent字符串并落地 ( 解base64,解DEFLATE压缩并落地)<br>* 适用到的交易:对账文件下载,批量交易状态查询<br>* @param resData 返回报文map<br>* @param fileDirectory 落地的文件目录(绝对路径)* @param encoding 上送请求报文域encoding字段的值<br>	*/public static String deCodeFileContent(Map<String, String> resData,String fileDirectory,String encoding) {// 解析返回文件String filePath = null;String fileContent = resData.get(SDKConstants.param_fileContent);if (null != fileContent && !"".equals(fileContent)) {FileOutputStream out = null;try {byte[] fileArray = SDKUtil.inflater(SecureUtil.base64Decode(fileContent.getBytes(encoding)));if (SDKUtil.isEmpty(resData.get("fileName"))) {filePath = fileDirectory + File.separator + resData.get("merId")+ "_" + resData.get("batchNo") + "_"+ resData.get("txnTime") + ".txt";} else {filePath = fileDirectory + File.separator + resData.get("fileName");}File file = new File(filePath);if (file.exists()) {file.delete();}file.createNewFile();out = new FileOutputStream(file);out.write(fileArray, 0, fileArray.length);out.flush();} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}finally{try {out.close();} catch (IOException e) {e.printStackTrace();}}}return filePath;}/*** 功能:将结果文件内容 转换成明文字符串:解base64,解压缩<br>* 适用到的交易:批量交易状态查询<br>* @param fileContent 批量交易状态查询返回的文件内容<br>* @return 内容明文<br>*/public static String getFileContent(String fileContent,String encoding){String fc = "";try {fc = new String(SDKUtil.inflater(SecureUtil.base64Decode(fileContent.getBytes())),encoding);} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return fc;}/*** 功能:持卡人信息域customerInfo构造<br>* 说明:不勾选对敏感信息加密权限使用旧的构造customerInfo域方式,不对敏感信息进行加密(对 phoneNo,cvn2, expired不加密),但如果送pin的话则加密<br>* @param customerInfoMap 信息域请求参数 key送域名value送值,必送<br>*        例如:customerInfoMap.put("certifTp", "01");					//证件类型<br>customerInfoMap.put("certifId", "341126197709218366");	//证件号码<br>customerInfoMap.put("customerNm", "互联网");				//姓名<br>customerInfoMap.put("phoneNo", "13552535506");			//手机号<br>customerInfoMap.put("smsCode", "123456");					//短信验证码<br>customerInfoMap.put("pin", "111111");						//密码(加密)<br>customerInfoMap.put("cvn2", "123");           			//卡背面的cvn2三位数字(不加密)<br>customerInfoMap.put("expired", "1711");  				    //有效期 年在前月在后(不加密)<br>* @param accNo  customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码pin,此字段可以不送<br>* @param encoding 上送请求报文域encoding字段的值<br>				  * @return base64后的持卡人信息域字段<br>*/public static String getCustomerInfo(Map<String,String> customerInfoMap,String accNo,String encoding) {if(customerInfoMap.isEmpty())return "{}";StringBuffer sf = new StringBuffer("{");for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){String key = it.next();String value = customerInfoMap.get(key);if(key.equals("pin")){if(null == accNo || "".equals(accNo.trim())){LogUtil.writeLog("送了密码(PIN),必须在getCustomerInfo参数中上传卡号");throw new RuntimeException("加密PIN没送卡号无法后续处理");}else{value = encryptPin(accNo,value,encoding);}}sf.append(key).append(SDKConstants.EQUAL).append(value);if(it.hasNext())sf.append(SDKConstants.AMPERSAND);}String customerInfo = sf.append("}").toString();LogUtil.writeLog("组装的customerInfo明文:"+customerInfo);try {return new String(SecureUtil.base64Encode(sf.toString().getBytes(encoding)),encoding);} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return customerInfo;}/*** 功能:持卡人信息域customerInfo构造,勾选对敏感信息加密权限 适用新加密规范,对pin和phoneNo,cvn2,expired加密 <br>* 适用到的交易: <br>* @param customerInfoMap 信息域请求参数 key送域名value送值,必送 <br>*        例如:customerInfoMap.put("certifTp", "01");					//证件类型 <br>customerInfoMap.put("certifId", "341126197709218366");	//证件号码 <br>customerInfoMap.put("customerNm", "互联网");				//姓名 <br>customerInfoMap.put("smsCode", "123456");					//短信验证码 <br>customerInfoMap.put("pin", "111111");						//密码(加密) <br>customerInfoMap.put("phoneNo", "13552535506");			//手机号(加密) <br>customerInfoMap.put("cvn2", "123");           			//卡背面的cvn2三位数字(加密) <br>customerInfoMap.put("expired", "1711");  				    //有效期 年在前月在后(加密) <br>* @param accNo  customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码PIN,此字段可以不送<br>* @param encoding 上送请求报文域encoding字段的值* @return base64后的持卡人信息域字段 <br>*/public static String getCustomerInfoWithEncrypt(Map<String,String> customerInfoMap,String accNo,String encoding) {if(customerInfoMap.isEmpty())return "{}";StringBuffer sf = new StringBuffer("{");//敏感信息加密域StringBuffer encryptedInfoSb = new StringBuffer("");for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){String key = it.next();String value = customerInfoMap.get(key);if(key.equals("phoneNo") || key.equals("cvn2") || key.equals("expired")){encryptedInfoSb.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);}else{if(key.equals("pin")){if(null == accNo || "".equals(accNo.trim())){LogUtil.writeLog("送了密码(PIN),必须在getCustomerInfoWithEncrypt参数中上传卡号");throw new RuntimeException("加密PIN没送卡号无法后续处理");}else{value = encryptPin(accNo,value,encoding);}}sf.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);}}if(!encryptedInfoSb.toString().equals("")){encryptedInfoSb.setLength(encryptedInfoSb.length()-1);//去掉最后一个&符号LogUtil.writeLog("组装的customerInfo encryptedInfo明文:"+ encryptedInfoSb.toString());sf.append("encryptedInfo").append(SDKConstants.EQUAL).append(encryptData(encryptedInfoSb.toString(), encoding));}else{sf.setLength(sf.length()-1);}String customerInfo = sf.append("}").toString();LogUtil.writeLog("组装的customerInfo明文:"+customerInfo);try {return new String(SecureUtil.base64Encode(sf.toString().getBytes(encoding)),encoding);} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return customerInfo;}/*** 解析返回报文(后台通知)中的customerInfo域:<br>* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>* @param customerInfo<br>* @param encoding<br>* @return*/public static Map<String,String> parseCustomerInfo(String customerInfo,String encoding){Map<String,String> customerInfoMap = null;try {byte[] b = SecureUtil.base64Decode(customerInfo.getBytes(encoding));String customerInfoNoBase64 = new String(b,encoding);LogUtil.writeLog("解base64后===>" +customerInfoNoBase64);//去掉前后的{}customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length()-1);customerInfoMap = SDKUtil.parseQString(customerInfoNoBase64);if(customerInfoMap.containsKey("encryptedInfo")){String encInfoStr = customerInfoMap.get("encryptedInfo");customerInfoMap.remove("encryptedInfo");String encryptedInfoStr = decryptData(encInfoStr, encoding);Map<String,String> encryptedInfoMap = SDKUtil.parseQString(encryptedInfoStr);customerInfoMap.putAll(encryptedInfoMap);}} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return customerInfoMap;}/*** 解析返回报文(后台通知)中的customerInfo域:<br>* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>* @param customerInfo<br>* @param encoding<br>* @return*/public static Map<String,String> parseCustomerInfo(String customerInfo, String certPath, String certPwd, String encoding){Map<String,String> customerInfoMap = null;try {byte[] b = SecureUtil.base64Decode(customerInfo.getBytes(encoding));String customerInfoNoBase64 = new String(b,encoding);LogUtil.writeLog("解base64后===>" +customerInfoNoBase64);//去掉前后的{}customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length()-1);customerInfoMap = SDKUtil.parseQString(customerInfoNoBase64);if(customerInfoMap.containsKey("encryptedInfo")){String encInfoStr = customerInfoMap.get("encryptedInfo");customerInfoMap.remove("encryptedInfo");String encryptedInfoStr = decryptData(encInfoStr, certPath, certPwd, encoding);Map<String,String> encryptedInfoMap = SDKUtil.parseQString(encryptedInfoStr);customerInfoMap.putAll(encryptedInfoMap);}} catch (UnsupportedEncodingException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}return customerInfoMap;}/*** 密码加密并做base64<br>* @param accNo 卡号<br>* @param pin 密码<br>* @param encoding<br>* @return 加密的内容<br>*/public static String encryptPin(String accNo, String pin, String encoding) {return SecureUtil.encryptPin(accNo, pin, encoding, CertUtil.getEncryptCertPublicKey());}/*** 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>* @param data 送 phoneNo,cvn2,有效期<br>* @param encoding<br>* @return 加密的密文<br>*/public static String encryptData(String data, String encoding) {return SecureUtil.encryptData(data, encoding, CertUtil.getEncryptCertPublicKey());}/*** 敏感信息解密,使用配置文件acp_sdk.properties解密<br>* @param base64EncryptedInfo 加密信息<br>* @param encoding<br>* @return 解密后的明文<br>*/public static String decryptData(String base64EncryptedInfo, String encoding) {return SecureUtil.decryptData(base64EncryptedInfo, encoding, CertUtil.getSignCertPrivateKey());}/*** 敏感信息解密,通过传入的私钥解密<br>* @param base64EncryptedInfo 加密信息<br>* @param certPath 私钥文件(带全路径)<br>* @param certPwd 私钥密码<br>* @param encoding<br>* @return*/public static String decryptData(String base64EncryptedInfo, String certPath, String certPwd, String encoding) {return SecureUtil.decryptData(base64EncryptedInfo, encoding, CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd));}/*** 5.0.0加密磁道信息,5.1.0接口请勿使用<br>* @param trackData 待加密磁道数据<br>* @param encoding 编码格式<br>* @return 加密的密文<br>* @deprecated*/public static String encryptTrack(String trackData, String encoding) {return SecureUtil.encryptData(trackData, encoding,CertUtil.getEncryptTrackPublicKey());}/*** 获取敏感信息加密证书的物理序列号<br>* @return*/public static String getEncryptCertId(){return CertUtil.getEncryptCertId();}/*** 对字符串做base64<br>* @param rawStr<br>* @param encoding<br>* @return<br>* @throws IOException*/public static String base64Encode(String rawStr,String encoding) throws IOException{byte [] rawByte = rawStr.getBytes(encoding);return new String(SecureUtil.base64Encode(rawByte),encoding);}/*** 对base64的字符串解base64<br>* @param base64Str<br>* @param encoding<br>* @return<br>* @throws IOException*/public static String base64Decode(String base64Str,String encoding) throws IOException{byte [] rawByte = base64Str.getBytes(encoding);return new String(SecureUtil.base64Decode(rawByte),encoding);	}/*** * 有卡交易信息域(cardTransData)构造<br>* 所有子域需用“{}”包含,子域间以“&”符号链接。格式如下:{子域名1=值&子域名2=值&子域名3=值}<br>* 说明:本示例仅供参考,开发时请根据接口文档中的报文要素组装<br>* * @param cardTransDataMap cardTransData的数据<br>* @param requestData 必须包含merId、orderId、txnTime、txnAmt,磁道加密时需要使用<br>* @param encoding 编码<br>* @return*/public static String getCardTransData(Map<String, String> cardTransDataMap, Map<String, String> requestData,String encoding) { {StringBuffer cardTransDataBuffer = new StringBuffer();if(cardTransDataMap.containsKey("track2Data")){StringBuffer track2Buffer = new StringBuffer();track2Buffer.append(requestData.get("merId")).append(SDKConstants.COLON).append(requestData.get("orderId")).append(SDKConstants.COLON).append(requestData.get("txnTime")).append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt")).append(SDKConstants.COLON).append(cardTransDataMap.get("track2Data"));cardTransDataMap.put("track2Data", AcpService.encryptData(track2Buffer.toString(),	encoding));}if(cardTransDataMap.containsKey("track3Data")){StringBuffer track3Buffer = new StringBuffer();track3Buffer.append(requestData.get("merId")).append(SDKConstants.COLON).append(requestData.get("orderId")).append(SDKConstants.COLON).append(requestData.get("txnTime")).append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt")).append(SDKConstants.COLON).append(cardTransDataMap.get("track3Data"));cardTransDataMap.put("track3Data", AcpService.encryptData(track3Buffer.toString(),	encoding));}return cardTransDataBuffer.append(SDKConstants.LEFT_BRACE).append(SDKUtil.coverMap2String(cardTransDataMap)).append(SDKConstants.RIGHT_BRACE).toString();}}/*** 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>* 更新成功则返回1,无更新返回0,失败异常返回-1<br>* @param resData 返回报文* @param encoding* @return*/public static int updateEncryptCert(Map<String, String> resData,String encoding) {return SDKUtil.getEncryptCert(resData, encoding);}/*** 获取* @param number* @return*/public static int genLuhn(String number){return SecureUtil.genLuhn(number);}
}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28     SSLSocket 链接工具类(用于https)* =============================================================================*/
package com.example.demo.union.service;import com.example.demo.union.util.LogUtil;import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;/*** * @ClassName BaseHttpSSLSocketFactory* @Description 忽略验证ssl证书* @date 2016-7-22 下午4:10:14**/
public class BaseHttpSSLSocketFactory extends SSLSocketFactory {private SSLContext getSSLContext() {return createEasySSLContext();}@Overridepublic Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2,int arg3) throws IOException {return getSSLContext().getSocketFactory().createSocket(arg0, arg1,arg2, arg3);}@Overridepublic Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)throws IOException, UnknownHostException {return getSSLContext().getSocketFactory().createSocket(arg0, arg1,arg2, arg3);}@Overridepublic Socket createSocket(InetAddress arg0, int arg1) throws IOException {return getSSLContext().getSocketFactory().createSocket(arg0, arg1);}@Overridepublic Socket createSocket(String arg0, int arg1) throws IOException,UnknownHostException {return getSSLContext().getSocketFactory().createSocket(arg0, arg1);}@Overridepublic String[] getSupportedCipherSuites() {// TODO Auto-generated method stubreturn null;}@Overridepublic String[] getDefaultCipherSuites() {// TODO Auto-generated method stubreturn null;}@Overridepublic Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3)throws IOException {return getSSLContext().getSocketFactory().createSocket(arg0, arg1,arg2, arg3);}private SSLContext createEasySSLContext() {try {SSLContext context = SSLContext.getInstance("SSL");context.init(null,new TrustManager[] { MyX509TrustManager.manger }, null);return context;} catch (Exception e) {LogUtil.writeErrorLog(e.getMessage(), e);return null;}}public static class MyX509TrustManager implements X509TrustManager {static MyX509TrustManager manger = new MyX509TrustManager();public MyX509TrustManager() {}public X509Certificate[] getAcceptedIssuers() {return null;}public void checkClientTrusted(X509Certificate[] chain, String authType) {}public void checkServerTrusted(X509Certificate[] chain, String authType) {}}/*** 解决由于服务器证书问题导致HTTPS无法访问的情况 PS:HTTPS hostname wrong: should be <localhost>*/public static class TrustAnyHostnameVerifier implements HostnameVerifier {public boolean verify(String hostname, SSLSession session) {//直接返回truereturn true;}}
}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28       MPI插件包常量定义* =============================================================================*/
package com.example.demo.union.constants;
/*** * @ClassName SDKConstants* @Description acpsdk常量类* @date 2016-7-22 下午4:05:54**/
public class SDKConstants {public final static String COLUMN_DEFAULT = "-";public final static String KEY_DELIMITER = "#";/** memeber variable: blank. */public static final String BLANK = "";/** member variabel: space. */public static final String SPACE = " ";/** memeber variable: unline. */public static final String UNLINE = "_";/** memeber varibale: star. */public static final String STAR = "*";/** memeber variable: line. */public static final String LINE = "-";/** memeber variable: add. */public static final String ADD = "+";/** memeber variable: colon. */public final static String COLON = "|";/** memeber variable: point. */public final static String POINT = ".";/** memeber variable: comma. */public final static String COMMA = ",";/** memeber variable: slash. */public final static String SLASH = "/";/** memeber variable: div. */public final static String DIV = "/";/** memeber variable: left . */public final static String LB = "(";/** memeber variable: right. */public final static String RB = ")";/** memeber variable: rmb. */public final static String CUR_RMB = "RMB";/** memeber variable: .page size */public static final int PAGE_SIZE = 10;/** memeber variable: String ONE. */public static final String ONE = "1";/** memeber variable: String ZERO. */public static final String ZERO = "0";/** memeber variable: number six. */public static final int NUM_SIX = 6;/** memeber variable: equal mark. */public static final String EQUAL = "=";/** memeber variable: operation ne. */public static final String NE = "!=";/** memeber variable: operation le. */public static final String LE = "<=";/** memeber variable: operation ge. */public static final String GE = ">=";/** memeber variable: operation lt. */public static final String LT = "<";/** memeber variable: operation gt. */public static final String GT = ">";/** memeber variable: list separator. */public static final String SEP = "./";/** memeber variable: Y. */public static final String Y = "Y";/** memeber variable: AMPERSAND. */public static final String AMPERSAND = "&";/** memeber variable: SQL_LIKE_TAG. */public static final String SQL_LIKE_TAG = "%";/** memeber variable: @. */public static final String MAIL = "@";/** memeber variable: number zero. */public static final int NZERO = 0;public static final String LEFT_BRACE = "{";public static final String RIGHT_BRACE = "}";/** memeber variable: string true. */public static final String TRUE_STRING = "true";/** memeber variable: string false. */public static final String FALSE_STRING = "false";/** memeber variable: forward success. */public static final String SUCCESS = "success";/** memeber variable: forward fail. */public static final String FAIL = "fail";/** memeber variable: global forward success. */public static final String GLOBAL_SUCCESS = "$success";/** memeber variable: global forward fail. */public static final String GLOBAL_FAIL = "$fail";public static final String UTF_8_ENCODING = "UTF-8";public static final String GBK_ENCODING = "GBK";public static final String CONTENT_TYPE = "Content-type";public static final String APP_XML_TYPE = "application/xml;charset=utf-8";public static final String APP_FORM_TYPE = "application/x-www-form-urlencoded;charset=";public static final String VERSION_1_0_0 = "1.0.0";public static final String VERSION_5_0_0 = "5.0.0";public static final String VERSION_5_0_1 = "5.0.1";public static final String VERSION_5_1_0 = "5.1.0";public static final String SIGNMETHOD_RSA = "01";public static final String SIGNMETHOD_SHA256 = "11";public static final String SIGNMETHOD_SM3 = "12";public static final String UNIONPAY_CNNAME = "中国银联股份有限公司";public static final String CERTTYPE_01 = "01";// 敏感信息加密公钥public static final String CERTTYPE_02 = "02";// 磁道加密公钥/******************************************** 5.0报文接口定义 ********************************************//** 版本号. */public static final String param_version = "version";/** 证书ID. */public static final String param_certId = "certId";/** 签名. */public static final String param_signature = "signature";/** 签名方法. */public static final String param_signMethod = "signMethod";/** 编码方式. */public static final String param_encoding = "encoding";/** 交易类型. */public static final String param_txnType = "txnType";/** 交易子类. */public static final String param_txnSubType = "txnSubType";/** 业务类型. */public static final String param_bizType = "bizType";/** 前台通知地址 . */public static final String param_frontUrl = "frontUrl";/** 后台通知地址. */public static final String param_backUrl = "backUrl";/** 接入类型. */public static final String param_accessType = "accessType";/** 收单机构代码. */public static final String param_acqInsCode = "acqInsCode";/** 商户类别. */public static final String param_merCatCode = "merCatCode";/** 商户类型. */public static final String param_merType = "merType";/** 商户代码. */public static final String param_merId = "merId";/** 商户名称. */public static final String param_merName = "merName";/** 商户简称. */public static final String param_merAbbr = "merAbbr";/** 二级商户代码. */public static final String param_subMerId = "subMerId";/** 二级商户名称. */public static final String param_subMerName = "subMerName";/** 二级商户简称. */public static final String param_subMerAbbr = "subMerAbbr";/** Cupsecure 商户代码. */public static final String param_csMerId = "csMerId";/** 商户订单号. */public static final String param_orderId = "orderId";/** 交易时间. */public static final String param_txnTime = "txnTime";/** 发送时间. */public static final String param_txnSendTime = "txnSendTime";/** 订单超时时间间隔. */public static final String param_orderTimeoutInterval = "orderTimeoutInterval";/** 支付超时时间. */public static final String param_payTimeoutTime = "payTimeoutTime";/** 默认支付方式. */public static final String param_defaultPayType = "defaultPayType";/** 支持支付方式. */public static final String param_supPayType = "supPayType";/** 支付方式. */public static final String param_payType = "payType";/** 自定义支付方式. */public static final String param_customPayType = "customPayType";/** 物流标识. */public static final String param_shippingFlag = "shippingFlag";/** 收货地址-国家. */public static final String param_shippingCountryCode = "shippingCountryCode";/** 收货地址-省. */public static final String param_shippingProvinceCode = "shippingProvinceCode";/** 收货地址-市. */public static final String param_shippingCityCode = "shippingCityCode";/** 收货地址-地区. */public static final String param_shippingDistrictCode = "shippingDistrictCode";/** 收货地址-详细. */public static final String param_shippingStreet = "shippingStreet";/** 商品总类. */public static final String param_commodityCategory = "commodityCategory";/** 商品名称. */public static final String param_commodityName = "commodityName";/** 商品URL. */public static final String param_commodityUrl = "commodityUrl";/** 商品单价. */public static final String param_commodityUnitPrice = "commodityUnitPrice";/** 商品数量. */public static final String param_commodityQty = "commodityQty";/** 是否预授权. */public static final String param_isPreAuth = "isPreAuth";/** 币种. */public static final String param_currencyCode = "currencyCode";/** 账户类型. */public static final String param_accType = "accType";/** 账号. */public static final String param_accNo = "accNo";/** 支付卡类型. */public static final String param_payCardType = "payCardType";/** 发卡机构代码. */public static final String param_issInsCode = "issInsCode";/** 持卡人信息. */public static final String param_customerInfo = "customerInfo";/** 交易金额. */public static final String param_txnAmt = "txnAmt";/** 余额. */public static final String param_balance = "balance";/** 地区代码. */public static final String param_districtCode = "districtCode";/** 附加地区代码. */public static final String param_additionalDistrictCode = "additionalDistrictCode";/** 账单类型. */public static final String param_billType = "billType";/** 账单号码. */public static final String param_billNo = "billNo";/** 账单月份. */public static final String param_billMonth = "billMonth";/** 账单查询要素. */public static final String param_billQueryInfo = "billQueryInfo";/** 账单详情. */public static final String param_billDetailInfo = "billDetailInfo";/** 账单金额. */public static final String param_billAmt = "billAmt";/** 账单金额符号. */public static final String param_billAmtSign = "billAmtSign";/** 绑定标识号. */public static final String param_bindId = "bindId";/** 风险级别. */public static final String param_riskLevel = "riskLevel";/** 绑定信息条数. */public static final String param_bindInfoQty = "bindInfoQty";/** 绑定信息集. */public static final String param_bindInfoList = "bindInfoList";/** 批次号. */public static final String param_batchNo = "batchNo";/** 总笔数. */public static final String param_totalQty = "totalQty";/** 总金额. */public static final String param_totalAmt = "totalAmt";/** 文件类型. */public static final String param_fileType = "fileType";/** 文件名称. */public static final String param_fileName = "fileName";/** 批量文件内容. */public static final String param_fileContent = "fileContent";/** 商户摘要. */public static final String param_merNote = "merNote";/** 商户自定义域. */// public static final String param_merReserved = "merReserved";//接口变更删除/** 请求方保留域. */public static final String param_reqReserved = "reqReserved";// 新增接口/** 保留域. */public static final String param_reserved = "reserved";/** 终端号. */public static final String param_termId = "termId";/** 终端类型. */public static final String param_termType = "termType";/** 交互模式. */public static final String param_interactMode = "interactMode";/** 发卡机构识别模式. */// public static final String param_recognitionMode = "recognitionMode";public static final String param_issuerIdentifyMode = "issuerIdentifyMode";// 接口名称变更/** 商户端用户号. */public static final String param_merUserId = "merUserId";/** 持卡人IP. */public static final String param_customerIp = "customerIp";/** 查询流水号. */public static final String param_queryId = "queryId";/** 原交易查询流水号. */public static final String param_origQryId = "origQryId";/** 系统跟踪号. */public static final String param_traceNo = "traceNo";/** 交易传输时间. */public static final String param_traceTime = "traceTime";/** 清算日期. */public static final String param_settleDate = "settleDate";/** 清算币种. */public static final String param_settleCurrencyCode = "settleCurrencyCode";/** 清算金额. */public static final String param_settleAmt = "settleAmt";/** 清算汇率. */public static final String param_exchangeRate = "exchangeRate";/** 兑换日期. */public static final String param_exchangeDate = "exchangeDate";/** 响应时间. */public static final String param_respTime = "respTime";/** 原交易应答码. */public static final String param_origRespCode = "origRespCode";/** 原交易应答信息. */public static final String param_origRespMsg = "origRespMsg";/** 应答码. */public static final String param_respCode = "respCode";/** 应答码信息. */public static final String param_respMsg = "respMsg";// 新增四个报文字段merUserRegDt merUserEmail checkFlag activateStatus/** 商户端用户注册时间. */public static final String param_merUserRegDt = "merUserRegDt";/** 商户端用户注册邮箱. */public static final String param_merUserEmail = "merUserEmail";/** 验证标识. */public static final String param_checkFlag = "checkFlag";/** 开通状态. */public static final String param_activateStatus = "activateStatus";/** 加密证书ID. */public static final String param_encryptCertId = "encryptCertId";/** 用户MAC、IMEI串号、SSID. */public static final String param_userMac = "userMac";/** 关联交易. */// public static final String param_relationTxnType = "relationTxnType";/** 短信类型 */public static final String param_smsType = "smsType";/** 风控信息域 */public static final String param_riskCtrlInfo = "riskCtrlInfo";/** IC卡交易信息域 */public static final String param_ICTransData = "ICTransData";/** VPC交易信息域 */public static final String param_VPCTransData = "VPCTransData";/** 安全类型 */public static final String param_securityType = "securityType";/** 银联订单号 */public static final String param_tn = "tn";/** 分期付款手续费率 */public static final String param_instalRate = "instalRate";/** 分期付款手续费率 */public static final String param_mchntFeeSubsidy = "mchntFeeSubsidy";/** 签名公钥证书 */public static final String param_signPubKeyCert = "signPubKeyCert";/** 加密公钥证书 */public static final String param_encryptPubKeyCert = "encryptPubKeyCert";/** 证书类型 */public static final String param_certType = "certType";}
/**** Licensed Property to China UnionPay Co., Ltd.* * (C) Copyright of China UnionPay Co., Ltd. 2010*     All Rights Reserved.** * Modification History:* =============================================================================*   Author         Date          Description*   ------------ ---------- ---------------------------------------------------*   xshu       2014-05-28       MPI基本参数工具类* =============================================================================*/
package com.example.demo.union.config;import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.util.LogUtil;
import com.example.demo.union.util.SDKUtil;
import org.apache.commons.lang.StringUtils;import java.io.*;
import java.util.Properties;/*** * @ClassName SDKConfig* @Description acpsdk配置文件acp_sdk.properties配置信息类* @date 2016-7-22 下午4:04:55**/
public class SDKConfig {public static final String FILE_NAME = "properties/acp_sdk.properties";/** 前台请求URL. */private String frontRequestUrl;/** 后台请求URL. */private String backRequestUrl;/** 单笔查询 */private String singleQueryUrl;/** 批量查询 */private String batchQueryUrl;/** 批量交易 */private String batchTransUrl;/** 文件传输 */private String fileTransUrl;/** 签名证书路径. */private String signCertPath;/** 签名证书密码. */private String signCertPwd;/** 签名证书类型. */private String signCertType;/** 加密公钥证书路径. */private String encryptCertPath;/** 验证签名公钥证书目录. */private String validateCertDir;/** 按照商户代码读取指定签名证书目录. */private String signCertDir;/** 磁道加密证书路径. */private String encryptTrackCertPath;/** 磁道加密公钥模数. */private String encryptTrackKeyModulus;/** 磁道加密公钥指数. */private String encryptTrackKeyExponent;/** 有卡交易. */private String cardRequestUrl;/** app交易 */private String appRequestUrl;/** 证书使用模式(单证书/多证书) */private String singleMode;/** 安全密钥(SHA256和SM3计算时使用) */private String secureKey;/** 中级证书路径  */private String middleCertPath;/** 根证书路径  */private String rootCertPath;/** 是否验证验签证书CN,除了false都验  */private boolean ifValidateCNName = true;/** 是否验证https证书,默认都不验  */private boolean ifValidateRemoteCert = false;/** signMethod,没配按01吧  */private String signMethod = "01";/** version,没配按5.0.0  */private String version = "5.0.0";/** frontUrl  */private String frontUrl;/** backUrl  */private String backUrl;/*缴费相关地址*/private String jfFrontRequestUrl;private String jfBackRequestUrl;private String jfSingleQueryUrl;private String jfCardRequestUrl;private String jfAppRequestUrl;private String qrcBackTransUrl;private String qrcB2cIssBackTransUrl;private String qrcB2cMerBackTransUrl;/** 配置文件中的前台URL常量. */public static final String SDK_FRONT_URL = "acpsdk.frontTransUrl";/** 配置文件中的后台URL常量. */public static final String SDK_BACK_URL = "acpsdk.backTransUrl";/** 配置文件中的单笔交易查询URL常量. */public static final String SDK_SIGNQ_URL = "acpsdk.singleQueryUrl";/** 配置文件中的批量交易查询URL常量. */public static final String SDK_BATQ_URL = "acpsdk.batchQueryUrl";/** 配置文件中的批量交易URL常量. */public static final String SDK_BATTRANS_URL = "acpsdk.batchTransUrl";/** 配置文件中的文件类交易URL常量. */public static final String SDK_FILETRANS_URL = "acpsdk.fileTransUrl";/** 配置文件中的有卡交易URL常量. */public static final String SDK_CARD_URL = "acpsdk.cardTransUrl";/** 配置文件中的app交易URL常量. */public static final String SDK_APP_URL = "acpsdk.appTransUrl";/** 以下缴费产品使用,其余产品用不到,无视即可 */// 前台请求地址public static final String JF_SDK_FRONT_TRANS_URL= "acpsdk.jfFrontTransUrl";// 后台请求地址public static final String JF_SDK_BACK_TRANS_URL="acpsdk.jfBackTransUrl";// 单笔查询请求地址public static final String JF_SDK_SINGLE_QUERY_URL="acpsdk.jfSingleQueryUrl";// 有卡交易地址public static final String JF_SDK_CARD_TRANS_URL="acpsdk.jfCardTransUrl";// App交易地址public static final String JF_SDK_APP_TRANS_URL="acpsdk.jfAppTransUrl";// 人到人public static final String QRC_BACK_TRANS_URL="acpsdk.qrcBackTransUrl";// 人到人public static final String QRC_B2C_ISS_BACK_TRANS_URL="acpsdk.qrcB2cIssBackTransUrl";// 人到人public static final String QRC_B2C_MER_BACK_TRANS_URL="acpsdk.qrcB2cMerBackTransUrl";/** 配置文件中签名证书路径常量. */public static final String SDK_SIGNCERT_PATH = "acpsdk.signCert.path";/** 配置文件中签名证书密码常量. */public static final String SDK_SIGNCERT_PWD = "acpsdk.signCert.pwd";/** 配置文件中签名证书类型常量. */public static final String SDK_SIGNCERT_TYPE = "acpsdk.signCert.type";/** 配置文件中密码加密证书路径常量. */public static final String SDK_ENCRYPTCERT_PATH = "acpsdk.encryptCert.path";/** 配置文件中磁道加密证书路径常量. */public static final String SDK_ENCRYPTTRACKCERT_PATH = "acpsdk.encryptTrackCert.path";/** 配置文件中磁道加密公钥模数常量. */public static final String SDK_ENCRYPTTRACKKEY_MODULUS = "acpsdk.encryptTrackKey.modulus";/** 配置文件中磁道加密公钥指数常量. */public static final String SDK_ENCRYPTTRACKKEY_EXPONENT = "acpsdk.encryptTrackKey.exponent";/** 配置文件中验证签名证书目录常量. */public static final String SDK_VALIDATECERT_DIR = "acpsdk.validateCert.dir";/** 配置文件中是否加密cvn2常量. */public static final String SDK_CVN_ENC = "acpsdk.cvn2.enc";/** 配置文件中是否加密cvn2有效期常量. */public static final String SDK_DATE_ENC = "acpsdk.date.enc";/** 配置文件中是否加密卡号常量. */public static final String SDK_PAN_ENC = "acpsdk.pan.enc";/** 配置文件中证书使用模式 */public static final String SDK_SINGLEMODE = "acpsdk.singleMode";/** 配置文件中安全密钥 */public static final String SDK_SECURITYKEY = "acpsdk.secureKey";/** 配置文件中根证书路径常量  */public static final String SDK_ROOTCERT_PATH = "acpsdk.rootCert.path";/** 配置文件中根证书路径常量  */public static final String SDK_MIDDLECERT_PATH = "acpsdk.middleCert.path";/** 配置是否需要验证验签证书CN,除了false之外的值都当true处理 */public static final String SDK_IF_VALIDATE_CN_NAME = "acpsdk.ifValidateCNName";/** 配置是否需要验证https证书,除了true之外的值都当false处理 */public static final String SDK_IF_VALIDATE_REMOTE_CERT = "acpsdk.ifValidateRemoteCert";/** signmethod */public static final String SDK_SIGN_METHOD ="acpsdk.signMethod";/** version */public static final String SDK_VERSION = "acpsdk.version";/** 后台通知地址  */public static final String SDK_BACKURL = "acpsdk.backUrl";/** 前台通知地址  */public static final String SDK_FRONTURL = "acpsdk.frontUrl";/** 操作对象. */private static SDKConfig config = new SDKConfig();/** 属性文件对象. */private Properties properties;private SDKConfig() {super();}/*** 获取config对象.* @return*/public static SDKConfig getConfig() {return config;}/*** 从properties文件加载* * @param rootPath*            不包含文件名的目录.*/public void loadPropertiesFromPath(String rootPath) {if (StringUtils.isNotBlank(rootPath)) {LogUtil.writeLog("从路径读取配置文件: " + rootPath+File.separator+FILE_NAME);File file = new File(rootPath + File.separator + FILE_NAME);InputStream in = null;if (file.exists()) {try {in = new FileInputStream(file);properties = new Properties();properties.load(in);loadProperties(properties);} catch (FileNotFoundException e) {LogUtil.writeErrorLog(e.getMessage(), e);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);} finally {if (null != in) {try {in.close();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}}}} else {// 由于此时可能还没有完成LOG的加载,因此采用标准输出来打印日志信息LogUtil.writeErrorLog(rootPath + FILE_NAME + "不存在,加载参数失败");}} else {loadPropertiesFromSrc();}}/*** 从classpath路径下加载配置参数*/public void loadPropertiesFromSrc() {InputStream in = null;try {LogUtil.writeLog("从classpath: " +SDKConfig.class.getClassLoader().getResource("").getPath()+" 获取属性文件"+FILE_NAME);in = this.getClass().getClassLoader().getResourceAsStream(FILE_NAME);if (null != in) {properties = new Properties();try {properties.load(in);} catch (IOException e) {throw e;}} else {LogUtil.writeErrorLog(FILE_NAME + "属性文件未能在classpath指定的目录下 "+SDKConfig.class.getClassLoader().getResource("").getPath()+" 找到!");return;}loadProperties(properties);} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);} finally {if (null != in) {try {in.close();} catch (IOException e) {LogUtil.writeErrorLog(e.getMessage(), e);}}}}/*** 根据传入的 {@link #(Properties)}对象设置配置参数* * @param pro*/public void loadProperties(Properties pro) {LogUtil.writeLog("开始从属性文件中加载配置项");String value = null;value = pro.getProperty(SDK_SIGNCERT_PATH);if (!SDKUtil.isEmpty(value)) {this.signCertPath = value.trim();LogUtil.writeLog("配置项:私钥签名证书路径==>"+SDK_SIGNCERT_PATH +"==>"+ value+" 已加载");}value = pro.getProperty(SDK_SIGNCERT_PWD);if (!SDKUtil.isEmpty(value)) {this.signCertPwd = value.trim();LogUtil.writeLog("配置项:私钥签名证书密码==>"+SDK_SIGNCERT_PWD +" 已加载");}value = pro.getProperty(SDK_SIGNCERT_TYPE);if (!SDKUtil.isEmpty(value)) {this.signCertType = value.trim();LogUtil.writeLog("配置项:私钥签名证书类型==>"+SDK_SIGNCERT_TYPE +"==>"+ value+" 已加载");}value = pro.getProperty(SDK_ENCRYPTCERT_PATH);if (!SDKUtil.isEmpty(value)) {this.encryptCertPath = value.trim();LogUtil.writeLog("配置项:敏感信息加密证书==>"+SDK_ENCRYPTCERT_PATH +"==>"+ value+" 已加载");}value = pro.getProperty(SDK_VALIDATECERT_DIR);if (!SDKUtil.isEmpty(value)) {this.validateCertDir = value.trim();LogUtil.writeLog("配置项:验证签名证书路径(这里配置的是目录,不要指定到公钥文件)==>"+SDK_VALIDATECERT_DIR +"==>"+ value+" 已加载");}value = pro.getProperty(SDK_FRONT_URL);if (!SDKUtil.isEmpty(value)) {this.frontRequestUrl = value.trim();}value = pro.getProperty(SDK_BACK_URL);if (!SDKUtil.isEmpty(value)) {this.backRequestUrl = value.trim();}value = pro.getProperty(SDK_BATQ_URL);if (!SDKUtil.isEmpty(value)) {this.batchQueryUrl = value.trim();}value = pro.getProperty(SDK_BATTRANS_URL);if (!SDKUtil.isEmpty(value)) {this.batchTransUrl = value.trim();}value = pro.getProperty(SDK_FILETRANS_URL);if (!SDKUtil.isEmpty(value)) {this.fileTransUrl = value.trim();}value = pro.getProperty(SDK_SIGNQ_URL);if (!SDKUtil.isEmpty(value)) {this.singleQueryUrl = value.trim();}value = pro.getProperty(SDK_CARD_URL);if (!SDKUtil.isEmpty(value)) {this.cardRequestUrl = value.trim();}value = pro.getProperty(SDK_APP_URL);if (!SDKUtil.isEmpty(value)) {this.appRequestUrl = value.trim();}value = pro.getProperty(SDK_ENCRYPTTRACKCERT_PATH);if (!SDKUtil.isEmpty(value)) {this.encryptTrackCertPath = value.trim();}value = pro.getProperty(SDK_SECURITYKEY);if (!SDKUtil.isEmpty(value)) {this.secureKey = value.trim();}value = pro.getProperty(SDK_ROOTCERT_PATH);if (!SDKUtil.isEmpty(value)) {this.rootCertPath = value.trim();}value = pro.getProperty(SDK_MIDDLECERT_PATH);if (!SDKUtil.isEmpty(value)) {this.middleCertPath = value.trim();}/**缴费部分**/value = pro.getProperty(JF_SDK_FRONT_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.jfFrontRequestUrl = value.trim();}value = pro.getProperty(JF_SDK_BACK_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.jfBackRequestUrl = value.trim();}value = pro.getProperty(JF_SDK_SINGLE_QUERY_URL);if (!SDKUtil.isEmpty(value)) {this.jfSingleQueryUrl = value.trim();}value = pro.getProperty(JF_SDK_CARD_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.jfCardRequestUrl = value.trim();}value = pro.getProperty(JF_SDK_APP_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.jfAppRequestUrl = value.trim();}value = pro.getProperty(QRC_BACK_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.qrcBackTransUrl = value.trim();}value = pro.getProperty(QRC_B2C_ISS_BACK_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.qrcB2cIssBackTransUrl = value.trim();}value = pro.getProperty(QRC_B2C_MER_BACK_TRANS_URL);if (!SDKUtil.isEmpty(value)) {this.qrcB2cMerBackTransUrl = value.trim();}value = pro.getProperty(SDK_ENCRYPTTRACKKEY_EXPONENT);if (!SDKUtil.isEmpty(value)) {this.encryptTrackKeyExponent = value.trim();}value = pro.getProperty(SDK_ENCRYPTTRACKKEY_MODULUS);if (!SDKUtil.isEmpty(value)) {this.encryptTrackKeyModulus = value.trim();}value = pro.getProperty(SDK_IF_VALIDATE_CN_NAME);if (!SDKUtil.isEmpty(value)) {if( SDKConstants.FALSE_STRING.equals(value.trim()))this.ifValidateCNName = false;}value = pro.getProperty(SDK_IF_VALIDATE_REMOTE_CERT);if (!SDKUtil.isEmpty(value)) {if( SDKConstants.TRUE_STRING.equals(value.trim()))this.ifValidateRemoteCert = true;}value = pro.getProperty(SDK_SIGN_METHOD);if (!SDKUtil.isEmpty(value)) {this.signMethod = value.trim();}value = pro.getProperty(SDK_SIGN_METHOD);if (!SDKUtil.isEmpty(value)) {this.signMethod = value.trim();}value = pro.getProperty(SDK_VERSION);if (!SDKUtil.isEmpty(value)) {this.version = value.trim();}value = pro.getProperty(SDK_FRONTURL);if (!SDKUtil.isEmpty(value)) {this.frontUrl = value.trim();}value = pro.getProperty(SDK_BACKURL);if (!SDKUtil.isEmpty(value)) {this.backUrl = value.trim();}}public String getFrontRequestUrl() {return frontRequestUrl;}public void setFrontRequestUrl(String frontRequestUrl) {this.frontRequestUrl = frontRequestUrl;}public String getBackRequestUrl() {return backRequestUrl;}public void setBackRequestUrl(String backRequestUrl) {this.backRequestUrl = backRequestUrl;}public String getSignCertPath() {return signCertPath;}public void setSignCertPath(String signCertPath) {this.signCertPath = signCertPath;}public String getSignCertPwd() {return signCertPwd;}public void setSignCertPwd(String signCertPwd) {this.signCertPwd = signCertPwd;}public String getSignCertType() {return signCertType;}public void setSignCertType(String signCertType) {this.signCertType = signCertType;}public String getEncryptCertPath() {return encryptCertPath;}public void setEncryptCertPath(String encryptCertPath) {this.encryptCertPath = encryptCertPath;}public String getValidateCertDir() {return validateCertDir;}public void setValidateCertDir(String validateCertDir) {this.validateCertDir = validateCertDir;}public String getSingleQueryUrl() {return singleQueryUrl;}public void setSingleQueryUrl(String singleQueryUrl) {this.singleQueryUrl = singleQueryUrl;}public String getBatchQueryUrl() {return batchQueryUrl;}public void setBatchQueryUrl(String batchQueryUrl) {this.batchQueryUrl = batchQueryUrl;}public String getBatchTransUrl() {return batchTransUrl;}public void setBatchTransUrl(String batchTransUrl) {this.batchTransUrl = batchTransUrl;}public String getFileTransUrl() {return fileTransUrl;}public void setFileTransUrl(String fileTransUrl) {this.fileTransUrl = fileTransUrl;}public String getSignCertDir() {return signCertDir;}public void setSignCertDir(String signCertDir) {this.signCertDir = signCertDir;}public Properties getProperties() {return properties;}public void setProperties(Properties properties) {this.properties = properties;}public String getCardRequestUrl() {return cardRequestUrl;}public void setCardRequestUrl(String cardRequestUrl) {this.cardRequestUrl = cardRequestUrl;}public String getAppRequestUrl() {return appRequestUrl;}public void setAppRequestUrl(String appRequestUrl) {this.appRequestUrl = appRequestUrl;}public String getEncryptTrackCertPath() {return encryptTrackCertPath;}public void setEncryptTrackCertPath(String encryptTrackCertPath) {this.encryptTrackCertPath = encryptTrackCertPath;}public String getJfFrontRequestUrl() {return jfFrontRequestUrl;}public void setJfFrontRequestUrl(String jfFrontRequestUrl) {this.jfFrontRequestUrl = jfFrontRequestUrl;}public String getJfBackRequestUrl() {return jfBackRequestUrl;}public void setJfBackRequestUrl(String jfBackRequestUrl) {this.jfBackRequestUrl = jfBackRequestUrl;}public String getJfSingleQueryUrl() {return jfSingleQueryUrl;}public void setJfSingleQueryUrl(String jfSingleQueryUrl) {this.jfSingleQueryUrl = jfSingleQueryUrl;}public String getJfCardRequestUrl() {return jfCardRequestUrl;}public void setJfCardRequestUrl(String jfCardRequestUrl) {this.jfCardRequestUrl = jfCardRequestUrl;}public String getJfAppRequestUrl() {return jfAppRequestUrl;}public void setJfAppRequestUrl(String jfAppRequestUrl) {this.jfAppRequestUrl = jfAppRequestUrl;}public String getSingleMode() {return singleMode;}public void setSingleMode(String singleMode) {this.singleMode = singleMode;}public String getEncryptTrackKeyExponent() {return encryptTrackKeyExponent;}public void setEncryptTrackKeyExponent(String encryptTrackKeyExponent) {this.encryptTrackKeyExponent = encryptTrackKeyExponent;}public String getEncryptTrackKeyModulus() {return encryptTrackKeyModulus;}public void setEncryptTrackKeyModulus(String encryptTrackKeyModulus) {this.encryptTrackKeyModulus = encryptTrackKeyModulus;}public String getSecureKey() {return secureKey;}public void setSecureKey(String securityKey) {this.secureKey = securityKey;}public String getMiddleCertPath() {return middleCertPath;}public void setMiddleCertPath(String middleCertPath) {this.middleCertPath = middleCertPath;}public boolean isIfValidateCNName() {return ifValidateCNName;}public void setIfValidateCNName(boolean ifValidateCNName) {this.ifValidateCNName = ifValidateCNName;}public boolean isIfValidateRemoteCert() {return ifValidateRemoteCert;}public void setIfValidateRemoteCert(boolean ifValidateRemoteCert) {this.ifValidateRemoteCert = ifValidateRemoteCert;}public String getSignMethod() {return signMethod;}public void setSignMethod(String signMethod) {this.signMethod = signMethod;}public String getQrcBackTransUrl() {return qrcBackTransUrl;}public void setQrcBackTransUrl(String qrcBackTransUrl) {this.qrcBackTransUrl = qrcBackTransUrl;}public String getQrcB2cIssBackTransUrl() {return qrcB2cIssBackTransUrl;}public void setQrcB2cIssBackTransUrl(String qrcB2cIssBackTransUrl) {this.qrcB2cIssBackTransUrl = qrcB2cIssBackTransUrl;}public String getQrcB2cMerBackTransUrl() {return qrcB2cMerBackTransUrl;}public void setQrcB2cMerBackTransUrl(String qrcB2cMerBackTransUrl) {this.qrcB2cMerBackTransUrl = qrcB2cMerBackTransUrl;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getFrontUrl() {return frontUrl;}public void setFrontUrl(String frontUrl) {this.frontUrl = frontUrl;}public String getBackUrl() {return backUrl;}public void setBackUrl(String backUrl) {this.backUrl = backUrl;}public String getRootCertPath() {return rootCertPath;}public void setRootCertPath(String rootCertPath) {this.rootCertPath = rootCertPath;}}

 

 


http://chatgpt.dhexx.cn/article/7BGk2VBR.shtml

相关文章

谈谈银联 js 支付

业务场景&#xff1a;银联 JS 支付指二维码为收单机构订单的 URL&#xff0c;用户扫描二维码后在 APP 浏览器中打开收单机构的 H5 页面&#xff0c;收单机构在确认支付后调用银联统一网关&#xff0c;由银联调用 APP 完成支付的场景。 银联 JS 支付为丰富 APP 受理范围&#x…

springboot整合银联支付B2C

springboot整合银联支付B2C 前期准备 需要注册银联&#xff0c;登录&#xff0c;下载相关的sdk 这里是整合B2C 注册&#xff0c;登录 注册登录地址 https://open.unionpay.com/tjweb/login 下载sdk sdk下载地址: https://open.unionpay.com/tjweb/acproduct/list?apiSvc…

银联支付接口申请流程-傲付宝

公司的业务主要是帮企业申请各种支付接口&#xff0c;申请得多就慢慢熟悉了一些门道&#xff0c;其实银联支付申请还是比较简单的&#xff08;不用上传app截图什么的&#xff09;&#xff0c;只是申请比较难&#xff0c;还有填写的资料比较多。下面我给大家介绍下银联支付接口中…

PHP对接银联电子支付接口开发

PHP对接银联电子支付接口开发 提示:https://service.chinapay.com/OSIWeb/plogin/toLogin.action#银联在线,注册用户,签约成为商户。成为商户后银联会发邮件到你的邮箱,内容有插件、企业门户控台证书申请、logo、cp.cer、商户接入手册,和商户号ID。 企业门户控台证书申请…

PHP银联支付接口

同事整理的银联支付接口&#xff1a; 链接&#xff1a;http://pan.baidu.com/s/1boMJ2z1 密码&#xff1a;qxcx 1.首先导入Api银联支付接口到项目的Aip目录下。 2.前端创建如下相对应页面 2-1 2-2 3.填写如上图2-2的真实个人信息数据。发form表单或者ajax到后台方法中。 如下图…

银联支付接口+支付宝接口统一支付功能

这个demo发出来已经比较久了&#xff0c;后面发现陆续还有同学找到我&#xff0c;我这里说下现在的情况。 这个接口中的银联支付接口实际也是走的支付宝接口&#xff0c;不需要单独申请银联了。但是&#xff01;支付宝的这个接口已经下架了&#xff0c;以前已经接入的此接口还可…

#DNS比较(北京联通宽带) @FDDLC

阿里&#xff1a;223.5.5.5 腾讯&#xff1a;119.29.29.29 百度&#xff1a;180.76.76.76 北京联通&#xff1a;202.106.46.151 北京联通&#xff1a;202.106.195.68

牛啊,全国DNS服务器IP地址都在这里了

各省公共DNS服务器IP大全 欢迎关注微信公众号【厦门微思网络】。www.xmws.cn专业IT认证培训19周年主要课程&#xff1a;思科、华为、红帽、ORACLE、VMware、CISP、PMP等认证培训及考证 名称各省公共DNS服务器IP大全114 DNS114.114.114.114114.114.115.115阿里 AliDNS223.5.5.52…

教育网系统服务器域名地址,北京教育网dns服务器地址

北京教育网dns服务器地址 内容精选 换一换 默认部署在VPC下的应用可以调用API。如果域名解析失败&#xff0c;则参考配置内网DNS&#xff0c;在当前终端节点上配置DNS服务器。配置完成后&#xff0c;部署在VPC下的应用可以调用API。配置DNS需要配置“/etc”目录下的resolv.conf…

全国DNS地址大全

电信DNS&#xff1a; 北京DNS地址&#xff1a; 202.96.199.133 202.96.0.133 202.106.0.20 202.106.148.1 202.97.16.195 上海DNS地址&#xff1a; 202.96.199.132 202.96.199.133 202.96.209.5 202.96.209.6 202.96.209.133 天津DNS地址&#xff1a; 202.99.96.68 10.10.64.68…

【EtherCAT】基础概念

一、定义 EtherCAT&#xff08;以太网控制自动化技术&#xff09;是最初由德国倍福&#xff08;Beckhoff&#xff09;公司开发的实时&#xff08;real—time&#xff09;工业以太网&#xff08;Ethernet&#xff09;技术&#xff0c;它是一种以以太网为基础的现场总线系统&…

EtherCAT是如何工作的

来源https://blog.csdn.net/u014311679/article/details/90713554 若有侵权&#xff0c;请联系删除。 这次讨论一下ETHERCAT是如何工作的&#xff1a; 应用层-数据链路层-物理层 物理层&#xff1a; RJ45标准连接器&#xff1b;TX to FX转换器&#xff1b;LVDS&#xff1a;低电…

IgH详解十、EtherCAT DC(1)理论

(Distributed Clocks) 分布式时钟 分布式时钟相当于每个节点都有一个时钟&#xff0c;在同一时刻&#xff0c;触发所有节点做给定的动作&#xff0c;要达到同一时刻触发就要求大家的时钟保持一致&#xff0c;也就是同步。 比如每个人都有手机&#xff0c;只要手机接入网络&…

EtherCAT 之邮箱

EtherCAT特点 由于工作需要&#xff0c; 最近在学习EtherCAT总线&#xff0c;对于主站和从站的通信&#xff0c;发表一下我个人的理解。 1. EtherCAT为保证低延迟高响应&#xff0c;不可能像MODBUS一样用软件解析数据包再转发的模式&#xff0c;而是采用“高速火车”不停车上下…

IgH详解八、EtherCAT SDO原理

主站通过使用 SDO对对象字典进行读取/写入操作,完成对从站设备进行参数设置&#xff64;状态监视等&#xff61; 从站SDO的读写是非实时的&#xff0c;所以需要多次访问来确保数据操作的正确性。 如下图写入操作&#xff0c;主站把数据发送到从站&#xff0c;从站返回WC确保数…

Ethercat学习笔记

1、EtherCAT原理介绍 EtherCAT从站设备在报文经过其节点时读取相应的数据报文&#xff0c;同样输入数据也是在报文经过时插入到报文中。整个过程报文只有几纳秒的时间延迟&#xff0c;实时性获得极大提高 EtherCAT作为一种工业以太网总线&#xff0c;充分利用了以太网的全双工…

igh ethercat主站文档(中文翻译上)

此翻译主要是用翻译软件翻译&#xff0c;部分自己修改&#xff0c;若有不当之处&#xff0c;可以下方评论或私聊我提醒修改&#xff0c;若转载&#xff0c;请注明出处 本文是&#xff11;&#xff0d;&#xff14;章节。 1 The IgH EtherCAT Master 本章介绍有关EtherCAT主站…

EtherCAT设备协议详解五、Modular Device Profile

ETG.5001.1: General MDP Device ModelETG5001定义了通用的模块设备模型&#xff0c;现在远程可扩展的IO模块基本都使用本协议实现 从总线上看是一个从站&#xff0c;实际上可以扩展多个卡片式IO模块 一个从站对应多个插槽&#xff0c;插槽可随机插入不同型号的卡片IO&#xff…

[工业互联-17]:常见EtherCAT主站与实现方法

目录 第1章 EtherCAT入门 1.1 简介 1.2 EtherCAT通信架构 第2章 EtherCAT主站方案 2.1 方案概述 2.2 主站配置步骤 2.3 EtherCAT主站软件 第3章 Windows操作系统支持EtherCAT的基本步骤 第4章 常见主站软件介绍&#xff1a;TwinCAT 4.1 简介 4.2 软件架构 4.3 应…

EtherCAT设备协议详解二、EtherCAT状态机及配置流程

EtherCAT状态机&#xff08;ESM&#xff09; EtherCAT状态机定义了每个EtherCAT从站设备的分步设置&#xff0c;并指示了可用的功能。设备可以拒绝来自主站的状态请求&#xff0c;并通过错误指示&#xff08;AL 状态寄存器中的错误标志&#xff09;和相关错误代码&#xff08;A…