From a9cbc215d64d8395a5303ec54c21513113a15c23 Mon Sep 17 00:00:00 2001
From: gongzuming <gongzuming>
Date: 星期二, 08 十月 2024 11:19:47 +0800
Subject: [PATCH] 支付

---
 src/main/java/com/mzl/flower/pay/SybConstants.java       |   41 ++
 src/main/java/com/mzl/flower/pay/SybPayService.java      |  221 +++++++++++++++
 src/main/java/com/mzl/flower/pay/SybUtil.java            |  230 ++++++++++++++++
 src/main/java/com/mzl/flower/pay/SSLUtil.java            |   54 +++
 src/main/java/com/mzl/flower/pay/SmUtil.java             |  138 +++++++++
 src/main/java/com/mzl/flower/pay/HttpConnectionUtil.java |  124 ++++++++
 pom.xml                                                  |    7 
 7 files changed, 815 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index ceb76b2..ff10f45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -269,6 +269,13 @@
             <version>3.15.6</version> <!-- 确保使用与你的 Spring Boot 版本兼容的 Redisson 版本 -->
         </dependency>
 
+        <dependency>
+            <groupId>net.sf.json-lib</groupId>
+            <artifactId>json-lib</artifactId>
+            <version>2.4</version>
+            <classifier>jdk15</classifier>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/src/main/java/com/mzl/flower/pay/HttpConnectionUtil.java b/src/main/java/com/mzl/flower/pay/HttpConnectionUtil.java
new file mode 100644
index 0000000..01b0f42
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/HttpConnectionUtil.java
@@ -0,0 +1,124 @@
+package com.mzl.flower.pay;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
+import org.apache.commons.logging.Log;
+
+public class HttpConnectionUtil {
+	private HttpURLConnection conn;
+	private String connectUrl;
+	
+	public HttpConnectionUtil(String connectUrl){
+		this.connectUrl = connectUrl;
+	}
+	
+	public void init() throws Exception{
+		URL url = new URL(connectUrl);
+		System.setProperty("java.protocol.handler.pkgs", "javax.net.ssl");
+	    HostnameVerifier hv = new HostnameVerifier() {
+	         public boolean verify(String urlHostName, SSLSession session) {
+	         return urlHostName.equals(session.getPeerHost());
+	         }
+	    };
+	    HttpsURLConnection.setDefaultHostnameVerifier(hv);
+    	URLConnection conn = url.openConnection();
+    	conn.setDoInput(true);
+    	conn.setDoOutput(true);
+    	conn.setReadTimeout(60000);
+    	conn.setConnectTimeout(30000);
+    	if (conn instanceof HttpsURLConnection){
+    		HttpsURLConnection httpsConn = (HttpsURLConnection)conn;
+    		httpsConn.setSSLSocketFactory(SSLUtil.getInstance().getSSLSocketFactory());
+    	} else if (conn instanceof HttpURLConnection){
+    		HttpURLConnection httpConn = (HttpURLConnection)conn;
+    	} else {
+    		throw new Exception("不是http/https协议的url");
+    	}
+    	this.conn = (HttpURLConnection)conn;
+    	initDefaultPost();
+	}
+	
+	public void destory(){
+		try{
+			if(this.conn!=null){
+				this.conn.disconnect();
+			}
+		}catch(Exception e){
+			
+		}
+	}
+	
+    private void initDefaultPost() throws Exception{
+    	conn.setDoOutput(true);
+    	conn.setDoInput(true);
+    	conn.setRequestMethod("POST");
+    	conn.setUseCaches(false);
+    	conn.setInstanceFollowRedirects(true);
+    	conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+    }
+    
+    public byte[] postParams(Map<String, String> params,boolean readreturn) throws IOException {
+    	StringBuilder outBuf = new StringBuilder();
+    	boolean isNotFirst = false;
+    	for (Map.Entry<String, String> entry: params.entrySet()){
+    		if (isNotFirst)
+    			outBuf.append('&');
+    		isNotFirst = true;
+    		outBuf
+    			.append(entry.getKey())
+    			.append('=')
+    			.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+    	}
+    	System.out.println("参数:"+outBuf.toString());
+    	return postParams(outBuf.toString(),readreturn);
+    }
+    
+    public byte[] postParams(String message,boolean readreturn) throws IOException {
+    	DataOutputStream out = new DataOutputStream(conn.getOutputStream());
+    	out.write(message.getBytes("UTF-8"));
+    	out.close();
+    	if(readreturn){
+    		return readBytesFromStream(conn.getInputStream());
+    	}else{
+    		return null;
+    	}
+    }
+    
+    public byte[] postParams(byte[] message,boolean readreturn) throws IOException {
+    	DataOutputStream out = new DataOutputStream(conn.getOutputStream());
+    	out.write(message);
+    	out.close();
+    	if(readreturn){
+    		return readBytesFromStream(conn.getInputStream());
+    	}else{
+    		return null;
+    	}
+    }
+    
+    private byte[] readBytesFromStream(InputStream is) throws IOException{
+    	ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    	int readLen;
+    	byte[] tmpBuf = new byte[4096];
+    	while ((readLen = is.read(tmpBuf)) > 0)
+    		baos.write(tmpBuf, 0, readLen);
+    	is.close();
+    	return baos.toByteArray();
+    }
+
+	public HttpURLConnection getConn() {
+		return conn;
+	}
+    
+}
diff --git a/src/main/java/com/mzl/flower/pay/SSLUtil.java b/src/main/java/com/mzl/flower/pay/SSLUtil.java
new file mode 100644
index 0000000..dd6f522
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/SSLUtil.java
@@ -0,0 +1,54 @@
+package com.mzl.flower.pay;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * SSL管理助手类
+ * @author Administrator
+ *
+ */
+public class SSLUtil implements X509TrustManager {
+	private SSLSocketFactory sslFactory = null;
+	
+	private SSLUtil(){
+	}
+	
+	public void checkClientTrusted(X509Certificate[] arg0, String arg1)
+			throws CertificateException {
+	}
+
+	public void checkServerTrusted(X509Certificate[] arg0, String arg1)
+			throws CertificateException {
+	}
+
+	public X509Certificate[] getAcceptedIssuers() {
+		return null;
+	}
+	
+	/** 获取SSL Socket工厂 */
+	public SSLSocketFactory getSSLSocketFactory(){
+		return sslFactory;
+	}
+	
+	private static SSLUtil _instance = null;
+	
+	/** 获取SSL管理助手类实例 */
+	synchronized public static SSLUtil getInstance() throws NoSuchAlgorithmException, KeyManagementException {
+		if (_instance == null){
+			_instance = new SSLUtil();
+			SSLContext sc = SSLContext.getInstance("SSLv3");
+			sc.init(null, new TrustManager[]{new SSLUtil()}, null);
+			_instance.sslFactory = sc.getSocketFactory();
+		}
+		return _instance;
+	}
+
+}
diff --git a/src/main/java/com/mzl/flower/pay/SmUtil.java b/src/main/java/com/mzl/flower/pay/SmUtil.java
new file mode 100644
index 0000000..e1fd5cb
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/SmUtil.java
@@ -0,0 +1,138 @@
+package com.mzl.flower.pay;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SmUtil {
+	static{
+        Security.addProvider(new BouncyCastleProvider());
+    }
+	/**算法常量:SM3withSM2*/
+	public static final String ALGORITHM_SM3SM2_BCPROV = "SM3withSM2";
+	private final static int SM3withSM2_RS_LEN=32;
+
+	public static void main(String[] args) throws Exception {
+		/**商户平台分配的appid,也是签名的certid**/
+		String appid = "00000156";
+		/**商户sm2私钥,用于向通联发起请求前进行签名**/
+		String cusPrivateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgjj4Rk+b0YjwO+UwXofnHf4bK+kaaY5Btkd8nMP2VimmgCgYIKoEcz1UBgi2hRANCAAQqlALW4qGC3bP1x3wo5QsKxaCMEZJ2ODTTwOQ+d8UGU7GoK/y/WMBQWf5upMnFU06p5FxGooXYYoBtldgm03hq";
+		/**商户sm2公钥,需要配置到通联商户平台**/
+		String cusPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEKpQC1uKhgt2z9cd8KOULCsWgjBGSdjg008DkPnfFBlOxqCv8v1jAUFn+bqTJxVNOqeRcRqKF2GKAbZXYJtN4ag==";
+		
+		/**通联平台sm2公钥,用于请求返回或者通联通知的验签**/
+		String tlPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/BnA8BawehBtH0ksPyayo4pmzL/u1FQ2sZcqwOp6bjVqQX4tjo930QAvHZPJ2eez8sCz/RYghcqv4LvMq+kloQ==";
+		
+		String blankStr = "请求待签名数据";
+		PrivateKey privkey = privKeySM2FromBase64Str(cusPrivateKey);
+		String sign = signSM3SM2RetBase64(privkey, appid, blankStr.getBytes("UTF-8"));//签名
+		System.out.println(sign);
+		
+		String rspBlankStr = "返回待验签数据";//通联返回的明文
+		String rspSign = "AovBKQGUe0xuJ0ox7FgIIX+yB3DzbudgUsnNvJmDV0IdHZtU2Y8vdeUY1pd2vmPUf08hNgdkoz+4WP/D/ktOcA==";//通联返回的签名
+		PublicKey publicKey = pubKeySM2FromBase64Str(tlPubKey);
+		boolean isOk = verifySM3SM2(publicKey, "Allinpay", Base64.decodeBase64(rspSign), rspBlankStr.getBytes("UTF-8"));
+		System.out.println("验签结果:"+isOk);
+		
+
+	}
+	
+	/**签名并BASE64编码-SM3WithSM2 */
+	public static String signSM3SM2RetBase64(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{
+		return Base64.encodeBase64String(signSM3SM2(privateKey, certid, data));
+	}
+	
+	/**签名-SM3WithSM2 */
+	public static byte[] signSM3SM2(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{
+		SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes());
+		Signature signer = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC");
+		signer.setParameter(parameterSpec);
+		signer.initSign(privateKey, new SecureRandom());
+		signer.update(data);
+		return byteAsn12BytePlain(signer.sign());
+	}
+	
+	/** 验证签名-SM3WithSM2*/
+	public static boolean verifySM3SM2(final PublicKey publicKey,String certid,final byte[] signData, final byte[] srcData) throws Exception {
+        SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes());
+        Signature verifier = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC");
+        verifier.setParameter(parameterSpec);
+        verifier.initVerify(publicKey);
+        verifier.update(srcData);
+        return verifier.verify(bytePlain2ByteAsn1(signData));
+	}
+	
+	/**从字符串读取私钥-目前支持PKCS8(keystr为BASE64格式)*/
+	public static PrivateKey privKeySM2FromBase64Str(String keystr) throws Exception {
+		KeyFactory keyFactory = KeyFactory.getInstance("EC");
+		return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(keystr)));
+	}
+	
+	/**从字符串读取RSA公钥(keystr为BASE64格式)*/
+	public static PublicKey pubKeySM2FromBase64Str(String keystr) throws Exception {
+		KeyFactory keyFactory = KeyFactory.getInstance("EC");
+		return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(keystr)));
+	}
+	
+	/**
+	 * 将普通字节数组转换为ASN1字节数组 适用于SM3withSM2验签时验签明文转换
+	 */
+	private static byte[] bytePlain2ByteAsn1(byte[] data) {
+		if (data.length != SM3withSM2_RS_LEN * 2) throw new RuntimeException("err data. ");
+		BigInteger r = new BigInteger(1, Arrays.copyOfRange(data, 0, SM3withSM2_RS_LEN));
+		BigInteger s = new BigInteger(1, Arrays.copyOfRange(data, SM3withSM2_RS_LEN, SM3withSM2_RS_LEN * 2));
+		ASN1EncodableVector v = new ASN1EncodableVector();
+		v.add(new ASN1Integer(r));
+		v.add(new ASN1Integer(s));
+		try {
+			return new DERSequence(v).getEncoded("DER");
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	/**
+	 * 将ASN1字节数组转换为普通字节数组 适用于SM3withSM2签名时签名结果转换
+	 */
+	private static byte[] byteAsn12BytePlain(byte[] dataAsn1) {
+		ASN1Sequence seq = ASN1Sequence.getInstance(dataAsn1);
+		byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
+		byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
+		byte[] result = new byte[SM3withSM2_RS_LEN * 2];
+		System.arraycopy(r, 0, result, 0, r.length);
+		System.arraycopy(s, 0, result, SM3withSM2_RS_LEN, s.length);
+		return result;
+	}
+	
+	private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) {
+		byte[] rs = rOrS.toByteArray();
+		if (rs.length == SM3withSM2_RS_LEN) return rs;
+		else if (rs.length == SM3withSM2_RS_LEN + 1 && rs[0] == 0)
+			return Arrays.copyOfRange(rs, 1, SM3withSM2_RS_LEN + 1);
+		else if (rs.length < SM3withSM2_RS_LEN) {
+			byte[] result = new byte[SM3withSM2_RS_LEN];
+			Arrays.fill(result, (byte) 0);
+			System.arraycopy(rs, 0, result, SM3withSM2_RS_LEN - rs.length, rs.length);
+			return result;
+		} else {
+			throw new RuntimeException("err rs: " + Hex.toHexString(rs));
+		}
+	}
+
+}
diff --git a/src/main/java/com/mzl/flower/pay/SybConstants.java b/src/main/java/com/mzl/flower/pay/SybConstants.java
new file mode 100644
index 0000000..76dfde8
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/SybConstants.java
@@ -0,0 +1,41 @@
+package com.mzl.flower.pay;
+
+public class SybConstants {
+//	测试环境测试参数
+//	public static final String SYB_ORGID = "";//集团/机构模式下该参数不为空,且appid与key是与次参数对应
+//	public static final String SYB_CUSID = "990581007426001";
+//	public static final String SYB_APPID = "00000051";
+//	public static final String SYB_MD5_APPKEY = "allinpay888";
+//	public static final String SYB_APIURL = "http://172.16.1.10/apiweb";
+	/**商户RSA私钥,用于向通联发起请求前进行签名**/
+//	public static final String SYB_RSACUSPRIKEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK5aIo+E1eyWwEIgMB8ZEZRAaWjSAglmfKVhzy8N1eLjAlqPjJgOCqXGEYt/r61AyIjCCJiYVDTHzcqstmbBU7HKpYjTsquCLjRWcL/fhMwMGBSg2bP5mqw5locSOz1gtRujmd3kZo9OIJuWtfG2+wgPPdKUdGZS+5K8WtWCF4z1AgMBAAECgYAPvvqvkPzb9tpqrmsCJ/qvM6kBazP9Ytjfe8ehFYQLT1qrUJsPMXdzNMHpYhD82eSyeymZFGrIcIIMq4/2lD+pYOMQTMGGjoVb2wnQhZFqPdgjXgOQ90E43X69jD3p5F8CuKVNa13I4l3iyfzlVIL780JPdJdug7yKEFdSeOQZUQJBAONlFpIqz87pbnwzfgO5kRTbbI7DcyObb8OEeCK3VlGB3r9P4NoMEDaXm+HnIdv53gnFq+xgbREWUt2nFq9dSUUCQQDESOIdSvIBc3KQTYR+cnlQTH0SOvm0Tlx4KekBCLxTFAFyBqnOBLdVyQb6Z1wxGz855AjnNbHy1rFhUYQ6hPfxAkAIRZUcnBITJMqwGe9rk0SDzbeVOebmVLEsG5WDLcgmDuNbcjxrsiSk178D6LSCnARHtrkaUCenh3hcN8fLeUlBAkABNP2G9pYEYkRbFM7yxBtw3feK7Cfq7uxspL1VD0uxKxdTLy1OIgNKmMDdO1N6zdMWtQtE+LSObLmMgqbQgU7RAkBFX5kl4+B3k+/aCYB/ndqd1nQIr4SNAtLFJDtlW2xah9W2lQL/7KQDT4o4dUMY51m7Bu61SAmKtralv7Hf25yf";
+//	/**通联平台RSA公钥,用于请求返回或者通联通知的验签**/
+//	public static final String SYB_RSATLPUBKEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYXfu4b7xgDSmEGQpQ8Sn3RzFgl5CE4gL4TbYrND4FtCYOrvbgLijkdFgIrVVWi2hUW4K0PwBsmlYhXcbR+JSmqv9zviVXZiym0lK3glJGVCN86r9EPvNTusZZPm40TOEKMVENSYaUjCxZ7JzeZDfQ4WCeQQr2xirqn6LdJjpZ5wIDAQAB";
+//
+//	/**商户sm2私钥,用于向通联发起请求前进行签名**/
+//	public static final String SYB_SM2PPRIVATEKEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgjj4Rk+b0YjwO+UwXofnHf4bK+kaaY5Btkd8nMP2VimmgCgYIKoEcz1UBgi2hRANCAAQqlALW4qGC3bP1x3wo5QsKxaCMEZJ2ODTTwOQ+d8UGU7GoK/y/WMBQWf5upMnFU06p5FxGooXYYoBtldgm03hq";
+//	/**通联平台sm2公钥,用于请求返回或者通联通知的验签**/
+//	public static final String SYB_SM2TLPUBKEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/BnA8BawehBtH0ksPyayo4pmzL/u1FQ2sZcqwOp6bjVqQX4tjo930QAvHZPJ2eez8sCz/RYghcqv4LvMq+kloQ==";
+	
+//	正式环境测试参数
+	public static final String SYB_ORGID = "";//集团/机构模式下该参数不为空,且appid与key是与次参数对应
+	public static final String SYB_CUSID = "660731051932PAJ";
+	public static final String SYB_APPID = "00324494";
+	public static final String SYB_MD5_APPKEY = "666888";
+	public static final String SYB_APIURL = "https://vsp.allinpay.com/apiweb";//生产环境
+	public static final String SYB_RSACUSPRIKEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcvMSSSSyjeIFXpoc/aFScXgp1o9qbAO6fIGPRGFtwexSO4g3oP7sse8lL80bUNtj5Q7ysAEjVKW0fDpe/iJ0/jtqJ2CEL5aXDr6Fu7YR7zMLhbCDa531t64Iw4l0ADg3c+5jDSY3ck2rp5pX3RxYLpBb5VB2P005nMNJpG5oRuIRTMa0og07/cFMjhV3Q10eN6pBMnZ4jlXRjUUcftZhMCVUS9zAH16bcnbf6X6HJpBghuPEeIXKEwb+p8usvm4ZVcPh7Ha5dEdHeUGP7bCY9360plVfv20bZvucwlR4GuP6lDmMgqLF11jrEeGa+Fu+2e8wD0LnIfZjeenKLvlqFAgMBAAECggEAA2zmfAKor6y6DRsndCNxVK/c57BwPwGHsm51PhRrrtYqHoGBh74dcHZHTGdtDPQA7/VOIzkp6CzpZZb7YdyD3C2iVItHEAEzTlMWZTtIWAd4arkb1WnMN311Gr8YhFyzeF9rInSsv+SFvzV11E2FGQ8l0B6vCsFTsSQ/z6O/A1FbT3IaW5LTcsd6oysQr7ShT5yuCtuBuBypXI/7lmFoOn4O0/o5WFtqXr1295rpjv10WTycqZyfKh8W7CW7AKwT3SfuEodLBhmVHtu9TD+RNJRhu6ekv58vrnFAaRI7YUh/Kg3SfkvE0+Csp9meDGJJB6pbRwF8jdvTVd2kuWeM4QKBgQD+Ax/rxLRIGhc22cdw11c7anK7JkMU6J/K7i/zFJ6FJTixaz+zWOAxwyDPYSkyVeA555CSKAsXAtWd9vJHjFjgA4avPeF96C5WSZu26K+6pAz9VfyRYXw/2pZ060M+iBIinlTKu/B9SiYoiB5Y86Me9xFag4FrQvcghmV4zzhuyQKBgQDedvtm2hI7Gfna7fBOcr/69eiwxEvKhZ/1K0oRv9O2Sr0tFfUT0kSNejC1/CtRhaJN0TM4ZET7ajJZxfgK/lzyB3Utr60Sp5Vk0KaKVrwN7H3HOjQ8zE+YlbQwPRphgTM0/y0/wgq8sTmg1FKHHrHi4k7EusS4KEsD4AQ2pAl/3QKBgQCiIJBxRb3j/3MtuVcI+QlejRTpywQqBkhnmBvWxSRgy18iLtgbuvBuEqOQee13iURy8RBfmVtqto5uIr6FWFkpslZ258JTQKzCuny/BEmVkTwRPp7UlCpf/z7lJXxoHyWkHvbz530aGwe7+xWTpP5Kt1vGMAWc1sMxaVa/A1R7sQKBgQCkBZXWf3JxoPVXNZe4P2LM5gJqUlCPQXZujTxPWd/+x5wzYb0QoDdrGGxRwVsvABhqx5O8QDGcLPBywEPh1ykwSc9bn+s7ldPQQQaJeOmuWm9iNMfPC9Sj67yu1F5fBaN7r6jtGWRRO1oB+onelgMSBUmOQyZHoW2trb1tjAR1AQKBgQCJ7wwrATqy+4/1kcm1zWrwjI39Db27lyLb1TR5jPdmdOHmCMMoLrsB4fkpZhnlXIA7uIzkZX5+rNMAkGrV84ZWZ0Q5QXCCMtFypFlZPxh9q8Jb1aB1Abn3UTKgJfUalDo4H8Q+h1COlNPU/6YuWPl8gU489NI/rkopt25j0GhFqQ==";
+	public static final String SYB_RSATLPUBKEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3LzEkkkso3iBV6aHP2hUnF4KdaPamwDunyBj0RhbcHsUjuIN6D+7LHvJS/NG1DbY+UO8rABI1SltHw6Xv4idP47aidghC+Wlw6+hbu2Ee8zC4Wwg2ud9beuCMOJdAA4N3PuYw0mN3JNq6eaV90cWC6QW+VQdj9NOZzDSaRuaEbiEUzGtKINO/3BTI4Vd0NdHjeqQTJ2eI5V0Y1FHH7WYTAlVEvcwB9em3J23+l+hyaQYIbjxHiFyhMG/qfLrL5uGVXD4ex2uXRHR3lBj+2wmPd+tKZVX79tG2b7nMJUeBrj+pQ5jIKixddY6xHhmvhbvtnvMA9C5yH2Y3npyi75ahQIDAQAB";
+
+
+
+	public static final String VERSION = "12";
+	public static final String RET_URL = "https://test.allinpaygd.com/JWeb/reccomparams.jsp";
+	public static final String NOTFIY_URL = "https://test.allinpaygd.com/JWeb/NotifyServlet";
+	public static final String VALID_TIME = "5";
+	public static final String SIGN_TYPE = "RSA";//
+//	/**商户sm2私钥,用于向通联发起请求前进行签名**/
+	public static final String SYB_SM2PPRIVATEKEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgjj4Rk+b0YjwO+UwXofnHf4bK+kaaY5Btkd8nMP2VimmgCgYIKoEcz1UBgi2hRANCAAQqlALW4qGC3bP1x3wo5QsKxaCMEZJ2ODTTwOQ+d8UGU7GoK/y/WMBQWf5upMnFU06p5FxGooXYYoBtldgm03hq";
+//	/**通联平台sm2公钥,用于请求返回或者通联通知的验签**/
+	public static final String SYB_SM2TLPUBKEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEBQicgWm0KAMqhO3bdqMUEDrKQvYg8cCXHhdGwq7CGE6oJDzJ1P/94HpuVdBf1KidmPxr7HOH+0DAnpeCcx9TcQ==";
+
+}
diff --git a/src/main/java/com/mzl/flower/pay/SybPayService.java b/src/main/java/com/mzl/flower/pay/SybPayService.java
new file mode 100644
index 0000000..8dc5e9e
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/SybPayService.java
@@ -0,0 +1,221 @@
+package com.mzl.flower.pay;
+
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class SybPayService {
+
+
+	/**
+	 *	封装订单信息
+	 * @param trxamt 订单金额单位为分
+	 * @param reqsn 商户订单号
+	 * @param unireqsn 商户唯一订单号
+	 * @param body 订单标题,订单描述信息,展示在收银台
+	 * @param remark 备注
+	 * @param notifyUrl 服务器异步通知页面路径,交易完成后,平台会将交易结果以后台通知的方式发送到该地址,商户需要以此通知判断用户订单交易是否成功。
+	 * @return
+	 * @throws Exception
+	 */
+	public  TreeMap<String,String> createOrder(long trxamt,
+											  String reqsn,
+											  String unireqsn,
+											  String body,
+											  String remark,
+											  String notifyUrl) throws Exception{
+		TreeMap<String,String> params = new TreeMap<String,String>();
+		if(!SybUtil.isEmpty(SybConstants.SYB_ORGID))
+			params.put("orgid", SybConstants.SYB_ORGID);
+			params.put("cusid", SybConstants.SYB_CUSID);
+			params.put("appid", SybConstants.SYB_APPID);
+			params.put("version", "12");
+			params.put("trxamt", String.valueOf(trxamt));
+			params.put("reqsn", reqsn);
+			params.put("unireqsn", unireqsn);
+			params.put("notify_url", notifyUrl);
+			params.put("body", body);
+			params.put("remark", remark);
+			params.put("paytype", "W06");
+			params.put("randomstr", SybUtil.getValidatecode(8));
+			params.put("signtype", SybConstants.SIGN_TYPE);
+			String appkey = "";
+			if(SybConstants.SIGN_TYPE.equals("RSA"))
+				appkey = SybConstants.SYB_RSACUSPRIKEY;
+			else if(SybConstants.SIGN_TYPE.equals("SM2"))
+				appkey = SybConstants.SYB_SM2PPRIVATEKEY;
+			else
+				appkey = SybConstants.SYB_MD5_APPKEY;
+			params.put("sign", SybUtil.unionSign(params,appkey,SybConstants.SIGN_TYPE));
+			return params;
+	}
+
+
+	/**
+	 *	撤销订单
+	 * @param trxamt 原订单金额
+	 * @param reqsn 商户的退款交易订单号
+	 * @param oldtrxid 原交易的收银宝平台流水
+	 * @param oldreqsn 原交易的商户交易单号
+	 * @return
+	 * @throws Exception
+	 */
+	public Map<String,String> cancel(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{
+		HttpConnectionUtil http = new HttpConnectionUtil(SybConstants.SYB_APIURL+"/tranx/cancel");
+		http.init();
+		TreeMap<String,String> params = new TreeMap<String,String>();
+		if(!SybUtil.isEmpty(SybConstants.SYB_ORGID))
+			params.put("orgid", SybConstants.SYB_ORGID);
+			params.put("cusid", SybConstants.SYB_CUSID);
+			params.put("appid", SybConstants.SYB_APPID);
+			params.put("version", "11");
+			params.put("trxamt", String.valueOf(trxamt));
+			params.put("reqsn", reqsn);
+			params.put("oldtrxid", oldtrxid);
+			params.put("oldreqsn", oldreqsn);
+			params.put("randomstr", SybUtil.getValidatecode(8));
+			params.put("signtype", SybConstants.SIGN_TYPE);
+			String appkey = "";
+			if(SybConstants.SIGN_TYPE.equals("RSA"))
+				appkey = SybConstants.SYB_RSACUSPRIKEY;
+			else if(SybConstants.SIGN_TYPE.equals("SM2"))
+				appkey = SybConstants.SYB_SM2PPRIVATEKEY;
+			else
+				appkey = SybConstants.SYB_MD5_APPKEY;
+			params.put("sign", SybUtil.unionSign(params,appkey,SybConstants.SIGN_TYPE));
+			byte[] bys = http.postParams(params, true);
+			String result = new String(bys,"UTF-8");
+			Map<String,String> map = handleResult(result);
+			return map;
+	}
+
+	/**
+	 * 退款
+	 * @param trxamt 退款金额单位为分
+	 * @param reqsn 商户的退款交易订单号
+	 * @param oldtrxid 原交易的收银宝平台流水
+	 * @param oldreqsn 原交易的商户订单号
+	 * @return
+	 * @throws Exception
+	 */
+	public Map<String,String> refund(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{
+		HttpConnectionUtil http = new HttpConnectionUtil(SybConstants.SYB_APIURL+"/tranx/refund");
+		http.init();
+		TreeMap<String,String> params = new TreeMap<String,String>();
+		if(!SybUtil.isEmpty(SybConstants.SYB_ORGID))
+			params.put("orgid", SybConstants.SYB_ORGID);
+			params.put("cusid", SybConstants.SYB_CUSID);
+			params.put("appid", SybConstants.SYB_APPID);
+			params.put("version", "11");
+			params.put("trxamt", String.valueOf(trxamt));
+			params.put("reqsn", reqsn);
+			params.put("oldreqsn", oldreqsn);
+			params.put("oldtrxid", oldtrxid);
+			params.put("randomstr", SybUtil.getValidatecode(8));
+			params.put("signtype", SybConstants.SIGN_TYPE);
+			String appkey = "";
+			if(SybConstants.SIGN_TYPE.equals("RSA"))
+				appkey = SybConstants.SYB_RSACUSPRIKEY;
+			else if(SybConstants.SIGN_TYPE.equals("SM2"))
+				appkey = SybConstants.SYB_SM2PPRIVATEKEY;
+			else
+				appkey = SybConstants.SYB_MD5_APPKEY;
+			params.put("sign", SybUtil.unionSign(params,appkey,SybConstants.SIGN_TYPE));
+			byte[] bys = http.postParams(params, true);
+			String result = new String(bys,"UTF-8");
+			Map<String,String> map = handleResult(result);
+			return map;
+	}
+
+	/**
+	 * 关闭订单
+	 * @param oldtrxid 原通联平台交易流水
+	 * @param oldreqsn 原商户订单号
+	 * @return
+	 * @throws Exception
+	 */
+	public Map<String,String> close(String oldtrxid,String oldreqsn) throws Exception{
+		HttpConnectionUtil http = new HttpConnectionUtil(SybConstants.SYB_APIURL+"/tranx/close");
+		http.init();
+		TreeMap<String,String> params = new TreeMap<String,String>();
+		if(!SybUtil.isEmpty(SybConstants.SYB_ORGID))
+			params.put("orgid", SybConstants.SYB_ORGID);
+			params.put("cusid", SybConstants.SYB_CUSID);
+			params.put("appid", SybConstants.SYB_APPID);
+			params.put("version", "12");
+			params.put("oldreqsn", oldreqsn);
+			params.put("oldtrxid", oldtrxid);
+			params.put("randomstr", SybUtil.getValidatecode(8));
+			params.put("signtype", SybConstants.SIGN_TYPE);
+			String appkey = "";
+			if(SybConstants.SIGN_TYPE.equals("RSA"))
+				appkey = SybConstants.SYB_RSACUSPRIKEY;
+			else if(SybConstants.SIGN_TYPE.equals("SM2"))
+				appkey = SybConstants.SYB_SM2PPRIVATEKEY;
+			else
+				appkey = SybConstants.SYB_MD5_APPKEY;
+			params.put("sign", SybUtil.unionSign(params,appkey,SybConstants.SIGN_TYPE));
+			byte[] bys = http.postParams(params, true);
+			String result = new String(bys,"UTF-8");
+			Map<String,String> map = handleResult(result);
+			return map;
+	}
+	
+	public Map<String,String> query(String reqsn,String trxid) throws Exception{
+		HttpConnectionUtil http = new HttpConnectionUtil(SybConstants.SYB_APIURL+"/tranx/query");
+		http.init();
+		TreeMap<String,String> params = new TreeMap<String,String>();
+		if(!SybUtil.isEmpty(SybConstants.SYB_ORGID))
+			params.put("orgid", SybConstants.SYB_ORGID);
+		params.put("cusid", SybConstants.SYB_CUSID);
+		params.put("appid", SybConstants.SYB_APPID);
+		params.put("version", "11");
+		params.put("reqsn", reqsn);
+		params.put("trxid", trxid);
+		params.put("randomstr", SybUtil.getValidatecode(8));
+		params.put("signtype", SybConstants.SIGN_TYPE);
+		String appkey = "";
+		if(SybConstants.SIGN_TYPE.equals("RSA"))
+			appkey = SybConstants.SYB_RSACUSPRIKEY;
+		else if(SybConstants.SIGN_TYPE.equals("SM2"))
+			appkey = SybConstants.SYB_SM2PPRIVATEKEY;
+		else 
+			appkey = SybConstants.SYB_MD5_APPKEY;
+		params.put("sign", SybUtil.unionSign(params,appkey,SybConstants.SIGN_TYPE));
+		byte[] bys = http.postParams(params, true);
+		String result = new String(bys,"UTF-8");
+		Map<String,String> map = handleResult(result);
+		return map;
+	}
+	
+	
+	public static Map<String,String> handleResult(String result) throws Exception{
+		System.out.println("ret:"+result);
+		Map map = SybUtil.json2Obj(result, Map.class);
+		if(map == null){
+			throw new Exception("返回数据错误");
+		}
+		if("SUCCESS".equals(map.get("retcode"))){
+			TreeMap tmap = new TreeMap();
+			tmap.putAll(map);
+			String appkey = "";
+			if(SybConstants.SIGN_TYPE.equals("RSA"))
+				appkey = SybConstants.SYB_RSATLPUBKEY;
+			else if(SybConstants.SIGN_TYPE.equals("SM2"))
+				appkey = SybConstants.SYB_SM2TLPUBKEY;
+			else 
+				appkey = SybConstants.SYB_MD5_APPKEY;
+			if(SybUtil.validSign(tmap, appkey, SybConstants.SIGN_TYPE)){
+				System.out.println("签名成功");
+				return map;
+			}else{
+				throw new Exception("验证签名失败");
+			}
+			
+		}else{
+			throw new Exception(map.get("retmsg").toString());
+		}
+	}
+	
+	
+}
diff --git a/src/main/java/com/mzl/flower/pay/SybUtil.java b/src/main/java/com/mzl/flower/pay/SybUtil.java
new file mode 100644
index 0000000..582c32c
--- /dev/null
+++ b/src/main/java/com/mzl/flower/pay/SybUtil.java
@@ -0,0 +1,230 @@
+package com.mzl.flower.pay;
+
+import java.io.UnsupportedEncodingException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+import org.apache.commons.codec.binary.Base64;
+import net.sf.json.JSONObject;
+
+public class SybUtil {
+	/**
+	 * js转化为实体
+	 * 
+	 * @param <T>
+	 * @param jsonstr
+	 * @param cls
+	 * @return
+	 */
+	public static <T> T json2Obj(String jsonstr, Class<T> cls) {
+		JSONObject jo = JSONObject.fromObject(jsonstr);
+		T obj = (T) JSONObject.toBean(jo, cls);
+		return obj;
+	}
+
+	/**
+	 * md5
+	 * 
+	 * @param b
+	 * @return
+	 */
+	public static String md5(byte[] b) {
+		try {
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			md.reset();
+			md.update(b);
+			byte[] hash = md.digest();
+			StringBuffer outStrBuf = new StringBuffer(32);
+			for (int i = 0; i < hash.length; i++) {
+				int v = hash[i] & 0xFF;
+				if (v < 16) {
+					outStrBuf.append('0');
+				}
+				outStrBuf.append(Integer.toString(v, 16).toLowerCase());
+			}
+			return outStrBuf.toString();
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return new String(b);
+		}
+	}
+
+	/**
+	 * 判断字符串是否为空
+	 * 
+	 * @param s
+	 * @return
+	 */
+	public static boolean isEmpty(String s) {
+		if (s == null || "".equals(s.trim()))
+			return true;
+		return false;
+	}
+
+	/**
+	 * 生成随机码
+	 * 
+	 * @param n
+	 * @return
+	 */
+	public static String getValidatecode(int n) {
+		Random random = new Random();
+		String sRand = "";
+		n = n == 0 ? 4 : n;// default 4
+		for (int i = 0; i < n; i++) {
+			String rand = String.valueOf(random.nextInt(10));
+			sRand += rand;
+		}
+		return sRand;
+	}
+
+
+
+	public static boolean validSign(TreeMap<String, String> param,
+			String appkey, String signType) throws Exception {
+		if (param != null && !param.isEmpty()) {
+			if (!param.containsKey("sign"))
+				return false;
+			String sign = param.remove("sign");
+			if ("MD5".equals(signType)) {// 如果是md5则需要把md5的key加入到排序
+				param.put("key", appkey);
+			}
+			StringBuilder sb = new StringBuilder();
+			for (Map.Entry<String, String> entry : param.entrySet()) {
+				if (entry.getValue() != null && entry.getValue().length() > 0) {
+					sb.append(entry.getKey()).append("=")
+							.append(entry.getValue()).append("&");
+				}
+			}
+			if (sb.length() > 0) {
+				sb.deleteCharAt(sb.length() - 1);
+			}
+			if ("MD5".equals(signType)) {
+				return sign.toLowerCase().equals(
+						md5(sb.toString().getBytes("UTF-8")).toLowerCase());
+			} else if("SM2".equals(signType)){
+				PublicKey publicKey = SmUtil.pubKeySM2FromBase64Str(appkey);
+				return SmUtil.verifySM3SM2(publicKey, "Allinpay", Base64.decodeBase64(sign), sb.toString().getBytes("UTF-8"));
+			}else {
+				return rsaVerifyPublickey(sb.toString(), sign, appkey, "UTF-8");
+			}
+		}
+		return false;
+	}
+
+	public static boolean rsaVerifyPublickey(String content, String sign,
+			String publicKey, String charset) throws Exception {
+		try {
+			PublicKey pubKey = getPublicKeyFromX509("RSA",
+					Base64.decodeBase64(publicKey.getBytes()));
+			return rsaVerifyPublickey(content, sign, pubKey, charset);
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw new Exception("RSAcontent = " + content + ",sign=" + sign
+					+ ",charset = " + charset, e);
+		}
+	}
+
+	public static boolean rsaVerifyPublickey(String content, String sign,
+			PublicKey pubKey, String charset) throws Exception {
+		try {
+			java.security.Signature signature = java.security.Signature
+					.getInstance("SHA1WithRSA");
+
+			signature.initVerify(pubKey);
+
+			if (charset == null || "".equals(charset)) {
+				signature.update(content.getBytes());
+			} else {
+				signature.update(content.getBytes(charset));
+			}
+
+			return signature.verify(Base64.decodeBase64(sign.getBytes()));
+		} catch (Exception e) {
+			throw e;
+		}
+	}
+	public static String unionSign(TreeMap<String, String> params,String appkey,
+			String signType) throws Exception {
+		// TODO Auto-generated method stub
+
+		params.remove("sign");
+		if ("MD5".equals(signType)) {// 如果是md5则需要把md5的key加入到排序
+			params.put("key", appkey);
+		}
+		StringBuilder sb = new StringBuilder();
+		for (Map.Entry<String, String> entry : params.entrySet()) {
+			if (entry.getValue() != null && entry.getValue().length() > 0) {
+				sb.append(entry.getKey()).append("=").append(entry.getValue())
+						.append("&");
+			}
+		}
+		if (sb.length() > 0) {
+			sb.deleteCharAt(sb.length() - 1);
+		}
+		String sign = "";
+		if ("MD5".equals(signType)) {
+			System.out.println(sb.toString());
+			sign = md5(sb.toString().getBytes("UTF-8"));// 记得是md5编码的加签
+			params.remove("key");
+		} else if("SM2".equals(signType)){
+			System.out.println(sb.toString());
+			PrivateKey privkey = SmUtil.privKeySM2FromBase64Str(appkey);
+			sign = SmUtil.signSM3SM2RetBase64(privkey, params.get("appid"), sb.toString().getBytes("UTF-8"));//签名
+		} else {
+			System.out.println(sb.toString());
+			sign = rsaSign(sb.toString(), appkey, "UTF-8");
+		}
+		return sign;
+	}
+
+	public static String rsaSign(String content, String privateKey,
+			String charset) throws Exception {
+		PrivateKey priKey = getPrivateKeyFromPKCS8("RSA",
+				Base64.decodeBase64(privateKey.getBytes()));
+		return rsaSign(content, priKey, charset);
+	}
+
+	public static String rsaSign(String content, byte[] privateKey,
+			String charset) throws Exception {
+		PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", privateKey);
+		return rsaSign(content, priKey, charset);
+	}
+
+	public static String rsaSign(String content, PrivateKey priKey,
+			String charset) throws Exception {
+		java.security.Signature signature = java.security.Signature
+				.getInstance("SHA1WithRSA");
+		signature.initSign(priKey);
+		if (charset == null || "".equals(charset)) {
+			signature.update(content.getBytes());
+		} else {
+			signature.update(content.getBytes(charset));
+		}
+		byte[] signed = signature.sign();
+
+		return new String(Base64.encodeBase64(signed));
+	}
+
+	public static PrivateKey getPrivateKeyFromPKCS8(String algorithm,
+			byte[] encodedKey) throws Exception {
+
+		KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
+
+		return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
+	}
+
+	public static PublicKey getPublicKeyFromX509(String algorithm,
+			byte[] encodedKey) throws Exception {
+		KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
+
+		return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
+	}
+}

--
Gitblit v1.9.3