大理二维码设计修改
diff --git a/bus-qrcode/build.gradle b/bus-qrcode/build.gradle
index 4132b55..aa6c290 100644
--- a/bus-qrcode/build.gradle
+++ b/bus-qrcode/build.gradle
@@ -7,7 +7,7 @@
group = rootProject.group
-def sdkVersion = gitVersion()
+def sdkVersion = '1.0.0'
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
}
@@ -37,18 +37,17 @@
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 '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'
+ 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..2998c85 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
@@ -83,7 +83,7 @@
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/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
keyCipher.init(mode, secret, ivSpec);
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..c671703 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
@@ -8,6 +8,8 @@
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.util.*;
@@ -19,15 +21,26 @@
public class QrCode {
private static Logger logger = LoggerFactory.getLogger(QrCode.class);
- 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有效
+ 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取的位数
-// static final ArrayList<String> DEFAULT_SCOPES = new ArrayList<>();
-// static {
-// DEFAULT_SCOPES.add("1"); //默认仅支持 1-大理车载消费场景
-// }
+ static final ArrayList<String> qrDataKeys = new ArrayList<>();
+
+ 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 +49,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 +103,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;
}
@@ -156,32 +175,26 @@
private TimeBasedOneTimePasswordGenerator totpGenerator(int passwordLength) {
try {
- return new TimeBasedOneTimePasswordGenerator(30, TimeUnit.SECONDS, passwordLength,
+ return new TimeBasedOneTimePasswordGenerator(timeStep, TimeUnit.SECONDS, passwordLength,
TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256);
} 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;
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_SHA256);
keys[i] = key;
} catch (final Exception e) {
System.out.println("Error : " + e);
@@ -195,87 +208,210 @@
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());
+ logger.info("uid=" + qrBuilder.uid);
+ logger.info("cardNo=" + qrBuilder.cardNo);
+ logger.info("cardType=" + qrBuilder.cardNo);
if (null != qrBuilder.prefix) logger.info("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 (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("QR code params is null");
}
+
+ if (!qrBuilder.rootKeySet) {
+ throw new RuntimeException("QR decode root key must be set!");
+ }
+
+ final String randomStr = getRandomString(2); //随机数
+ final String totp = genTOTPWithSeed(qrBuilder.seed, totpDigits);
+ 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) {
+ logger.info("qrData = " + 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);
+ final String code = encodeBase64(encData);
+ String result = code;
+ if (qrBuilder.prefix != null) {
+ result = qrBuilder.prefix + code;
+ }
+ if (qrBuilder.debug) {
+ logger.info("QR code = " + result);
+ }
+ 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);
+
+ 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 (null == qrcode) {
+ throw new RuntimeException("QR code must not be null");
+ }
+
+ if (!qrBuilder.rootKeySet) {
+ throw new RuntimeException("QR decode root key must be set!");
+ }
+
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("Unable to recognize QR code!");
+ }
+ final String encData = new String(code);
if (qrBuilder.debug) {
- logger.info("Decode data : <" + encDataPlain + ">");
+ logger.info("QR code data: " + encData );
}
- String[] fields = encDataPlain.split(DELIMITER);
- if (fields.length < 4) {
- throw new RuntimeException("qrcode plain text format error!");
+ final String[] fields = encData.split(DELIMITER);
+ if (fields.length < 6) {
+ throw new RuntimeException("QR code plain text format error!");
}
- String uid = fields[0];
- String[] scopes = fields[1].split(SCOPE_DELIMITER);
- String totp = fields[2];
-
- if (!verifyTotp(totp, qrBuilder.seed, TOTP_OFFSET)) {
- throw new RuntimeException("qrcode is invalid!"); //TOTP校验
+ 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));
+ if (qrBuilder.debug) {
+ logger.info("calcSign=" + new String(calcSign));
+ logger.info("sign=" + result.get(FIELD_SIGN));
+ }
+ if (!new String(calcSign).equalsIgnoreCase(result.get(FIELD_SIGN))) {
+ throw new RuntimeException("QR code sign was not matched!");
}
- 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("QR code parsing failure!");
}
- 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("QR code parsing failure!");
}
- 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("qrcode is invalid!"); //二维码已无效
+ }
+
+ 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("Please decode QR code first!");
+ }
+ if (this.qrBuilder.debug) {
+ logger.info("mac = " + this.qrBuilder.mac);
+ }
+
+ final String encData = this.qrBuilder.mac + amount + transDate + transTime + "{dlsmk}";
+ return encodeBase64(sha256(encData.getBytes())).toLowerCase();
}
}
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..f67a828 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,37 @@
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 = "Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=";
+ final String ivHex = "55b6f5b3287c535f8274b99354676d0e";
- @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");
- 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/payapi/build.gradle b/payapi/build.gradle
index 5d8338c..f10aab8 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -57,6 +57,16 @@
docker.dependsOn(jar)
+repositories {
+ maven {
+ url "http://ykt-nx.supwisdom.com/repository/ecard-repo/"
+ credentials {
+ username 'ecard'
+ password 'Ecard4SUP'
+ }
+ }
+}
+
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
@@ -114,6 +124,9 @@
/*支付宝SDK*/
implementation group: 'com.alipay.sdk', name: 'alipay-sdk-java', version: '3.7.110.ALL'
+ /*大理二维码jar*/
+ implementation 'com.supwisdom:dlsmk-qrcode:1.0.0'
+
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.projectlombok:lombok:1.18.8'
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..f8c4e5b 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,7 @@
@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?
}
\ 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 79dbbb9..a94e50b 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
@@ -124,8 +127,8 @@
query.setParameter("endtime", enddate)
query.unwrap(NativeQueryImpl::class.java).setResultTransformer(Transformers.aliasToBean(CitizenCardInfo::class.java))
val list = query.resultList as List<CitizenCardInfo>
- resp.retcode=0
- resp.retmsg="OK"
+ resp.retcode = 0
+ resp.retmsg = "OK"
resp.cards = list
return resp
}
@@ -172,7 +175,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
@@ -223,4 +226,12 @@
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)
+ }
+ }
}
\ 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..cbdeaef 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
@@ -7,6 +7,7 @@
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.TradeDict
@@ -64,33 +65,66 @@
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
+ }
- 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))
+ 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 = key
+ resp.retmsg = qrcode
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 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()) {
+ 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