记得上下班打卡 | git大法好,push需谨慎

Commit 4bb7fbf3 authored by anjiabin's avatar anjiabin

提交unionpay代码

parent cb347737
...@@ -35,5 +35,6 @@ liquidnet: ...@@ -35,5 +35,6 @@ liquidnet:
appId: wx3498304dda39c5a1 appId: wx3498304dda39c5a1
partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK
unionpay: unionpay:
gateway-url: https://gateway.test.95516.comm gateway-url: https://gateway.test.95516.com
certs-path: /Users/anjiabin/certs
...@@ -33,4 +33,6 @@ liquidnet: ...@@ -33,4 +33,6 @@ liquidnet:
gataway-url: https://openapi.alipay.com/gateway.do gataway-url: https://openapi.alipay.com/gateway.do
merchantId: 1551961491 merchantId: 1551961491
appId: wx3498304dda39c5a1 appId: wx3498304dda39c5a1
partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK
\ No newline at end of file unionpay:
gateway-url: https://gateway.test.95516.com
\ No newline at end of file
...@@ -197,26 +197,22 @@ acpsdk: ...@@ -197,26 +197,22 @@ acpsdk:
# 多证书的情况证书路径为代码指定,可不对此块做配置。 # 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。 # 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例: # windows样例:
signCert: signCertPath: ${liquidnet.dragon.unionpay.certs-path}/acp_test_sign.pfx
path: D:/certs/acp_test_sign.pfx
# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败 # 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
pwd: 000000 signCertPwd: '000000'
# 签名证书类型,固定不需要修改 # 签名证书类型,固定不需要修改
type: PKCS12 signCertType: PKCS12
##########################加密证书配置################################ ##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用) # 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
encryptCert: encryptCertPath: ${liquidnet.dragon.unionpay.certs-path}/acp_test_enc.cer
path: d:/certs/acp_test_enc.cer
##########################验签证书配置################################ ##########################验签证书配置################################
# 验签中级证书路径(银联提供) # 验签中级证书路径(银联提供)
middleCert: middleCertPath: ${liquidnet.dragon.unionpay.certs-path}/acp_test_middle.cer
path: D:/certs/acp_test_middle.cer
# 验签根证书路径(银联提供) # 验签根证书路径(银联提供)
rootCert: rootCertPath: ${liquidnet.dragon.unionpay.certs-path}/acp_test_root.cer
path: D:/certs/acp_test_root.cer
# ----------------------------------------------------------- # -----------------------------------------------------------
# ----------------------------------------------------------- # -----------------------------------------------------------
\ No newline at end of file
...@@ -30,6 +30,9 @@ public class AcpService { ...@@ -30,6 +30,9 @@ public class AcpService {
@Autowired @Autowired
private CertUtil certUtil; private CertUtil certUtil;
@Autowired
private SDKUtil sdkUtil;
/** /**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br> * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br> * 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
...@@ -96,13 +99,13 @@ public class AcpService { ...@@ -96,13 +99,13 @@ public class AcpService {
if(VERSION_5_0_1.equals(version) || VERSION_5_0_0.equals(version)){ if(VERSION_5_0_1.equals(version) || VERSION_5_0_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) { if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd)); data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa(data, certPath, certPwd, encoding)); data.put(SDKConstants.param_signature, sdkUtil.signRsa(data, certPath, certPwd, encoding));
return data; return data;
} }
} else if(VERSION_5_1_0.equals(version)){ } else if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) { if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd)); data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa2(data, certPath, certPwd, encoding)); data.put(SDKConstants.param_signature, sdkUtil.signRsa2(data, certPath, certPwd, encoding));
return data; return data;
} }
} }
...@@ -783,8 +786,8 @@ public class AcpService { ...@@ -783,8 +786,8 @@ public class AcpService {
* 更新成功则返回1,无更新返回0,失败异常返回-1<br> * 更新成功则返回1,无更新返回0,失败异常返回-1<br>
* @return * @return
*/ */
public static int updateEncryptCert(Map<String, String> data) { public int updateEncryptCert(Map<String, String> data) {
return SDKUtil.updateEncryptCert(data.get(SDKConstants.param_encryptPubKeyCert), data.get(SDKConstants.param_certType)); return sdkUtil.updateEncryptCert(data.get(SDKConstants.param_encryptPubKeyCert), data.get(SDKConstants.param_certType));
} }
/** /**
......
...@@ -25,6 +25,12 @@ public class QrcService { ...@@ -25,6 +25,12 @@ public class QrcService {
@Autowired @Autowired
private CertUtil certUtil; private CertUtil certUtil;
@Autowired
private SDKUtil sdkUtil;
@Autowired
private AcpService acpService;
/** /**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br> * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br> * 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
...@@ -73,11 +79,11 @@ public class QrcService { ...@@ -73,11 +79,11 @@ public class QrcService {
|| "0420000903".equals(reqType) || "0420000903".equals(reqType)
|| "0410000903".equals(reqType)) { || "0410000903".equals(reqType)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd)); data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa2(data, certPath, certPwd, encoding)); data.put(SDKConstants.param_signature, sdkUtil.signRsa2(data, certPath, certPwd, encoding));
return data; return data;
} else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) { } else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd)); data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa(data, certPath, certPwd, encoding)); data.put(SDKConstants.param_signature, sdkUtil.signRsa(data, certPath, certPwd, encoding));
return data; return data;
} else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) { } else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) {
log.error("国密算法按要求必须通过加密机签名,本sdk不提供。"); log.error("国密算法按要求必须通过加密机签名,本sdk不提供。");
...@@ -151,8 +157,8 @@ public class QrcService { ...@@ -151,8 +157,8 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return 加密的内容<br> * @return 加密的内容<br>
*/ */
public static String encryptPin(String accNo, String pin, String encoding) { public String encryptPin(String accNo, String pin, String encoding) {
return AcpService.encryptPin(accNo, pin, encoding); return acpService.encryptPin(accNo, pin, encoding);
} }
/** /**
...@@ -161,8 +167,8 @@ public class QrcService { ...@@ -161,8 +167,8 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return 加密的密文<br> * @return 加密的密文<br>
*/ */
public static String encryptData(String data, String encoding) { public String encryptData(String data, String encoding) {
return AcpService.encryptData(data, encoding); return acpService.encryptData(data, encoding);
} }
/** /**
...@@ -171,8 +177,8 @@ public class QrcService { ...@@ -171,8 +177,8 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return 解密后的明文<br> * @return 解密后的明文<br>
*/ */
public static String decryptData(String base64EncryptedInfo, String encoding) { public String decryptData(String base64EncryptedInfo, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, encoding); return acpService.decryptData(base64EncryptedInfo, encoding);
} }
/** /**
...@@ -183,9 +189,9 @@ public class QrcService { ...@@ -183,9 +189,9 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return * @return
*/ */
public static String decryptData(String base64EncryptedInfo, String certPath, public String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) { String certPwd, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, certPath, certPwd, encoding); return acpService.decryptData(base64EncryptedInfo, certPath, certPwd, encoding);
} }
/** /**
...@@ -203,8 +209,8 @@ public class QrcService { ...@@ -203,8 +209,8 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return 应答http 200返回true ,其他false<br> * @return 应答http 200返回true ,其他false<br>
*/ */
public static Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) { public Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) {
return AcpService.post(reqData, reqUrl, encoding); return acpService.post(reqData, reqUrl, encoding);
} }
/** /**
...@@ -227,12 +233,12 @@ public class QrcService { ...@@ -227,12 +233,12 @@ public class QrcService {
* @param encoding * @param encoding
* @return * @return
*/ */
public static String getKVEncBase64Field(Map<String, String> map,String encoding){ public String getKVEncBase64Field(Map<String, String> map,String encoding){
StringBuffer sf = new StringBuffer(); StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE) String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, encoding)) .append(SDKUtil.createLinkString(map, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString(); .append(SDKConstants.RIGHT_BRACE).toString();
return QrcService.encryptData(info, encoding); return this.encryptData(info, encoding);
} }
/** /**
...@@ -255,8 +261,8 @@ public class QrcService { ...@@ -255,8 +261,8 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return * @return
*/ */
public static Map<String, String> parseKVEncBase64Field(String base64data, String encoding){ public Map<String, String> parseKVEncBase64Field(String base64data, String encoding){
String data = QrcService.decryptData(base64data, encoding); String data = this.decryptData(base64data, encoding);
data = data.substring(1, data.length() - 1); data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data); return SDKUtil.parseRespString(data);
} }
...@@ -268,9 +274,9 @@ public class QrcService { ...@@ -268,9 +274,9 @@ public class QrcService {
* @param encoding<br> * @param encoding<br>
* @return * @return
*/ */
public static Map<String, String> parseKVEncBase64Field(String base64data, String certPath, public Map<String, String> parseKVEncBase64Field(String base64data, String certPath,
String certPwd, String encoding){ String certPwd, String encoding){
String data = QrcService.decryptData(base64data, certPath, certPwd, encoding); String data = this.decryptData(base64data, certPath, certPwd, encoding);
data = data.substring(1, data.length() - 1); data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data); return SDKUtil.parseRespString(data);
} }
......
...@@ -72,7 +72,7 @@ public class SDKConfig { ...@@ -72,7 +72,7 @@ public class SDKConfig {
/** 有卡交易. */ /** 有卡交易. */
private String cardRequestUrl; private String cardRequestUrl;
/** app交易 */ /** app交易 */
private String appRequestUrl; private String appTransUrl;
/** 证书使用模式(单证书/多证书) */ /** 证书使用模式(单证书/多证书) */
private String singleMode; private String singleMode;
/** 安全密钥(SHA256和SM3计算时使用) */ /** 安全密钥(SHA256和SM3计算时使用) */
......
package com.liquidnet.service.scpsdk;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
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>
* 版权: 中国银联<br>
* 声明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己需要,按照技术文档编写。该代码仅供参考,不提供编码,性能,规范性等方面的保障<br>
*/
@Component
public class DemoBase {
@Autowired
private SDKConfig sdkConfig;
//默认配置的是UTF-8
public static String encoding = "UTF-8";
// //全渠道固定值
// public String version = sdkConfig.getVersion();
//
// //后台服务对应的写法参照 FrontRcvResponse.java
// public String frontUrl = sdkConfig.getFrontUrl();
//
// //后台服务对应的写法参照 BackRcvResponse.java
// public String backUrl = sdkConfig.getBackUrl();//受理方和发卡方自选填写的域[O]--后台通知地址
// 商户发送交易时间 格式:yyyyMMddHHmmss
public static String getCurrentTime() {
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
}
// AN8..40 商户订单号,不能含"-"或"_"
public static String getOrderId() {
return new SimpleDateFormat("yyyyMMddHHmmssSSS").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>");
}else
sf.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="gbk"; //文件是gbk编码
File file=new File(filePath);
if(file.isFile() && file.exists()){ //判断文件是否存在
InputStreamReader read = new InputStreamReader(
new FileInputStream(file), "iso-8859-1");
BufferedReader bufferedReader = new BufferedReader(read);
String lineTxt = null;
while((lineTxt = bufferedReader.readLine()) != null){
byte[] bs = lineTxt.getBytes("iso-8859-1");
//解析的结果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 = new String(Arrays.copyOfRange(bs, leftIndex,rightIndex), encoding);
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;
}
}
\ No newline at end of file
package com.liquidnet.service.scpsdk;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.dragon.channel.unionpay.sdk.AcpService;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: TestAcpService
* @Package com.liquidnet.service
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/9 12:11
*/
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class TestAcpService {
@Autowired
private DemoBase demoBase;
@Autowired
private AcpService acpService;
@Autowired
private SDKConfig sdkConfig;
@Test
public void testAppTrans()
throws ServletException, IOException {
String merId = "821690048160PQY";
String txnAmt = "1";
String orderId = IDGenerator.payCode();
//设置订单过期时间
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmss);
Map<String, String> contentData = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
contentData.put("version", sdkConfig.getVersion()); //版本号 全渠道默认值
contentData.put("encoding", DemoBase.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
contentData.put("signMethod", sdkConfig.getSignMethod()); //签名方法
contentData.put("txnType", "01"); //交易类型 01:消费
contentData.put("txnSubType", "01"); //交易子类 01:消费
contentData.put("bizType", "000201"); //填写000201
contentData.put("channelType", "08"); //渠道类型 08手机
/***商户接入参数***/
contentData.put("merId", merId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
contentData.put("accessType", "0"); //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
contentData.put("orderId", orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
contentData.put("txnTime", txnTime); //订单发送时间,取系统时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
contentData.put("accType", "01"); //账号类型 01:银行卡02:存折03:IC卡帐号类型(卡介质)
contentData.put("txnAmt", txnAmt); //交易金额 单位为分,不能带小数点
contentData.put("currencyCode", "156"); //境内商户固定 156 人民币
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// contentData.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。
// contentData.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));
//后台通知地址(需设置为外网能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,【支付失败的交易银联不会发送后台通知】
//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码
// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200或302,那么银联会间隔一段时间再次发送。总共发送5次,银联后续间隔1、2、4、5 分钟后会再次通知。
// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败
contentData.put("backUrl", sdkConfig.getBackUrl());
/**对请求参数进行签名并发送http post请求,接收同步应答报文**/
Map<String, String> reqData = acpService.sign(contentData,DemoBase.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String requestAppUrl = sdkConfig.getAppTransUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = acpService.post(reqData,requestAppUrl,DemoBase.encoding); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, DemoBase.encoding)){
log.info("验证签名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//成功,获取tn号
//String tn = resmap.get("tn");
//TODO
}else{
//其他应答码为失败请排查原因或做失败处理
//TODO
}
}else{
log.error("验证签名失败");
//TODO 检查验证签名失败的原因
}
}else{
//未返回正确的http状态
log.error("未获取到返回报文或返回http状态码非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>" + "应答报文:</br>"+rspMessage+"");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment