新版二维码相关修改
diff --git a/bus-qrcode/build.gradle b/bus-qrcode/build.gradle
index aa6c290..55a94ee 100644
--- a/bus-qrcode/build.gradle
+++ b/bus-qrcode/build.gradle
@@ -7,7 +7,7 @@
group = rootProject.group
-def sdkVersion = '1.0.0'
+def sdkVersion = '1.3.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8
@@ -36,9 +36,11 @@
dependencies {
implementation "org.apache.commons:commons-lang3:3.7"
- implementation 'com.eatthepath:java-otp:0.1.0'
+ implementation 'com.eatthepath:java-otp:0.2.0'
implementation 'org.slf4j:slf4j-api:1.7.26'
implementation 'commons-codec:commons-codec:1.9'
+ 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'
}
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 2998c85..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/CBC/PKCS5Padding");
+ 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 c671703..6e5fd89 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,8 +3,6 @@
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;
@@ -12,6 +10,8 @@
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;
@@ -19,8 +19,6 @@
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";
@@ -163,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);
@@ -175,8 +172,8 @@
private TimeBasedOneTimePasswordGenerator totpGenerator(int passwordLength) {
try {
- return new TimeBasedOneTimePasswordGenerator(timeStep, 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);
}
@@ -185,7 +182,7 @@
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 {
@@ -194,7 +191,7 @@
while (steps.length() < 16) {
steps = "0" + steps;
}
- String key = TOTP.generateTOTP(seed, steps, String.valueOf(totpDigits), 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);
@@ -237,27 +234,31 @@
public String qrcode() {
if (qrBuilder.debug) {
- logger.info("uid=" + qrBuilder.uid);
- logger.info("cardNo=" + qrBuilder.cardNo);
- logger.info("cardType=" + qrBuilder.cardNo);
- if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
+ 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) logger.info("seed=" + qrBuilder.seed);
- if (qrBuilder.rootKeySet) logger.info("rootKey=" + encodeBase64(qrBuilder.rootKey));
- if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
- logger.info("=======================================================");
+ 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.uid || null == qrBuilder.cardNo || null == qrBuilder.cardType) {
- throw new RuntimeException("QR code params is null");
+ throw new RuntimeException("二维码必要参数为空!");
}
if (!qrBuilder.rootKeySet) {
- throw new RuntimeException("QR decode root key must be set!");
+ throw new RuntimeException("二维码rootKey必须设置!");
}
- final String randomStr = getRandomString(2); //随机数
+ 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)
@@ -266,8 +267,9 @@
.add(totp)
.add(randomStr).toString();
if (qrBuilder.debug) {
- logger.info("qrData = " + qrData);
+ System.out.println("QR Data=[" + qrData + "]");
}
+
final byte[] sign = sha256(byteConcat(qrData.getBytes(), factor));
final byte[] encDataPlain = byteConcat(qrData.getBytes(), DELIMITER.getBytes(), sign);
final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain, qrBuilder.iv);
@@ -277,28 +279,31 @@
result = qrBuilder.prefix + code;
}
if (qrBuilder.debug) {
- logger.info("QR code = " + result);
+ 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) {
- logger.info("Start parsing QR code= "+ qrcode);
- if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix);
+ System.out.println("====================== Start Decode QR Code =================================");
+ if (null != qrBuilder.prefix) System.out.println("prefix=[" + qrBuilder.prefix + "]");
- if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed);
- if (qrBuilder.rootKeySet) logger.info("rootKey=" + encodeBase64(qrBuilder.rootKey));
- if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv));
- logger.info("=======================================================");
+ 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("QR code must not be null");
+ throw new RuntimeException("请传递二维码内容!");
}
if (!qrBuilder.rootKeySet) {
- throw new RuntimeException("QR decode root key must be set!");
+ throw new RuntimeException("二维码rootKey必须设置!");
}
if (qrBuilder.prefix != null) {
@@ -307,15 +312,15 @@
byte[] code = aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv);
if (null == code) {
- throw new RuntimeException("Unable to recognize QR code!");
+ throw new RuntimeException("无法识别二维码!");
}
final String encData = new String(code);
if (qrBuilder.debug) {
- logger.info("QR code data: " + encData );
+ System.out.println("encDataPlain=[" + encData + "]");
}
- final String[] fields = encData.split(DELIMITER);
+ final String[] fields = encData.split(DELIMITER, 6);
if (fields.length < 6) {
- throw new RuntimeException("QR code plain text format error!");
+ throw new RuntimeException("二维码数据异常!");
}
Map<String, String> result = new HashMap<>();
for (int i = 0; i < fields.length && i < qrDataKeys.size(); ++i) {
@@ -333,11 +338,12 @@
final byte[] factor = getSignFactor(uid);
final byte[] calcSign = sha256(byteConcat(qrData.getBytes(), factor));
if (qrBuilder.debug) {
- logger.info("calcSign=" + new String(calcSign));
- logger.info("sign=" + result.get(FIELD_SIGN));
+ 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(calcSign).equalsIgnoreCase(result.get(FIELD_SIGN))) {
- throw new RuntimeException("QR code sign was not matched!");
+ throw new RuntimeException("二维码验证错误!");
}
return result;
@@ -345,11 +351,11 @@
/**
* 返回码数据不校验totp
- * */
+ */
public Map<String, String> decodeWithoutTotpCheck(String qrcode) {
final Map<String, String> data = decode(qrcode);
if (null == data || data.isEmpty()) {
- throw new RuntimeException("QR code parsing failure!");
+ throw new RuntimeException("二维码解析失败!");
}
this.qrBuilder.mac = new StringJoin(DELIMITER)
@@ -369,23 +375,23 @@
/**
* 返回码数据校验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("QR code parsing failure!");
+ throw new RuntimeException("二维码解析失败!");
}
//校验totp
if (!verifyTotp(data.get(FIELD_TOTP), this.qrBuilder.seed, offset)) {
- throw new RuntimeException("qrcode is invalid!"); //二维码已无效
+ throw new RuntimeException("二维码已失效!"); //二维码已无效
}
this.qrBuilder.mac = new StringJoin(DELIMITER)
@@ -404,14 +410,18 @@
public String tac(Integer amount, String transDate, String transTime) {
if (null == this.qrBuilder.mac) {
- throw new RuntimeException("Please decode QR code first!");
+ throw new RuntimeException("请先解析二维码!");
}
if (this.qrBuilder.debug) {
- logger.info("mac = " + this.qrBuilder.mac);
+ System.out.println("mac=[" + this.qrBuilder.mac + "]");
}
final String encData = this.qrBuilder.mac + amount + transDate + transTime + "{dlsmk}";
- return encodeBase64(sha256(encData.getBytes())).toLowerCase();
+ 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 f67a828..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
@@ -10,8 +10,9 @@
final String uid = "0a5de6ce985d43989b7ebe64ad8eb9c3";
final String cardNo = "00001252";
final String cardtype = "80";
- final String rootKeyB64 = "Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=";
+ final String rootKeyB64 = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA=";
final String ivHex = "55b6f5b3287c535f8274b99354676d0e";
+ final String qr="Szgp1QkGFy0DJ6mPVg4wMuOE5XuSe7eZrocdPDAs_iroqlRsDfitBrL5LFl3TwWhehBkE-5JTh1dbOydgxhDVdHrDjsdnX5Ydi4GcxACUMBXnpm3GaRZW66fD6A7rjCW";
try {
QrCode handler = QrCode.builder()
@@ -29,6 +30,7 @@
.debug(true).create();
Map<String, String> result = decHandler.decodeWithTotpCheck(qrcode);
String tac = decHandler.tac(1000, "20201028", "144521");
+ System.out.println("tac="+tac);
} catch (Exception e) {
e.printStackTrace();
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/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/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/build.gradle b/payapi/build.gradle
index f10aab8..1462f81 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -119,13 +119,13 @@
implementation 'log4j:log4j:1.2.17'
implementation 'com.alibaba:fastjson:1.2.60'
- implementation 'com.eatthepath:java-otp:0.1.0'
+ 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.0.0'
+ implementation 'com.supwisdom:dlsmk-qrcode:1.3.0'
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
annotationProcessor 'org.projectlombok:lombok:1.18.8'
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
index a31419d..22c5757 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/UserSecretDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/UserSecretDao.java
@@ -2,6 +2,9 @@
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/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/service/card_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt
index f8c4e5b..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
@@ -36,4 +36,6 @@
@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 a94e50b..1985c94 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
@@ -234,4 +234,8 @@
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 cbdeaef..5560a5d 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
@@ -1,7 +1,7 @@
package com.supwisdom.dlpay.api.service.impl
-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
@@ -9,16 +9,16 @@
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.*
+import com.supwisdom.dlpay.util.ConstantUtil
import mu.KotlinLogging
import org.apache.commons.lang.math.NumberUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service
-import java.time.Duration
@Service
class QRCodeServiceImpl:QRCodeService{
@@ -35,11 +35,31 @@
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"
+ 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")
+ val totpOffset = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.offset")
+ val debugEnable = systemUtilService.getBusinessValue("dlsmk.qrcode.log.debug")
+ if (NumberUtils.isDigits(totpStep)) step = totpStep.trim()
+ if (NumberUtils.isDigits(totpOffset)) offset = totpOffset.trim()
+ if ("true".equals(debugEnable, true)) debug = "true"
+ return mapOf("rootkey" to rootkey, "iv" to iv, "prefix" to prefix, "step" to step, "offset" to offset, "debug" to debug)
+ }
+
+ 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"]
+
+ if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) {
resp.retcode = 1
resp.retmsg = "秘钥未配置"
return resp
@@ -86,16 +106,25 @@
return resp
}
- val handle = QrCode.builder()
- .rootKey(rootkey)
- .iv(iv)
- .uid(uid)
- .card(cityCard.cardno, cityCard.busCardType ?: "80")
- .create()
- val qrcode = handle.qrcode()
- resp.retcode = 0
- resp.retmsg = qrcode
- return resp
+ 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
+ }
// val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey)
// val rowdata = QrcodeRawData()
@@ -117,122 +146,278 @@
override fun encodeCode(param: QrcodeParam): ApiResponse {
val resp = ApiResponse()
val uid = param.uid
- val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
- val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+
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
+ }
+ 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
}
+ 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
+ }
+
+// 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 {
+ val resp = DoorQrcodeResponse()
+ val config = qrcodeConfig()
+ val rootkey = config["rootkey"]
+ val iv = config["iv"]
+ val prefix = config["prefix"] ?: ""
+ val debug = "true" == config["debug"]
+ 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
+ }
+ println("vtoken=[" + qrcode + "],length=[" + qrcode.length + "]")
+
+ //解密
+ 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
+ }
+
+ 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 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
+ }
+ 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/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/mobile/MobileApi.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index 8de6f1e..1aab576 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
@@ -611,10 +612,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 bbe2e84..600f469 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
@@ -3,6 +3,7 @@
import com.supwisdom.dlpay.api.bean.BaseResp
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
@@ -30,4 +31,6 @@
fun findByUseridAndStatus(userid:String,status:String):List<TBMobileUser>?
fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard?
+
+ fun findUserSecretByUid(uid:String):TUserSecret?
}
\ 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 2aebcee..e5e6ec6 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
@@ -209,4 +209,8 @@
cardDao.findBankcardByCitizencard(userid, cardtype, cardphyid)
}
}
+
+ override fun findUserSecretByUid(uid: String): TUserSecret? {
+ return userSecretDao.getByUid(uid)
+ }
}
\ No newline at end of file