From dfd2cc68c58f28a3ba1518914a666b80eb7cb9c7 Mon Sep 17 00:00:00 2001 From: Actinia-517 <412605202@qq.com> Date: Tue, 5 Dec 2017 16:24:55 +0800 Subject: [PATCH] =?UTF-8?q?#=E6=96=B0=E5=A2=9E#=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E6=8E=A5=E5=8F=A3=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=AD=89=E6=B3=A8=E9=87=8A=20#=E4=BF=AE?= =?UTF-8?q?=E6=94=B9#=20=E6=9B=B4=E6=96=B0=E6=96=B0=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/util/sign/SM3/SM3.java | 310 +++++++ .../pay/common/util/sign/SM3/SM3Digest.java | 143 +++ .../egzosn/pay/common/util/sign/SM3/Util.java | 662 ++++++++++++++ .../pay/common/util/sign/SecureUtil.java | 166 ++++ .../pay/common/util/sign/encrypt/RSA.java | 23 + .../pay/common/util/sign/encrypt/SHA256.java | 89 ++ pay-java-demo/pom.xml | 5 + .../pay/demo/controller/PayController.java | 20 +- .../pay/demo/dao/ApyAccountRepository.java | 16 + .../com/egzosn/pay/demo/entity/PayType.java | 29 +- .../egzosn/pay/demo/service/PayResponse.java | 25 +- .../handler/UnionPayMessageHandler.java | 37 + .../pay/fuiou/api/FuiouPayConfigStorage.java | 3 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 5 +- .../egzosn/pay/fuiou/bean/FuiouCurType.java | 7 +- .../pay/fuiou/bean/FuiouTransactionType.java | 7 +- pay-java-union/pom.xml | 5 +- pay-java-union/resource/acp_sdk.properties | 69 ++ pay-java-union/resource/acp_test_enc.cer | 25 + pay-java-union/resource/acp_test_middle.cer | 23 + pay-java-union/resource/acp_test_root.cer | 22 + pay-java-union/resource/acp_test_sign.pfx | 0 .../com/egzosn/pay/union/SDK/CertUtil.java | 822 ++++++++++++++++++ .../com/egzosn/pay/union/SDK/SDKConfig.java | 690 +++++++++++++++ .../egzosn/pay/union/SDK/SDKConstants.java | 402 +++++++++ .../com/egzosn/pay/union/SDK/SDKUtils.java | 204 +++++ .../pay/union/api/UnionPayConfigStorage.java | 71 +- .../egzosn/pay/union/api/UnionPayService.java | 464 ++++++++++ .../pay/union/enums/UnionTransactionType.java | 98 +++ .../pay/union/request/UnionQueryOrder.java | 82 ++ pom.xml | 9 +- 31 files changed, 4472 insertions(+), 61 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3Digest.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/Util.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/SHA256.java create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java create mode 100644 pay-java-union/resource/acp_sdk.properties create mode 100644 pay-java-union/resource/acp_test_enc.cer create mode 100644 pay-java-union/resource/acp_test_middle.cer create mode 100644 pay-java-union/resource/acp_test_root.cer create mode 100644 pay-java-union/resource/acp_test_sign.pfx create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/SDK/CertUtil.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConfig.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConstants.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKUtils.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/enums/UnionTransactionType.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/request/UnionQueryOrder.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3.java new file mode 100644 index 0000000..f945530 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3.java @@ -0,0 +1,310 @@ +package com.egzosn.pay.common.util.sign.SM3; + +public class SM3 +{ + public static final byte[] iv = { 0x73, (byte) 0x80, 0x16, 0x6f, 0x49, + 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7, + (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, + (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3, + (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, + 0x4e }; + + public static int[] Tj = new int[64]; + + static + { + for (int i = 0; i < 16; i++) + { + Tj[i] = 0x79cc4519; + } + + for (int i = 16; i < 64; i++) + { + Tj[i] = 0x7a879d8a; + } + } + + public static byte[] CF(byte[] V, byte[] B) + { + int[] v, b; + v = convert(V); + b = convert(B); + return convert(CF(v, b)); + } + + private static int[] convert(byte[] arr) + { + int[] out = new int[arr.length / 4]; + byte[] tmp = new byte[4]; + for (int i = 0; i < arr.length; i += 4) + { + System.arraycopy(arr, i, tmp, 0, 4); + out[i / 4] = bigEndianByteToInt(tmp); + } + return out; + } + + private static byte[] convert(int[] arr) + { + byte[] out = new byte[arr.length * 4]; + byte[] tmp = null; + for (int i = 0; i < arr.length; i++) + { + tmp = bigEndianIntToByte(arr[i]); + System.arraycopy(tmp, 0, out, i * 4, 4); + } + return out; + } + + public static int[] CF(int[] V, int[] B) + { + int a, b, c, d, e, f, g, h; + int ss1, ss2, tt1, tt2; + a = V[0]; + b = V[1]; + c = V[2]; + d = V[3]; + e = V[4]; + f = V[5]; + g = V[6]; + h = V[7]; + + int[][] arr = expand(B); + int[] w = arr[0]; + int[] w1 = arr[1]; + + for (int j = 0; j < 64; j++) + { + ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j)); + ss1 = bitCycleLeft(ss1, 7); + ss2 = ss1 ^ bitCycleLeft(a, 12); + tt1 = FFj(a, b, c, j) + d + ss2 + w1[j]; + tt2 = GGj(e, f, g, j) + h + ss1 + w[j]; + d = c; + c = bitCycleLeft(b, 9); + b = a; + a = tt1; + h = g; + g = bitCycleLeft(f, 19); + f = e; + e = P0(tt2); + + /*System.out.print(j+" "); + System.out.print(Integer.toHexString(a)+" "); + System.out.print(Integer.toHexString(b)+" "); + System.out.print(Integer.toHexString(c)+" "); + System.out.print(Integer.toHexString(d)+" "); + System.out.print(Integer.toHexString(e)+" "); + System.out.print(Integer.toHexString(f)+" "); + System.out.print(Integer.toHexString(g)+" "); + System.out.print(Integer.toHexString(h)+" "); + System.out.println("");*/ + } +// System.out.println(""); + + int[] out = new int[8]; + out[0] = a ^ V[0]; + out[1] = b ^ V[1]; + out[2] = c ^ V[2]; + out[3] = d ^ V[3]; + out[4] = e ^ V[4]; + out[5] = f ^ V[5]; + out[6] = g ^ V[6]; + out[7] = h ^ V[7]; + + return out; + } + + private static int[][] expand(int[] B) + { + int W[] = new int[68]; + int W1[] = new int[64]; + for (int i = 0; i < B.length; i++) + { + W[i] = B[i]; + } + + for (int i = 16; i < 68; i++) + { + W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15)) + ^ bitCycleLeft(W[i - 13], 7) ^ W[i - 6]; + } + + for (int i = 0; i < 64; i++) + { + W1[i] = W[i] ^ W[i + 4]; + } + + int arr[][] = new int[][] { W, W1 }; + return arr; + } + + private static byte[] bigEndianIntToByte(int num) + { + return back(Util.intToBytes(num)); + } + + private static int bigEndianByteToInt(byte[] bytes) + { + return Util.byteToInt(back(bytes)); + } + + private static int FFj(int X, int Y, int Z, int j) + { + if (j >= 0 && j <= 15) + { + return FF1j(X, Y, Z); + } + else + { + return FF2j(X, Y, Z); + } + } + + private static int GGj(int X, int Y, int Z, int j) + { + if (j >= 0 && j <= 15) + { + return GG1j(X, Y, Z); + } + else + { + return GG2j(X, Y, Z); + } + } + + // 逻辑位运算函数 + private static int FF1j(int X, int Y, int Z) + { + int tmp = X ^ Y ^ Z; + return tmp; + } + + private static int FF2j(int X, int Y, int Z) + { + int tmp = ((X & Y) | (X & Z) | (Y & Z)); + return tmp; + } + + private static int GG1j(int X, int Y, int Z) + { + int tmp = X ^ Y ^ Z; + return tmp; + } + + private static int GG2j(int X, int Y, int Z) + { + int tmp = (X & Y) | (~X & Z); + return tmp; + } + + private static int P0(int X) + { + int y = rotateLeft(X, 9); + y = bitCycleLeft(X, 9); + int z = rotateLeft(X, 17); + z = bitCycleLeft(X, 17); + int t = X ^ y ^ z; + return t; + } + + private static int P1(int X) + { + int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23); + return t; + } + + /** + * 对最后一个分组字节数据padding + * + * @param in + * @param bLen + * 分组个数 + * @return + */ + public static byte[] padding(byte[] in, int bLen) + { + int k = 448 - (8 * in.length + 1) % 512; + if (k < 0) + { + k = 960 - (8 * in.length + 1) % 512; + } + k += 1; + byte[] padd = new byte[k / 8]; + padd[0] = (byte) 0x80; + long n = in.length * 8 + bLen * 512; + byte[] out = new byte[in.length + k / 8 + 64 / 8]; + int pos = 0; + System.arraycopy(in, 0, out, 0, in.length); + pos += in.length; + System.arraycopy(padd, 0, out, pos, padd.length); + pos += padd.length; + byte[] tmp = back(Util.longToBytes(n)); + System.arraycopy(tmp, 0, out, pos, tmp.length); + return out; + } + + /** + * 字节数组逆序 + * + * @param in + * @return + */ + private static byte[] back(byte[] in) + { + byte[] out = new byte[in.length]; + for (int i = 0; i < out.length; i++) + { + out[i] = in[out.length - i - 1]; + } + + return out; + } + + public static int rotateLeft(int x, int n) + { + return (x << n) | (x >> (32 - n)); + } + + private static int bitCycleLeft(int n, int bitLen) + { + bitLen %= 32; + byte[] tmp = bigEndianIntToByte(n); + int byteLen = bitLen / 8; + int len = bitLen % 8; + if (byteLen > 0) + { + tmp = byteCycleLeft(tmp, byteLen); + } + + if (len > 0) + { + tmp = bitSmall8CycleLeft(tmp, len); + } + + return bigEndianByteToInt(tmp); + } + + private static byte[] bitSmall8CycleLeft(byte[] in, int len) + { + byte[] tmp = new byte[in.length]; + int t1, t2, t3; + for (int i = 0; i < tmp.length; i++) + { + t1 = (byte) ((in[i] & 0x000000ff) << len); + t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len)); + t3 = (byte) (t1 | t2); + tmp[i] = (byte) t3; + } + + return tmp; + } + + private static byte[] byteCycleLeft(byte[] in, int byteLen) + { + byte[] tmp = new byte[in.length]; + System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen); + System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen); + return tmp; + } +} \ No newline at end of file diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3Digest.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3Digest.java new file mode 100644 index 0000000..f86288e --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/SM3Digest.java @@ -0,0 +1,143 @@ +package com.egzosn.pay.common.util.sign.SM3; + +public class SM3Digest +{ + /** SM3值的长度 */ + private static final int BYTE_LENGTH = 32; + + /** SM3分组长度 */ + private static final int BLOCK_LENGTH = 64; + + /** 缓冲区长度 */ + private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1; + + /** 缓冲区 */ + private byte[] xBuf = new byte[BUFFER_LENGTH]; + + /** 缓冲区偏移量 */ + private int xBufOff; + + /** 初始向量 */ + private byte[] V = SM3.iv.clone(); + + private int cntBlock = 0; + + public SM3Digest() { + } + + public SM3Digest(SM3Digest t) + { + System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); + this.xBufOff = t.xBufOff; + System.arraycopy(t.V, 0, this.V, 0, t.V.length); + } + + /** + * SM3结果输出 + * + * @param out 保存SM3结构的缓冲区 + * @param outOff 缓冲区偏移量 + * @return + */ + public int doFinal(byte[] out, int outOff) + { + byte[] tmp = doFinal(); + System.arraycopy(tmp, 0, out, 0, tmp.length); + return BYTE_LENGTH; + } + + public void reset() + { + xBufOff = 0; + cntBlock = 0; + V = SM3.iv.clone(); + } + + /** + * 明文输入 + * + * @param in + * 明文输入缓冲区 + * @param inOff + * 缓冲区偏移量 + * @param len + * 明文长度 + */ + public void update(byte[] in, int inOff, int len) + { + int partLen = BUFFER_LENGTH - xBufOff; + int inputLen = len; + int dPos = inOff; + if (partLen < inputLen) + { + System.arraycopy(in, dPos, xBuf, xBufOff, partLen); + inputLen -= partLen; + dPos += partLen; + doUpdate(); + while (inputLen > BUFFER_LENGTH) + { + System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH); + inputLen -= BUFFER_LENGTH; + dPos += BUFFER_LENGTH; + doUpdate(); + } + } + + System.arraycopy(in, dPos, xBuf, xBufOff, inputLen); + xBufOff += inputLen; + } + + private void doUpdate() + { + byte[] B = new byte[BLOCK_LENGTH]; + for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) + { + System.arraycopy(xBuf, i, B, 0, B.length); + doHash(B); + } + xBufOff = 0; + } + + private void doHash(byte[] B) + { + byte[] tmp = SM3.CF(V, B); + System.arraycopy(tmp, 0, V, 0, V.length); + cntBlock++; + } + + private byte[] doFinal() + { + byte[] B = new byte[BLOCK_LENGTH]; + byte[] buffer = new byte[xBufOff]; + System.arraycopy(xBuf, 0, buffer, 0, buffer.length); + byte[] tmp = SM3.padding(buffer, cntBlock); + for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) + { + System.arraycopy(tmp, i, B, 0, B.length); + doHash(B); + } + return V; + } + + public void update(byte in) + { + byte[] buffer = new byte[] { in }; + update(buffer, 0, 1); + } + + public int getDigestSize() + { + return BYTE_LENGTH; + } +// +// public static void main(String[] args) +// { +// byte[] md = new byte[32]; +// byte[] msg1 = "ererfeiisgod".getBytes(); +// SM3Digest sm3 = new SM3Digest(); +// sm3.update(msg1, 0, msg1.length); +// sm3.doFinal(md, 0); +// String s = new String(Hex.encode(md)); +// System.out.println(s.toUpperCase()); +// } +} \ No newline at end of file diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/Util.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/Util.java new file mode 100644 index 0000000..26a6801 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SM3/Util.java @@ -0,0 +1,662 @@ +package com.egzosn.pay.common.util.sign.SM3; + +import java.math.BigInteger; + +public class Util +{ + /** + * 整形转换成网络传输的字节流(字节数组)型数据 + * + * @param num 一个整型数据 + * @return 4个字节的自己数组 + */ + public static byte[] intToBytes(int num) + { + byte[] bytes = new byte[4]; + bytes[0] = (byte) (0xff & (num >> 0)); + bytes[1] = (byte) (0xff & (num >> 8)); + bytes[2] = (byte) (0xff & (num >> 16)); + bytes[3] = (byte) (0xff & (num >> 24)); + return bytes; + } + + /** + * 四个字节的字节数据转换成一个整形数据 + * + * @param bytes 4个字节的字节数组 + * @return 一个整型数据 + */ + public static int byteToInt(byte[] bytes) + { + int num = 0; + int temp; + temp = (0x000000ff & (bytes[0])) << 0; + num = num | temp; + temp = (0x000000ff & (bytes[1])) << 8; + num = num | temp; + temp = (0x000000ff & (bytes[2])) << 16; + num = num | temp; + temp = (0x000000ff & (bytes[3])) << 24; + num = num | temp; + return num; + } + + /** + * 长整形转换成网络传输的字节流(字节数组)型数据 + * + * @param num 一个长整型数据 + * @return 4个字节的自己数组 + */ + public static byte[] longToBytes(long num) + { + byte[] bytes = new byte[8]; + for (int i = 0; i < 8; i++) + { + bytes[i] = (byte) (0xff & (num >> (i * 8))); + } + + return bytes; + } + + /** + * 大数字转换字节流(字节数组)型数据 + * + * @param n + * @return + */ + public static byte[] byteConvert32Bytes(BigInteger n) + { + byte tmpd[] = (byte[])null; + if(n == null) + { + return null; + } + + if(n.toByteArray().length == 33) + { + tmpd = new byte[32]; + System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32); + } + else if(n.toByteArray().length == 32) + { + tmpd = n.toByteArray(); + } + else + { + tmpd = new byte[32]; + for(int i = 0; i < 32 - n.toByteArray().length; i++) + { + tmpd[i] = 0; + } + System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length); + } + return tmpd; + } + + /** + * 换字节流(字节数组)型数据转大数字 + * + * @param b + * @return + */ + public static BigInteger byteConvertInteger(byte[] b) + { + if (b[0] < 0) + { + byte[] temp = new byte[b.length + 1]; + temp[0] = 0; + System.arraycopy(b, 0, temp, 1, b.length); + return new BigInteger(temp); + } + return new BigInteger(b); + } + + /** + * 根据字节数组获得值(十六进制数字) + * + * @param bytes + * @return + */ + public static String getHexString(byte[] bytes) + { + return getHexString(bytes, true); + } + + /** + * 根据字节数组获得值(十六进制数字) + * + * @param bytes + * @param upperCase + * @return + */ + public static String getHexString(byte[] bytes, boolean upperCase) + { + String ret = ""; + for (int i = 0; i < bytes.length; i++) + { + ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1); + } + return upperCase ? ret.toUpperCase() : ret; + } + + /** + * 打印十六进制字符串 + * + * @param bytes + */ + public static void printHexString(byte[] bytes) + { + for (int i = 0; i < bytes.length; i++) + { + String hex = Integer.toHexString(bytes[i] & 0xFF); + if (hex.length() == 1) + { + hex = '0' + hex; + } + System.out.print("0x" + hex.toUpperCase() + ","); + } + System.out.println(""); + } + + /** + * Convert hex string to byte[] + * + * @param hexString + * the hex string + * @return byte[] + */ + public static byte[] hexStringToBytes(String hexString) + { + if (hexString == null || hexString.equals("")) + { + return null; + } + + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) + { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + /** + * Convert char to byte + * + * @param c + * char + * @return byte + */ + public static byte charToByte(char c) + { + return (byte) "0123456789ABCDEF".indexOf(c); + } + + /** + * 用于建立十六进制字符的输出的小写字符数组 + */ + private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 用于建立十六进制字符的输出的大写字符数组 + */ + private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @return 十六进制char[] + */ + public static char[] encodeHex(byte[] data) { + return encodeHex(data, true); + } + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 + * @return 十六进制char[] + */ + public static char[] encodeHex(byte[] data, boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @param toDigits 用于控制输出的char[] + * @return 十六进制char[] + */ + protected static char[] encodeHex(byte[] data, char[] toDigits) { + int l = data.length; + char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @return 十六进制String + */ + public static String encodeHexString(byte[] data) { + return encodeHexString(data, true); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 + * @return 十六进制String + */ + public static String encodeHexString(byte[] data, boolean toLowerCase) { + return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @param toDigits 用于控制输出的char[] + * @return 十六进制String + */ + protected static String encodeHexString(byte[] data, char[] toDigits) { + return new String(encodeHex(data, toDigits)); + } + + /** + * 将十六进制字符数组转换为字节数组 + * + * @param data 十六进制char[] + * @return byte[] + * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 + */ + public static byte[] decodeHex(char[] data) { + int len = data.length; + + if ((len & 0x01) != 0) { + throw new RuntimeException("Odd number of characters."); + } + + byte[] out = new byte[len >> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + /** + * 将十六进制字符转换成一个整数 + * + * @param ch 十六进制char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 + */ + protected static int toDigit(char ch, int index) { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new RuntimeException("Illegal hexadecimal character " + ch + + " at index " + index); + } + return digit; + } + + /** + * 数字字符串转ASCII码字符串 + * + * @param String + * 字符串 + * @return ASCII字符串 + */ + public static String StringToAsciiString(String content) { + String result = ""; + int max = content.length(); + for (int i = 0; i < max; i++) { + char c = content.charAt(i); + String b = Integer.toHexString(c); + result = result + b; + } + return result; + } + + /** + * 十六进制转字符串 + * + * @param hexString + * 十六进制字符串 + * @param encodeType + * 编码类型4:Unicode,2:普通编码 + * @return 字符串 + */ + public static String hexStringToString(String hexString, int encodeType) { + String result = ""; + int max = hexString.length() / encodeType; + for (int i = 0; i < max; i++) { + char c = (char) hexStringToAlgorism(hexString + .substring(i * encodeType, (i + 1) * encodeType)); + result += c; + } + return result; + } + + /** + * 十六进制字符串装十进制 + * + * @param hex + * 十六进制字符串 + * @return 十进制数值 + */ + public static int hexStringToAlgorism(String hex) { + hex = hex.toUpperCase(); + int max = hex.length(); + int result = 0; + for (int i = max; i > 0; i--) { + char c = hex.charAt(i - 1); + int algorism = 0; + if (c >= '0' && c <= '9') { + algorism = c - '0'; + } else { + algorism = c - 55; + } + result += Math.pow(16, max - i) * algorism; + } + return result; + } + + /** + * 十六转二进制 + * + * @param hex + * 十六进制字符串 + * @return 二进制字符串 + */ + public static String hexStringToBinary(String hex) { + hex = hex.toUpperCase(); + String result = ""; + int max = hex.length(); + for (int i = 0; i < max; i++) { + char c = hex.charAt(i); + switch (c) { + case '0': + result += "0000"; + break; + case '1': + result += "0001"; + break; + case '2': + result += "0010"; + break; + case '3': + result += "0011"; + break; + case '4': + result += "0100"; + break; + case '5': + result += "0101"; + break; + case '6': + result += "0110"; + break; + case '7': + result += "0111"; + break; + case '8': + result += "1000"; + break; + case '9': + result += "1001"; + break; + case 'A': + result += "1010"; + break; + case 'B': + result += "1011"; + break; + case 'C': + result += "1100"; + break; + case 'D': + result += "1101"; + break; + case 'E': + result += "1110"; + break; + case 'F': + result += "1111"; + break; + } + } + return result; + } + + /** + * ASCII码字符串转数字字符串 + * + * @param String + * ASCII字符串 + * @return 字符串 + */ + public static String AsciiStringToString(String content) { + String result = ""; + int length = content.length() / 2; + for (int i = 0; i < length; i++) { + String c = content.substring(i * 2, i * 2 + 2); + int a = hexStringToAlgorism(c); + char b = (char) a; + String d = String.valueOf(b); + result += d; + } + return result; + } + + /** + * 将十进制转换为指定长度的十六进制字符串 + * + * @param algorism + * int 十进制数字 + * @param maxLength + * int 转换后的十六进制字符串长度 + * @return String 转换后的十六进制字符串 + */ + public static String algorismToHexString(int algorism, int maxLength) { + String result = ""; + result = Integer.toHexString(algorism); + + if (result.length() % 2 == 1) { + result = "0" + result; + } + return patchHexString(result.toUpperCase(), maxLength); + } + + /** + * 字节数组转为普通字符串(ASCII对应的字符) + * + * @param bytearray + * byte[] + * @return String + */ + public static String byteToString(byte[] bytearray) { + String result = ""; + char temp; + + int length = bytearray.length; + for (int i = 0; i < length; i++) { + temp = (char) bytearray[i]; + result += temp; + } + return result; + } + + /** + * 二进制字符串转十进制 + * + * @param binary + * 二进制字符串 + * @return 十进制数值 + */ + public static int binaryToAlgorism(String binary) { + int max = binary.length(); + int result = 0; + for (int i = max; i > 0; i--) { + char c = binary.charAt(i - 1); + int algorism = c - '0'; + result += Math.pow(2, max - i) * algorism; + } + return result; + } + + /** + * 十进制转换为十六进制字符串 + * + * @param algorism + * int 十进制的数字 + * @return String 对应的十六进制字符串 + */ + public static String algorismToHEXString(int algorism) { + String result = ""; + result = Integer.toHexString(algorism); + + if (result.length() % 2 == 1) { + result = "0" + result; + + } + result = result.toUpperCase(); + + return result; + } + + /** + * HEX字符串前补0,主要用于长度位数不足。 + * + * @param str + * String 需要补充长度的十六进制字符串 + * @param maxLength + * int 补充后十六进制字符串的长度 + * @return 补充结果 + */ + static public String patchHexString(String str, int maxLength) { + String temp = ""; + for (int i = 0; i < maxLength - str.length(); i++) { + temp = "0" + temp; + } + str = (temp + str).substring(0, maxLength); + return str; + } + + /** + * 将一个字符串转换为int + * + * @param s + * String 要转换的字符串 + * @param defaultInt + * int 如果出现异常,默认返回的数字 + * @param radix + * int 要转换的字符串是什么进制的,如16 8 10. + * @return int 转换后的数字 + */ + public static int parseToInt(String s, int defaultInt, int radix) { + int i = 0; + try { + i = Integer.parseInt(s, radix); + } catch (NumberFormatException ex) { + i = defaultInt; + } + return i; + } + + /** + * 将一个十进制形式的数字字符串转换为int + * + * @param s + * String 要转换的字符串 + * @param defaultInt + * int 如果出现异常,默认返回的数字 + * @return int 转换后的数字 + */ + public static int parseToInt(String s, int defaultInt) { + int i = 0; + try { + i = Integer.parseInt(s); + } catch (NumberFormatException ex) { + i = defaultInt; + } + return i; + } + + /** + * 十六进制串转化为byte数组 + * + * @return the array of byte + */ + public static byte[] hexToByte(String hex) + throws IllegalArgumentException { + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException(); + } + char[] arr = hex.toCharArray(); + byte[] b = new byte[hex.length() / 2]; + for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) { + String swap = "" + arr[i++] + arr[i]; + int byteint = Integer.parseInt(swap, 16) & 0xFF; + b[j] = new Integer(byteint).byteValue(); + } + return b; + } + + /** + * 字节数组转换为十六进制字符串 + * + * @param b + * byte[] 需要转换的字节数组 + * @return String 十六进制字符串 + */ + public static String byteToHex(byte b[]) { + if (b == null) { + throw new IllegalArgumentException( + "Argument b ( byte array ) is null! "); + } + String hs = ""; + String stmp = ""; + for (int n = 0; n < b.length; n++) { + stmp = Integer.toHexString(b[n] & 0xff); + if (stmp.length() == 1) { + hs = hs + "0" + stmp; + } else { + hs = hs + stmp; + } + } + return hs.toUpperCase(); + } + + public static byte[] subByte(byte[] input, int startIndex, int length) { + byte[] bt = new byte[length]; + for (int i = 0; i < length; i++) { + bt[i] = input[i + startIndex]; + } + return bt; + } +} \ No newline at end of file diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java new file mode 100644 index 0000000..3982fdd --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java @@ -0,0 +1,166 @@ +package com.egzosn.pay.common.util.sign; + +import com.egzosn.pay.common.util.sign.SM3.SM3Digest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.PublicKey; +import java.security.Signature; + +public class SecureUtil { + //日志 + protected static final Log log = LogFactory.getLog(SecureUtil.class); + /** + * 算法常量: 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"; + + /** + * 获取摘要 + * + * @param data 待计算的数据 + * @param algorithm 算法名 + * @return 计算结果 + */ + private static byte[] digestByData (byte[] data,String algorithm) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance(algorithm); + md.reset(); + md.update(data); + return md.digest(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * sha1计算后进行16进制转换 + * + * @param data 待计算的数据 + * @param encoding 编码 + * @return 计算结果 + */ + public static byte[] sha1X16 (String data, String encoding) { + try { + byte[] bytes = digestByData(data.getBytes(encoding),ALGORITHM_SHA1); + 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])); + } + } + return sha1StrBuff.toString().getBytes(encoding); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return null; + } + } + + + + /** + * sha256计算后进行16进制转换 + * + * @param data + * 待计算的数据 + * @param encoding + * 编码 + * @return 计算结果 + */ + public static String sha256X16Str(String data, String encoding) { + byte[] bytes =null; + try { + bytes = digestByData(data.getBytes(encoding),ALGORITHM_SHA1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + 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(); + } + + /** + * 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; + } + + /** + * sm3计算后进行16进制转换 + * + * @param data + * 待计算的数据 + * @param encoding + * 编码 + * @return 计算结果 + */ + public static String sm3X16Str(String data, String encoding) { + byte[] bytes = new byte[new SM3Digest().getDigestSize()]; + try { + bytes = SecureUtil.sm3(data.getBytes(encoding)); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + 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(); + } + + 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); + } + + 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); + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java index 32a50c0..430dd01 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java @@ -47,6 +47,29 @@ public class RSA{ return null; } + /** + * RSA签名 + * @param content 待签名数据 + * @param privateKey 私钥 + * @return 签名值 + */ + public static String sign(byte[] content,PrivateKey privateKey) { + try { + + java.security.Signature signature = java.security.Signature.getInstance(privateKey.getAlgorithm()); + + signature.initSign(privateKey); + signature.update(content); + + byte[] signed = signature.sign(); + + return Base64.encode(signed); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } /** * RSA签名 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/SHA256.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/SHA256.java new file mode 100644 index 0000000..a2ae7f1 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/SHA256.java @@ -0,0 +1,89 @@ +package com.egzosn.pay.common.util.sign.encrypt;/** + * Description: + * author: Fuzx + * date: 2017/11/27 0027 + */ + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; + +/** + * @author Actinia + * @email hayesfu@qq.com + * @create 2017 2017/11/27 0027 + */ +public class SHA256 { + //日志 + protected static final Log log = LogFactory.getLog(SHA256.class); + + /** + * 算法常量: SHA256 + */ + private static final String ALGORITHM_SHA256 = "SHA-256"; + /** + * 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) { + log.error(e.getMessage(), 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) { + log.error("SHA256计算失败", 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) { + log.error("SHA256计算失败", e); + return null; + } + } +} diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 43905b5..5449e0d 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -79,6 +79,11 @@ jackson-databind 2.8.4 + + com.egzosn + pay-java-union + 2.0.5-SNAPSHOT + diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java index 8bcd0aa..bc9e1f8 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java @@ -4,15 +4,18 @@ package com.egzosn.pay.demo.controller; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.Callback; +import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.demo.entity.ApyAccount; +import com.egzosn.pay.demo.entity.PayType; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.ApyAccountService; import com.egzosn.pay.demo.service.PayResponse; -import com.egzosn.pay.demo.entity.PayType; -import com.egzosn.pay.common.api.PayConfigStorage; +import com.egzosn.pay.union.api.UnionPayService; +import com.egzosn.pay.union.enums.UnionTransactionType; +import com.egzosn.pay.union.request.UnionQueryOrder; import com.egzosn.pay.wx.bean.WxTransactionType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -300,6 +303,19 @@ public class PayController { PayResponse payResponse = service.getPayResponse(order.getPayId()); return payResponse.getService().query(order.getTradeNo(), order.getOutTradeNo()); } + /** + * 查询 + * + * @param order 订单的请求体 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("unionRefundOrConsumeUndo") + public Map unionQuery(UnionQueryOrder order,String transactionType) { + PayResponse payResponse = service.getPayResponse(order.getPayId()); + UnionPayService service = (UnionPayService)payResponse.getService(); + + return service.unionRefundOrConsumeUndo(order,UnionTransactionType.valueOf(transactionType)); + } /** * 交易关闭接口 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java index 1d59f06..e535a56 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java @@ -81,6 +81,22 @@ public class ApyAccountRepository { apyAccount3.setMsgType(MsgType.xml); apyAccounts.put(apyAccount3.getPayId(), apyAccount3); + ApyAccount apyAccount4 = new ApyAccount(); + apyAccount4.setPayId(4); + apyAccount4.setPartner("777290058110097"); +// apyAccount4.setAppid("777290058110097"); + apyAccount4.setPublicKey(""); + apyAccount4.setPrivateKey("000000"); + apyAccount4.setNotifyUrl("http://sailinmu.iok.la:19088/backRcvResponse"); + // 无需同步回调可不填 app填这个就可以 + apyAccount4.setReturnUrl("http://sailinmu.iok.la:19088/backRcvResponse"); + apyAccount4.setSeller(""); + apyAccount4.setInputCharset("UTF-8"); + apyAccount4.setSignType(SignUtils.RSA.name()); + apyAccount4.setPayType(PayType.unionPay); + apyAccount4.setMsgType(MsgType.json); + apyAccounts.put(apyAccount4.getPayId(), apyAccount3); + } //_____________________________________________________________ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java index 6b456b6..5a5175d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java @@ -6,12 +6,12 @@ import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.BasePayType; import com.egzosn.pay.common.bean.TransactionType; -import com.egzosn.pay.demo.service.handler.FuiouPayMessageHandler; import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; import com.egzosn.pay.fuiou.api.FuiouPayService; import com.egzosn.pay.fuiou.bean.FuiouTransactionType; -import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; -import com.egzosn.pay.fuiou.api.FuiouPayService; +import com.egzosn.pay.union.api.UnionPayConfigStorage; +import com.egzosn.pay.union.api.UnionPayService; +import com.egzosn.pay.union.enums.UnionTransactionType; import com.egzosn.pay.wx.api.WxPayConfigStorage; import com.egzosn.pay.wx.api.WxPayService; import com.egzosn.pay.wx.bean.WxTransactionType; @@ -144,6 +144,29 @@ public enum PayType implements BasePayType { } + },unionPay{ + + @Override + public PayService getPayService(ApyAccount apyAccount) { + UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); + unionPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); + unionPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); + unionPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); + unionPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); + unionPayConfigStorage.setSignType(apyAccount.getSignType()); + unionPayConfigStorage.setPayType(apyAccount.getPayType().toString()); + unionPayConfigStorage.setMsgType(apyAccount.getMsgType()); + unionPayConfigStorage.setInputCharset(apyAccount.getInputCharset()); + unionPayConfigStorage.setTest(apyAccount.isTest()); + return new UnionPayService(unionPayConfigStorage); + } + + @Override + public TransactionType getTransactionType(String transactionType) { + return UnionTransactionType.valueOf(transactionType); + } + + }; public abstract PayService getPayService(ApyAccount apyAccount); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java index e556fa1..a47c66b 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java @@ -1,21 +1,15 @@ package com.egzosn.pay.demo.service; -import com.egzosn.pay.common.api.PayMessageRouter; -import com.egzosn.pay.common.http.HttpConfigStorage; -import com.egzosn.pay.demo.service.handler.FuiouPayMessageHandler; -import com.egzosn.pay.demo.service.handler.YouDianPayMessageHandler; -import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; -import com.egzosn.pay.demo.entity.ApyAccount; -import com.egzosn.pay.demo.entity.PayType; -import com.egzosn.pay.demo.service.handler.AliPayMessageHandler; -import com.egzosn.pay.demo.service.handler.FuiouPayMessageHandler; -import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; -import com.egzosn.pay.demo.service.handler.YouDianPayMessageHandler; -import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayMessageRouter; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.MsgType; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.demo.entity.ApyAccount; +import com.egzosn.pay.demo.entity.PayType; +import com.egzosn.pay.demo.service.handler.*; +import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; import com.egzosn.pay.demo.service.interceptor.YoudianPayMessageInterceptor; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; @@ -116,7 +110,12 @@ public class PayResponse { .payType(PayType.fuiou.name()) .handler(autowire(new FuiouPayMessageHandler(payId))) .end() - + .rule() + .async(false) + .msgType(MsgType.json.name()) + .payType(PayType.unionPay.name()) + .handler(autowire(new UnionPayMessageHandler(payId))) + .end() ; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java new file mode 100644 index 0000000..5782d45 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java @@ -0,0 +1,37 @@ +package com.egzosn.pay.demo.service.handler; + +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; + +import java.util.Map; + +/** + * @author Actinia + * @email hayesfu@qq.com + *
+create 2017 2017/11/4 0004
+ * 
+ */ +public class UnionPayMessageHandler extends BasePayMessageHandler { + + + + + public UnionPayMessageHandler (Integer payId) { + super(payId); + } + + @Override + public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + //交易状态 + if ("0000".equals(payMessage.getPayMessage().get("order_pay_code"))){ + /////这里进行成功的处理 + + return PayOutMessage.JSON().content("success","成功").build(); + } + + return PayOutMessage.JSON().content("fail", "失败").build(); + } +} diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java index db69a38..b10e78c 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java @@ -2,7 +2,8 @@ package com.egzosn.pay.fuiou.api; import com.egzosn.pay.common.api.BasePayConfigStorage; /** - * @author Fuzx + * @author Actinia + * @email hayesfu@qq.com *
  * create 2017 2017/1/16 0016
  * 
diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index a52f920..dcde16f 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -20,8 +20,9 @@ import java.text.SimpleDateFormat; import java.util.*; /** - * @author Fuzx - *
+ * @author Actinia
+ * @email hayesfu@qq.com
+ *  
  * create 2017 2017/1/16 0016
  * 
*/ diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouCurType.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouCurType.java index 9fea7a1..264c1ed 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouCurType.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouCurType.java @@ -4,8 +4,11 @@ import com.egzosn.pay.common.bean.CurType; /** * 货币类型 - * @author Fuzx - * create 2017 2017/1/24 0024 + * @author Actinia + * @email hayesfu@qq.com + *
+ * create 2017 2017/1/16 0016
+ * 
*/ public enum FuiouCurType implements CurType { diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouTransactionType.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouTransactionType.java index 273b383..9280ecf 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouTransactionType.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouTransactionType.java @@ -4,8 +4,11 @@ import com.egzosn.pay.common.bean.TransactionType; /** * 支付类型 - * @author Fuzx - * create 2017 2017/1/24 0024 + * @author Actinia + * @email hayesfu@qq.com + *
+ * create 2017 2017/1/16 0016
+ * 
*/ public enum FuiouTransactionType implements TransactionType { B2B("B2B"), diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 5bd46e2..e1ce040 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -19,6 +19,9 @@ pay-java-common - + + org.bouncycastle + bcprov-debug-jdk15on + \ No newline at end of file diff --git a/pay-java-union/resource/acp_sdk.properties b/pay-java-union/resource/acp_sdk.properties new file mode 100644 index 0000000..a998100 --- /dev/null +++ b/pay-java-union/resource/acp_sdk.properties @@ -0,0 +1,69 @@ +##############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ԻfalsetruefalseֵĬ϶true +acpsdk.ifValidateCNName=false + +# Ƿ֤https֤飬ԻfalseȳtruefalsetrueֵĬ϶false +acpsdk.ifValidateRemoteCert=false + +#ַ̨֪ͨд̨֪ͨĵַܷ +#acpsdk.backUrl=http://222.222.222.222:8080/ACPSample_B2C/backRcvResponse +acpsdk.backUrl=http://sailinmu.iok.la:19088/backRcvResponse + +#ǰַ̨֪ͨдǰ̨֪ͨĵַܷ +acpsdk.frontUrl=http://sailinmu.iok.la:19088/frontRcvResponse + +#########################Իǩ֤ ################################ +# ֤֤·ΪָɲԴ˿á +# ǩ֤·ʹþ·ʹþ·ʵ·ȡ֤ķ֤̻ÿеIJǩ֤飬cfcaصõ +# windows +acpsdk.signCert.path=D:/certs/acp_test_sign.pfx +# linuxע⣺linux¶ȡ֤Ҫ֤֤бӦöȨޣ·Ҳͬ˵ +#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/acp_test_sign.pfx + +# ǩ֤룬Ի̶000000޸Ϊcfcaصʽ֤룬ʽ֤λСڵ6λϴ̻վʧ +acpsdk.signCert.pwd=000000 +# ǩ̶֤ͣҪ޸ +acpsdk.signCert.type=PKCS12 + +##########################֤################################ +# Ϣ֤·(̻ſ̻ͨϢܵȨޣҪ accNopinphoneNocvn2expiredܣЩ͵ĻϢʹ) +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 + diff --git a/pay-java-union/resource/acp_test_enc.cer b/pay-java-union/resource/acp_test_enc.cer new file mode 100644 index 0000000..74f23d4 --- /dev/null +++ b/pay-java-union/resource/acp_test_enc.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIFEAJkkicwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC +Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTUxMjE1MDkxMTM1WhcN +MTcxMjE1MDkxMTM1WjCBgTELMAkGA1UEBhMCY24xFzAVBgNVBAoTDkNGQ0EgVEVT +VCBPQ0ExMRIwEAYDVQQLEwlDRkNBIFRFU1QxFDASBgNVBAsTC0VudGVycHJpc2Vz +MS8wLQYDVQQDFCYwNDFAWjIwMTQtMTEtMTFAMDAwNDAwMDA6U0lHTkAwMDAwMDAw +NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHnoPx0JZKZmFjIURxN +AbLlWAw2jiFFWBnDF2MIGkya2r0fGiR0knq8zkKUnoIyC+tzEiOavniQaSu0ucuv +/V4ugz66PSRxw1gaPcR2dDVdgojF00TcewxlJEA65fK3eKhUYfC3NbRaVQOMMdwv +7nNEvzxvdExE47ceMya7FmsUPyLFu9X++chFQiYfr8nH+wdDeYo8w8vCX+Jd2vRu +qDOah29CQfkAmXsx3D68zg0q4AjlLI1t5gLKiU5YoG6yWrigPyreEHh716rV8HkT +jGWx3cxF/HsLZ/E4SgIr5yIZA6qw8RFqaSXuyw3iDrNf6aSJGO0GKlvxnvD20oGR +JokCAwEAAaOB6TCB5jAfBgNVHSMEGDAWgBTPcJ1h6518Lrj3ywJA9wmd/jN0gDBI +BgNVHSAEQTA/MD0GCGCBHIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cu +Y2ZjYS5jb20uY24vdXMvdXMtMTQuaHRtMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6 +Ly91Y3JsLmNmY2EuY29tLmNuL1JTQS9jcmw0NTE3LmNybDALBgNVHQ8EBAMCA+gw +HQYDVR0OBBYEFGjriHHUE1MnYX7H6GrFi+8R2zmUMBMGA1UdJQQMMAoGCCsGAQUF +BwMCMA0GCSqGSIb3DQEBBQUAA4IBAQAjsN0fyDqcxS9YKMpY3CIdlarCjvnus+wS +ExjNnPv7n2urqhz2Jf3yJuhxVVPzdgKT51C2UiR+/i1OJPWFx0IUos/v8js/TM5j +mTdPkBsRSxSDieHHiuE1nPUwGXUEO7mlOVkkzmLI75bJ86foxNflbQCF0+VvpMe7 +KwQoNOR8DxIBxHdlsjSxE2RKM/ftXLhptrK4GK3K4FAcSiqBMEn5PF/5V9mHp5N6 +3LdkMYqBj4pRcy8vrclucq99b2glmMLw7CI6Kxu22WVoRnZESjcgXiMVLLe+qy55 +0pWcZ2BChS7Ln19tj49LnS3vFp6xf4qNSqxEBaQuNLEx0ObjI6pz +-----END CERTIFICATE----- \ No newline at end of file diff --git a/pay-java-union/resource/acp_test_middle.cer b/pay-java-union/resource/acp_test_middle.cer new file mode 100644 index 0000000..3275b90 --- /dev/null +++ b/pay-java-union/resource/acp_test_middle.cer @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzjCCAragAwIBAgIKGNDz/H99Hd/CxjANBgkqhkiG9w0BAQUFADBZMQswCQYD +VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODMwMDMx +NDMzWhcNMzEwNTExMDMxNDMzWjBYMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp +bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRcwFQYDVQQDEw5D +RkNBIFRFU1QgT0NBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiL +J/BrdvHSbXNfLIMTwUg9tDtVjMRGXOl6aZnu9IpxjI5SMUJ4hVwgJnmbTokxs6GF +IXKsCLSm5H1jHLI22ysc/ltByEybLWj5jjJuC9+Uknbl3/Ls1RBG6MogUCqZckuo +hKrf5DmlV3C/jVLxGn3pUeanvmqVUi4TKpXxgm5QqKSPF8VtQY4qCpNcQwwZqbMr +D+IfJtfpGAeVrP+Kg6i1t65seeEnVSaLhqpRUDU0PTblOuUv3OhiKJWA3cYWxUrg +7U7SIHNJLSEUWmjy4mKty+g7Cnjzt29F9qXFb6oB2mR8yt4GHCilw1Rc5RBXY63H +eTuOwdtGE3M2p7Q++OECAwEAAaOBmDCBlTAfBgNVHSMEGDAWgBR03sWNCn0QGqpp +g1tNIc6Gm8xxODAMBgNVHRMEBTADAQH/MDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6 +Ly8yMTAuNzQuNDIuMy90ZXN0cmNhL1JTQS9jcmwxLmNybDALBgNVHQ8EBAMCAQYw +HQYDVR0OBBYEFM9wnWHrnXwuuPfLAkD3CZ3+M3SAMA0GCSqGSIb3DQEBBQUAA4IB +AQC0JOazrbkk0XMxMMeBCc3lgBId1RjQLgWUZ7zaUISpPstGIrE5A9aB6Ppq0Sxl +pt2gkFhPEKUqgOFN1CzCDEbP3n4H0chqK1DOMrgTCD8ID5UW+ECTYNe35rZ+1JiF +lOPEhFL3pv6XSkiKTfDnjum8+wFwUBGlfoWK1Hcx0P2Hk1jcZZKwGTx1IAkesF83 +pufhxHE2Ur7W4d4tfp+eC7XXcA91pdd+VUrAfkj9eKHcDEYZz66HvHzmt6rtJVBa +pwrtCi9pW3rcm8c/1jSnEETZIaokai0fD7260h/LkD/GrNCibSWxFj1CqyP9Y5Yv +cj6aA5LnUcJYeNkrQ3V4XvVc +-----END CERTIFICATE----- diff --git a/pay-java-union/resource/acp_test_root.cer b/pay-java-union/resource/acp_test_root.cer new file mode 100644 index 0000000..779647c --- /dev/null +++ b/pay-java-union/resource/acp_test_root.cer @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIKUhN+zB19hbc65jANBgkqhkiG9w0BAQUFADBZMQswCQYD +VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODI5MDUw +MTI5WhcNMzIwODI5MDUwMTI5WjBZMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp +bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRgwFgYDVQQDEw9D +RkNBIFRFU1QgQ1MgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +rMJGruH6rOBPFxUI7T1ybydSRRtOM1xvkVjQNX0qmYir8feE6Tb0ctgtKR7a20DI +YCj9kZ5ANBQqjRcj3Soq9XH3cirqhYHJ723OKyTpS0RPQ0N6vtVt3P5JQ+ztjWHd +qIbbTOQ6O024TGTiqi6uHgMuz9/OVur81X3a5YVkK7jFeZ9o8cTcvQxD853/1sgZ +QcmR9aUSw0RXH4XFLTrn7n4QSfWKiNotlD8Ag5gS1pH9ONUb6nGkMn3gh1xfJqjm +ONMSknPXTGiNpXtqvYi8oIvByVCbUDO59IwPP1r1SYyE3P8Nr7DdQRu0KQSdXLoG +iugSR3fn+toObVAQmplDAgMBAAGjXTBbMB8GA1UdIwQYMBaAFHTexY0KfRAaqmmD +W00hzoabzHE4MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBR0 +3sWNCn0QGqppg1tNIc6Gm8xxODANBgkqhkiG9w0BAQUFAAOCAQEAM0eTkM35D4hj +RlGC63wY0h++wVPUvOrObqAVBbzEEQ7ScBienmeY8Q6lWMUTXM9ALibZklpJPcJv +3ntht7LL6ztd4wdX7E9RzZCQnRvbL9A/BU3NxWdeSpCg/OyPod5oCKP+6Uc7kApi +F9OtYNWnt3l2Zp/NiedzEQD8H4qEWQLAq+0dFo5BkfVhb/jPcktndpfPOuH1IMhP +tVcvo6jpFHw4U/nP2Jv59osIE97KJz/SPt2JAYnZOlIDqWwp9/Afvt0/MDr8y0PK +Q9c6eqIzBx7a9LpUTUl5u1jS+xSDZ/KF2lXnjwaFp7jICLWEMlBstCoogi7KwH9A +LpJP7/dj9g== +-----END CERTIFICATE----- diff --git a/pay-java-union/resource/acp_test_sign.pfx b/pay-java-union/resource/acp_test_sign.pfx new file mode 100644 index 0000000..e69de29 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/CertUtil.java b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/CertUtil.java new file mode 100644 index 0000000..f90c2e4 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/CertUtil.java @@ -0,0 +1,822 @@ +/** + * + * 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.egzosn.pay.union.SDK; + +import com.egzosn.pay.common.util.str.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +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 + * + */ +public class CertUtil { + //日志 + protected static final Log log = LogFactory.getLog(CertUtil.class); + + public static final String UNIONPAY_CNNAME = "中国银联股份有限公司"; + + /** 证书容器,存储对商户请求报文签名私钥证书. */ + 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 certMap = new HashMap(); + /** 商户私钥存储Map */ + private final static Map keyStoreMap = new ConcurrentHashMap(); + + + + /** + * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)
+ * 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回
+ * @param reqData 请求报文map
+ * @param encoding 上送请求报文域encoding字段的值
+ * @return 签名后的map对象
+ */ + public static Map sign(Map reqData,String encoding) { + reqData = filterBlank(reqData); + SDKUtils.signParams(reqData, encoding); + return reqData; + } + /** + * 过滤请求报文中的空字符串或者空字符串 + * @param contentData + * @return + */ + public static Map filterBlank(Map contentData){ + Map submitFromData = new HashMap(); + Set keyset = contentData.keySet(); + for(String key:keyset){ + String value = contentData.get(key); + if (StringUtils.isNotBlank(value)) { + // 对value值进行去除前后空处理 + submitFromData.put(key, value.trim()); + } + } + return submitFromData; + } + + + + + + static { + init(); + } + + /** + * 初始化所有证书. + */ + private static void init() { + try { + addProvider();//向系统添加BC provider + initSignCert();//初始化签名私钥证书 + initMiddleCert();//初始化验签证书的中级证书 + initRootCert();//初始化验签证书的根证书 + initEncryptCert();//初始化加密公钥 + initTrackKey();//构建磁道加密公钥 + initValidateCertFromDir();//初始化所有的验签证书 + } catch (Exception e) { + e.printStackTrace(); + log.error("init失败。(如果是用对称密钥签名的可无视此异常。)", e); + } + } + + /** + * 添加签名,验签,加密算法提供者 + */ + private static void addProvider(){ + if (Security.getProvider("BC") == null) { + log.info("add BC provider"); + Security.addProvider(new BouncyCastleProvider()); + } else { + Security.removeProvider("BC"); //解决eclipse调试时tomcat自动重新加载时,BC存在不明原因异常的问题。 + Security.addProvider(new 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) { + e.printStackTrace(); + log.error("InitSignCert Error", e); + } + } + + /** + * 用配置文件acp_sdk.properties配置路径 加载敏感信息加密证书 + */ + private static void initMiddleCert() { +// log.info("加载中级证书==>"+SDKConfig.getConfig().getMiddleCertPath()); + if (!StringUtils.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 (!StringUtils.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 (!StringUtils.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 (!StringUtils.isEmpty(SDKConfig.getConfig().getEncryptTrackKeyModulus()) + && !StringUtils.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 (StringUtils.isEmpty(dir)) { + log.error("WARN: acpsdk.validateCert.dir is empty"); + return; + } + CertificateFactory cf = null; + FileInputStream in = null; + try { + cf = CertificateFactory.getInstance("X.509", "BC"); + }catch (NoSuchProviderException e) { + log.error("LoadVerifyCert Error: No BC Provider", e); + return ; + } catch (CertificateException e) { + log.error("LoadVerifyCert Error", e); + return ; + } + File fileDir = new File(dir); + File[] files = fileDir.listFiles(new CerFilter()); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + try { + in = new FileInputStream(file.getAbsolutePath()); + validateCert = (X509Certificate) cf.generateCertificate(in); + if(validateCert == null) { + log.error("Load verify cert error, " + file.getAbsolutePath() + " has error cert content."); + continue; + } + certMap.put(validateCert.getSerialNumber().toString(), + validateCert); + // 打印证书加载信息,供测试阶段调试 + log.info("[" + file.getAbsolutePath() + "][CertId=" + + validateCert.getSerialNumber().toString() + "]"); + } catch (CertificateException e) { + log.error("LoadVerifyCert Error", e); + e.printStackTrace(); + }catch (FileNotFoundException e) { + log.error("LoadVerifyCert Error File Not Found", e); + e.printStackTrace(); + }finally { + if (null != in) { + try { + in.close(); + } catch (IOException e) { + log.error(e.toString()); + e.printStackTrace(); + } + } + } + } + log.info("LoadVerifyCert Finish"); + } + + /** + * 用给定的路径和密码 加载签名证书,并保存到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); + e.printStackTrace(); + } + } + + /** + * 通过证书路径初始化为公钥证书 + * @param path + * @return + */ + private static X509Certificate initCert(String path) { + X509Certificate encryptCertTemp = null; + CertificateFactory cf = null; + FileInputStream in = null; + try { + cf = CertificateFactory.getInstance("X.509", "BC"); + in = new FileInputStream(path); + encryptCertTemp = (X509Certificate) cf.generateCertificate(in); + // 打印证书加载信息,供测试阶段调试 + log.info("[" + path + "][CertId=" + + encryptCertTemp.getSerialNumber().toString() + "]"); + } catch (CertificateException e) { + log.error("InitCert Error", e); + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + log.error("InitCert Error File Not Found", e); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + log.error("LoadVerifyCert Error No BC Provider", e); + } finally { + if (null != in) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.toString()); + } + } + } + return encryptCertTemp; + } + + /** + * 通过keyStore 获取私钥签名证书PrivateKey对象 + * + * @return + */ + public static PrivateKey getSignCertPrivateKey() { + try { + Enumeration 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) { + e.printStackTrace(); + log.error("getSignCertPrivateKey Error", e); + return null; + } catch (UnrecoverableKeyException e) { + e.printStackTrace(); + log.error("getSignCertPrivateKey Error", e); + return null; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + 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 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) { + e.printStackTrace(); + log.error("getSignCertPrivateKeyByStoreMap Error", e); + return null; + } catch (UnrecoverableKeyException e) { + e.printStackTrace(); + log.error("getSignCertPrivateKeyByStoreMap Error", e); + return null; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error("getSignCertPrivateKeyByStoreMap Error", e); + return null; + } + } + + /** + * 获取敏感信息加密证书PublicKey + * + * @return + */ + public static PublicKey getEncryptCertPublicKey() { + if (null == encryptCert) { + String path = SDKConfig.getConfig().getEncryptCertPath(); + if (!StringUtils.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 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 (!StringUtils.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); + FileInputStream fis = null; + try { + KeyStore ks = KeyStore.getInstance(type, "BC"); + log.info("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=["+ keypwd + "],type=["+type+"]"); + fis = new FileInputStream(pfxkeyfile); + 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); + e.printStackTrace(); + 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 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 (!StringUtils.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 (!StringUtils.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 trustAnchors = new HashSet(); + trustAnchors.add(new TrustAnchor(rootCert, null)); + PKIXBuilderParameters pkixParams = new PKIXBuilderParameters( + trustAnchors, selector); + + Set intermediateCerts = new HashSet(); + 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; + } + + /** + * 检查证书链 + * + * @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(!UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))) { + log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert)); + return false; + } + } else { + // 验证公钥是否属于银联 + if(!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; + } + } + @Override + public boolean accept(File dir, String name) { + return isCer(name); + } + } + +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConfig.java b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConfig.java new file mode 100644 index 0000000..f94fa41 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConfig.java @@ -0,0 +1,690 @@ +package com.egzosn.pay.union.SDK; + +import com.egzosn.pay.common.util.str.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.*; +import java.util.Properties; + +/** + * + * @ClassName SDKConfig + * @Description acpsdk配置文件acp_sdk.properties配置信息类 + * @date 2016-7-22 下午4:04:55 + * @lastUpdate Actinia Date:2017/11/7 0007 21:55 + * + */ +public class SDKConfig { + protected final Log log = LogFactory.getLog(SDKConfig.class); + + public static final String FILE_NAME = "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; + } + + /** + * 从classpath路径下加载配置参数 + */ + public void loadPropertiesFromSrc() { + InputStream in = null; + try { + log.info("从classpath: " +SDKConfig.class.getClassLoader().getResource("").getPath()+" 获取属性文件"+FILE_NAME); + in = SDKConfig.class.getClassLoader().getResourceAsStream(FILE_NAME); + if (null != in) { + properties = new Properties(); + try { + properties.load(in); + } catch (IOException e) { + throw e; + } + } else { + log.info(FILE_NAME + "属性文件未能在classpath指定的目录下 "+SDKConfig.class.getClassLoader().getResource("").getPath()+" 找到!"); + return; + } + loadProperties(properties); + } catch (IOException e) { + log.info(e.getMessage(), e); + } finally { + if (null != in) { + try { + in.close(); + } catch (IOException e) { + log.info(e.getMessage(), e); + } + } + } + } + + /** + * 根据传入的 {@link #load(java.util.Properties)}对象设置配置参数 + * + * @param pro + */ + public void loadProperties(Properties pro) { + log.info("开始从属性文件中加载配置项"); + String value = null; + + value = pro.getProperty(SDK_SIGNCERT_PATH); + if (!StringUtils.isEmpty(value)) { + this.signCertPath = value.trim(); + } + value = pro.getProperty(SDK_SIGNCERT_PWD); + if (!StringUtils.isEmpty(value)) { + this.signCertPwd = value.trim(); + } + value = pro.getProperty(SDK_SIGNCERT_TYPE); + if (!StringUtils.isEmpty(value)) { + this.signCertType = value.trim(); + } + value = pro.getProperty(SDK_ENCRYPTCERT_PATH); + if (!StringUtils.isEmpty(value)) { + this.encryptCertPath = value.trim(); + } + value = pro.getProperty(SDK_VALIDATECERT_DIR); + if (!StringUtils.isEmpty(value)) { + this.validateCertDir = value.trim(); + } + value = pro.getProperty(SDK_FRONT_URL); + if (!StringUtils.isEmpty(value)) { + this.frontRequestUrl = value.trim(); + } + value = pro.getProperty(SDK_BACK_URL); + if (!StringUtils.isEmpty(value)) { + this.backRequestUrl = value.trim(); + } + value = pro.getProperty(SDK_BATQ_URL); + if (!StringUtils.isEmpty(value)) { + this.batchQueryUrl = value.trim(); + } + value = pro.getProperty(SDK_BATTRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.batchTransUrl = value.trim(); + } + value = pro.getProperty(SDK_FILETRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.fileTransUrl = value.trim(); + } + value = pro.getProperty(SDK_SIGNQ_URL); + if (!StringUtils.isEmpty(value)) { + this.singleQueryUrl = value.trim(); + } + value = pro.getProperty(SDK_CARD_URL); + if (!StringUtils.isEmpty(value)) { + this.cardRequestUrl = value.trim(); + } + value = pro.getProperty(SDK_APP_URL); + if (!StringUtils.isEmpty(value)) { + this.appRequestUrl = value.trim(); + } + value = pro.getProperty(SDK_ENCRYPTTRACKCERT_PATH); + if (!StringUtils.isEmpty(value)) { + this.encryptTrackCertPath = value.trim(); + } + + value = pro.getProperty(SDK_SECURITYKEY); + if (!StringUtils.isEmpty(value)) { + this.secureKey = value.trim(); + } + value = pro.getProperty(SDK_ROOTCERT_PATH); + if (!StringUtils.isEmpty(value)) { + this.rootCertPath = value.trim(); + } + value = pro.getProperty(SDK_MIDDLECERT_PATH); + if (!StringUtils.isEmpty(value)) { + this.middleCertPath = value.trim(); + } + + /**缴费部分**/ + value = pro.getProperty(JF_SDK_FRONT_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.jfFrontRequestUrl = value.trim(); + } + + value = pro.getProperty(JF_SDK_BACK_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.jfBackRequestUrl = value.trim(); + } + + value = pro.getProperty(JF_SDK_SINGLE_QUERY_URL); + if (!StringUtils.isEmpty(value)) { + this.jfSingleQueryUrl = value.trim(); + } + + value = pro.getProperty(JF_SDK_CARD_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.jfCardRequestUrl = value.trim(); + } + + value = pro.getProperty(JF_SDK_APP_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.jfAppRequestUrl = value.trim(); + } + + value = pro.getProperty(QRC_BACK_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.qrcBackTransUrl = value.trim(); + } + + value = pro.getProperty(QRC_B2C_ISS_BACK_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.qrcB2cIssBackTransUrl = value.trim(); + } + + value = pro.getProperty(QRC_B2C_MER_BACK_TRANS_URL); + if (!StringUtils.isEmpty(value)) { + this.qrcB2cMerBackTransUrl = value.trim(); + } + + value = pro.getProperty(SDK_ENCRYPTTRACKKEY_EXPONENT); + if (!StringUtils.isEmpty(value)) { + this.encryptTrackKeyExponent = value.trim(); + } + + value = pro.getProperty(SDK_ENCRYPTTRACKKEY_MODULUS); + if (!StringUtils.isEmpty(value)) { + this.encryptTrackKeyModulus = value.trim(); + } + + value = pro.getProperty(SDK_IF_VALIDATE_CN_NAME); + if (!StringUtils.isEmpty(value)) { + if( "false".equals(value.trim())) { + this.ifValidateCNName = false; + } + } + + value = pro.getProperty(SDK_IF_VALIDATE_REMOTE_CERT); + if (!StringUtils.isEmpty(value)) { + if( "true".equals(value.trim())) { + this.ifValidateRemoteCert = true; + } + } + + value = pro.getProperty(SDK_SIGN_METHOD); + if (!StringUtils.isEmpty(value)) { + this.signMethod = value.trim(); + } + + value = pro.getProperty(SDK_SIGN_METHOD); + if (!StringUtils.isEmpty(value)) { + this.signMethod = value.trim(); + } + value = pro.getProperty(SDK_VERSION); + if (!StringUtils.isEmpty(value)) { + this.version = value.trim(); + } + value = pro.getProperty(SDK_FRONTURL); + if (!StringUtils.isEmpty(value)) { + this.frontUrl = value.trim(); + } + value = pro.getProperty(SDK_BACKURL); + if (!StringUtils.isEmpty(value)) { + this.backUrl = value.trim(); + } + } + + public String getSignMethod () { + return signMethod; + } + + public void setSignMethod (String signMethod) { + this.signMethod = signMethod; + } + + public String getSignMethodByStr(String signStr) { + if(StringUtils.isBlank(signStr)){ + return SDKConstants.SIGNMETHOD_RSA; + } + signStr = signStr.toUpperCase(); + switch (signStr) { + case "RSA": + return SDKConstants.SIGNMETHOD_RSA; + case "SHA256": + return SDKConstants.SIGNMETHOD_SHA256; + case "SM3": + return SDKConstants.SIGNMETHOD_SM3; + default: + return SDKConstants.SIGNMETHOD_RSA; + } + } + + 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 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; + } + +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConstants.java b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConstants.java new file mode 100644 index 0000000..3599713 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKConstants.java @@ -0,0 +1,402 @@ +/** + * 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.egzosn.pay.union.SDK; + +/** + * + * @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"; + + /** 渠道类型*/ + public static final String param_channelType = "channelType"; + + /** C2B码,1-20位数字*/ + public static final String param_qrNo = "qrNo"; + + /*原交易商户订单号*/ + public static final String param_origOrderId = "origOrderId"; + + /*原交易商户发送交易时间*/ + public static final String param_origTxnTime = "origTxnTime"; +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKUtils.java b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKUtils.java new file mode 100644 index 0000000..c8918dc --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/SDK/SDKUtils.java @@ -0,0 +1,204 @@ +package com.egzosn.pay.union.SDK;/** + * Description: + * author: Fuzx + * date: 2017/11/27 0027 + */ + +import com.egzosn.pay.common.util.sign.SecureUtil; +import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.sign.encrypt.Base64; +import com.egzosn.pay.common.util.sign.encrypt.RSA; +import com.egzosn.pay.common.util.sign.encrypt.SHA256; +import com.egzosn.pay.common.util.str.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.*; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.zip.Inflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static com.egzosn.pay.union.SDK.SDKConstants.*; + +/** + * @author Actinia + * @email hayesfu@qq.com + * @create 2017 2017/11/27 0027 + */ +public class SDKUtils { + //日志 + protected static final Log log = LogFactory.getLog(SDKUtils.class); + + /** + * 验证签名 + * + * @param resData + * 返回报文数据 + * @param encoding + * 编码格式 + * @return + */ + public static boolean validate(Map resData, String encoding) { + log.info("验签处理开始"); + if (StringUtils.isEmpty(encoding)) { + encoding = "UTF-8"; + } + String signMethod = resData.get(SDKConstants.param_signMethod).toString(); + String version = resData.get(SDKConstants.param_version).toString(); + 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).toString(); + log.info("签名原文:["+stringSign+"]"); + // 从返回报文中获取certId ,然后去证书静态Map中查询对应验签证书对象 + String certId = resData.get(SDKConstants.param_certId).toString(); + log.info("对返回报文串验签使用的验签公钥序列号:["+certId+"]"); + // 将Map信息转换成key1=value1&key2=value2的形式 + String stringData = SignUtils.parameterText(resData); + log.info("待验签返回报文串:["+stringData+"]"); + try { + // 验证签名需要用银联发给商户的公钥证书. + return SecureUtil.validateSignBySoft(CertUtil.getValidatePublicKey(certId), Base64.decode(stringSign), + SecureUtil.sha1X16(stringData, encoding)); + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage(), e); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } else if (VERSION_5_1_0.equals(version)) { + // 1.从返回报文中获取公钥信息转换成公钥对象 + String strCert = resData.get(SDKConstants.param_signPubKeyCert).toString(); + log.info("验签公钥证书:["+strCert+"]"); + X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert); + if(x509Cert == null) { + log.error("convert signPubKeyCert failed"); + return false; + } + // 2.验证证书链 + if (!CertUtil.verifyCertificate(x509Cert)) { + log.error("验证公钥证书失败,证书信息:["+strCert+"]"); + return false; + } + + // 3.验签 + String stringSign = resData.get(SDKConstants.param_signature).toString(); + log.info("签名原文:["+stringSign+"]"); + // 将Map信息转换成key1=value1&key2=value2的形式 + String stringData = SignUtils.parameterText(resData); + log.info("待验签返回报文串:["+stringData+"]"); + try { + // 验证签名需要用银联发给商户的公钥证书. + boolean result = SecureUtil.validateSignBySoft256(x509Cert + .getPublicKey(), Base64.decode(stringSign + ), SHA256.sha256X16( + stringData, encoding)); + log.info("验证签名" + (result? "成功":"失败")); + return result; + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage(), e); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + } else if (SIGNMETHOD_SHA256.equals(signMethod)) { + // 1.进行SHA256验证 + String stringSign = resData.get(SDKConstants.param_signature).toString(); + log.info("签名原文:["+stringSign+"]"); + // 将Map信息转换成key1=value1&key2=value2的形式 + String stringData = SignUtils.parameterText(resData); + log.info("待验签返回报文串:["+stringData+"]"); + String strBeforeSha256 = stringData + + SDKConstants.AMPERSAND + + SecureUtil.sha256X16Str(SDKConfig.getConfig() + .getSecureKey(), encoding); + String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256, + encoding); + boolean result = stringSign.equals(strAfterSha256); + log.info("验证签名" + (result? "成功":"失败")); + return result; + } else if (SIGNMETHOD_SM3.equals(signMethod)) { + // 1.进行SM3验证 + String stringSign = resData.get(SDKConstants.param_signature).toString(); + log.info("签名原文:["+stringSign+"]"); + // 将Map信息转换成key1=value1&key2=value2的形式 + String stringData = SignUtils.parameterText(resData); + log.info("待验签返回报文串:["+stringData+"]"); + String strBeforeSM3 = stringData + + SDKConstants.AMPERSAND + + SecureUtil.sm3X16Str(SDKConfig.getConfig() + .getSecureKey(), encoding); + String strAfterSM3 = SecureUtil + .sm3X16Str(strBeforeSM3, encoding); + boolean result = stringSign.equals(strAfterSM3); + log.info("验证签名" + (result? "成功":"失败")); + return result; + } + return false; + } + + /** + * 对参数加密 + * @param data + * @param encoding + * @return + */ + public static boolean signParams(Map data,String encoding){ + if (StringUtils.isBlank(encoding)) { + encoding = "UTF-8"; + } + String signMethod = data.get(SDKConstants.param_signMethod); + String version = data.get(SDKConstants.param_version); + if(StringUtils.isBlank(signMethod)){ + log.info("银联签名方式不能为空"); + return false; + } + if(StringUtils.isBlank(version)){ + log.info("版本号不能为空"); + return false; + } + if (SIGNMETHOD_RSA.equals(signMethod)|| VERSION_1_0_0.equals(version) || SDKConstants.VERSION_5_0_1.equals(version)) { + if (SDKConstants.VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || SDKConstants.VERSION_5_0_1.equals(version)) { + data.put(SDKConstants.param_certId, CertUtil.getSignCertId()); + String stringData = SignUtils.parameterText(data); + String stringSign = null; + try { + // 通过SHA1进行摘要并转16进制 + byte[] signDigest = SecureUtil + .sha1X16(stringData, encoding); + stringSign = RSA.sign(signDigest, CertUtil.getSignCertPrivateKey()); + // 设置签名域值 + data.put(SDKConstants.param_signature, stringSign); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + }else if (SDKConstants.SIGNMETHOD_SHA256.equals(signMethod)) { + String stringData = SignUtils.parameterText(data); + String strBeforeSha256 = stringData + + SDKConstants.AMPERSAND + + SecureUtil.sha256X16Str(SDKConfig.getConfig().getSecureKey(), encoding); + String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256, + encoding); + data.put(SDKConstants.param_signature, strAfterSha256); + } else if (SDKConstants.SIGNMETHOD_SM3.equals(signMethod)) { + String stringData = SignUtils.parameterText(data); + String strBeforeSM3 = stringData + + SDKConstants.AMPERSAND + + SecureUtil.sm3X16Str(SDKConfig.getConfig().getSecureKey(), encoding); + String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding); + // 设置签名域值 + data.put(SDKConstants.param_signature, strAfterSM3); + } + + return false; + } + + +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java index 0b847ee..650e608 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java @@ -3,83 +3,88 @@ package com.egzosn.pay.union.api; import com.egzosn.pay.common.api.BasePayConfigStorage; /** - * 支付客户端配置存储 - * author egan - * - * email egzosn@gmail.com - * date 2016-5-18 14:09:01 + * @author Actinia + * @email hayesfu@qq.com + *

+    create 2017 2017/11/4 0004
+ * 
*/ public class UnionPayConfigStorage extends BasePayConfigStorage { - // 商户PID - public volatile String merId ; - // 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner - public volatile String pid ; - //partner_id的简称,合作伙伴身份 -// public volatile String partner ; + /** + * 商户号 + */ + public volatile String merId; - // 商户收款账号 - public volatile String seller; + /** + * 商户收款账号 + */ + public volatile String seller; //公钥 private volatile String aliPublicKey; - //全渠道固定值 - public static String version = "5.0.0"; - public String getAliPublicKey() { + + + public String getAliPublicKey () { return aliPublicKey; } - public void setAliPublicKey(String aliPublicKey) { + public void setAliPublicKey (String aliPublicKey) { setKeyPublic(aliPublicKey); this.aliPublicKey = aliPublicKey; } - @Override - public String getAppid() { + public String getAppid () { return null; } /** - * @see #getPid() * @return 合作者id + * @see #getPid() */ @Deprecated @Override - public String getPartner() { - return pid; + public String getPartner () { + return merId; } /** * 设置合作者id - * @see #setPid(String) + * * @param partner 合作者id + * @see #setPid(String) */ @Deprecated - public void setPartner(String partner) { - this.pid = partner; + public void setPartner (String partner) { + this.merId = partner; } @Override - public String getPid() { - return pid; + public String getPid () { + return merId; } - public void setPid(String pid) { - this.pid = pid; + public void setPid (String pid) { + this.merId = pid; } - - public String getSeller() { + @Override + public String getSeller () { return seller; } - public void setSeller(String seller) { + public void setSeller (String seller) { this.seller = seller; } + public String getMerId () { + return merId; + } - + public void setMerId (String merId) { + this.merId = merId; + } } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java new file mode 100644 index 0000000..3136169 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -0,0 +1,464 @@ +package com.egzosn.pay.union.api; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.api.Callback; +import com.egzosn.pay.common.api.PayConfigStorage; +import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.util.MatrixToImageWriter; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.union.SDK.CertUtil; +import com.egzosn.pay.union.SDK.SDKConfig; +import com.egzosn.pay.union.SDK.SDKConstants; +import com.egzosn.pay.union.SDK.SDKUtils; +import com.egzosn.pay.union.enums.UnionTransactionType; +import com.egzosn.pay.union.request.UnionQueryOrder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.math.BigDecimal; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Actinia + * @email hayesfu@qq.com + * @create 2017 2017/11/5 0005 + */ +public class UnionPayService extends BasePayService { + //日志 + protected static final Log log = LogFactory.getLog(UnionPayService.class); + + + public UnionPayService (PayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + public UnionPayService (PayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + SDKConfig.getConfig().loadPropertiesFromSrc(); + } + + /** + * 银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改 + * @return 返回参数集合 + */ + private Map getCommonParam(){ + Map params = new HashMap<>(); + params.put(SDKConstants.param_version, SDKConfig.getConfig().getVersion()); + params.put(SDKConstants.param_encoding, payConfigStorage.getInputCharset().toUpperCase()); + params.put(SDKConstants.param_signMethod, SDKConfig.getConfig().getSignMethodByStr(payConfigStorage.getSignType())); + params.put(SDKConstants.param_merId, payConfigStorage.getPid()); + //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户) + params.put(SDKConstants.param_accessType, "0"); + DateFormat df = new SimpleDateFormat("YYYYMMDDhhmmss"); + params.put(SDKConstants.param_txnTime, df.format(new Date())); + params.put(SDKConstants.param_backUrl, SDKConfig.getConfig().getBackUrl()); + return params; + } + + + /** + * 回调校验 + * + * @param result 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify (Map result) { + if(result != null){ + if(SDKUtils.validate(result, payConfigStorage.getInputCharset())){ + String respCode = result.get("respCode").toString(); + if(("00").equals(respCode)){ + + //成功,获取tn号 + //String tn = resmap.get("tn"); + //TODO + return true; + }else{ + //其他应答码为失败请排查原因或做失败处理 + //TODO + } + }else{ +// 校验失败 + } + }else{ + + } + return false; + } + + /** + * 签名校验 + * + * @param params 参数集 + * @param sign 签名 + * @return 签名校验 true通过 + */ + @Override + public boolean signVerify (Map params, String sign) { + return false; + } + + /** + * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 + * 校验数据来源 + * + * @param id 业务id, 数据的真实性. + * @return true通过 + */ + @Override + public boolean verifySource (String id) { + return false; + } + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo (PayOrder order) { + Map params = this.getCommonParam(); + UnionTransactionType type = (UnionTransactionType)order.getTransactionType(); + type.convertMap(params); + switch (type){ + case APPLY_QR_CODE: + params.put(SDKConstants.param_orderId, order.getOutTradeNo()); + params.put(SDKConstants.param_txnAmt, order.getPrice().toString()); + params.put(SDKConstants.param_currencyCode, "156"); + break; + case CONSUME: + params.put(SDKConstants.param_orderId, order.getOutTradeNo()); + params.put(SDKConstants.param_txnAmt, order.getPrice().toString()); + params.put(SDKConstants.param_currencyCode, "156"); + + params.put(SDKConstants.param_qrNo, order.getAuthCode()); + params.put(SDKConstants.param_termId, order.getDeviceInfo()); + break; + default: + } + + return params; + } + + + /** + * 获取输出二维码,用户返回给支付端, + * + * @param order 发起支付的订单信息 + * @return 返回图片信息,支付时需要的 + */ + @Override + public BufferedImage genQrPay (PayOrder order) { + Map params = orderInfo(order); + CertUtil.sign(params,payConfigStorage.getInputCharset().toUpperCase()); + JSONObject response = getHttpRequestTemplate().postForObject(SDKConfig.getConfig().getBackRequestUrl(),params,JSONObject.class); + if(SDKUtils.validate(response,payConfigStorage.getInputCharset().toUpperCase())){ + if("00".equals(response.getString(SDKConstants.param_respCode))){ + //成功,获取tn号 + return MatrixToImageWriter.writeInfoToJpgBuff( response.getString(SDKConstants.param_respCode)); + }else{ + throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); + } + }else{ + throw new PayErrorException(new PayException("1000", "验证签名失败", response.toJSONString())); + } + } + + /** + * 刷卡付,pos主动扫码付款(条码付) + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + @Override + public Map microPay (PayOrder order) { + Map params = orderInfo(order); + CertUtil.sign(params,payConfigStorage.getInputCharset().toUpperCase()); + return getHttpRequestTemplate().postForObject(SDKConfig.getConfig().getBackRequestUrl(),params,JSONObject.class); + } + + + /** + * 交易查询接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query (String tradeNo, String outTradeNo) { + Map params = this.getCommonParam(); + UnionTransactionType.QUERY.convertMap(params); + params.put(SDKConstants.param_orderId,outTradeNo); + CertUtil.sign(params,payConfigStorage.getInputCharset().toUpperCase()); + JSONObject response = getHttpRequestTemplate().postForObject(SDKConfig.getConfig().getBackRequestUrl(),params,JSONObject.class); + if(SDKUtils.validate(response,payConfigStorage.getInputCharset().toUpperCase())){ + if("00".equals(response.getString(SDKConstants.param_respCode))){ + String origRespCode = response.getString(SDKConstants.param_origRespCode); + if(("00").equals(origRespCode)){ + //交易成功,更新商户订单状态 + //TODO + return response; + }else{ + throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); + } + }else{ + throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); + } + }else{ + throw new PayErrorException(new PayException("1000", "验证签名失败", response.toJSONString())); + } + } + + /** + * 消费撤销/退货接口 + * + * @return 返回支付方申请退款后的结果 + */ + public Map unionRefundOrConsumeUndo (UnionQueryOrder queryOrder,UnionTransactionType type) { + Map params = this.getCommonParam(); + type.convertMap(params); + params.put(SDKConstants.param_orderId,queryOrder.getOrderId()); + params.put(SDKConstants.param_txnAmt,queryOrder.getTxnAmt()); + if(StringUtils.isNotBlank(queryOrder.getOrigQryId())) { + params.put(SDKConstants.param_origQryId, queryOrder.getOrigQryId()); + } + if(StringUtils.isNotBlank(queryOrder.getOrigOrderId())){ + params.put(SDKConstants.param_origOrderId,queryOrder.getOrigOrderId()); + } + if(StringUtils.isNotBlank(queryOrder.getOrigTxnTime())) { + params.put(SDKConstants.param_origTxnTime, queryOrder.getOrigOrderId()); + } + CertUtil.sign(params,payConfigStorage.getInputCharset().toUpperCase()); + JSONObject response = getHttpRequestTemplate().postForObject(SDKConfig.getConfig().getBackRequestUrl(),params,JSONObject.class); + if(SDKUtils.validate(response,payConfigStorage.getInputCharset().toUpperCase())){ + if("00".equals(response.getString(SDKConstants.param_respCode))){ + String origRespCode = response.getString(SDKConstants.param_origRespCode); + //交易成功,更新商户订单状态 + //TODO + return response; + + }else{ + throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); + } + }else{ + throw new PayErrorException(new PayException("1000", "验证签名失败", response.toJSONString())); + } + } + + /** + * 将请求参数或者请求流转化为 Map + * + * @param parameterMap 请求参数 + * @param is 请求流 + * @return 获得回调的请求参数 + */ + @Override + public Map getParameter2Map (Map parameterMap, InputStream is) { + return null; + } + + /** + * 获取输出消息,用户返回给支付端 + * + * @param code 状态 + * @param message 消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage getPayOutMessage (String code, String message) { + return null; + } + + /** + * 获取成功输出消息,用户返回给支付端 + * 主要用于拦截器中返回 + * + * @param payMessage 支付回调消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage successPayOutMessage (PayMessage payMessage) { + return null; + } + + /** + * 获取输出消息,用户返回给支付端, 针对于web端 + * + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @return 获取输出消息,用户返回给支付端, 针对于web端 + * @see MethodType 请求类型 + */ + @Override + public String buildRequest (Map orderInfo, MethodType method) { + return null; + } + + + /** + * 交易查询接口,带处理器 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @return 返回查询回来的结果集 + */ + @Override + public T query (String tradeNo, String outTradeNo, Callback callback) { + return null; + } + + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close (String tradeNo, String outTradeNo) { + return null; + } + + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public T close (String tradeNo, String outTradeNo, Callback callback) { + return null; + } + + /** + * 申请退款接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param refundAmount 退款金额 + * @param totalAmount 总金额 + * @return 返回支付方申请退款后的结果 + */ + @Override + public Map refund (String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { + return null; + } + + + + /** + * 申请退款接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param refundAmount 退款金额 + * @param totalAmount 总金额 + * @param callback 处理器 + * @return 返回支付方申请退款后的结果 + */ + @Override + public T refund (String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount, Callback callback) { + return null; + } + + /** + * 查询退款 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery (String tradeNo, String outTradeNo) { + return null; + } + + /** + * 查询退款 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @return 返回支付方查询退款后的结果 + */ + @Override + public T refundquery (String tradeNo, String outTradeNo, Callback callback) { + return null; + } + + /** + * 下载对账单 + * + * @param billDate 账单时间 + * @param billType 账单类型 + * @return 返回fileContent 请自行将数据落地 + */ + @Override + public Object downloadbill (Date billDate, String billType) { + Map params = this.getCommonParam(); + UnionTransactionType.File_Transfer.convertMap(params); + DateFormat df = new SimpleDateFormat("MMDD"); + params.put(SDKConstants.param_settleDate,df.format(new Date())); + params.put(SDKConstants.param_fileType,billType); + CertUtil.sign(params,payConfigStorage.getInputCharset().toUpperCase()); + Map response = getHttpRequestTemplate().postForObject(SDKConfig.getConfig().getBackRequestUrl(),params,Map.class); + if(SDKUtils.validate(response,payConfigStorage.getInputCharset().toUpperCase())){ + if("00".equals(response.get(SDKConstants.param_respCode))){ + + return response.get(SDKConstants.param_fileContent).toString(); + + }else{ + throw new PayErrorException(new PayException(response.get(SDKConstants.param_respCode).toString(), response.get(SDKConstants.param_respMsg).toString(), response.toString())); + } + }else{ + throw new PayErrorException(new PayException("1000", "验证签名失败", response.toString())); + } + } + + /** + * 下载对账单 + * + * @param billDate 账单时间:具体请查看对应支付平台 + * @param billType 账单类型,具体请查看对应支付平台 + * @param callback 处理器 + * @return 返回支付方下载对账单的结果 + */ + @Override + public T downloadbill (Date billDate, String billType, Callback callback) { + return null; + } + + /** + * 通用查询接口 + * + * @param tradeNoOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 + * @param callback 处理器 + * @return 返回支付方对应接口的结果 + */ + @Override + public T secondaryInterface (Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback) { + return null; + } + + + + +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/enums/UnionTransactionType.java b/pay-java-union/src/main/java/com/egzosn/pay/union/enums/UnionTransactionType.java new file mode 100644 index 0000000..19b2318 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/enums/UnionTransactionType.java @@ -0,0 +1,98 @@ +package com.egzosn.pay.union.enums; + +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.union.SDK.SDKConstants; + +import java.util.Map; + +/** + * @author Actinia + * @email hayesfu@qq.com + *
+    create 2017 2017/11/4 0004
+ * 
+ */ +public enum UnionTransactionType implements TransactionType{ + //申码(主扫场景) + APPLY_QR_CODE("01","07","000000","08"), + //消费(被扫场景) + CONSUME("01","06","000000","08"), + //消费撤销 + CONSUME_UNDO("31","00","000000","08"), + //查询 + QUERY("00","00","000201",""), + //退款 + REFUND("04","00","000000","08"), + //对账文件下载 + File_Transfer("00","00","000201","") + ; + + /** + * 交易类型 + */ + private String txnType; + /** + * 交易子类 + */ + private String txnSubType; + + /** + * 业务类型 + */ + private String bizType; + + /** + * 渠道类型 + */ + private String channelType; + + + UnionTransactionType (String txnType, String txnSubType, String bizType, String channelType) { + this.txnType = txnType; + this.txnSubType = txnSubType; + this.bizType = bizType; + this.channelType = channelType; + } + + public void convertMap(Map contentData){ + contentData.put(SDKConstants.param_txnType, this.getTxnType()); + contentData.put(SDKConstants.param_txnSubType,this.getTxnSubType()); + contentData.put(SDKConstants.param_bizType,this.getBizType()); + contentData.put(SDKConstants.param_channelType,this.getChannelType()); + } + + + public String getTxnType () { + return txnType; + } + + public String getTxnSubType () { + return txnSubType; + } + + public String getBizType () { + return bizType; + } + + public String getChannelType () { + return channelType; + } + + /** + *获取交易对类型枚举 + * + * @return 交易类型 + */ + @Override + public String getType () { + return this.name(); + } + + /** + * + */ + @Override + public String getMethod () { + return null; + } +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/request/UnionQueryOrder.java b/pay-java-union/src/main/java/com/egzosn/pay/union/request/UnionQueryOrder.java new file mode 100644 index 0000000..8aad82a --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/request/UnionQueryOrder.java @@ -0,0 +1,82 @@ +package com.egzosn.pay.union.request; + +/** + * @author Actinia + * @email hayesfu@qq.com + *
+create 2017 2017/11/4 0004
+ * 
+ */ +public class UnionQueryOrder { + + private Integer payId; + /** + * 支付平台订单号 + */ + private String orderId; + /** + * 金额 + */ + private String txnAmt; + /** + * 原交易查询流水号 + */ + private String origQryId; + /** + * 原交易商户订单号 + */ + private String origOrderId; + /** + * 原交易商户发送交易时间: + */ + private String origTxnTime; + + + public Integer getPayId() { + return payId; + } + + public void setPayId(Integer payId) { + this.payId = payId; + } + + public String getOrderId () { + return orderId; + } + + public void setOrderId (String orderId) { + this.orderId = orderId; + } + + public String getTxnAmt () { + return txnAmt; + } + + public void setTxnAmt (String txnAmt) { + this.txnAmt = txnAmt; + } + + public String getOrigQryId () { + return origQryId; + } + + public void setOrigQryId (String origQryId) { + this.origQryId = origQryId; + } + + public String getOrigOrderId () { + return origOrderId; + } + + public void setOrigOrderId (String origOrderId) { + this.origOrderId = origOrderId; + } + + public String getOrigTxnTime () { + return origTxnTime; + } + + public void setOrigTxnTime (String origTxnTime) { + this.origTxnTime = origTxnTime; + } +} diff --git a/pom.xml b/pom.xml index 4ee3d9f..27e4dc2 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ Actinia - 412605202@qq.com + hayesfu@qq.com https://github.com/Actinian @@ -76,7 +76,12 @@ fastjson 1.2.17 - + + + org.bouncycastle + bcprov-debug-jdk15on + 1.58 +