Merge tag '1.0.29' into develop
脱机码
diff --git a/bus-qrcode/build.gradle b/bus-qrcode/build.gradle
index 4132b55..0817cb6 100644
--- a/bus-qrcode/build.gradle
+++ b/bus-qrcode/build.gradle
@@ -7,7 +7,7 @@
group = rootProject.group
-def sdkVersion = gitVersion()
+def sdkVersion = '1.3.5'
sourceCompatibility = 1.8
targetCompatibility = 1.8
@@ -15,7 +15,7 @@
publications {
mavenJava(MavenPublication) {
groupId = project.group
- artifactId = 'bus-qrcode'
+ artifactId = 'dlsmk-qrcode'
version = sdkVersion
from components.java
}
@@ -36,19 +36,20 @@
dependencies {
implementation "org.apache.commons:commons-lang3:3.7"
- implementation 'com.eatthepath:java-otp:0.1.0'
- implementation 'org.slf4j:slf4j-api:1.7.25'
+ implementation 'com.eatthepath:java-otp:0.2.0'
+ implementation 'org.slf4j:slf4j-api:1.7.26'
implementation 'commons-codec:commons-codec:1.9'
- runtime 'org.slf4j:slf4j-parent:1.7.25'
- runtime 'org.slf4j:slf4j-simple:1.7.25'
+ implementation 'org.bouncycastle:bcprov-jdk15to18:1.66'
+ implementation 'org.bouncycastle:bcpkix-jdk15on:1.66'
+ runtime 'org.slf4j:slf4j-parent:1.7.26'
testImplementation 'junit:junit:4.12'
}
jar {
enabled = true
- baseName = "bus-qrcode"
+ baseName = "dlsmk-qrcode"
manifest {
- attributes('Bus-QRcode-Version': sdkVersion)
+ attributes('dlsmk-qrcode-version': sdkVersion)
}
}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java
index b04ecd4..9b00aa0 100644
--- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java
@@ -58,10 +58,10 @@
}
public static String encodeBase64(byte[] data) {
- return new Base64(true).encodeAsString(data);
+ return Base64.encodeBase64URLSafeString(data);
}
public static byte[] decodeBase64(String data) {
- return new Base64(true).decode(data);
+ return Base64.decodeBase64(data);
}
}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java
index 839e3c0..e3a98b8 100644
--- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java
@@ -1,20 +1,23 @@
package com.supwisdom.dlpay.busqrcode;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
public class CryptoUtil {
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
private static byte[] doCipher(Cipher cipher, SecretKey key, int mode, byte[] data) {
try {
cipher.init(mode, key);
@@ -83,12 +86,12 @@
private static byte[] doAESCipher(byte[] key, byte[] iv, byte[] data, int mode) {
try {
- Cipher keyCipher = Cipher.getInstance("AES/CFB/NoPadding");
+ Cipher keyCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
keyCipher.init(mode, secret, ivSpec);
return keyCipher.doFinal(data);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | NoSuchProviderException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java
index fdbbf72..93070d0 100644
--- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java
+++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java
@@ -3,13 +3,15 @@
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -17,17 +19,26 @@
import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*;
public class QrCode {
- private static Logger logger = LoggerFactory.getLogger(QrCode.class);
+ public static final String FIELD_UID = "uid";
+ public static final String FIELD_CARDNO = "cardNo";
+ public static final String FIELD_CARDTYPE = "cardType";
+ private static final String FIELD_TOTP = "totp";
+ private static final String FIELD_RANDOM = "random";
+ private static final String FIELD_SIGN = "sign";
+ private static final String DELIMITER = ":";
+ public static final long timeStep = 5; //totp步长 5s
+ public static final int totpDigits = 8; //totp取的位数
- public static final String DELIMITER = ":";
- public static final String SCOPE_DELIMITER = ",";
- public static final String DEFAULT_SCOPE = "1"; //默认仅支持 1-大理车载消费场景
- public static final int TOTP_OFFSET = 10; //TODO: 前后偏移10步,30*10=300s=5m 约前后5分钟内totp有效
+ static final ArrayList<String> qrDataKeys = new ArrayList<>();
-// static final ArrayList<String> DEFAULT_SCOPES = new ArrayList<>();
-// static {
-// DEFAULT_SCOPES.add("1"); //默认仅支持 1-大理车载消费场景
-// }
+ static {
+ qrDataKeys.add(FIELD_UID);
+ qrDataKeys.add(FIELD_CARDNO);
+ qrDataKeys.add(FIELD_CARDTYPE);
+ qrDataKeys.add(FIELD_TOTP);
+ qrDataKeys.add(FIELD_RANDOM);
+// qrDataKeys.add(FIELD_SIGN);
+ }
private Builder qrBuilder;
@@ -36,29 +47,49 @@
}
public static class Builder {
- private String appid;
- private String uid;
- private String scope;
- private String seed = "a8aae1a41ffab089428e8e21381cc34af3b614ac7b7cba50f58470b8e50c6072";
- private boolean resetSeed = false;
- private byte[] iv = decodeHex("55b6f5b3287c535f8274a99354676d0b");
- private boolean resetIv = false;
+ private String uid; //手机注册的uid 系统用户唯一ID
+ private String cardNo; //市民卡号
+ private String cardType; //公交卡类别
+ private String prefix; //码前缀
+ private String mac;
+
private byte[] rootKey;
- private byte[] sKey;
- private byte[] des3Key;
- private String prefix;
- private boolean debug;
+ private byte[] iv = decodeHex("55b6f5b3287c535f8274b99354676d0e");
+ private String seed = "125ea2f97689988b6501";
+ private boolean rootKeySet = false;
+ private boolean resetIv = false;
+ private boolean resetSeed = false;
+
+ private boolean debug = false;
public QrCode create() {
return new QrCode(this);
}
- public Builder appid(String appid) {
- if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) {
- throw new RuntimeException("appid is invalid");
+ public Builder rootKey(String rootKey) {
+ if (StringUtils.isBlank(rootKey)) {
+ throw new RuntimeException("rootKey is empty");
}
- this.appid = appid;
- this.des3Key = md5(appid.getBytes());
+ this.rootKey = decodeBase64(rootKey);
+ rootKeySet = true;
+ return this;
+ }
+
+ public Builder iv(String iv) {
+ if (StringUtils.isBlank(iv)) {
+ throw new RuntimeException("iv is empty");
+ }
+ this.iv = decodeHex(iv);
+ resetIv = true;
+ return this;
+ }
+
+ public Builder seed(String seed) {
+ if (StringUtils.isBlank(seed)) {
+ throw new RuntimeException("seed is empty");
+ }
+ this.seed = seed;
+ this.resetSeed = true;
return this;
}
@@ -70,45 +101,31 @@
return this;
}
- public Builder scope(String scope) {
- if (StringUtils.isBlank(scope)) {
- throw new RuntimeException("scope is empty");
+ public Builder cardNo(String cardNo) {
+ if (StringUtils.isBlank(cardNo) || !StringUtils.isAsciiPrintable(cardNo)) {
+ throw new RuntimeException("cardNo is invalid");
}
- this.scope = scope;
+ this.cardNo = cardNo;
return this;
}
- public Builder rootkey(String key) {
- this.rootKey = decodeHex(key);
+ public Builder cardType(String cardType) {
+ if (StringUtils.isBlank(cardType) || !StringUtils.isAsciiPrintable(cardType)) {
+ throw new RuntimeException("cardType is invalid");
+ }
+ this.cardType = cardType;
return this;
}
- public Builder rootkey(String appid, String skey){
- if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) {
- throw new RuntimeException("appid is invalid");
+ public Builder card(String cardNo, String cardType) {
+ if (StringUtils.isBlank(cardNo) || !StringUtils.isAsciiPrintable(cardNo)) {
+ throw new RuntimeException("cardNo is invalid");
}
- if (StringUtils.isBlank(skey)) {
- throw new RuntimeException("sKey is empty");
+ if (StringUtils.isBlank(cardType) || !StringUtils.isAsciiPrintable(cardType)) {
+ throw new RuntimeException("cardType is invalid");
}
- this.appid = appid;
- this.sKey = decodeHex(skey);
- this.des3Key = md5(appid.getBytes());
- this.rootKey = desEncryptECB(this.des3Key, this.sKey);
- return this;
- }
-
- public Builder seed(String s) {
- if (StringUtils.isBlank(s)) {
- throw new RuntimeException("seed is empty");
- }
- this.seed = s;
- this.resetSeed = true;
- return this;
- }
-
- public Builder ivHex(String v) {
- this.iv = decodeHex(v);
- this.resetIv = true;
+ this.cardNo = cardNo;
+ this.cardType = cardType;
return this;
}
@@ -144,10 +161,9 @@
private String genTOTPWithSeed(String seed, int pl) {
final TimeBasedOneTimePasswordGenerator alg = totpGenerator(pl);
final SecretKey secretKey = new SecretKeySpec(decodeHex(seed),
- TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);
try {
- Date now = Calendar.getInstance().getTime();
- return StringUtils.leftPad(String.format("%d", alg.generateOneTimePassword(secretKey, now)),
+ return StringUtils.leftPad(String.format("%d", alg.generateOneTimePassword(secretKey, Instant.now())),
pl, '0');
} catch (InvalidKeyException e) {
throw new RuntimeException("TOTP Error", e);
@@ -156,32 +172,26 @@
private TimeBasedOneTimePasswordGenerator totpGenerator(int passwordLength) {
try {
- return new TimeBasedOneTimePasswordGenerator(30, TimeUnit.SECONDS, passwordLength,
- TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ return new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(timeStep), passwordLength,
+ TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("TOTP Error", e);
}
}
- private boolean checkScope(List<String> scopes) {
- return scopes.contains(DEFAULT_SCOPE);
- }
-
- private boolean verifyTotp(String totp, String secret, int offset) {
- String second = "30";
- String seed = secret;
+ private boolean verifyTotp(String totp, String seed, int offset) {
long T0 = 0;
String[] keys = new String[offset * 2 + 1];
- long time = Calendar.getInstance().getTime().getTime() / 1000;
+ long time = Instant.now().toEpochMilli() / 1000;
for (int i = 0; i < keys.length; i++) {
String steps = "0";
try {
- long T = (time - T0) / Long.parseLong(second);
+ long T = (time - T0) / timeStep;
steps = Long.toHexString(T + (i - offset)).toUpperCase();
while (steps.length() < 16) {
steps = "0" + steps;
}
- String key = TOTP.generateTOTP(secret, steps, "8", TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
+ String key = TOTP.generateTOTP(seed, steps, String.valueOf(totpDigits), TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);
keys[i] = key;
} catch (final Exception e) {
System.out.println("Error : " + e);
@@ -195,87 +205,228 @@
return false;
}
- public String qrcode() {
- if (qrBuilder.debug) {
- logger.info("appid=" + qrBuilder.appid);
- logger.info("uid=" + qrBuilder.uid);
- logger.info("scope=" + qrBuilder.scope);
- if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed);
- if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
- if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase());
- if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
-
- logger.info("rootkey=" + encodeHex(qrBuilder.rootKey).toUpperCase());
- logger.info("=======================================================");
- }
-
- final String randomStr = getRandomString(6); //随机数
- final String totp = genTOTPWithSeed(qrBuilder.seed, 8);
- final String encDataPlain = new StringJoin(DELIMITER)
- .add(qrBuilder.uid)
- .add(qrBuilder.scope)
- .add(totp)
- .add(randomStr).toString();
-
- final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain.getBytes(), qrBuilder.iv);
- final String code = encodeBase64(encData);
- if (qrBuilder.debug) {
- logger.info("encdata plain= " + encDataPlain);
- }
-
- if (qrBuilder.prefix != null) {
- return qrBuilder.prefix + code;
- }
- return code;
+ private byte[] getSignFactor(String data) {
+ return sha256(("{dlsmk_}" + data).getBytes());
}
- public String decodeUid(String qrcode) {
+ private byte[] byteConcat(byte[] first, byte[] second) {
+ try {
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
+ output.write(first);
+ output.write(second);
+ return output.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("byte concat method error!!!");
+ }
+ }
+
+ private byte[] byteConcat(byte[] first, byte[] second, byte[] third) {
+ try {
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
+ output.write(first);
+ output.write(second);
+ output.write(third);
+ return output.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("byte concat method error!!!");
+ }
+ }
+
+ public String qrcode() {
if (qrBuilder.debug) {
- logger.info("appid=" + qrBuilder.appid);
- if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed);
- if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
- if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase());
- if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
- logger.info("=======================================================");
+ System.out.println("====================== Start Build QR Code =================================");
+ System.out.println("uid=[" + qrBuilder.uid + "]");
+ System.out.println("cardNo=[" + qrBuilder.cardNo + "]");
+ System.out.println("cardType=[" + qrBuilder.cardType + "]");
+ if (null != qrBuilder.prefix) System.out.println("prefix=[" + qrBuilder.prefix + "]");
+
+ if (qrBuilder.resetSeed) System.out.println("seed=[" + qrBuilder.seed + "]");
+ if (qrBuilder.rootKeySet) System.out.println("rootKey=[" + encodeBase64(qrBuilder.rootKey) + "]");
+ if (qrBuilder.resetIv) System.out.println("iv=[" + encodeHex(qrBuilder.iv) + "]");
+ System.out.println("*****************************************************************************");
}
- if (null == qrBuilder.rootKey) {
- throw new RuntimeException("qr decode root key must not be null");
+
+ if (null == qrBuilder.uid || null == qrBuilder.cardNo || null == qrBuilder.cardType) {
+ throw new RuntimeException("二维码必要参数为空!");
}
+
+ if (!qrBuilder.rootKeySet) {
+ throw new RuntimeException("二维码rootKey必须设置!");
+ }
+
+ if (qrBuilder.debug) {
+ System.out.println("totp time=[" + Instant.now().toEpochMilli() / 1000 + "]");
+ }
+ final String totp = genTOTPWithSeed(qrBuilder.seed, totpDigits);
+ final String randomStr = getRandomString(2); //随机数
+ final byte[] factor = getSignFactor(qrBuilder.uid);
+ final String qrData = new StringJoin(DELIMITER)
+ .add(qrBuilder.uid)
+ .add(qrBuilder.cardNo)
+ .add(qrBuilder.cardType)
+ .add(totp)
+ .add(randomStr).toString();
+ if (qrBuilder.debug) {
+ System.out.println("QR Data=[" + qrData + "]");
+ }
+
+ final byte[] sign = sha256(byteConcat(qrData.getBytes(), factor));
+// final byte[] signT = new byte[20];
+// System.arraycopy(sign, 0, signT, 0, 20); //取前20位
+ final byte[] encDataPlain = byteConcat(qrData.getBytes(), DELIMITER.getBytes(), sign);
+// final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain, qrBuilder.iv);
+ final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, qrData.getBytes(), qrBuilder.iv);
+ final String code = encodeBase64(encData);
+ String result = code;
+ if (qrBuilder.prefix != null) {
+ result = qrBuilder.prefix + code;
+ }
+ if (qrBuilder.debug) {
+ System.out.println("hex sign=[" + encodeHex(sign) + "]");
+ System.out.println("hex encDataPlain=[" + encodeHex(encData) + "]");
+ System.out.println("QR code=[" + result + "]");
+ System.out.println("====================== Build QR Code End =================================");
+ }
+ return result;
+ }
+
+ private Map<String, String> decode(String qrcode) {
+ if (qrBuilder.debug) {
+ System.out.println("====================== Start Decode QR Code =================================");
+ if (null != qrBuilder.prefix) System.out.println("prefix=[" + qrBuilder.prefix + "]");
+
+ if (qrBuilder.resetSeed) System.out.println("seed=[" + qrBuilder.seed + "]");
+ if (qrBuilder.rootKeySet) System.out.println("rootKey=[" + encodeBase64(qrBuilder.rootKey) + "]");
+ if (qrBuilder.resetIv) System.out.println("iv=[" + encodeHex(qrBuilder.iv) + "]");
+ System.out.println("*****************************************************************************");
+ }
+
+ if (null == qrcode) {
+ throw new RuntimeException("请传递二维码内容!");
+ }
+
+ if (!qrBuilder.rootKeySet) {
+ throw new RuntimeException("二维码rootKey必须设置!");
+ }
+
if (qrBuilder.prefix != null) {
qrcode = qrcode.substring(qrBuilder.prefix.length());
}
- final String encDataPlain = new String(aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv));
+
+ byte[] code = aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv);
+ if (null == code) {
+ throw new RuntimeException("无法识别二维码!");
+ }
+ final String encData = new String(code);
if (qrBuilder.debug) {
- logger.info("Decode data : <" + encDataPlain + ">");
+ System.out.println("encDataPlain=[" + encData + "]");
}
- String[] fields = encDataPlain.split(DELIMITER);
- if (fields.length < 4) {
- throw new RuntimeException("qrcode plain text format error!");
+ final String[] fields = encData.split(DELIMITER, 5);
+ if (fields.length < 5) {
+ throw new RuntimeException("二维码数据异常!");
}
- String uid = fields[0];
- String[] scopes = fields[1].split(SCOPE_DELIMITER);
- String totp = fields[2];
+ Map<String, String> result = new HashMap<>();
+ for (int i = 0; i < fields.length && i < qrDataKeys.size(); ++i) {
+ result.put(qrDataKeys.get(i), fields[i]);
+ }
+ final String qrData;
+ {
+ final StringJoin sj = new StringJoin(DELIMITER);
+ for (int i = 0; i < 5; ++i) {
+ sj.add(result.get(qrDataKeys.get(i)));
+ }
+ qrData = sj.toString();
+ }
+ final String uid = result.get(FIELD_UID);
+ final byte[] factor = getSignFactor(uid);
+ final byte[] calcSign = sha256(byteConcat(qrData.getBytes(), factor));
+// final byte[] signT = new byte[20];
+// System.arraycopy(calcSign, 0, signT, 0, 20); //取前20位
+ if (qrBuilder.debug) {
+// System.out.println("calcSign=[" + new String(calcSign) + "]");
+ System.out.println(" sign=[" + result.get(FIELD_SIGN) + "]");
+ System.out.println("====================== Decode QR Code End =================================");
+ }
+// if (!new String(signT).equalsIgnoreCase(result.get(FIELD_SIGN))) {
+// throw new RuntimeException("二维码验证错误!");
+// }
- if (!verifyTotp(totp, qrBuilder.seed, TOTP_OFFSET)) {
- throw new RuntimeException("qrcode is invalid!"); //TOTP校验
- }
-
- if (!checkScope(Arrays.asList(scopes))) {
- throw new RuntimeException("qrcode is not supported!"); //应用场景校验
- }
-
- return uid;
+ return result;
}
- public String tac(String uid, Integer amount, String transdate, String transtime) {
- if (null == qrBuilder.appid) {
- throw new RuntimeException("appid is null");
+ /**
+ * 返回码数据不校验totp
+ */
+ public Map<String, String> decodeWithoutTotpCheck(String qrcode) {
+ final Map<String, String> data = decode(qrcode);
+ if (null == data || data.isEmpty()) {
+ throw new RuntimeException("二维码解析失败!");
}
- if (null == qrBuilder.sKey) {
- throw new RuntimeException("sKey is null");
+
+ this.qrBuilder.mac = new StringJoin(DELIMITER)
+ .add(data.get(FIELD_UID))
+ .add(data.get(FIELD_CARDNO))
+ .add(data.get(FIELD_CARDTYPE))
+ .add(data.get(FIELD_TOTP))
+ .add(data.get(FIELD_RANDOM)).toString(); //计算流水TAC的因子
+
+ Map<String, String> result = new HashMap<>();
+ result.put(FIELD_UID, data.get(FIELD_UID));
+ result.put(FIELD_CARDNO, data.get(FIELD_CARDNO));
+ result.put(FIELD_CARDTYPE, data.get(FIELD_CARDTYPE));
+ result.put(FIELD_TOTP, data.get(FIELD_TOTP)); //不校验totp,直接返回
+ return result;
+ }
+
+ /**
+ * 返回码数据校验totp
+ */
+ public Map<String, String> decodeWithTotpCheck(String qrcode) {
+ return decodeWithTotpCheck(qrcode, 3); //默认15s失效
+ }
+
+ /**
+ * 返回码数据校验totp
+ */
+ public Map<String, String> decodeWithTotpCheck(String qrcode, int offset) {
+ final Map<String, String> data = decode(qrcode);
+ if (null == data || data.isEmpty()) {
+ throw new RuntimeException("二维码解析失败!");
}
- final String encdata = uid + qrBuilder.appid + amount + transdate + transtime + "{" + encodeHex(qrBuilder.sKey).toUpperCase() + "}";
- return encodeHex(sha256(encdata.getBytes())).toUpperCase();
+
+ //校验totp
+ if (!verifyTotp(data.get(FIELD_TOTP), this.qrBuilder.seed, offset)) {
+ throw new RuntimeException("二维码已失效!"); //二维码已无效
+ }
+
+ this.qrBuilder.mac = new StringJoin(DELIMITER)
+ .add(data.get(FIELD_UID))
+ .add(data.get(FIELD_CARDNO))
+ .add(data.get(FIELD_CARDTYPE))
+ .add(data.get(FIELD_TOTP))
+ .add(data.get(FIELD_RANDOM)).toString(); //计算流水TAC的因子
+
+ Map<String, String> result = new HashMap<>();
+ result.put(FIELD_UID, data.get(FIELD_UID));
+ result.put(FIELD_CARDNO, data.get(FIELD_CARDNO));
+ result.put(FIELD_CARDTYPE, data.get(FIELD_CARDTYPE));
+ return result;
+ }
+
+ public String tac(Integer amount, String transDate, String transTime) {
+ if (null == this.qrBuilder.mac) {
+ throw new RuntimeException("请先解析二维码!");
+ }
+ if (this.qrBuilder.debug) {
+ System.out.println("mac=[" + this.qrBuilder.mac + "]");
+ }
+
+ final String encData = this.qrBuilder.mac + amount + transDate + transTime + "{dlsmk}";
+ final String tac = encodeBase64(sha256(encData.getBytes()));
+ if (this.qrBuilder.debug) {
+ System.out.println("tac=[" + tac + "]");
+ }
+ return tac;
}
}
diff --git a/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java
index 9e8bae1..fc3b3a0 100644
--- a/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java
+++ b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java
@@ -1,61 +1,39 @@
package com.supwisdom.dlpay.busqrcode;
import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import static com.supwisdom.dlpay.busqrcode.BinUtil.*;
-import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*;
-import static org.junit.Assert.*;
+import java.util.Map;
public class QrcodeTest {
- private Logger logger = LoggerFactory.getLogger(QrcodeTest.class);
+ @Test
+ public void testBusQrcode() {
+ final String uid = "0a5de6ce985d43989b7ebe64ad8eb9c3";
+ final String cardNo = "00001252";
+ final String cardtype = "80";
+ final String rootKeyB64 = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA=";
+ final String ivHex = "55b6f5b3287c535f8274b99354676d0e";
+ final String qr="Szgp1QkGFy0DJ6mPVg4wMuOE5XuSe7eZrocdPDAs_iroqlRsDfitBrL5LFl3TwWhehBkE-5JTh1dbOydgxhDVdHrDjsdnX5Ydi4GcxACUMBXnpm3GaRZW66fD6A7rjCW";
- @Test
- public void testBusQrcode() {
- String prefix = "dlsmk_";
- String appid = "10001";
- String uid = "ff8080816cf13e79016cf15d63f40011";
- String scope = "1,2";
- String rkey = "0A5DE6CE985D43989B7EBE64AD8EB9C3";
- Integer amount = 100;
- String transdate = "20200220";
- String transtime = "090921";
+ try {
+ QrCode handler = QrCode.builder()
+ .rootKey(rootKeyB64)
+ .iv(ivHex)
+ .uid(uid)
+ .card(cardNo, cardtype)
+ .debug(true)
+ .create();
+ final String qrcode = handler.qrcode();
- String skey = encodeHex(desDecryptECB(md5(appid.getBytes()), decodeHex(rkey))).toUpperCase();
- logger.info("skey=[" + skey + "]");
- String rootKey = encodeHex(desEncryptECB(md5(appid.getBytes()), decodeHex(skey))).toUpperCase();
- assertEquals("rootkey与skey互为3des加密关系错误!", rkey, rootKey);
+ QrCode decHandler = QrCode.builder()
+ .rootKey(rootKeyB64)
+ .iv(ivHex)
+ .debug(true).create();
+ Map<String, String> result = decHandler.decodeWithTotpCheck(qrcode);
+ String tac = decHandler.tac(1000, "20201028", "144521");
+ System.out.println("tac="+tac);
- QrCode encQr = QrCode.builder()
- .appid(appid)
- .rootkey(rkey)
- .uid(uid)
- .scope(scope)
- .prefix(prefix)
- .debug(true)
- .create();
- String qrcode = encQr.qrcode();
- logger.info("qrcode=[" + qrcode + "]");
-
- QrCode decQr = QrCode.builder()
- .rootkey(appid, skey)
- .prefix(prefix)
- .debug(true)
- .create();
-
-// try {
-// Thread.sleep(6*60*1000); //totp测试
-// } catch (InterruptedException e) {
-// e.printStackTrace();
-// }
-
- String decUid = decQr.decodeUid(qrcode);
- assertEquals("解码uid错误!", uid, decUid);
-
- String tac = decQr.tac(uid, amount, transdate, transtime);
- String tacData = uid + appid + amount + transdate + transtime + "{" + skey + "}";
- String testTac = encodeHex(sha256(tacData.getBytes())).toUpperCase();
- assertEquals("tac计算错误", tac, testTac);
+ } catch (Exception e) {
+ e.printStackTrace();
}
+ }
}
\ No newline at end of file
diff --git a/config/application-devel-pg-xkx.properties b/config/application-devel-pg-xkx.properties
index 3f78169..16830f5 100644
--- a/config/application-devel-pg-xkx.properties
+++ b/config/application-devel-pg-xkx.properties
@@ -8,15 +8,16 @@
# Postgresql settings
spring.datasource.platform=postgresql
spring.datasource.url=jdbc:postgresql://localhost:15432/payapi
-spring.datasource.username=admin
+spring.datasource.username=supwisdom
spring.datasource.password=123456
database.dbtype=postgresql
logging.level.org.hibernate.SQL=ERROR
# Redis settings
spring.redis.host=localhost
-spring.redis.port=16379
-spring.redis.password=kingstar
+spring.redis.port=6379
+spring.redis.password=
+spring.redis.database=0
# jwt settings
jwt.secret=Zj5taLomEbrM0lk+NMQZbHfSxaDU1wekjT+kiC3YzDw=
# timeout seconds
@@ -24,14 +25,26 @@
# user password
auth.password.bcrypt.seed=
security.request.sign=false
-##################################################
+###################### dev ############################
## quartz task scheduler
shopbalance.updater.cron=-
+download.ynrcc.chkfile.cron =-
+query.third.transdtl.result.cron=-
+dayend.settletask.cron=-
+payapi.sourcetype.checker.scheduler=-
+citizencard.dolosstask.cron=-
+send.delay.notice.task.cron=-
+points.outdate.cron=-
+points.consume.cron=-
+##################################################
#download.ynrcc.chkfile.cron =3 0/2 * * * ?
#query.third.transdtl.result.cron=7 0/1 * * * ?
#dayend.settletask.cron=0 0/2 * * * ?
#payapi.sourcetype.checker.scheduler=0 0/2 * * * ?
-citizencard.dolosstask.cron=3 0/1 * * * ?
+#citizencard.dolosstask.cron=3 0/1 * * * ?
+#send.delay.notice.task.cron=29 0/1 * * * ?
+#points.outdate.cron=0 0 0 * * ?
+#points.consume.cron=0 0 0 * * ?
#############################################
spring.cloud.consul.enabled=false
spring.cloud.consul.host=172.28.201.70
@@ -42,24 +55,24 @@
resttemplate.proxy.port=8087
#============== kafka ===================
-# 指定kafka 代理地址,可以多个
+# \u6307\u5B9Akafka \u4EE3\u7406\u5730\u5740\uFF0C\u53EF\u4EE5\u591A\u4E2A
#spring.kafka.bootstrap-servers=172.28.201.101:9192
#=============== provider =======================
spring.kafka.producer.retries=3
-# 每次批量发送消息的数量
+# \u6BCF\u6B21\u6279\u91CF\u53D1\u9001\u6D88\u606F\u7684\u6570\u91CF
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
-# 指定消息key和消息体的编解码方式
+# \u6307\u5B9A\u6D88\u606Fkey\u548C\u6D88\u606F\u4F53\u7684\u7F16\u89E3\u7801\u65B9\u5F0F
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
#===============kafka consumer =======================
spring.kafka.listen.auto.start=false
-# 指定默认消费者group id
+# \u6307\u5B9A\u9ED8\u8BA4\u6D88\u8D39\u8005group id
spring.kafka.consumer.group-id=epaymessager1
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100
-# 指定消息key和消息体的编解码方式
+# \u6307\u5B9A\u6D88\u606Fkey\u548C\u6D88\u606F\u4F53\u7684\u7F16\u89E3\u7801\u65B9\u5F0F
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties
index 06713bb..371f88b 100644
--- a/config/application-devel-pg.properties
+++ b/config/application-devel-pg.properties
@@ -3,6 +3,8 @@
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
+spring.jpa.show-sql=false
+logging.level.org.hibernate.SQL=error
# Postgresql settings
spring.datasource.platform=postgresql
#spring.datasource.url=jdbc:postgresql://ykt.supwisdom.com:15432/payapidev
@@ -28,31 +30,38 @@
##################################################
## quartz task scheduler
shopbalance.updater.cron=-
-##################多租户配置 end################################
+dayend.settletask.cron=-
+query.third.transdtl.result.cron=-
+payapi.sourcetype.checker.scheduler=-
+citizencard.dolosstask.cron=-
+send.delay.notice.task.cron=29 0/1 * * * ?
+points.outdate.cron=0 0 0 * * ?
+points.consume.cron=0 0 0 * * ?
+##################\u591A\u79DF\u6237\u914D\u7F6E end################################
#############################################
spring.cloud.consul.enabled=false
spring.cloud.consul.host=172.28.201.70
spring.cloud.consul.port=8500
#============== kafka ===================
-# 指定kafka 代理地址,可以多个
+# \u6307\u5B9Akafka \u4EE3\u7406\u5730\u5740\uFF0C\u53EF\u4EE5\u591A\u4E2A
spring.kafka.bootstrap-servers=172.28.201.101:9192
#=============== provider =======================
spring.kafka.producer.retries=3
-# 每次批量发送消息的数量
+# \u6BCF\u6B21\u6279\u91CF\u53D1\u9001\u6D88\u606F\u7684\u6570\u91CF
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
-# 指定消息key和消息体的编解码方式
+# \u6307\u5B9A\u6D88\u606Fkey\u548C\u6D88\u606F\u4F53\u7684\u7F16\u89E3\u7801\u65B9\u5F0F
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
#===============kafka consumer =======================
-# 指定默认消费者group id
+# \u6307\u5B9A\u9ED8\u8BA4\u6D88\u8D39\u8005group id
spring.kafka.listen.auto.start=false
spring.kafka.consumer.group-id=epaymessager1
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100
-# 指定消息key和消息体的编解码方式
+# \u6307\u5B9A\u6D88\u606Fkey\u548C\u6D88\u606F\u4F53\u7684\u7F16\u89E3\u7801\u65B9\u5F0F
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/BindCardParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/BindCardParam.java
index 92428f0..e8ada9d 100644
--- a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/BindCardParam.java
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/BindCardParam.java
@@ -26,6 +26,18 @@
@Sign
@NotEmpty(message = "手机号不能为空")
private String phone;
+ @Sign
+ @NotEmpty(message = "手机用户id不能为空")
+ private String uid;
+ @Sign
+ @NotEmpty(message = "rsaprivate不能为空")
+ private String rsaprivate;
+ @Sign
+ @NotEmpty(message = "secertkey不能为空")
+ private String secertkey;
+ @Sign
+ @NotEmpty(message = "rsapublic不能为空")
+ private String rsapublic;
@Override
public boolean checkParam() throws RequestParamCheckException {
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKey.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKey.java
new file mode 100644
index 0000000..d40cbc9
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKey.java
@@ -0,0 +1,18 @@
+package com.supwisdom.dlpay.api.bean;
+
+import lombok.*;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+public class CitizenQrcodeKey extends ApiResponse {
+ private String rootKey;
+ private String iv;
+ private String prefix; //二维码前缀
+ private long offsetSec = 0; //系统时间 - 请求的时间。默认系统时钟无偏差
+ private int totpStep = 5; //计算totp的step 默认5s算一个totp
+ private int totpOffset = 3; //校验totp的偏移量 默认前后5*3=15s二维码有效
+
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKeyParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKeyParam.java
new file mode 100644
index 0000000..004aa07
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodeKeyParam.java
@@ -0,0 +1,21 @@
+package com.supwisdom.dlpay.api.bean;
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.Sign;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+import com.supwisdom.dlpay.api.util.DateUtil;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CitizenQrcodeKeyParam extends APIRequestParam {
+ @Sign
+ private String timestamp;
+
+ @Override
+ public boolean checkParam() throws RequestParamCheckException {
+ if (!DateUtil.checkDatetimeValid(timestamp, DateUtil.DATETIME_FMT)) throw new RequestParamCheckException("时间戳格式错误[yyyyMMddHHmmss]");
+ return true;
+ }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodePayinitParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodePayinitParam.java
new file mode 100644
index 0000000..08d6789
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CitizenQrcodePayinitParam.java
@@ -0,0 +1,71 @@
+package com.supwisdom.dlpay.api.bean;
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.Sign;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+import com.supwisdom.dlpay.api.util.Constants;
+import com.supwisdom.dlpay.api.util.DateUtil;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Positive;
+import java.util.Arrays;
+
+@Getter
+@Setter
+public class CitizenQrcodePayinitParam extends APIRequestParam {
+ @Sign
+ @NotNull(message = "支付码不能为空")
+ private String qrcode;
+
+ @Sign
+ @NotNull(message = "请指定支付码是联机还是脱机")
+ private String qrcodeType; //online 或 offline
+
+ @Sign
+ @NotNull(message = "请指定交易商户")
+ private String shopaccno;
+
+ @Sign
+ @NotNull(message = "交易金额不能为空")
+ @Positive(message = "交易金额必须大于零")
+ private Integer amount;
+
+ @Sign
+ @NotNull(message = "对接系统唯一订单号不能为空")
+ private String billno;
+
+ @Sign
+ @NotNull(message = "交易日期不能为空")
+ private String transdate;
+
+ @Sign
+ @NotNull(message = "交易时间不能为空")
+ private String transtime;
+
+ @Sign
+ @NotNull(message = "流水类型不能为空")
+ private String dtltype;
+
+ @Sign
+ private String cardNo; //市民卡号
+
+ @Sign
+ private String tac; //脱机消费时要传流水的tac
+
+
+ @Override
+ public boolean checkParam() throws RequestParamCheckException {
+ if (!DateUtil.checkDatetimeValid(transdate, DateUtil.DATE_FMT))
+ throw new RequestParamCheckException("交易日期错误[yyyyMMdd]");
+ if (!DateUtil.checkDatetimeValid(transtime, DateUtil.TIME_FMT))
+ throw new RequestParamCheckException("交易时间错误[HHmmss]");
+ if (!Arrays.asList(Constants.STATE_ONLINE, Constants.STATE_OFFLINE).contains(qrcodeType))
+ throw new RequestParamCheckException("请指定支付码是联机还是脱机");
+ if (Constants.STATE_OFFLINE.equals(qrcodeType) && StringUtils.isEmpty(tac))
+ throw new RequestParamCheckException("脱机码时流水TAC不能为空");
+ return true;
+ }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodeParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodeParam.java
index cca7278..044235d 100644
--- a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodeParam.java
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodeParam.java
@@ -18,10 +18,10 @@
@NotEmpty(message = "手机用户id不能为空")
private String uid;
@Sign
- @NotEmpty(message = "secertkey不能为空")
+// @NotEmpty(message = "secertkey不能为空")
private String secertkey;
@Sign
- @NotEmpty(message = "rsapublic不能为空")
+// @NotEmpty(message = "rsapublic不能为空")
private String rsapublic;
@Override
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/SignBxyParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/SignBxyParam.java
new file mode 100644
index 0000000..43161f2
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/SignBxyParam.java
@@ -0,0 +1,39 @@
+package com.supwisdom.dlpay.api.bean;
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.Sign;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+
+@Getter
+@Setter
+public class SignBxyParam extends APIRequestParam {
+ @Sign
+ @NotEmpty(message = "用户id不能为空")
+ private String userid;
+ @Sign
+ @NotEmpty(message = "手机用户id不能为空")
+ private String uid;
+ @Sign
+ @NotEmpty(message = "rsaprivate不能为空")
+ private String rsaprivate;
+ @Sign
+ @NotEmpty(message = "secertkey不能为空")
+ private String secertkey;
+ @Sign
+ @NotEmpty(message = "rsapublic不能为空")
+ private String rsapublic;
+ @Sign
+ @NotEmpty(message = "验证码不能为空")
+ private String code;
+ @Sign
+ @NotEmpty(message = "手机号不能为空")
+ private String phone;
+ @Override
+ public boolean checkParam() throws RequestParamCheckException {
+ return true;
+ }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/util/Constants.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/util/Constants.java
index 3c4aebe..17484d4 100644
--- a/payapi-common/src/main/java/com/supwisdom/dlpay/api/util/Constants.java
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/util/Constants.java
@@ -4,4 +4,8 @@
public static final String SEX_MALE = "male";
public static final String SEX_FEMALE = "female";
+ public static final String STATE_ONLINE = "online";
+ public static final String STATE_OFFLINE = "offline";
+
+
}
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/CitizenCardPayProxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/CitizenCardPayProxy.java
index d68a238..d4c42b7 100644
--- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/CitizenCardPayProxy.java
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/CitizenCardPayProxy.java
@@ -1,9 +1,8 @@
package com.supwisdom.dlpay.paysdk.proxy;
-import com.supwisdom.dlpay.api.bean.CitizenCardPayfinishParam;
-import com.supwisdom.dlpay.api.bean.CitizenCardPayinitParam;
-import com.supwisdom.dlpay.api.bean.CitizenPayResponse;
+import com.supwisdom.dlpay.api.bean.*;
import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -17,4 +16,11 @@
@RequestMapping(value = "/api/consume/citizencard/payfinish", method = RequestMethod.GET)
CitizenPayResponse citizencardPayFinish(@RequestBody CitizenCardPayfinishParam param);
+
+ @PostMapping("/api/consume/citizencard/qrcodekey")
+ CitizenQrcodeKey queryQrcodeKey(@RequestBody CitizenQrcodeKeyParam param);
+
+ @PostMapping("/api/consume/citizencard/qrcodepayinit")
+ CitizenPayResponse citizencardQrcodePayinit(@RequestBody CitizenQrcodePayinitParam param);
+
}
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
index d336ab1..341b604 100644
--- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
@@ -78,7 +78,7 @@
Map<String, Object> bindCardCode(@RequestParam("userid") String userid,@RequestParam("phone") String phone);
@PostMapping("/api/user/signbxy")
- ApiResponse signbxy(@RequestParam("userid") String userid,@RequestParam("code") String code,@RequestParam("phone") String phone);
+ ApiResponse signbxy(@RequestBody SignBxyParam param);
@PostMapping("/api/user/unsignbxy")
ApiResponse unsignbxy(@RequestParam("userid") String userid,@RequestParam("phone") String phone);
@@ -88,4 +88,7 @@
@PostMapping("/api/user/consumetask")
ApiResponse consumeTask(@RequestParam("userid") String userid);
+
+ @PostMapping("/api/user/queryCitizenCard")
+ DoorQrcodeResponse queryCitizenCard(@RequestParam("uid") String uid);
}
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 364856c..62de5b2 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -57,55 +57,89 @@
docker.dependsOn(jar)
+repositories {
+ maven {
+ url "http://ykt-nx.supwisdom.com/repository/ecard-repo/"
+ credentials {
+ username 'ecard'
+ password 'Ecard4SUP'
+ }
+ }
+}
+
dependencies {
- implementation project(":payapi-common")
+ implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+ implementation 'org.springframework.boot:spring-boot-starter-data-redis'
+ implementation 'org.springframework.boot:spring-boot-starter-web'
+ implementation 'org.springframework.boot:spring-boot-starter-security'
+ implementation 'org.springframework.boot:spring-boot-starter-cache'
+ implementation 'org.springframework.boot:spring-boot-autoconfigure'
+ implementation 'org.springframework.security:spring-security-oauth2-jose'
+ implementation 'org.springframework.security:spring-security-oauth2-client'
+ implementation 'org.springframework.security:spring-security-oauth2-jose'
+ implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.5.RELEASE'
+ implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
+ implementation 'org.springframework.session:spring-session-data-redis'
+ implementation 'org.springframework.boot:spring-boot-starter-cache'
+ implementation 'org.springframework.kafka:spring-kafka'
+ implementation 'org.springframework.social:spring-social-web:1.1.6.RELEASE'
+ implementation 'org.springframework.kafka:spring-kafka:2.2.8.RELEASE'
+ implementation 'org.jetbrains.kotlin:kotlin-reflect'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ implementation 'commons-codec:commons-codec:1.12'
+ implementation 'org.apache.commons:commons-lang3:3.9'
+ implementation 'net.javacrumbs.shedlock:shedlock-spring:2.5.0'
+ implementation 'net.javacrumbs.shedlock:shedlock-provider-redis-spring:2.5.0'
- implementation "org.springframework.boot:spring-boot-starter-web"
- implementation "org.springframework.boot:spring-boot-starter-security"
- implementation "org.springframework.boot:spring-boot-starter-cache"
- implementation "org.springframework.boot:spring-boot-autoconfigure"
- implementation "org.springframework.security:spring-security-oauth2-jose"
- implementation "org.springframework.security:spring-security-oauth2-client"
- implementation "org.springframework.security:spring-security-oauth2-jose"
- implementation "org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure"
- implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
- implementation "org.springframework.session:spring-session-data-redis"
- implementation "org.springframework.boot:spring-boot-starter-cache"
- implementation "org.springframework.kafka:spring-kafka"
- implementation "org.springframework.social:spring-social-web:${springSocialVersion}"
- implementation "org.springframework.kafka:spring-kafka:${springKafkaVersion}"
+ implementation 'org.springframework.cloud:spring-cloud-starter'
+ implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery'
+ implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
+ implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard'
- implementation "org.springframework.cloud:spring-cloud-starter"
- implementation "org.springframework.cloud:spring-cloud-starter-consul-discovery"
- implementation "org.springframework.cloud:spring-cloud-starter-netflix-hystrix"
- implementation "org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard"
-
+ implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
runtime("org.springframework.boot:spring-boot-devtools")
- implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
+ implementation 'org.postgresql:postgresql:42.2.5'
+ implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
+ implementation 'com.jcabi:jcabi-manifests:1.1'
implementation 'org.bitbucket.b_c:jose4j:0.6.5'
+ implementation 'io.github.microutils:kotlin-logging:1.6.26'
+ implementation 'org.slf4j:slf4j-parent:1.7.26'
implementation 'com.github.penggle:kaptcha:2.3.2'
+ implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
implementation group: 'com.sun.jersey', name: 'jersey-client', version: '1.19'
implementation group: 'javax.servlet', name: 'jstl', version: '1.2'
implementation group: 'taglibs', name: 'standard', version: '1.1.2'
+ implementation group: 'commons-codec', name: 'commons-codec', version: '1.13'
implementation files('libs/masmgc.sdk.sms-0.0.1-SNAPSHOT.jar')
+ // implementation files('libs/ojdbc6.jar')
+ implementation 'commons-dbcp:commons-dbcp:1.4'
implementation 'commons-beanutils:commons-beanutils:1.9.3'
- implementation 'com.eatthepath:java-otp:0.1.0'
+
+ implementation 'log4j:log4j:1.2.17'
+ implementation 'com.alibaba:fastjson:1.2.60'
+
+ implementation 'com.eatthepath:java-otp:0.2.0'
implementation project(':payapi-common')
+ /*支付宝SDK*/
+ implementation group: 'com.alipay.sdk', name: 'alipay-sdk-java', version: '3.7.110.ALL'
+
+ /*大理二维码jar*/
+ implementation 'com.supwisdom:dlsmk-qrcode:1.3.5'
+
+ annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
+ annotationProcessor 'org.projectlombok:lombok:1.18.8'
+ compileOnly 'org.projectlombok:lombok:1.18.8'
implementation "org.apache.commons:commons-lang3:${lang3Version}"
implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
implementation "net.javacrumbs.shedlock:shedlock-provider-redis-spring:${shedlockVersion}"
- implementation "org.bitbucket.b_c:jose4j:${jose4jVersion}"
- implementation files("libs/masmgc.sdk.sms-0.0.1-SNAPSHOT.jar")
- implementation "commons-beanutils:commons-beanutils:${beanutilsVersion}"
- /*支付宝SDK*/
- implementation "com.alipay.sdk:alipay-sdk-java:${alipaySDKVersion}"
-
- implementation "com.github.penggle:kaptcha:${kaptchaVersion}"
- implementation "com.sun.jersey:jersey-client:${jerseyClientVersion}"
-
+ // providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
+ testImplementation 'org.springframework.boot:spring-boot-starter-test'
+ testImplementation 'io.rest-assured:rest-assured:3.3.0'
+ testImplementation 'io.rest-assured:spring-mock-mvc:3.3.0'
+ testImplementation 'org.hamcrest:hamcrest:2.1'
}
tasks.withType(JavaCompile) {
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/citizencard/YnrccUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/citizencard/YnrccUtil.java
index 79daf31..ed46360 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/agent/citizencard/YnrccUtil.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/citizencard/YnrccUtil.java
@@ -23,12 +23,13 @@
public static final String YNRCC_SIGNKEY = "ynrcc.agent.signkey"; //农商行前置md5key
public static final String YNRCC_MERCHANT_BANKCARDNO = "merchant.bankcardno";
public static final String YNRCC_MERCHANT_BANKACCNAME = "merchant.bankaccname";
+ public static final String YNRCC_MERCHANT_BANKCARDNO_SEQNO = "merchant.bankcardno.seqno";
public static final String PARAM_CONFIG_ERROR = "90000"; // 参数未配置
public static final String PARAM_VALUE_ERROR = "90001"; // 参数值错误
- public static final int AGENT_CONNECT_TIMEOUT = 10;
- public static final int AGENT_READ_TIMEOUT = 10;
+ public static final int AGENT_CONNECT_TIMEOUT = 20;
+ public static final int AGENT_READ_TIMEOUT = 20;
public static final String TRANSTYPE_SIGNCARD = "1"; //签约
public static final String TRANSTYPE_UNSIGNCARD = "2"; //解约
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/UserSecretDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/UserSecretDao.java
new file mode 100644
index 0000000..22c5757
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/UserSecretDao.java
@@ -0,0 +1,10 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TUserSecret;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+public interface UserSecretDao extends JpaRepository<TUserSecret, String> {
+ @Query("from TUserSecret a where a.uid=?1 ")
+ TUserSecret getByUid(String uid);
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TUserSecret.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TUserSecret.java
new file mode 100644
index 0000000..e5598e9
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TUserSecret.java
@@ -0,0 +1,66 @@
+package com.supwisdom.dlpay.api.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "tb_user_secret")
+public class TUserSecret {
+ @Id
+ @Column(name = "uid", nullable = false, length = 32)
+ private String uid;
+
+ @Column(name = "userid", length = 32)
+ private String userid;
+
+ @Column(name = "rsaprivate", length = 1000)
+ private String rsaprivate;
+
+ @Column(name = "rsapublic", length = 1000)
+ private String rsapublic;
+
+ @Column(name = "secertkey", length = 64)
+ private String secertkey;
+
+ public String getUid() {
+ return uid;
+ }
+
+ public void setUid(String uid) {
+ this.uid = uid;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+ public String getRsaprivate() {
+ return rsaprivate;
+ }
+
+ public void setRsaprivate(String rsaprivate) {
+ this.rsaprivate = rsaprivate;
+ }
+
+ public String getRsapublic() {
+ return rsapublic;
+ }
+
+ public void setRsapublic(String rsapublic) {
+ this.rsapublic = rsapublic;
+ }
+
+ public String getSecertkey() {
+ return secertkey;
+ }
+
+ public void setSecertkey(String secertkey) {
+ this.secertkey = secertkey;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
index 14631ee..62c471f 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
@@ -148,7 +148,7 @@
if (!StringUtil.isEmpty(list)) {
for (TSourceTypeConfig config : list) {
if (config.getGlobalflag()) {
- result.put(config.getConfigid(), config.getConfigValue()); //统用参数
+ result.put(config.getConfigid(), config.getConfigValue()); //通用参数
}
}
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/citizencard/dao/MonitorDlsmkCardpayDao.java b/payapi/src/main/java/com/supwisdom/dlpay/citizencard/dao/MonitorDlsmkCardpayDao.java
new file mode 100644
index 0000000..283cc05
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/citizencard/dao/MonitorDlsmkCardpayDao.java
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.citizencard.dao;
+
+import com.supwisdom.dlpay.citizencard.domain.TMonitorDlsmkCardpay;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface MonitorDlsmkCardpayDao extends JpaRepository<TMonitorDlsmkCardpay, String> {
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/citizencard/domain/TMonitorDlsmkCardpay.java b/payapi/src/main/java/com/supwisdom/dlpay/citizencard/domain/TMonitorDlsmkCardpay.java
new file mode 100644
index 0000000..6ffcaa3
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/citizencard/domain/TMonitorDlsmkCardpay.java
@@ -0,0 +1,64 @@
+package com.supwisdom.dlpay.citizencard.domain;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "TB_MONITOR_DLSMK_CARDPAY",
+ indexes = {@Index(name = "idx_monitor_dlsmk_cardpay", columnList = "request_url, createtime")})
+public class TMonitorDlsmkCardpay {
+ @Id
+ @Column(name = "refno", length = 32, nullable = false)
+ private String refno;
+
+ @Column(name = "use_milli_sec", precision = 9)
+ private Long useMilliSec;
+
+ @Column(name = "resp_json", length = 600)
+ private String respJson;
+
+ @Column(name = "request_url", length = 200)
+ private String requestUrl;
+
+ @Column(name = "createtime", length = 14)
+ private String createtime;
+
+ public String getRefno() {
+ return refno;
+ }
+
+ public void setRefno(String refno) {
+ this.refno = refno;
+ }
+
+ public Long getUseMilliSec() {
+ return useMilliSec;
+ }
+
+ public void setUseMilliSec(Long useMilliSec) {
+ this.useMilliSec = useMilliSec;
+ }
+
+ public String getRespJson() {
+ return respJson;
+ }
+
+ public void setRespJson(String respJson) {
+ this.respJson = respJson;
+ }
+
+ public String getRequestUrl() {
+ return requestUrl;
+ }
+
+ public void setRequestUrl(String requestUrl) {
+ this.requestUrl = requestUrl;
+ }
+
+ public String getCreatetime() {
+ return createtime;
+ }
+
+ public void setCreatetime(String createtime) {
+ this.createtime = createtime;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/PointsDtlDao.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/PointsDtlDao.java
index 5e3d58e..47f82c4 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/PointsDtlDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/dao/PointsDtlDao.java
@@ -19,8 +19,12 @@
TPointsdtl findAllByUseridAndTransdateAndRefnoAndStatusAndFlagstatus(String userid,String transdate,Integer refno, String status,String flagstatus);
+ TPointsdtl findAllByUseridAndTransdateAndRefnoAndStatusAndFlagstatusAndType(String userid,String transdate,Integer refno, String status,String flagstatus,String type);
+
TPointsdtl findAllByUseridAndTransdateStartingWithAndRefnoAndStatusAndFlagstatus(String userid,String transdate,Integer refno, String status,String flagstatus);
+ TPointsdtl findAllByUseridAndTransdateStartingWithAndRefnoAndStatusAndFlagstatusAndType(String userid,String transdate,Integer refno, String status,String flagstatus,String type);
+
TPointsdtl findAllByUseridAndBillnoAndTypeAndRefno(String userid,String billno,String type,Integer refno);
TPointsdtl findAllByUseridAndBillnoAndType(String userid,String billno,String type);
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/PointsServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/PointsServiceImpl.java
index 2d638b6..15c08ce 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/PointsServiceImpl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/PointsServiceImpl.java
@@ -77,7 +77,7 @@
@Override
public LevelBean getLevel() {
LevelBean levelBean = new LevelBean();
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TPersonLevel> list = personLevelDao.findAll(sort);
if (!StringUtil.isEmpty(list)) levelBean.settPersonLevels(list);
return levelBean;
@@ -197,7 +197,7 @@
@Override
public LevelBean getConpoints() {
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TConsumePoints> monthlist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_MONTH, sort);
List<TConsumePoints> daylist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_DAY, sort);
LevelBean levelBean = new LevelBean();
@@ -364,7 +364,7 @@
List<TPersondtl> tPersondtl = getShopdtlByUseridAndTransdateAndReverseFlagAndStatus(userid, DateUtil.getNow("yyyyMMdd"), "none", "success");
Double allConsume = -tPersondtl.stream().mapToDouble(o -> o.getAmount()).sum();
// 2.循环日消费奖励规则
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TConsumePoints> daylist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_DAY, sort);
for (TConsumePoints temp : daylist) {
// 3.判断用户是否达到日消费奖励积分的额度,达到再次判断是否获得过该规则的奖励
@@ -409,7 +409,7 @@
List<TPersondtl> tPersondtl = getShopdtlByUseridAndTransdateStartingWithAndReverseFlagAndStatus(userid, DateUtil.getNow("yyyyMM"), "none", "success");
Double allConsume = -tPersondtl.stream().mapToDouble(o -> o.getAmount()).sum();
// 2.循环月消费奖励规则
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TConsumePoints> monthlist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_MONTH, sort);
for (TConsumePoints temp : monthlist) {
// 3.判断用户是否达到月消费奖励积分的额度,达到再次判断是否获得过该规则的奖励
@@ -472,12 +472,12 @@
List<TPersondtl> tPersondtl = getShopdtlByUseridAndTransdateAndReverseFlagAndStatus(userid, tPointsdtl.getTransdate(), "none", "success");
Double allConsume = -tPersondtl.stream().mapToDouble(o -> o.getAmount()).sum();
// 2.2.循环日消费奖励规则
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TConsumePoints> daylist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_DAY, sort);
for (TConsumePoints temp : daylist) {
// 2.3.判断用户是否达到日消费奖励积分的额度,没有达到再次判断是否获得过该规则的奖励
if (allConsume < Double.parseDouble(temp.getConsumeamount())) {
- TPointsdtl tPointsdtltemp = pointsDtlDao.findAllByUseridAndTransdateAndRefnoAndStatusAndFlagstatus(userid, tPointsdtl.getTransdate(), temp.getId(),"init","in");
+ TPointsdtl tPointsdtltemp = pointsDtlDao.findAllByUseridAndTransdateAndRefnoAndStatusAndFlagstatusAndType(userid, tPointsdtl.getTransdate(), temp.getId(),"init","in",TradeDict.CONSUME_FLAG_DAY);
if (tPointsdtltemp != null) {
tPointsdtltemp.setStatus("refund");
pointsDtlDao.save(tPointsdtltemp);
@@ -502,7 +502,7 @@
for (TConsumePoints temp : monthlist) {
// 3.3.判断用户是否达到月消费奖励积分的额度,没有达到再次判断是否获得过该规则的奖励
if (allConsumeMonth < Double.parseDouble(temp.getConsumeamount())) {
- TPointsdtl tPointsdtltemp = pointsDtlDao.findAllByUseridAndTransdateStartingWithAndRefnoAndStatusAndFlagstatus(userid, tPointsdtl.getTransdate(), temp.getId(),"init","in");
+ TPointsdtl tPointsdtltemp = pointsDtlDao.findAllByUseridAndTransdateStartingWithAndRefnoAndStatusAndFlagstatusAndType(userid, tPointsdtl.getTransdate(), temp.getId(),"init","in",TradeDict.CONSUME_FLAG_MONTH);
if (tPointsdtltemp != null) {
tPointsdtltemp.setStatus("refund");
pointsDtlDao.save(tPointsdtltemp);
@@ -587,7 +587,7 @@
}
public TPersonLevel getLevelByPoints(Integer points) {
- Sort sort = new Sort(Sort.Direction.DESC, "id");
+ Sort sort = Sort.by(Sort.Order.desc("id"));
List<TPersonLevel> all = personLevelDao.findAll(sort);
for (TPersonLevel temp : all) {
if (points >= temp.getPointsLower()) {
@@ -763,7 +763,7 @@
personLevelResult.setPointsName("消费积分");
levelBeans.add(personLevelResult);
//统计日累计消费积分
- Sort sort = new Sort(Sort.Direction.ASC, "id");
+ Sort sort = Sort.by("id");
List<TConsumePoints> daylist = consumePointsDao.findAllByAndRuletype(TradeDict.CONSUME_POINTS_DAY, sort);
List<Integer> dayTemp = daylist.stream().map(TConsumePoints::getId).collect(Collectors.toList());
LevelBean dayTempBean = new LevelBean();
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/UserDataServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/UserDataServiceImpl.java
index a7ed10e..33d8df2 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/UserDataServiceImpl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/service/impl/UserDataServiceImpl.java
@@ -196,7 +196,9 @@
@Override
public PageResult<CitizenCardShowBean> getUserCitizenCardPage(CitizenCardSearchBean param, int pageNo, int pageSize) {
- StringBuffer querySql = new StringBuffer("select t.id as cid,t.cardno,t.cardtype,t.cardphyid,case when t.status='closed' then 'closed' else t.trans_status end as status,t.expiredate,a.id as bkid,a.cardno as bankcardno,a.signed,t.lastsaved,t.userid,p.name as username,p.idtype,p.idno,p.mobile,p.email,p.sex,t.bus_card_type as buscardtype \n" +
+ StringBuffer querySql = new StringBuffer("select t.id as cid,t.cardno,t.cardtype,t.cardphyid, \n" +
+ "case when t.status='closed' or a.status='closed' then 'closed' when t.status='normal' and a.status='normal' and t.trans_status='normal' and a.trans_status='normal' then 'normal' when t.trans_status='normal' then a.trans_status else t.trans_status end as status, \n" +
+ "t.expiredate,a.id as bkid,a.cardno as bankcardno,a.signed,t.lastsaved,t.userid,p.name as username,p.idtype,p.idno,p.mobile,p.email,p.sex,t.bus_card_type as buscardtype \n" +
"from tb_card t left join tb_card a on a.cardtype='bankcard' and a.cardphyid=t.cardphyid and a.userid=t.userid left join tb_person p on p.userid=t.userid \n" +
"where t.cardtype='citizencard' ");
StringBuffer countSql = new StringBuffer("select count(t.id) as cnt \n" +
@@ -313,7 +315,7 @@
if (!StringUtil.isEmpty(cardList)) throw new WebCheckException("物理卡号已经存在!");
TPerson person = personDao.findByIdentity(idtype.trim(), idno.trim());
- if (null != person && username.trim().equals(person.getName()))
+ if (null != person && !username.trim().equals(person.getName()))
throw new WebCheckException("证件号对应的用户已经存在,且姓名不匹配!");
SystemDateTime dt = systemUtilService.getSysdatetime();
@@ -394,9 +396,10 @@
bankCard.setCardtype(ConstantUtil.CARDTYPE_BANKCARD);
bankCard.setCardphyid(cardphyid.trim());
bankCard.setStatus(TradeDict.STATUS_NORMAL);
- bankCard.setTransStatus(TradeDict.STATUS_NORMAL);
+ bankCard.setTransStatus(cardstatus.trim());
if (TradeDict.STATUS_CLOSED.equals(cardstatus.trim())) {
bankCard.setStatus(TradeDict.STATUS_CLOSED);
+ bankCard.setTransStatus(TradeDict.STATUS_ABNORMAL);
}
bankCard.setExpiredate("21991231");
bankCard.setSigned("1".equals(signstatus.trim()));
@@ -535,6 +538,7 @@
cardUpdate = true;
}
} else if (!cardstatus.trim().equals(cityCard.getTransStatus())) {
+ cityCard.setStatus(TradeDict.STATUS_NORMAL);
cityCard.setTransStatus(cardstatus.trim());
cardUpdate = true;
}
@@ -562,10 +566,17 @@
bankCard.setCardphyid(cityCard.getCardphyid());
bankcardUpdate = true;
}
- if(TradeDict.STATUS_CLOSED.equals(cardstatus.trim()) && !TradeDict.STATUS_CLOSED.equals(bankCard.getStatus())){
- bankCard.setStatus(TradeDict.STATUS_CLOSED);
+ if (TradeDict.STATUS_CLOSED.equals(cardstatus.trim())) {
+ if (!TradeDict.STATUS_CLOSED.equals(bankCard.getStatus())) {
+ bankCard.setStatus(TradeDict.STATUS_CLOSED);
+ bankcardUpdate = true;
+ }
+ } else if (!cardstatus.trim().equals(bankCard.getTransStatus())) {
+ bankCard.setStatus(TradeDict.STATUS_NORMAL);
+ bankCard.setTransStatus(cardstatus.trim());
bankcardUpdate = true;
}
+
if(!bankCard.getSigned().equals("1".equals(signstatus.trim()))){
bankCard.setSigned("1".equals(signstatus.trim()));
bankcardUpdate = true;
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
index 526d447..b5c246d 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_service.kt
@@ -9,6 +9,7 @@
import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
import com.supwisdom.dlpay.agent.domain.QrcodePayTrans
import com.supwisdom.dlpay.api.domain.TTransactionMain
+import com.supwisdom.dlpay.api.service.TransactionMonitorService
import com.supwisdom.dlpay.framework.util.MoneyUtil
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@@ -37,6 +38,9 @@
@Autowired
private lateinit var citizencardPayService: CitizencardPayService
+ @Autowired
+ private lateinit var transactionMonitorService: TransactionMonitorService
+
private fun agentCode(code: String, msg: String?): org.springframework.data.util.Pair<AgentCode, YnrccRespCode> {
return YnrccUtil.errcode.firstOrNull {
it.second.code == code
@@ -55,8 +59,11 @@
}
override fun pay(transaction: TTransactionMain): AgentResponse<DtlStatus> {
+ val start = System.currentTimeMillis()
val resp = citizencardPayService.cardPay(transaction.shopDtl.shopaccno, transaction.personDtl.userid, transaction.accdate,
Math.abs(MoneyUtil.YuanToFen(transaction.personDtl.amount)), transaction.dtltype, transaction.refno)
+ val end = System.currentTimeMillis()
+ transactionMonitorService.doMonitorCitizenCardPay(transaction.refno, end - start, resp)
return AgentResponse<DtlStatus>().also {
val code = agentCode(resp.code, resp.message)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
index 0d97656..7756d8c 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/citizencard_service_impl.kt
@@ -170,7 +170,8 @@
}
val merchantBankcardno = config[YnrccUtil.YNRCC_MERCHANT_BANKCARDNO]
val merchantBankaccname = config[YnrccUtil.YNRCC_MERCHANT_BANKACCNAME]
- if (StringUtil.isEmpty(merchantBankcardno) || StringUtil.isEmpty(merchantBankaccname)) {
+ val merchantBankcardnoSeqno = config[YnrccUtil.YNRCC_MERCHANT_BANKCARDNO_SEQNO]
+ if (StringUtil.isEmpty(merchantBankcardno) || StringUtil.isEmpty(merchantBankaccname) || StringUtil.isEmpty(merchantBankcardnoSeqno)) {
resp.code = YnrccUtil.PARAM_CONFIG_ERROR
resp.message = "系统参数未配置[商户收款银行账号]"
logger.error(resp.message)
@@ -190,7 +191,7 @@
resp.message = "用户[${person.userid}]未绑定银行卡"
logger.error(resp.message)
return resp
- } else if (TradeDict.STATUS_NORMAL != userBankcard.status) {
+ } else if (TradeDict.STATUS_NORMAL != userBankcard.transStatus) {
resp.code = "99"
resp.message = "用户[${person.userid}]绑定银行卡状态异常"
logger.error(resp.message)
@@ -216,6 +217,7 @@
params["idno"] = person.idno
params["merchant_bankcardno"] = merchantBankcardno!!
params["merchant_bankaccname"] = merchantBankaccname!!
+ params["merchant_bankcardno_seqno"] = merchantBankcardnoSeqno!!
params["amount"] = amount.toString()
params["scenario"] = scenario
params["description"] = "市民卡代扣消费"
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
index 87e054c..692be0c 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
@@ -11,6 +11,7 @@
import com.supwisdom.dlpay.api.bean.groups.InitAction
import com.supwisdom.dlpay.api.domain.TSourceType
import com.supwisdom.dlpay.api.service.*
+import com.supwisdom.dlpay.api.util.Constants
import com.supwisdom.dlpay.exception.TransactionCheckException
import com.supwisdom.dlpay.framework.ResponseBodyBuilder
import com.supwisdom.dlpay.framework.service.SystemUtilService
@@ -780,12 +781,12 @@
* */
@PostMapping("/qrcodequery")
fun qrcodeQuery(@RequestBody param: DoorQRCodeParam): ResponseEntity<Any> {
- val token = redisTemplate.opsForValue().get(param.qrcode)
- if (token.isNullOrEmpty()) {
- return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(1, "二维码不存在或已失效"))
- }
- val ret = qrCodeService.decodeCode(token)
+// val token = redisTemplate.opsForValue().get(param.qrcode)
+// if (token.isNullOrEmpty()) {
+// return ResponseEntity.ok(ResponseBodyBuilder.create()
+// .fail(1, "二维码不存在或已失效"))
+// }
+ val ret = qrCodeService.decodeCode(param.qrcode)
return if (ret.retcode == 0) {
ResponseEntity.ok(ResponseBodyBuilder.create()
.success(ret, "成功"))
@@ -794,4 +795,48 @@
.fail(ret.retcode, ret.retmsg))
}
}
+
+ /**
+ * ============================================================================
+ * 获取市民卡二维码key
+ * ============================================================================
+ * */
+ @PostMapping("/citizencard/qrcodekey")
+ fun queryQrcodeKey(@RequestBody param: CitizenQrcodeKeyParam): ResponseEntity<Any> {
+ val ret = qrCodeService.queryQRCodeKeys(param.timestamp)
+ return if (ret.retcode == 0) {
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .success(ret, "成功"))
+ } else {
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .fail(ret.retcode, ret.retmsg))
+ }
+ }
+
+ /**
+ * ============================================================================
+ * 市民卡二维码交易初始化
+ * ============================================================================
+ * */
+ @PostMapping("/citizencard/qrcodepayinit")
+ fun citizencardQrcodePayinit(@Valid @RequestBody param: CitizenQrcodePayinitParam): ResponseEntity<Any> {
+ val pInfo = qrCodeService.decodeCode(param.qrcode, "online" == param.qrcodeType, param.cardNo, param.tac, param.amount, param.transdate, param.transtime)
+ if (pInfo.retcode != 0) {
+ //二维码解析错误
+ return ResponseEntity.ok(ResponseBodyBuilder.create().fail(pInfo.retcode, pInfo.retmsg))
+ }
+
+ //直接走 市民卡[交易初始化] 接口
+ return citizencardPayinit(CitizenCardPayinitParam().apply {
+ cardNo = pInfo.citycardno
+ shopaccno = param.shopaccno
+ amount = param.amount
+ billno = param.billno
+ transdate = param.transdate
+ transtime = param.transtime
+ dtltype = param.dtltype
+ })
+ }
+
+
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
index 2c80fa3..047cf60 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
@@ -537,10 +537,14 @@
}
if (resp.sinstatus == YnrccUtil.TRANSTYPE_SIGNCARD ) {
signed = TradeDict.STATUS_YES
- if(!card.signed){
- card.signed = true
- mobileApiService.saveCard(card)
- }
+ //绑卡返回已签约,保存UserSecret
+ mobileApiService.signBxy(card,SignBxyParam().apply {
+ this.userid = person.userid
+ this.uid = param.uid
+ this.secertkey = param.secertkey
+ this.rsapublic = param.rsapublic
+ this.rsaprivate = param.rsaprivate
+ })
}
return ResponseEntity.ok(ResponseBodyBuilder.create().data("signed", signed)
.success("ok"))
@@ -579,19 +583,18 @@
* 签约银行协议
*/
@PostMapping("/signbxy")
- fun signbxy(userid: String, code: String, phone: String): ResponseEntity<Any> {
- val card = mobileApiService.findCardByUserid(userid)
+ fun signbxy(@RequestBody param:SignBxyParam): ResponseEntity<Any> {
+ val card = mobileApiService.findCardByUserid(param.userid)
?: return ResponseEntity.ok(ResponseBodyBuilder.create()
.fail(400, "卡片不存在,请重新绑定"))
//call sign api
val person = userService.findOnePersonByUserid(card.userid)
- val resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, phone, YnrccUtil.TRANSTYPE_SIGNCARD, code)
+ val resp = citizencardPayService.signCard(card.cardno, person.name, person.idtype, person.idno, param.phone, YnrccUtil.TRANSTYPE_SIGNCARD, param.code)
if (resp.code != "0000") {
return ResponseEntity.ok(ResponseBodyBuilder.create()
.fail(500, resp.message))
}
- card.signed = true
- mobileApiService.saveCard(card)
+ mobileApiService.signBxy(card,param)
return ResponseEntity.ok(ResponseBodyBuilder.create()
.success("ok"))
}
@@ -647,4 +650,19 @@
.fail(400,"未完成首次消费任务"))
}
}
+
+ /**
+ * 根据手机用户uid查询市民卡信息
+ * */
+ @PostMapping("/queryCitizenCard")
+ fun queryCitizenCard(uid: String): ResponseEntity<Any> {
+ val ret = mobileApiService.findCardInfoByMobileUid(uid)
+ return if (ret.retcode == 0) {
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .success(ret, "成功"))
+ } else {
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .fail(ret.retcode, ret.retmsg))
+ }
+ }
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt
index e99baab..5c60767 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt
@@ -33,4 +33,9 @@
@Transactional(rollbackFor = arrayOf(Exception::class))
fun updateCardTransStatus(cardno:String,status:String): ApiResponse
+
+ @Transactional(rollbackFor = arrayOf(Exception::class), readOnly = true)
+ fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard?
+
+ fun findCardByCardnoAndCardtype(cardno: String, cardtype: String): TCard?
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt
index 6c67732..34e3cbf 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt
@@ -26,10 +26,13 @@
class CardServiceImpl : CardService {
@Autowired
lateinit var cardDao: CardDao
+
@Autowired
lateinit var personDao: PersonDao
+
@Autowired
- lateinit var citizencardLossApplyDao:CitizencardLossApplyDao
+ lateinit var citizencardLossApplyDao: CitizencardLossApplyDao
+
@Autowired
lateinit var systemUtilService: SystemUtilService
@@ -125,8 +128,8 @@
query.unwrap(NativeQueryImpl::class.java).setResultTransformer(Transformers.aliasToBean(CitizenCardInfo::class.java))
@Suppress("UNCHECKED_CAST")
val list = query.resultList as List<CitizenCardInfo>
- resp.retcode=0
- resp.retmsg="OK"
+ resp.retcode = 0
+ resp.retmsg = "OK"
resp.cards = list
return resp
}
@@ -173,7 +176,7 @@
override fun getCardByCardNoOrUserid(param: QueryCardParam): TCard? {
if (!StringUtil.isEmpty(param.cardno)) {
return cardDao.findCardByCardnoAndCardtype(param.cardno, param.cardtype)
- }else if (!StringUtil.isEmpty(param.userid)) {
+ } else if (!StringUtil.isEmpty(param.userid)) {
return cardDao.findCardByUseridAndCardtype(param.userid, param.cardtype)
}
return null
@@ -224,4 +227,16 @@
this.retmsg = "ok"
}
}
+
+ override fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard? {
+ return if (cardphyid.isNullOrEmpty()) {
+ cardDao.findCardByUseridAndCardtype(userid, cardtype)
+ } else {
+ cardDao.findBankcardByCitizencard(userid, cardtype, cardphyid)
+ }
+ }
+
+ override fun findCardByCardnoAndCardtype(cardno: String, cardtype: String): TCard? {
+ return cardDao.findCardByCardnoAndCardtype(cardno, cardtype)
+ }
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt
index 38b6374..a55d1e9 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt
@@ -2,13 +2,16 @@
import com.google.gson.Gson
import com.supwisdom.dlpay.api.bean.ApiResponse
+import com.supwisdom.dlpay.api.bean.CitizenQrcodeKey
import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
import com.supwisdom.dlpay.api.bean.QrcodeParam
import com.supwisdom.dlpay.api.service.CardService
import com.supwisdom.dlpay.api.service.QRCodeService
import com.supwisdom.dlpay.api.service.UserService
+import com.supwisdom.dlpay.busqrcode.QrCode
import com.supwisdom.dlpay.framework.service.SystemUtilService
-import com.supwisdom.dlpay.framework.util.MD5
+import com.supwisdom.dlpay.framework.util.DateUtil
+import com.supwisdom.dlpay.framework.util.StringUtil
import com.supwisdom.dlpay.framework.util.TradeDict
import com.supwisdom.dlpay.mobile.service.MobileApiService
import com.supwisdom.dlpay.util.*
@@ -17,6 +20,7 @@
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service
+import com.supwisdom.dlpay.framework.util.MD5
import java.time.Duration
@Service
@@ -34,22 +38,46 @@
val logger = KotlinLogging.logger { }
- override fun encodeCode(uid:String): ApiResponse {
+ private fun qrcodeConfig(): Map<String, String> {
+ var step = "5"
+ var offset = "3"
+ var debug = "false"
+ var offlineQrcode = "false"
+ val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")?.trim() ?: ""
+ val iv = systemUtilService.getBusinessValue("aes.cfb.iv")?.trim() ?: ""
+ val prefix = systemUtilService.getBusinessValue("dlsmk.qrcode.prefix")?.trim() ?: ""
+ val totpStep = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.step") //fixme: jar包写死了5s
+ val totpOffset = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.offset")
+ val debugEnable = systemUtilService.getBusinessValue("dlsmk.qrcode.log.debug")
+ val newQrcode = systemUtilService.getBusinessValue("dlsmk.qrcode.offline.enable")
+ if (NumberUtils.isDigits(totpStep)) step = totpStep.trim()
+ if (NumberUtils.isDigits(totpOffset)) offset = totpOffset.trim()
+ if ("true".equals(debugEnable, true)) debug = "true"
+ if ("true".equals(newQrcode, true)) offlineQrcode = "true"
+ return mapOf("rootkey" to rootkey, "iv" to iv, "prefix" to prefix, "step" to step, "offset" to offset, "debug" to debug, "offline" to offlineQrcode)
+ }
+
+ override fun encodeCode(uid: String): ApiResponse {
var resp = ApiResponse()
- val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
- val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
- if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) {
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+ val offlineQRCode = "true" == config["offline"]
+
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
resp.retcode = 1
resp.retmsg = "秘钥未配置"
return resp
}
val muser = mobileApiService.findUserById(uid)
- if(muser==null||TradeDict.STATUS_NORMAL!=muser.status){
+ if (muser == null || TradeDict.STATUS_NORMAL != muser.status) {
resp.retcode = 1
resp.retmsg = "用户不存在或状态异常"
return resp
}
- if(muser.userid.isNullOrEmpty()){
+ if (muser.userid.isNullOrEmpty()) {
resp.retcode = 1
resp.retmsg = "用户未绑定身份"
return resp
@@ -63,142 +91,477 @@
resp.retcode = 1
resp.retmsg = "银行卡未签约"
return resp
+ } else if (TradeDict.STATUS_NORMAL != bankCard.transStatus) {
+ resp.retcode = 1
+ resp.retmsg = when (bankCard.transStatus) {
+ TradeDict.STATUS_UNUSE -> "银行卡未启用!"
+ TradeDict.STATUS_LOST -> "银行卡已挂失!"
+ TradeDict.STATUS_LOCKED -> "银行卡已锁定!"
+ TradeDict.STATUS_FROZEN -> "银行卡已冻结!"
+ else -> "银行卡状态异常!"
+ }
+ return resp
}
- val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey)
- val rowdata = QrcodeRawData()
- rowdata.userid = muser.userid
- rowdata.setTotp(totp)
- val orgData = Gson().toJson(rowdata)
- val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic)
- val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData))
- val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding")
+ val cityCard = cardService.findCardByUseridAndCardphyid(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid)
+ if (null == cityCard) {
+ resp.retcode = 1
+ resp.retmsg = "用户未绑定市民卡"
+ return resp
+ } else if (TradeDict.STATUS_NORMAL != cityCard.status) {
+ resp.retcode = 1
+ resp.retmsg = "市民卡已注销"
+ return resp
+ } else if (TradeDict.STATUS_NORMAL != cityCard.transStatus) {
+ resp.retcode = 1
+ resp.retmsg = when (cityCard.transStatus) {
+ TradeDict.STATUS_UNUSE -> "市民卡未启用!"
+ TradeDict.STATUS_LOST -> "市民卡已挂失!"
+ TradeDict.STATUS_LOCKED -> "市民卡已锁定!"
+ TradeDict.STATUS_FROZEN -> "市民卡已冻结!"
+ else -> "市民卡状态异常!"
+ }
+ return resp
+ }
- val key = MD5.encodeByMD5ToURLSafeBase64(qrcode)
- redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20))
- resp.retcode = 0
- resp.retmsg = key
- return resp
+ if (offlineQRCode) {
+ //脱机码,新版本统一码
+ try {
+ val handle = QrCode.builder()
+ .rootKey(rootkey)
+ .iv(iv)
+ .uid(uid)
+ .card(cityCard.cardno, cityCard.busCardType ?: "80")
+ .prefix(prefix)
+ .debug(debug)
+ .create()
+ val qrcode = handle.qrcode()
+ resp.retcode = 0
+ resp.retmsg = qrcode
+ return resp
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码生成失败!${ex.message}"
+ return resp
+ }
+
+ } else {
+ //老版本H5码,存redis
+ val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey)
+ val rowdata = QrcodeRawData()
+ rowdata.userid = muser.userid
+ rowdata.setTotp(totp)
+ val orgData = Gson().toJson(rowdata)
+ val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic)
+ val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData))
+ val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding")
+
+ val key = MD5.encodeByMD5ToURLSafeBase64(qrcode)
+ redisTemplate.opsForValue().set(key, qrcode, Duration.ofSeconds(20))
+
+ resp.retcode = 0
+ resp.retmsg = key
+ return resp
+ }
+
}
override fun encodeCode(param: QrcodeParam): ApiResponse {
- var resp = ApiResponse()
+ val resp = ApiResponse()
val uid = param.uid
- val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
- val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
- if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) {
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+ val offlineQRCode = "true" == config["offline"]
+
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
resp.retcode = 1
resp.retmsg = "秘钥未配置"
return resp
}
- val totp = QrCodeTotpUtil.generateTOTP(param.secertkey)
- val rowdata = QrcodeRawData()
- rowdata.userid = param.userid
- rowdata.setTotp(totp)
- val orgData = Gson().toJson(rowdata)
- val publicKey = RSAKeysGenerate.getPublicKey(param.rsapublic)
- val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData))
- val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding")
- val key = MD5.encodeByMD5ToURLSafeBase64(qrcode)
- redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20))
- resp.retcode = 0
- resp.retmsg = key
- return resp
- }
-
- override fun decodeCode(qrcode :String): DoorQrcodeResponse {
-
- //解密
- var resp = DoorQrcodeResponse()
- try {
- val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
- val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
- val totpoffset = systemUtilService.getBusinessValue("aes.cfb.totp.offset")
- var offset = 3
- if (NumberUtils.isDigits(totpoffset)) {
- offset = Integer.valueOf(totpoffset).toInt()
- }
- if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) {
- resp.retcode = 1
- resp.retmsg = "秘钥未配置"
- return resp
- }
-
- println("vtoken=[" + qrcode + "],length=[" + qrcode.length + "]")
- val encdataBack = AesUtil.decryptCFB(qrcode, rootkey, iv, "AES/CFB/NoPadding")
- if (encdataBack == null) {
- resp.retcode = 1
- resp.retmsg = "二维码解析失败"
- return resp
- }
- val uid = encdataBack.substring(0, encdataBack.indexOf(":"))
- val muser = mobileApiService.findUserById(uid)
- if(muser==null||TradeDict.STATUS_NORMAL!=muser.status){
- resp.retcode = 1
- resp.retmsg = "用户不存在或状态异常"
- return resp
- }
- val seed32 = muser.secertkey
- val rawEncdata = encdataBack.substring(encdataBack.indexOf(":") + 1)
-
- val privateKey = RSAKeysGenerate.getPrivateKey(muser.rsaprivate)
- val rawdataBack = RSAKeysGenerate.decryptbyte(privateKey, rawEncdata)
- logger.info("rawdata_back=$rawdataBack")
-
- val rawData = Gson().fromJson(rawdataBack, QrcodeRawData::class.java)
- logger.info("userid=" + rawData.userid)
- if (null == rawData || rawData.userid.isNullOrEmpty()) {
- resp.retcode = 1
- resp.retmsg = "用户不存在"
- return resp
- }
- val person = userService.findOnePersonByUserid(rawData.userid)
-
- val bankcard = mobileApiService.findCardByUserid(person.userid)
- if (null == bankcard) {
- resp.retcode = 9
- resp.retmsg = "二维码识别错误,手机绑定的银行卡已注销"
- return resp
- }
- //银行卡必定存在对应的市民卡
- val citycard = mobileApiService.findCardByUseridAndCardphyid(person.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankcard.cardphyid)
- if (null == citycard) {
- resp.retcode = 8
- resp.retmsg = "二维码识别异常"
- return resp
- }
-
- val verifyBarcode = QrCodeTotpUtil.verifyCode(rawData.totp, seed32, offset)
- return if (verifyBarcode) {
- resp.retcode = 0
- resp.retmsg = "OK"
- resp.userid = person.userid
- resp.username = person.name
- resp.sex = person.sex
- resp.idtype = person.idtype
- resp.idno = person.idno
- resp.phone = person.mobile
-
- resp.citycardno = citycard.cardno
- resp.cardphyid = citycard.cardphyid
- resp.expiredate = citycard.expiredate
- resp.cardstatus = citycard.status
- resp.transstatus = citycard.transStatus
- resp.bankcardno = bankcard.cardno
- resp
- } else {
- resp.retcode = 1
- resp.retmsg = "二维码校验失败"
- resp
- }
- } catch (e: Exception) {
- e.printStackTrace()
+ val userSec = mobileApiService.findUserSecretByUid(uid)
+ if (null == userSec) {
resp.retcode = 1
- resp.retmsg = "二维码识别异常"
+ resp.retmsg = "识别手机用户身份失败!"
+ return resp
+ } else if (userSec.userid != param.userid) {
+ resp.retcode = 1
+ resp.retmsg = "手机用户身份识别异常!"
+ return resp
+ }
+ val bankCard = cardService.getBankcardByUserid(param.userid)
+ if (null == bankCard) {
+ resp.retcode = 1
+ resp.retmsg = "用户未绑定银行卡"
+ return resp
+ } else if (!bankCard.signed) {
+ resp.retcode = 1
+ resp.retmsg = "银行卡未签约"
+ return resp
+ } else if (TradeDict.STATUS_NORMAL != bankCard.transStatus) {
+ resp.retcode = 1
+ resp.retmsg = when (bankCard.transStatus) {
+ TradeDict.STATUS_UNUSE -> "银行卡未启用!"
+ TradeDict.STATUS_LOST -> "银行卡已挂失!"
+ TradeDict.STATUS_LOCKED -> "银行卡已锁定!"
+ TradeDict.STATUS_FROZEN -> "银行卡已冻结!"
+ else -> "银行卡状态异常!"
+ }
return resp
}
+ val cityCard = cardService.findCardByUseridAndCardphyid(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid)
+ if (null == cityCard) {
+ resp.retcode = 1
+ resp.retmsg = "用户未绑定市民卡"
+ return resp
+ } else if (TradeDict.STATUS_NORMAL != cityCard.status) {
+ resp.retcode = 1
+ resp.retmsg = "市民卡已注销"
+ return resp
+ } else if (TradeDict.STATUS_NORMAL != cityCard.transStatus) {
+ resp.retcode = 1
+ resp.retmsg = when (cityCard.transStatus) {
+ TradeDict.STATUS_UNUSE -> "市民卡未启用!"
+ TradeDict.STATUS_LOST -> "市民卡已挂失!"
+ TradeDict.STATUS_LOCKED -> "市民卡已锁定!"
+ TradeDict.STATUS_FROZEN -> "市民卡已冻结!"
+ else -> "市民卡状态异常!"
+ }
+ return resp
+ }
+
+ if (offlineQRCode) {
+ //脱机码,新版本统一码
+ try {
+ val handle = QrCode.builder()
+ .rootKey(rootkey)
+ .iv(iv)
+ .uid(uid)
+ .card(cityCard.cardno, cityCard.busCardType ?: "80")
+ .prefix(prefix)
+ .debug(debug)
+ .create()
+ val qrcode = handle.qrcode()
+ resp.retcode = 0
+ resp.retmsg = qrcode
+ return resp
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码生成失败!${ex.message}"
+ return resp
+ }
+
+ } else {
+ //老版本H5码,存redis
+ val totp = QrCodeTotpUtil.generateTOTP(userSec.secertkey)
+ val rowdata = QrcodeRawData()
+ rowdata.userid = userSec.userid
+ rowdata.setTotp(totp)
+ val orgData = Gson().toJson(rowdata)
+ val publicKey = RSAKeysGenerate.getPublicKey(userSec.rsapublic)
+ val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData))
+ val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding")
+
+ val key = MD5.encodeByMD5ToURLSafeBase64(qrcode)
+ redisTemplate.opsForValue().set(key, qrcode, Duration.ofSeconds(20))
+
+ resp.retcode = 0
+ resp.retmsg = key
+ return resp
+ }
+
+ }
+
+ override fun decodeCode(qrcode: String): DoorQrcodeResponse {
+ val resp = DoorQrcodeResponse()
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+ val offlineQRCode = "true" == config["offline"]
+ var offset = 3
+ if (offlineQRCode) {
+ val totpoffset = config["offset"]
+ if (NumberUtils.isDigits(totpoffset)) {
+ offset = totpoffset!!.toInt()
+ }
+ } else {
+ val totpoffset = systemUtilService.getBusinessValue("aes.cfb.totp.offset")
+ if (NumberUtils.isDigits(totpoffset)) {
+ offset = Integer.valueOf(totpoffset).toInt()
+ }
+ }
+
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "秘钥未配置"
+ return resp
+ }
+ println("vtoken=[" + qrcode + "],length=[" + qrcode.length + "]")
+
+ //解密
+ if(offlineQRCode){
+ //脱机码,新版本统一码
+ var data = HashMap<String, String>(0)
+ try {
+ val handle = QrCode.builder().rootKey(rootkey).iv(iv).prefix(prefix).debug(debug).create()
+ //fixme:解码已校验totp
+ data = handle.decodeWithTotpCheck(qrcode, offset) as HashMap<String, String>
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码解析报错,${ex.message}"
+ return resp
+ }
+ val uid = data[QrCode.FIELD_UID]
+ val cardno = data[QrCode.FIELD_CARDNO]
+ val cardtype = data[QrCode.FIELD_CARDTYPE] //公交卡类型
+ if (uid.isNullOrEmpty() || cardno.isNullOrEmpty() || cardtype.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "二维码解析后数据异常!"
+ return resp
+ }
+ val userSec = mobileApiService.findUserSecretByUid(uid)
+ val cityCard = cardService.findCardByCardnoAndCardtype(cardno, ConstantUtil.CARDTYPE_CITIZENCARD)
+ if(null==userSec || null == cityCard || cityCard.userid != userSec.userid){
+ resp.retcode = 1
+ resp.retmsg = "二维码用户信息错误,请刷新二维码后重试"
+ return resp
+ }
+ val person = userService.findPersonByUserid(cityCard.userid)
+ val bankcard = mobileApiService.findCardByUseridAndCardphyid(cityCard.userid, ConstantUtil.CARDTYPE_BANKCARD, cityCard.cardphyid)
+ if (null == person || null == bankcard) {
+ resp.retcode = 8
+ resp.retmsg = "用户市民卡信息异常"
+ return resp
+ }
+
+ resp.retcode = 0
+ resp.retmsg = "OK"
+ resp.userid = person.userid
+ resp.username = person.name
+ resp.sex = person.sex
+ resp.idtype = person.idtype
+ resp.idno = person.idno
+ resp.phone = person.mobile
+
+ resp.citycardno = cityCard.cardno
+ resp.cardphyid = cityCard.cardphyid
+ resp.expiredate = cityCard.expiredate
+ resp.cardstatus = cityCard.status
+ resp.transstatus = cityCard.transStatus
+ resp.bankcardno = bankcard.cardno
+ return resp
+
+ }else{
+ //老版本H5码,存redis
+ try {
+ val token = redisTemplate.opsForValue().get(qrcode) //fixme: 密文在redis中
+ if (token.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "二维码不存在或已失效"
+ return resp
+ }
+
+ val encdataBack = AesUtil.decryptCFB(token, rootkey, iv, "AES/CFB/NoPadding")
+ if (encdataBack == null) {
+ resp.retcode = 1
+ resp.retmsg = "二维码解析失败"
+ return resp
+ }
+ val uid = encdataBack.substring(0, encdataBack.indexOf(":"))
+// val muser = mobileApiService.findUserById(uid)
+ val muser = mobileApiService.findUserSecretByUid(uid) //查找userSecret
+ if (muser == null) {
+ resp.retcode = 1
+ resp.retmsg = "用户不存在或状态异常"
+ return resp
+ }
+ val seed32 = muser.secertkey
+ val rawEncdata = encdataBack.substring(encdataBack.indexOf(":") + 1)
+
+ val privateKey = RSAKeysGenerate.getPrivateKey(muser.rsaprivate)
+ val rawdataBack = RSAKeysGenerate.decryptbyte(privateKey, rawEncdata)
+ logger.info("rawdata_back=$rawdataBack")
+ val rawData = Gson().fromJson(rawdataBack, QrcodeRawData::class.java)
+ logger.info("userid=" + rawData.userid)
+ if (null == rawData || rawData.userid.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "用户不存在"
+ return resp
+ }
+
+ val person = userService.findOnePersonByUserid(rawData.userid)
+ val bankcard = mobileApiService.findCardByUserid(person.userid)
+ if (null == bankcard) {
+ resp.retcode = 9
+ resp.retmsg = "二维码识别错误,手机绑定的银行卡已注销"
+ return resp
+ }
+
+ //银行卡必定存在对应的市民卡
+ val citycard = mobileApiService.findCardByUseridAndCardphyid(person.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankcard.cardphyid)
+ if (null == citycard) {
+ resp.retcode = 8
+ resp.retmsg = "二维码识别异常"
+ return resp
+ }
+
+ val verifyBarcode = QrCodeTotpUtil.verifyCode(rawData.totp, seed32, offset)
+ return if (verifyBarcode) {
+ resp.retcode = 0
+ resp.retmsg = "OK"
+ resp.userid = person.userid
+ resp.username = person.name
+ resp.sex = person.sex
+ resp.idtype = person.idtype
+ resp.idno = person.idno
+ resp.phone = person.mobile
+
+ resp.citycardno = citycard.cardno
+ resp.cardphyid = citycard.cardphyid
+ resp.expiredate = citycard.expiredate
+ resp.cardstatus = citycard.status
+ resp.transstatus = citycard.transStatus
+ resp.bankcardno = bankcard.cardno
+ resp
+ } else {
+ resp.retcode = 1
+ resp.retmsg = "二维码校验失败"
+ resp
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码识别异常"
+ return resp
+ }
+ }
+
+ }
+
+ override fun decodeCode(qrcode: String, chkTotp: Boolean, cardno: String?, tac: String?, amount: Int?, transdate: String?, transtime: String?): DoorQrcodeResponse {
+ val resp = DoorQrcodeResponse()
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+ val offlineQRCode = "true" == config["offline"]
+ val totpoffset = config["offset"]
+ var offset = 3
+ if (NumberUtils.isDigits(totpoffset)) {
+ offset = totpoffset!!.toInt()
+ }
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "秘钥未配置"
+ return resp
+ }
+ if (!offlineQRCode) {
+ return decodeCode(qrcode) // fixme: 未启用脱机码,这里直接老版解码
+ }
+ println("citizencard QRcode=[" + qrcode + "],length=[" + qrcode.length + "]")
+
+ //解密
+ var data = HashMap<String, String>(0)
+ try {
+ val handle = QrCode.builder().rootKey(rootkey).iv(iv).prefix(prefix).debug(debug).create()
+ if (chkTotp) {
+ //联机解码需校验totp
+ data = handle.decodeWithTotpCheck(qrcode, offset) as HashMap<String, String>
+ } else {
+ //脱机码上传无需校验totp
+ data = handle.decodeWithoutTotpCheck(qrcode) as HashMap<String, String>
+ if (!data.isNullOrEmpty()) {
+ if (!StringUtil.isEmpty(cardno) && cardno!!.trim() != data[QrCode.FIELD_CARDNO]) {
+ resp.retcode = 1
+ resp.retmsg = "市民卡号错误!"
+ return resp //验证卡号是否一致
+ }
+ if (handle.tac(amount, transdate, transtime) != tac) {
+ resp.retcode = 1
+ resp.retmsg = "流水tac验证错误!"
+ return resp //验证脱机流水tac是否一致
+ }
+ }
+ }
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码解析报错,${ex.message}"
+ return resp
+ }
+
+ val uid = data[QrCode.FIELD_UID]
+ val cardno = data[QrCode.FIELD_CARDNO]
+ val cardtype = data[QrCode.FIELD_CARDTYPE] //公交卡类型
+ if (uid.isNullOrEmpty() || cardno.isNullOrEmpty() || cardtype.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "二维码解析后数据异常!"
+ return resp
+ }
+ val userSec = mobileApiService.findUserSecretByUid(uid)
+ val cityCard = cardService.findCardByCardnoAndCardtype(cardno, ConstantUtil.CARDTYPE_CITIZENCARD)
+ if (null == userSec || null == cityCard || cityCard.userid != userSec.userid) {
+ resp.retcode = 1
+ resp.retmsg = "二维码用户信息错误,请刷新二维码后重试"
+ return resp
+ }
+ val person = userService.findPersonByUserid(cityCard.userid)
+ val bankcard = mobileApiService.findCardByUseridAndCardphyid(cityCard.userid, ConstantUtil.CARDTYPE_BANKCARD, cityCard.cardphyid)
+ if (null == person || null == bankcard) {
+ resp.retcode = 8
+ resp.retmsg = "用户市民卡信息异常"
+ return resp
+ }
+
+ resp.retcode = 0
+ resp.retmsg = "OK"
+ resp.userid = person.userid
+ resp.username = person.name
+ resp.sex = person.sex
+ resp.idtype = person.idtype
+ resp.idno = person.idno
+ resp.phone = person.mobile
+
+ resp.citycardno = cityCard.cardno
+ resp.cardphyid = cityCard.cardphyid
+ resp.expiredate = cityCard.expiredate
+ resp.cardstatus = cityCard.status
+ resp.transstatus = cityCard.transStatus
+ resp.bankcardno = bankcard.cardno
+ return resp
+ }
+
+ override fun queryQRCodeKeys(timestamp: String): CitizenQrcodeKey {
+ val resp = CitizenQrcodeKey()
+ val offsetSec = DateUtil.getInterval(timestamp, DateUtil.getNow(DateUtil.DATETIME_FMT)) / 1000 //系统时间-请求时间 秒
+
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "秘钥未配置"
+ return resp
+ }
+ resp.retcode = 0
+ resp.retmsg = "success"
+ resp.rootKey = rootkey
+ resp.iv = iv
+ resp.prefix = config["prefix"] ?: ""
+ resp.offsetSec = offsetSec
+ resp.totpStep = config["step"]?.toInt() ?: 5
+ resp.totpOffset = config["offset"]?.toInt() ?: 3
+ return resp
}
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_monitor_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_monitor_service_impl.kt
new file mode 100644
index 0000000..b9d0031
--- /dev/null
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_monitor_service_impl.kt
@@ -0,0 +1,26 @@
+package com.supwisdom.dlpay.api.service.impl
+
+import com.google.gson.Gson
+import com.supwisdom.dlpay.agent.citizencard.DlpayResp
+import com.supwisdom.dlpay.api.service.TransactionMonitorService
+import com.supwisdom.dlpay.citizencard.dao.MonitorDlsmkCardpayDao
+import com.supwisdom.dlpay.citizencard.domain.TMonitorDlsmkCardpay
+import com.supwisdom.dlpay.framework.util.DateUtil
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class TransactionMonitorServiceImpl: TransactionMonitorService {
+ @Autowired
+ private lateinit var monitorDlsmkCardpayDao: MonitorDlsmkCardpayDao
+
+ override fun doMonitorCitizenCardPay(refno: String, milliSecond: Long, resp: DlpayResp) {
+ monitorDlsmkCardpayDao.save(TMonitorDlsmkCardpay().apply {
+ this.refno = refno
+ this.useMilliSec = milliSecond
+ this.respJson = Gson().toJson(resp)
+ this.requestUrl = "/api/consume/citizencard/payfinish"
+ this.createtime = DateUtil.getNow()
+ })
+ }
+}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt
index d8b21ce..a9b2db2 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt
@@ -1,6 +1,7 @@
package com.supwisdom.dlpay.api.service
import com.supwisdom.dlpay.api.bean.ApiResponse
+import com.supwisdom.dlpay.api.bean.CitizenQrcodeKey
import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
import com.supwisdom.dlpay.api.bean.QrcodeParam
@@ -8,4 +9,6 @@
fun encodeCode(uid: String): ApiResponse
fun encodeCode(param: QrcodeParam): ApiResponse
fun decodeCode(qrcode: String): DoorQrcodeResponse
+ fun decodeCode(qrcode: String, chkTotp: Boolean, cardno: String?, tac: String?, amount:Int?, transdate:String?, transtime:String?): DoorQrcodeResponse
+ fun queryQRCodeKeys(timestamp: String): CitizenQrcodeKey
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_monitor_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_monitor_service.kt
new file mode 100644
index 0000000..1929347
--- /dev/null
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_monitor_service.kt
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.api.service
+
+import com.supwisdom.dlpay.agent.citizencard.DlpayResp
+import org.springframework.transaction.annotation.Transactional
+
+interface TransactionMonitorService {
+ @Transactional(rollbackFor = [Exception::class])
+ fun doMonitorCitizenCardPay(refno: String, milliSecond: Long, resp: DlpayResp)
+}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index 8de6f1e..1214614 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -3,6 +3,7 @@
import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
import com.supwisdom.dlpay.agent.service.CitizencardPayService
import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.api.bean.SignBxyParam
import com.supwisdom.dlpay.api.service.QRCodeService
import com.supwisdom.dlpay.api.service.UserService
import com.supwisdom.dlpay.api.util.MobileNumberCheck
@@ -427,14 +428,18 @@
var needupdate = false
if (resp.sinstatus == YnrccUtil.TRANSTYPE_SIGNCARD ) {
signed = TradeDict.STATUS_YES
- if(!card.signed){
- card.signed = true
- mobileApiService.saveCard(card)
- }
- user.signedtime = DateUtil.getNow();
+ // 如果银行返回已签约,保存密钥到UserSecret表
+ mobileApiService.signBxy(card, SignBxyParam().apply {
+ this.uid = user.uid
+ this.userid = user.userid
+ this.secertkey = user.secertkey
+ this.rsapublic = user.rsapublic
+ this.rsaprivate = user.rsaprivate
+ })
+
+ user.signedtime = DateUtil.getNow()
mobileApiService.saveUser(user)
needupdate = true;
-
}
if( user.userid.isNullOrEmpty()){
user.userid = person.userid
@@ -480,12 +485,17 @@
if (resp.sinstatus == YnrccUtil.TRANSTYPE_SIGNCARD ) {
signed = TradeDict.STATUS_YES
- if(!card.signed){
- card.signed = true
- mobileApiService.saveCard(card)
- }
- user.signedtime = DateUtil.getNow();
+ // 如果银行返回已签约,保存密钥到UserSecret表
+ mobileApiService.signBxy(card, SignBxyParam().apply {
+ this.uid=user.uid
+ this.userid = user.userid
+ this.secertkey = user.secertkey
+ this.rsapublic = user.rsapublic
+ this.rsaprivate = user.rsaprivate
+ })
+ user.signedtime = DateUtil.getNow()
mobileApiService.saveUser(user)
+
}
return JsonResult.ok("OK")
@@ -611,10 +621,15 @@
if (resp.code != "0000") {
return JsonResult.error(resp.message)
}
- card.signed = true
user.signedtime = DateUtil.getNow()
- mobileApiService.saveCard(card)
mobileApiService.saveUser(user)
+ mobileApiService.signBxy(card, SignBxyParam().apply {
+ this.uid=user.uid
+ this.userid = user.userid
+ this.secertkey = user.secertkey
+ this.rsapublic = user.rsapublic
+ this.rsaprivate = user.rsaprivate
+ })
signed = TradeDict.STATUS_YES
} else {
return JsonResult.error("请先绑定银行卡")
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
index 55d717c..3d029aa 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
@@ -1,7 +1,10 @@
package com.supwisdom.dlpay.mobile.service
import com.supwisdom.dlpay.api.bean.BaseResp
+import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
+import com.supwisdom.dlpay.api.bean.SignBxyParam
import com.supwisdom.dlpay.api.domain.TCard
+import com.supwisdom.dlpay.api.domain.TUserSecret
import com.supwisdom.dlpay.mobile.domain.TBMobileUser
import com.supwisdom.dlpay.mobile.domain.TBPages
@@ -22,9 +25,15 @@
fun saveCard(card:TCard):TCard
+ fun signBxy(card:TCard,param: SignBxyParam)
+
fun sendSms(phone:String,code:String):BaseResp
fun findByUseridAndStatus(userid:String,status:String):List<TBMobileUser>?
fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard?
+
+ fun findUserSecretByUid(uid:String):TUserSecret?
+
+ fun findCardInfoByMobileUid(uid: String): DoorQrcodeResponse
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
index f9c2b1e..de74d84 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
@@ -2,8 +2,13 @@
import com.mascloud.sdkclient.Client
import com.supwisdom.dlpay.api.bean.BaseResp
+import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
+import com.supwisdom.dlpay.api.bean.SignBxyParam
import com.supwisdom.dlpay.api.dao.CardDao
+import com.supwisdom.dlpay.api.dao.PersonDao
+import com.supwisdom.dlpay.api.dao.UserSecretDao
import com.supwisdom.dlpay.api.domain.TCard
+import com.supwisdom.dlpay.api.domain.TUserSecret
import com.supwisdom.dlpay.framework.service.SystemUtilService
import com.supwisdom.dlpay.mobile.dao.MobileUserDao
import com.supwisdom.dlpay.mobile.dao.PagesDao
@@ -26,9 +31,15 @@
lateinit var cardDao: CardDao
@Autowired
+ lateinit var personDao: PersonDao
+
+ @Autowired
lateinit var pagesDao: PagesDao
@Autowired
+ lateinit var userSecretDao: UserSecretDao
+
+ @Autowired
lateinit var systemUtilService: SystemUtilService
companion object {
var isMsgLogined: Boolean = false
@@ -71,6 +82,28 @@
return cardDao.save(card)
}
+ override fun signBxy(card: TCard, param: SignBxyParam) {
+ card.signed = true
+ cardDao.save(card)
+ val optional = userSecretDao.findById(param.uid)
+ if (optional.isPresent) {
+ val userSecret = optional.get()
+ userSecret.userid = param.userid
+ userSecret.rsaprivate = param.rsaprivate
+ userSecret.rsapublic = param.rsapublic
+ userSecret.secertkey = param.secertkey
+ userSecretDao.save(userSecret)
+ } else {
+ val userSecret = TUserSecret()
+ userSecret.uid = param.uid
+ userSecret.userid = param.userid
+ userSecret.rsaprivate = param.rsaprivate
+ userSecret.rsapublic = param.rsapublic
+ userSecret.secertkey = param.secertkey
+ userSecretDao.save(userSecret)
+ }
+ }
+
override fun sendSms(phone: String, code: String): BaseResp {
var resp = BaseResp()
var url = systemUtilService.getBusinessValue("sms.url")
@@ -181,4 +214,53 @@
cardDao.findBankcardByCitizencard(userid, cardtype, cardphyid)
}
}
+
+ override fun findUserSecretByUid(uid: String): TUserSecret? {
+ return userSecretDao.getByUid(uid)
+ }
+
+ override fun findCardInfoByMobileUid(uid: String): DoorQrcodeResponse {
+ val resp = DoorQrcodeResponse()
+ val mobileUser = userSecretDao.getByUid(uid)
+ if (null == mobileUser) {
+ resp.retcode = 1
+ resp.retmsg = "用户不存在!"
+ return resp
+ }
+ val person = personDao.findByUserid(mobileUser.userid)
+ if (null == person) {
+ resp.retcode = 1
+ resp.retmsg = "用户不存在!"
+ return resp
+ }
+ val bankCard = cardDao.findCardByUseridAndCardtype(person.userid, ConstantUtil.CARDTYPE_BANKCARD)
+ if (null == bankCard) {
+ resp.retcode = 1
+ resp.retmsg = "用户未绑定银行卡!"
+ return resp
+ }
+ val cityCard = cardDao.findBankcardByCitizencard(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid)
+ if (null == cityCard) {
+ resp.retcode = 1
+ resp.retmsg = "用户市民卡信息错误!"
+ return resp
+ }
+
+ resp.retcode = 0
+ resp.retmsg = "OK"
+ resp.userid = person.userid
+ resp.username = person.name
+ resp.sex = person.sex
+ resp.idtype = person.idtype
+ resp.idno = person.idno
+ resp.phone = person.mobile
+
+ resp.citycardno = cityCard.cardno
+ resp.cardphyid = cityCard.cardphyid
+ resp.expiredate = cityCard.expiredate
+ resp.cardstatus = cityCard.status
+ resp.transstatus = cityCard.transStatus
+ resp.bankcardno = bankCard.cardno
+ return resp
+ }
}
\ No newline at end of file
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
index dd5b1fd..e1e28e9 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/bean/DlpayReq.java
@@ -42,6 +42,8 @@
private Long stlamt; //清算金额
private Integer jnlcount; //交易流水笔数
+ private String merchantBcnoSeqno; //商户银行卡序号
+
/**
* 市民卡绑定请求XML
*/
@@ -104,9 +106,12 @@
.append("<ID_NO>").append(idNo).append("</ID_NO>")
.append("<MERCHANT_BCNO>").append(merchantBcno).append("</MERCHANT_BCNO>")
.append("<MERCHANT_NAME>").append(merchantName).append("</MERCHANT_NAME>")
+// .append("<MERCHANT_BCNOSEQ>").append(merchantBcnoSeqno).append("</MERCHANT_BCNOSEQ>") //fixme: 银行测试环境新加 2020.11.27
.append("<AMOUNT>").append(amount).append("</AMOUNT>")
+// .append("<SCENARIO>").append(scenario).append("</SCENARIO>") //fixme: 银行测试环境新加字段 2020.11.27
.append("<DESCRIPTION>").append(description).append("</DESCRIPTION>")
.append("<MAC>").append(MD5.encodeByMD5ForYnrcc(merchantBcno + bcNo + amount)).append("</MAC>")
+// .append("<MAC>").append(MD5.encodeByMD5ForYnrcc(merchantBcno + bcNo + amount + "ynnx@2020|dlsmk" + sn)).append("</MAC>") //fixme: 银行测试环境修改MAC算法 2020.11.27
.append("</root>");
return String.format("%08d", xml.toString().getBytes("GBK").length) + xml.toString();
}
@@ -158,6 +163,7 @@
.append("<SN>").append(sn).append("</SN>")
.append("<DZDATE>").append(chkdate).append("</DZDATE>")
.append("<MERCHANT_BCNO>").append("</MERCHANT_BCNO>")
+// .append("<MERCHANT_BCNOSEQ>").append("</MERCHANT_BCNOSEQ>") //fixme: 银行测试环境新加商户银行卡序号字段 2020.11.27
.append("</root>");
return String.format("%08d", xml.toString().getBytes("GBK").length) + xml.toString();
}
@@ -174,6 +180,7 @@
.append("<TRANSTIME>").append(transtime).append("</TRANSTIME>")
.append("<SN>").append(sn).append("</SN>")
.append("<STLTRXDATE>").append(stltrxdate).append("</STLTRXDATE>")
+// .append("<STLSNDATE>").append(stltrxdate).append("</STLSNDATE>") //fixme: 银行测试环境改了字段名(STLTRXDATE 改为 STLSNDATE) 2020.11.27
.append("<STLAMT>").append(stlamt).append("</STLAMT>")
.append("<JNLCOUNT>").append(jnlcount).append("</JNLCOUNT>")
.append("</root>");
@@ -374,4 +381,12 @@
public void setJnlcount(Integer jnlcount) {
this.jnlcount = jnlcount;
}
+
+ public String getMerchantBcnoSeqno() {
+ return merchantBcnoSeqno;
+ }
+
+ public void setMerchantBcnoSeqno(String merchantBcnoSeqno) {
+ this.merchantBcnoSeqno = merchantBcnoSeqno;
+ }
}
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
index 5b34717..e3f9ea1 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/controller/YnrccApiController.java
@@ -203,13 +203,14 @@
@FormParam("idtype") String idtype, @FormParam("idno") String idno,
@FormParam("merchant_bankcardno") String merchant_bankcardno,
@FormParam("merchant_bankaccname") String merchant_bankaccname,
+ @FormParam("merchant_bankcardno_seqno") String merchant_bankcardno_seqno,
@FormParam("amount") Integer amount,
@FormParam("scenario") String scenario,
@FormParam("description") String description,
@FormParam("sign_type") String sign_type, @FormParam("sign") String sign) {
DlpayResp resp = new DlpayResp();
if (!ynrccParamCheckService.checkCardPayParam(transcode, transdate, transtime, refno, categorie, bankcardno,
- username, idtype, idno, merchant_bankcardno, merchant_bankaccname, amount,scenario, description, sign_type, sign, resp)) {
+ username, idtype, idno, merchant_bankcardno, merchant_bankaccname, merchant_bankcardno_seqno, amount, scenario, description, sign_type, sign, resp)) {
logger.error(resp.errPrint());
return resp;
}
@@ -226,6 +227,7 @@
params.put("idno", idno);
params.put("merchant_bankcardno", merchant_bankcardno);
params.put("merchant_bankaccname", merchant_bankaccname);
+ params.put("merchant_bankcardno_seqno", merchant_bankcardno_seqno);
params.put("amount", String.valueOf(amount));
params.put("scenario",scenario);
params.put("description", description);
@@ -248,6 +250,7 @@
req.setIdNo(idno);
req.setMerchantBcno(merchant_bankcardno);
req.setMerchantName(merchant_bankaccname);
+ req.setMerchantBcnoSeqno(merchant_bankcardno_seqno);
req.setAmount(amount);
req.setScenario(scenario);
req.setDescription(description);
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
index 35b38ed..f68d4aa 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/YnrccParamCheckService.java
@@ -15,8 +15,8 @@
String idtype, String idno, String phone, String captcha,String transtype, String sign_type, String sign, DlpayResp resp);
boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username,
- String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount,String scenario, String description,
- String sign_type, String sign, DlpayResp resp);
+ String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, String merchant_bankcardno_seqno,
+ Integer amount,String scenario, String description, String sign_type, String sign, DlpayResp resp);
boolean checkPayRefundParam(String transcode, String transdate, String transtime, String refno, String refundRefno, Integer amount, String description,
String sign_type, String sign, DlpayResp resp);
diff --git a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
index 6b925f7..5c6955c 100644
--- a/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
+++ b/ynrcc-agent/src/main/java/com/supwisdom/agent/api/service/impl/YnrccParamCheckServiceImpl.java
@@ -144,7 +144,7 @@
}
@Override
- public boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username, String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, Integer amount, String scenario,String description, String sign_type, String sign, DlpayResp resp) {
+ public boolean checkCardPayParam(String transcode, String transdate, String transtime, String refno, String categorie, String bankcardno, String username, String idtype, String idno, String merchant_bankcardno, String merchant_bankaccname, String merchant_bankcardno_seqno, Integer amount, String scenario,String description, String sign_type, String sign, DlpayResp resp) {
if (!checkYnrccBaseParam(transcode, transdate, transtime, refno, sign_type, sign, resp)) {
return false;
}
@@ -184,6 +184,12 @@
resp.setMessage("请求参数错误[商户银行开户名为空]");
return false;
}
+ if (StringUtil.isEmpty(merchant_bankcardno_seqno)){
+ resp.setCode(ErrorCode.REQ_PARAM_ERROR);
+ resp.setMessage("请求参数错误[商户银行卡序号为空]");
+ return false;
+ }
+
if (null == amount) {
resp.setCode(ErrorCode.REQ_PARAM_ERROR);
resp.setMessage("请求参数错误[交易金额为空]");