springboot 银联支付(快捷支付)

应用:持卡人预留银行卡号,使用手机验证码等方式完成支付

官方文档:https://open.unionpay.com/tjweb/api/dictionary?apiSvcId=449

*************************

示例

*********************

配置文件

acp_sdk.properties

##############SDK配置文件(证书方式签名)################
# 说明:
# 1. 使用时请将此文件复制到src文件夹下替换原来的acp_sdk.properties。
# 2. 具体配置项请根据注释修改。
#
##########################################################################入网测试环境交易发送地址(线上测试需要使用生产环境交易请求地址)###############################交易请求地址
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.backUrl=http://98vj52.natappfree.cc/notify#前台通知地址,填写银联前台通知的地址,必须外网能访问
acpsdk.frontUrl=http://98vj52.natappfree.cc/return#########################入网测试环境签名证书配置 ################################
# 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例:
acpsdk.signCert.path=D:/certs/acp_test_sign.pfx
# linux样例(注意:在linux下读取证书需要保证证书有被应用读的权限)(后续其他路径配置也同此条说明)
#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/acp700000000000001.pfx# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
acpsdk.signCert.pwd=000000
# 签名证书类型,固定不需要修改
acpsdk.signCert.type=PKCS12##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
acpsdk.encryptCert.path=D:/certs/acp_test_enc.cer##########################验签证书配置################################
# 验签中级证书路径(银联提供)
acpsdk.middleCert.path=D:/certs/acp_test_middle.cer
# 验签根证书路径(银联提供)
acpsdk.rootCert.path=D:/certs/acp_test_root.cer
acpsdk.validateCert.dir=D:/certs/

*********************

config 层

UnionpayConfig:加载配置文件,初始化SDKConfig

@Configuration
public class UnionpayConfig implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) {SDKConfig.getConfig().loadPropertiesFromSrc();}
}

*********************

service 层

UnionpayService

public interface UnionpayService {String pay(String orderId,String txnAmt);    //支付接口,orderId随机生成,txnAmt为订单金额Map<String,String> rollBack(String orderId,String txnTime);    //取消支付超时或者支付结果未知的交易,交易时间昨日23:00-当天23:00,支付异常时使用//orderId为冲正的订单orderId,txnTime为订单发送时间Map<String,String> cancel(String orderId,String queryId);      //撤销当日已完成的交易,全额退款//orderId为新生成的值,queryId为撤销的订单返回的queryId值Map<String,String> refund(String orderId,String queryId);      //退款,支持部分退款、全额退款//orderId为新生成的值,queryId为退款订单返回的queryId值Map<String,String> query(String orderId,String txnTime);       //查询交易状态,orderId为查询的订单id,txnTime为订单发送时间}

*********************

serviceImpl 层

UnionpayServiceImpl

@Service
public class UnionpayServiceImpl implements UnionpayService {@Overridepublic String pay(String orderId, String txnAmt) {Map<String, String> requestData = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/requestData.put("version", SDKConstants.VERSION_5_1_0);       //版本号,全渠道默认值requestData.put("encoding", SDKConstants.UTF_8_ENCODING);      //字符集编码,可以使用UTF-8,GBK两种方式requestData.put("signMethod", SDKConfig.getConfig().getSignMethod());  //签名方法requestData.put("txnType", "01");      //交易类型 ,01:消费requestData.put("txnSubType", "01");   //交易子类型, 01:自助消费requestData.put("bizType", "000301");  //业务类型,B2C网关支付,手机wap支付requestData.put("channelType", "07");  //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板 08:手机/***商户接入参数***/requestData.put("merId", "777***");         //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号requestData.put("accessType", "0");                    //接入类型,0:直连商户requestData.put("orderId",orderId);      //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则requestData.put("txnTime", DemoBase.getCurrentTime());     //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效requestData.put("currencyCode", "156");   //交易币种(境内商户一般是156 人民币)requestData.put("txnAmt", txnAmt);        //交易金额,单位分,不要带小数点requestData.put("accType","01");         //账号类型String accNo=AcpService.encryptData("6216261000000000018",SDKConstants.UTF_8_ENCODING);requestData.put("accNo",accNo);requestData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId,配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下Map<String,String> customerInfoMap=new HashMap<>();customerInfoMap.put("phoneNo","13552535506");String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,null,DemoBase.encoding);requestData.put("customerInfo",customerInfoStr);//前台通知地址 (需设置为外网能访问 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);// 报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt/**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**///报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。Map<String, String> submitFromData = AcpService.sign(requestData, SDKConstants.UTF_8_ENCODING);//获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrlString requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();//生成自动跳转的Html表单return AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,SDKConstants.UTF_8_ENCODING);}@Overridepublic Map<String, String> query(String orderId,String txnTime) {Map<String, String> data = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", SDKConstants.VERSION_5_1_0);    //版本号data.put("encoding", SDKConstants.UTF_8_ENCODING);  //字符集编码data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "00");                             //交易类型data.put("txnSubType", "00");                          //交易子类型data.put("bizType", "000000");                         //业务类型/***商户接入参数***/data.put("merId","777***");                  //商户号码,请改成自己申请的商户号或者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.singleQueryUrlMap<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);  //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考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)){//交易成功,更新商户订单状态//TODOSystem.out.println("交易成功");}else if(("03").equals(origRespCode)||("04").equals(origRespCode)||("05").equals(origRespCode)){//订单处理中或交易状态未明,需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】//TODOSystem.out.println("交易状态未知,请稍后重试");}else{//其他应答码为交易失败//TODOSystem.out.println("交易失败");}}else if(("34").equals(rspData.get("respCode"))){//订单不存在,可认为交易状态未明,需要稍后发起交易状态查询,或依据对账结果为准System.out.println("订单不存在");}else{//查询交易本身失败,如应答码10/11检查查询报文是否正确//TODOSystem.out.println("查询失败");}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因System.out.println("验签失败");}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");System.out.println("未获取到报文,或者http状态非200");}return rspData;}@Overridepublic Map<String, String> rollBack(String orderId,String txnTime) {Map<String, String> data = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", SDKConstants.VERSION_5_1_0);    //版本号data.put("encoding", SDKConstants.UTF_8_ENCODING);  //字符集编码,可以使用Utf-8,GBKdata.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "99");                           //交易类型 99-冲正data.put("txnSubType", "01");                        //交易子类型data.put("bizType", "000000");                     //填写000000data.put("channelType", "08");                       //渠道类型,07-PC,08-手机/***商户接入参数***/data.put("merId", "777***");                //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0");                         //接入类型,商户接入固定填0,不需修改data.put("orderId", orderId);                       //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则data.put("txnTime", txnTime);       //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效// 请求方保留域,// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。//data.put("reqReserved", "透传信息1|透传信息2|透传信息3");// 2. 内容可能出现&={}[]"'符号时:// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);// 2) 如果对账文件没有显示要求,可做一下base64(如下)。//    注意控制数据长度,实际传输的数据长度不能超过1024位。//    查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。//data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/Map<String, String> reqData  = AcpService.sign(data,DemoBase.encoding);        //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String url = SDKConfig.getConfig().getBackRequestUrl();                                 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrlMap<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");String respCode = rspData.get("respCode") ;if(("00").equals(respCode)){//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。//TODOSystem.out.println("交易已经受理,稍后请查询交易状态");}else if(("03").equals(respCode)||("04").equals(respCode)||("05").equals(respCode)){//后续需发起交易状态查询交易确定交易状态//TODOSystem.out.println("稍后查询交易状态");}else{//其他应答码为失败请排查原因//TODOSystem.out.println("冲正交易失败");}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");}Map<String,String> result=new HashMap<>();String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);result.put("reqMessage",reqMessage);result.put("resMessage",rspMessage);return result;}@Overridepublic Map<String, String> cancel(String orderId, String queryId) {Map<String, String> data = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", SDKConstants.VERSION_5_1_0);  //版本号data.put("encoding", DemoBase.encoding);          //字符集编码 可以使用UTF-8,GBK两种方式data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "31");                        //交易类型 31-消费撤销data.put("txnSubType", "00");                     //交易子类型  默认00data.put("bizType", "000301");                    //业务类型data.put("channelType", "07");                    //渠道类型,07-PC,08-手机/***商户接入参数***/data.put("merId", "777***");             //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0");                      //接入类型,商户接入固定填0,不需修改data.put("orderId", orderId);                    //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费data.put("txnTime", DemoBase.getCurrentTime());    //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效data.put("txnAmt", "10000");                       //【撤销金额】,消费撤销时必须和原消费金额相同data.put("currencyCode", "156");                  //交易币种(境内商户一般是156 人民币)data.put("backUrl", DemoBase.backUrl);            //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费撤销交易 商户通知,其他说明同消费交易的商户通知/***要调通交易以下字段必须修改***/data.put("origQryId", queryId);                  //【原始交易流水号】,原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取// 请求方保留域,// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。//  data.put("reqReserved", "透传信息1|透传信息2|透传信息3");// 2. 内容可能出现&={}[]"'符号时:// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);// 2) 如果对账文件没有显示要求,可做一下base64(如下)。//    注意控制数据长度,实际传输的数据长度不能超过1024位。//    查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。// data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文**/Map<String, String> reqData  = AcpService.sign(data,DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String url = SDKConfig.getConfig().getBackRequestUrl();//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrlMap<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding);//发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");String respCode = rspData.get("respCode") ;if(("00").equals(respCode)){//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。//TODOSystem.out.println("撤销成功");}else if(("03").equals(respCode)||("04").equals(respCode)||("05").equals(respCode)){//后续需发起交易状态查询交易确定交易状态//TODOSystem.out.println("交易状态不明,请稍后查询");}else{//其他应答码为失败请排查原因//TODOSystem.out.println("撤销交易失败");}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因System.out.println("验证签名失败");}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");System.out.println("未收到报文");}Map<String,String> result=new HashMap<>();String reqMessage = DemoBase.genHtmlResult(reqData);String rspMessage = DemoBase.genHtmlResult(rspData);result.put("reqMessage",reqMessage);result.put("resMessage",rspMessage);return result;}@Overridepublic Map<String, String> refund(String orderId,String queryId) {Map<String, String> data = new HashMap<>();/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/data.put("version", SDKConstants.VERSION_5_1_0);               //版本号data.put("encoding", SDKConstants.UTF_8_ENCODING);             //字符集编码 可以使用UTF-8,GBK两种方式data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法data.put("txnType", "04");                           //交易类型 04-退货data.put("txnSubType", "00");                        //交易子类型  默认00data.put("bizType", "000301");                       //业务类型data.put("channelType", "07");                       //渠道类型,07-PC,08-手机/***商户接入参数***/data.put("merId", "777***");                //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试data.put("accessType", "0");                         //接入类型,商户接入固定填0,不需修改data.put("orderId", orderId);          //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费data.put("txnTime", DemoBase.getCurrentTime());      //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效data.put("currencyCode", "156");                     //交易币种(境内商户一般是156 人民币)data.put("txnAmt", "4000");                          //****退货金额,单位分,不要带小数点。退货金额小于等于原消费金额,当小于的时候可以多次退货至退货累计金额等于原消费金额data.put("backUrl", SDKConfig.getConfig().getBackUrl());       //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知/***要调通交易以下字段必须修改***/data.put("origQryId", queryId);      //****原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取// 请求方保留域,// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。//  data.put("reqReserved", "透传信息1|透传信息2|透传信息3");// 2. 内容可能出现&={}[]"'符号时:// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);// 2) 如果对账文件没有显示要求,可做一下base64(如下)。//    注意控制数据长度,实际传输的数据长度不能超过1024位。//    查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。//   data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/Map<String, String> reqData  = AcpService.sign(data,SDKConstants.UTF_8_ENCODING);   //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。String url = SDKConfig.getConfig().getBackRequestUrl();                                                           //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrlMap<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**///应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》if(!rspData.isEmpty()){if(AcpService.validate(rspData, DemoBase.encoding)){LogUtil.writeLog("验证签名成功");String respCode = rspData.get("respCode") ;if(("00").equals(respCode)){//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。//TODOSystem.out.println("退款成功");}else if(("03").equals(respCode)||("04").equals(respCode)||("05").equals(respCode)){//后续需发起交易状态查询交易确定交易状态//TODOSystem.out.println("请稍后查询退款状态");}else{//其他应答码为失败请排查原因//TODOSystem.out.println("退款失败");}}else{LogUtil.writeErrorLog("验证签名失败");//TODO 检查验证签名失败的原因System.out.println("验证签名失败");}}else{//未返回正确的http状态LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");System.out.println("未收到报文");}return rspData;}
}

*********************

controller 层

UnionpayController

@RestController
public class UnionpayController {@Resourceprivate UnionpayService unionpayService;@RequestMapping("/pay")public void pay(HttpServletResponse response) throws Exception{response.setHeader("content-type", "text/html;charset=UTF-8");response.getWriter().write(unionpayService.pay(DemoBase.getOrderId(),"10000"));}@RequestMapping("/query")public Map<String,String> query(@RequestParam("orderId") String orderId,@RequestParam("txnTime") String txnTime){return unionpayService.query(orderId,txnTime);}@RequestMapping("/cancel")public Map<String,String> cancel(@RequestParam("queryId") String queryId){return unionpayService.cancel(DemoBase.getOrderId(),queryId);}@RequestMapping("/rollback")public Map<String,String> rollback(@RequestParam("orderId") String orderId,@RequestParam("txnTime") String txnTime){return unionpayService.rollBack(orderId,txnTime);}@RequestMapping("/refund")public Map<String,String> refund(@RequestParam("queryId") String queryId){return unionpayService.refund(DemoBase.getOrderId(),queryId);}@RequestMapping("/return")public String fun(HttpServletRequest request){Map<String,String> result=new HashMap<>();System.out.println("====== 前台通知 ======");Enumeration<String> names=request.getParameterNames();if (names!=null){while (names.hasMoreElements()){String name=names.nextElement();String value=request.getParameter(name);result.put(name,value);System.out.println(name+" ==> "+value);if (result.get(name)==null||"".equals(result.get(name))){result.remove(name);}}}if (AcpService.validate(result, SDKConstants.UTF_8_ENCODING)){return "success";}else {return "验签失败";}}@RequestMapping("/notify")public void hello2(HttpServletRequest request,HttpServletResponse response) throws Exception{Map<String,String> result=new HashMap<>();System.out.println("======= 后台通知 ========");Enumeration<String> names=request.getParameterNames();if (names!=null){while (names.hasMoreElements()){String name=names.nextElement();String value=request.getParameter(name);result.put(name,value);System.out.println(name+" ==> "+value);if (result.get(name)==null||"".equals(result.get(name))){result.remove(name);}}}if (AcpService.validate(result, SDKConstants.UTF_8_ENCODING)){System.out.println("后台验签成功");}else {System.out.println("后台验签失败");}response.getWriter().print("ok");}
}

*************************

使用测试

localhost:8080/pay

说明:输入验证码即可完成支付,测试环境使用111111

springboot 银联支付(快捷支付)相关推荐

  1. iOS开发之网银在线支付快捷支付商户接口规范

    网银在线支付快捷支付商户接口规范   如果您在开发过程中遇到任何技术问题,可以咨询我们的技术支持人员: 联系邮箱:010-62428538.62428630 (1)商户根据网银在线支付平台提供的接口规 ...

  2. 第三方支付——快捷支付接口测试要点

    首先,什么是快捷支付? 快捷支付指用户购买商品时,不需开通网银,只需提供银行卡卡号.户名.手机号码等信息,银行验证手机号码正确性后,第三方支付发送手机动态口令到用户手机号上,用户输入正确的手机动态口令 ...

  3. 报关软件java_爱农支付快捷支付+支付单报关demo

    工作期间客户指定要用这个,实际做的时间对方没有demo,有也是1.0的md5签名,网上搜也找不到案例,官网不提供文档任何相关内容,一切都要按照对接人员提供的文档自己写,不过好的是我写出来了,这里直接提 ...

  4. 关于快捷支付,你需要了解的

    为什么80%的码农都做不了架构师?>>>    最近快捷支付的问题真是闹得风生水起 比如这个         为什么第三方支付都热衷于用户开快捷支付,有什么动力驱使他们这样干?甚至故 ...

  5. 网上支付、协议支付与快捷支付区别

    区别 网上支付 快捷支付(以支付宝为例) 协议支付(以支付宝卡通为例) 客户体验 支付时需要输入银行账号和交易密码等多个步骤才能完成支付. 支付时只需输入支付宝账户的支付密码(或无需输入)即可完成支付 ...

  6. 网关支付,代付,代扣,协议支付,账户支付,快捷支付的概念是什么?

    最近在学习支付相关的东西,对于各种支付方式,感觉傻傻分不清楚,多方搜集网上的资料,现在按照自己的理解整合一下,希望有问题的地方大家多多指正,欢迎补充 1.网关支付 网关支付是银行内部系统与intern ...

  7. 无卡支付?快捷支付?代扣?傻傻分不清

    一.无卡支付 交易时需录入四要素完成支付,四要素包括:卡号.户名.身份证号.手机号,如果是信用卡还需要录入有效期和CVN,无卡支付平台一般的银行渠道还需要签约三方支付协议,具体取决于对接的银行或支付通 ...

  8. 西米支付:支付二维码的简单介绍

    二维码支付从1.0到3.0时代的历史进程 实际上二维码技术被推出来已经有十多年了,这段悠久绵长的英雄无用武之地的时代属于二维码1.0时代,得益于互联网电子商务的飞速发展首先将二维码应用于支付并发扬光大 ...

  9. 软件测试之第三方快捷支付_支付宝遭遇最强对手:银联正面PK快捷支付

    对于仅靠提供支付交易网关服务而生存的企业,银联"无卡支付"的正式上线无疑是一大噩耗. 没有网银照样支付 线上银联很强大 "无卡支付"正面PK"快捷支付 ...

最新文章

  1. Windows Server 2012系列之一安装初体验
  2. 【转】Ubuntu下使用sysv-rc-conf管理服务
  3. win python 判断 所有 子进程 结束_python 多进程 进程池子进程结束怎么获取
  4. 如何解锁excel表格保护_Excel表格技巧—如何计算矩阵相乘
  5. win10计算机安全策略设置,win10系统重置本地安全策略所有设置的操作方法
  6. 2021年MEMS传感器产业链变化与趋势
  7. word2vec查询中文词向量同义词
  8. Unity Camera Filed Of View
  9. 集群断电重启The connection to the server apiserver.k8s:6443 was refused - did you specify the right
  10. 经颅电刺激促进睡眠振荡及其功能耦合增强轻度认知障碍患者的记忆巩固
  11. 新媒体运营教程:线上线下用户转化的核心流程!
  12. Windows+cygwin下构造arm-linux交叉编译环境最简单的方法
  13. 你所不了解的分布性系统之高可用篇
  14. 端口号是什么以及常见端口号
  15. Shells:一款功能强大的反向Shell快速生成工具
  16. Field XXXMapper in cn.zcbigdata.YYY.service.Impl.XXXServicel required a bean of type
  17. 通过模拟器实现APP抓包
  18. 城镇建设适宜性评价之灾害评价算法
  19. python实现绚烂的烟花绽放效果
  20. ROS命令行工具 小乌龟入门

热门文章

  1. Android开发在路上:少去踩坑,多走捷径(上)
  2. 汇编语言中word ptr | byte ptr分别是什么意思
  3. C语言编写SSH终端
  4. Python入门笔记(三)
  5. 好用的磁盘管理工具:DiskCatalogMaker for Mac
  6. 运维工程师的宿命(困兽之斗)
  7. nested exception is redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication requir
  8. Excel 表格删除重复数据
  9. Seata分布式事务控制整合Mybatis-Plus导致事务控制失效
  10. windowsxp最新版本_雨林木风U盘装系统启动盘制作工具更新最新版9.0