两码转换支持配置
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 5560a5d..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
@@ -1,5 +1,6 @@
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
@@ -13,12 +14,14 @@
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.ConstantUtil
+import com.supwisdom.dlpay.util.*
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 com.supwisdom.dlpay.framework.util.MD5
+import java.time.Duration
@Service
class QRCodeServiceImpl:QRCodeService{
@@ -39,16 +42,19 @@
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")
+ 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"
- return mapOf("rootkey" to rootkey, "iv" to iv, "prefix" to prefix, "step" to step, "offset" to offset, "debug" to debug)
+ 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 {
@@ -58,6 +64,7 @@
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
@@ -65,12 +72,12 @@
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
@@ -84,7 +91,18 @@
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
@@ -106,41 +124,47 @@
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()
+ 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 = qrcode
- return resp
- } catch (ex: Exception) {
- ex.printStackTrace()
- resp.retcode = 1
- resp.retmsg = "二维码生成失败!${ex.message}"
+ resp.retmsg = key
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 {
@@ -151,6 +175,7 @@
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
@@ -177,7 +202,18 @@
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
@@ -199,40 +235,47 @@
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()
+ 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 = qrcode
- return resp
- } catch (ex: Exception) {
- ex.printStackTrace()
- resp.retcode = 1
- resp.retmsg = "二维码生成失败!${ex.message}"
+ resp.retmsg = key
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 {
@@ -242,11 +285,20 @@
val iv = config["iv"]
val prefix = config["prefix"] ?: ""
val debug = "true" == config["debug"]
- val totpoffset = config["offset"]
+ val offlineQRCode = "true" == config["offline"]
var offset = 3
- if (NumberUtils.isDigits(totpoffset)) {
- offset = totpoffset!!.toInt()
+ 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 = "秘钥未配置"
@@ -255,56 +307,144 @@
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}"
+ 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
- }
- 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
+
+ }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
+ }
}
- 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 {
@@ -314,6 +454,7 @@
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)) {
@@ -324,6 +465,9 @@
resp.retmsg = "秘钥未配置"
return resp
}
+ if (!offlineQRCode) {
+ return decodeCode(qrcode) // fixme: 未启用脱机码,这里直接老版解码
+ }
println("citizencard QRcode=[" + qrcode + "],length=[" + qrcode.length + "]")
//解密