新版二维码相关修改
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