From 7128e665b2e14d3680c244bae5724a75916a731e Mon Sep 17 00:00:00 2001 From: "kaixiang.xia" Date: Fri, 16 Apr 2021 10:01:32 +0800 Subject: [PATCH] =?utf8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95?= =?utf8?q?=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../dlpay/paysdk/proxy/TransactionProxy.java | 3 + .../dlpay/agent/ynrccpay/YnrccPayChkfile.java | 138 +++++++++++++++++ .../dlpay/agent/ynrccpay/YnrccPayUtil.java | 7 +- .../api/dao/ShopSourceTypeConfigDao.java | 3 + .../dlpay/api/dao/TransactionMainDao.java | 9 ++ .../dlpay/api/service/SourceTypeService.java | 3 + .../service/impl/SourceTypeServiceImpl.java | 7 + .../service/impl/ynrcc_netpay_service_impl.kt | 8 +- .../agent/service/ynrcc_netpay_service.kt | 2 +- .../service/ynrccpay_checkfile_service.kt | 143 +++++++++++++----- .../com/supwisdom/dlpay/api/async_tasks.kt | 50 +++++- .../api/controller/consume_api_controller.kt | 98 ++++++++++-- ...transaction_reconciliation_service_impl.kt | 70 +++++++++ .../transaction_reconciliation_service.kt | 5 + .../controller/framework_controller.kt | 30 ++++ 15 files changed, 518 insertions(+), 58 deletions(-) create mode 100644 payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java index 570587af..5d6be325 100644 --- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java +++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java @@ -15,4 +15,7 @@ public interface TransactionProxy { @PostMapping("/api/consume/queryresult") QueryTransDtlResponse queryDtlResult(@RequestBody QueryDtlResultParam param); + + @PostMapping("/api/consume/thirdquery") + QueryTransDtlResponse queryDtlResultByThird(@RequestBody QueryDtlResultParam param); } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java new file mode 100644 index 00000000..16908470 --- /dev/null +++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java @@ -0,0 +1,138 @@ +package com.supwisdom.dlpay.agent.ynrccpay; + +import java.util.Arrays; +import java.util.List; + +/** + * 农商行快捷支付对账单 + * 4 + * ZF01|20210308|20201225161726|20210308151427000066|5607894487|10020104721000000001||0.01|156|0|0.01||20201225161726000115 + * ZF01|20210308|20210308161616|20210308140757000064|5607894462|10020104721000000001||0.01|156|0|0.01||20210113170353000126 + * ZF01|20210308|20210308181818|20210308150900000065|5607894483|10020104721000000001||0.01|156|0|0.01||20201225110202000098 + * ZF02|20210308|20210308152004|114075700006420210308152004|20210308140757000064|10020104721000000001||0.01|156|0|0.01|| + * */ +public class YnrccPayChkfile { + + private String payFlag; //交易代码:ZF01-支付;ZF02-退款 + private String clearDate; //清算日期 + private String transDateTime; //交易时间 yyyyMMddHHmmss + private String billNo; //商户订单号 + private String agentRefno; //银行流水号 + private String merchantNo; //商户号 + private String termNo; //终端号 + private String amount; //交易金额,单位:元 + private String currencyType; //币种 156-人民币 + private String feeAmount; //手续费,单位:元 + private String diffAmount; //交易金额与手续费金额的差值(交易金额-手续费金额),单位:元 + private String remark1; //备注1 + private String remark2; //备注2 + + public static final String PAY = "ZF01"; + public static final String REFUND = "ZF02"; + public static final List HEADER = Arrays.asList("payFlag", "clearDate", "transDateTime", "billNo", "agentRefno", + "merchantNo", "termNo", "amount", "currencyType", "feeAmount", "diffAmount", "remark1", "remark2"); + + public String getPayFlag() { + return payFlag; + } + + public void setPayFlag(String payFlag) { + this.payFlag = payFlag; + } + + public String getClearDate() { + return clearDate; + } + + public void setClearDate(String clearDate) { + this.clearDate = clearDate; + } + + public String getTransDateTime() { + return transDateTime; + } + + public void setTransDateTime(String transDateTime) { + this.transDateTime = transDateTime; + } + + public String getBillNo() { + return billNo; + } + + public void setBillNo(String billNo) { + this.billNo = billNo; + } + + public String getAgentRefno() { + return agentRefno; + } + + public void setAgentRefno(String agentRefno) { + this.agentRefno = agentRefno; + } + + public String getMerchantNo() { + return merchantNo; + } + + public void setMerchantNo(String merchantNo) { + this.merchantNo = merchantNo; + } + + public String getTermNo() { + return termNo; + } + + public void setTermNo(String termNo) { + this.termNo = termNo; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getCurrencyType() { + return currencyType; + } + + public void setCurrencyType(String currencyType) { + this.currencyType = currencyType; + } + + public String getFeeAmount() { + return feeAmount; + } + + public void setFeeAmount(String feeAmount) { + this.feeAmount = feeAmount; + } + + public String getDiffAmount() { + return diffAmount; + } + + public void setDiffAmount(String diffAmount) { + this.diffAmount = diffAmount; + } + + public String getRemark1() { + return remark1; + } + + public void setRemark1(String remark1) { + this.remark1 = remark1; + } + + public String getRemark2() { + return remark2; + } + + public void setRemark2(String remark2) { + this.remark2 = remark2; + } +} diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java index 38a6982a..ce51759f 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java @@ -17,10 +17,10 @@ public class YnrccPayUtil { public static final String MER_NAME = "ynrcc.netpay.Mer_IdName"; public static final String MER_URL = "ynrcc.netpay.MerURL"; - + public static final String chkFileDelimiter = "|"; public static final String SUCCESS = "ZF0000"; public static final String WAIT_FOR_QUERY = "ZF0098"; //等待查询结果 - public static final String NO_RECORDS_TODAY = "0406"; //当日无交易明细 + public static final String NO_RECORDS_TODAY = "ZF0101"; //当日无交易明细 public static String toJson(Object obj) { @@ -76,12 +76,13 @@ public class YnrccPayUtil { errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF00A0", "成功接收订单"))); errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF00A1", "支付方取消订单"))); - errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF0101", "无相关数据"))); + errcode.add(Pair.of(AgentCode.REFNO_NOT_EXISTS, new YnrccRespCode("ZF0101", "无相关数据"))); errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF0214", "平台已冻结或已注销"))); //自定义错误 ZExxxx errcode.add(Pair.of(AgentCode.CONFIG_ERROR, new YnrccRespCode("ZE0001", "系统参数未配置"))); errcode.add(Pair.of(AgentCode.COMMON_ERROR, new YnrccRespCode("ZE0002", "签名计算错误"))); + errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZE0097", "请求农商行网关快捷支付返回数据有误"))); errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZE0098", "请求农商行网关快捷支付返回数据为空"))); errcode.add(Pair.of(AgentCode.SERVER_INTERNAL_ERROR, new YnrccRespCode("ZE0099", "请求农商行网关快捷支付httpStatus非200"))); } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java index e861165a..9c59a827 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java @@ -14,4 +14,7 @@ public interface ShopSourceTypeConfigDao extends JpaRepository getSourcetypeBindShopaccnos(String sourceType); } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java index b72f8d21..10a3139c 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java @@ -8,6 +8,7 @@ import org.springframework.data.repository.CrudRepository; import javax.persistence.LockModeType; import javax.persistence.QueryHint; +import java.util.List; public interface TransactionMainDao extends CrudRepository { @@ -28,4 +29,12 @@ public interface TransactionMainDao extends CrudRepository findByRefundSuccessTransdtl(String refno); + + @Query("from TTransactionMain t where t.reverseRefno=?1 order by t.refno ") + List findByRefundTransdtl(String refno); + + } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java b/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java index 8e4048b6..0e81d214 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java @@ -52,4 +52,7 @@ public interface SourceTypeService { @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) TSourceTypeCheckStatus saveOrUpdateSourceTypeCheckStatus(TSourceTypeCheckStatus s); + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true) + List getShopaccnoBySourcetype(String sourcetype); + } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java index 62c471f2..f0bd5292 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java @@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -210,4 +211,10 @@ public class SourceTypeServiceImpl implements SourceTypeService { } return sourceTypeCheckDao.save(s); } + + @Override + public List getShopaccnoBySourcetype(String sourcetype) { + List list = shopSourceTypeConfigDao.getSourcetypeBindShopaccnos(sourcetype); + return list != null ? list : new ArrayList(0); + } } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt index 3949c553..d8b5af11 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt @@ -22,6 +22,7 @@ import org.springframework.stereotype.Service import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap import org.springframework.web.client.RestTemplate +import java.net.URLDecoder import java.nio.charset.StandardCharsets @Service @@ -186,7 +187,10 @@ class YnrccNetPayServiceImpl(val restTemplate: RestTemplate, return error("ZE0098", "请求农商行网关【$opt】返回数据为空!") } - val resData = YNRCCFastGateWayPayApi.verify(resJson.signature, resJson.plain) //调SDK验证签名 + val resData = when ("对账" == opt) { + true -> YNRCCFastGateWayPayApi.verify(resJson.signature, URLDecoder.decode(resJson.plain,"UTF-8")) //对账需先解码 + false -> YNRCCFastGateWayPayApi.verify(resJson.signature, resJson.plain) //调SDK验证签名 + } logger.info("农商行网关快捷支付请求【$opt】返回:\nurl=[$url]\nResponse Plain=[" + YnrccPayUtil.toJson(resData) + "]") val respCode = resData["RespCode"]?.trim()?.replace("\r\n", "") return if (YnrccPayUtil.SUCCESS == respCode) { @@ -421,7 +425,7 @@ class YnrccNetPayServiceImpl(val restTemplate: RestTemplate, resp.code = result["RespCode"] resp.message = result["RespDesc"] if (YnrccPayUtil.SUCCESS == result["RespCode"]) { - resp.plain = result["Plain"] //对账单 + resp.plain = result["FileContent"] //对账单 } return resp } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt index 127e319e..2510ab40 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt @@ -86,7 +86,7 @@ class YnrccNetPayAgent: AgentPayService{ } override fun query(transaction: TTransactionMain): AgentResponse { - val resp = ynrccNetPayService.doQueryYnrccPayResult(transaction.shopDtl.shopaccno, transaction.refno, transaction.shopDtl.transdate) + val resp = ynrccNetPayService.doQueryYnrccPayResult(transaction.shopDtl.shopaccno, transaction.refno, transaction.shopDtl.accdate) return AgentResponse().also { val code = agentCode(resp.code, resp.message) diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt index 0c7126e6..21fc8c1f 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt @@ -3,6 +3,7 @@ package com.supwisdom.dlpay.agent.service import com.supwisdom.dlpay.agent.AgentCode import com.supwisdom.dlpay.agent.AgentResponse import com.supwisdom.dlpay.agent.CheckFileProvider +import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayUtil import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus import com.supwisdom.dlpay.api.domain.TTransactionChkfile @@ -10,10 +11,16 @@ import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionReconciliationService import com.supwisdom.dlpay.exception.TransactionException import com.supwisdom.dlpay.framework.service.SystemUtilService +import com.supwisdom.dlpay.framework.util.StringUtil +import com.supwisdom.dlpay.framework.util.TradeDict import com.supwisdom.dlpay.util.ConstantUtil import mu.KotlinLogging import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component +import java.io.BufferedReader +import java.io.ByteArrayInputStream +import java.io.InputStreamReader +import java.nio.charset.Charset @Component("ynrccpayCheckFileProvider") class YnrccpayCheckFileProvider : CheckFileProvider { @@ -58,49 +65,89 @@ class YnrccpayCheckFileProvider : CheckFileProvider { return successDownload("[$billDate]日对账数据已入库成功", checkStatus) } - val shopaccno = "" - val resp = ynrccNetPayService.doQueryYnrccPayChkfile(billDate, shopaccno) - when (resp.code) { - YnrccPayUtil.SUCCESS -> { - //TODO: 农商行网关快捷支付 对账文件解析 - val agentUrl = "" - - //成功 - return AgentResponse().also { - it.code = AgentCode.FAIL - it.agentMsg = "获取对账文件逻辑暂缺" -// it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply { -// this.checkFileOk = false -// this.chkfileUrl = agentUrl -// }) + val fileDataList = ArrayList(0) + val shopaccList = sourceTypeService.getShopaccnoBySourcetype(TradeDict.PAYTYPE_YNRCC_PAY) //所有开通快捷支付的商户 + for (shopacc in shopaccList) { + val resp = ynrccNetPayService.doQueryYnrccPayChkfile(billDate, shopacc) + when (resp.code) { + YnrccPayUtil.SUCCESS -> { + //成功 + BufferedReader(InputStreamReader(resp.plain?.byteInputStream(Charset.forName("UTF-8")))).use { reader -> + val totalCnt = reader.readLine()?.trim()?.toInt() //总条目数 + var records = 0 + while (true) { + val line = reader.readLine() ?: break + if (line.isEmpty()) continue + val columns = line.split(YnrccPayUtil.chkFileDelimiter) + val bean = YnrccPayChkfile() + try { + StringUtil.transforToBean(YnrccPayChkfile.HEADER, columns, bean) + }catch (etr:Exception){ + throw TransactionException(97, "商户[$shopacc]对账单下载[$billDate]文件明细解析错误!${etr.message}") + } + fileDataList.add(bean) + records++ + } + if (totalCnt != records) { + throw TransactionException(97, "商户[$shopacc]对账单下载[$billDate]文件总交易笔数和明细不符!") + } + } + } + YnrccPayUtil.NO_RECORDS_TODAY -> { + //fixme:当日无交易明细 + } + else -> { + //报错,退出对账单拉取 + logger.error("农商行网关快捷支付商户[$shopacc]对账单下载[$billDate]报错:${resp.message}") + chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR + chkfile.remark = "商户[$shopacc]请求获取对账文件报错:${resp.message}" + transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile) + + //失败,直接返回 + return AgentResponse().also { + it.code = AgentCode.FAIL + it.agentMsg = chkfile.remark + it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply { + this.remark = chkfile.remark + }) + } } - } - YnrccPayUtil.NO_RECORDS_TODAY -> { - //当日无交易明细,也创建空记录 - transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细") - //成功 - return successDownload("当日无交易明细", checkStatus.apply { + } + + if(fileDataList.isEmpty()){ + //当日无交易明细,也创建空记录 + transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细") + //成功 + return successDownload("当日无交易明细", checkStatus.apply { + this.chkfileUrl = "none" + }) + } + + //保存对账单 + val failcnt = doBatchSaveYnrccPayCheckDetails(chkfile, fileDataList) + if (failcnt > 0) { + chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR + chkfile.remark = "对账单数据存在保存失败记录,请手动核对对账单数据" + transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile) + + return AgentResponse().also { + it.code = AgentCode.FAIL + it.agentMsg = chkfile.remark + it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply { + this.checkFileOk = false this.chkfileUrl = "none" + this.remark = chkfile.remark }) } - else -> { - //报错,退出对账单拉取 - logger.error("农商行网关快捷支付对账单下载[$billDate]报错:${resp.message}") - chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR - chkfile.remark = "请求获取对账文件报错:${resp.message}" - transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile) - - //失败 - return AgentResponse().also { - it.code = AgentCode.FAIL - it.agentMsg = chkfile.remark - it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply { - this.remark = chkfile.remark - }) - } - } } + + //成功 + transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "对账单数据下载成功") + return successDownload("[$billDate]日对账数据入库成功", checkStatus.apply { + this.chkfileUrl = "none" + }) + } catch (et: TransactionException) { return AgentResponse().also { it.code = AgentCode.FAIL @@ -154,4 +201,26 @@ class YnrccpayCheckFileProvider : CheckFileProvider { } } + private fun doBatchSaveYnrccPayCheckDetails(chkfile: TTransactionChkfile, list: ArrayList): Int { + try { + transactionReconciliationService.doBatchSaveYnrccPayTransactionChkDtl(chkfile, list) + return 0 //批量保存成功,无失败 + } catch (e1: Exception) { + } + + //批量保存有异常,逐笔保存 + var failcnt = 0 + var index = 1 + for (bean in list) { + try { + transactionReconciliationService.saveYnrccPayTransactionChkDtl(chkfile, bean, index++) + } catch (e2: Exception) { + failcnt++ + e2.printStackTrace() + continue + } + } + return failcnt + } + } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt index f01d8708..fd77c55a 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt @@ -7,6 +7,7 @@ import com.supwisdom.dlpay.api.domain.TDtlQuery import com.supwisdom.dlpay.api.domain.TShopdtl import com.supwisdom.dlpay.api.domain.TTransactionMain import com.supwisdom.dlpay.api.repositories.ShopaccService +import com.supwisdom.dlpay.api.service.ConsumePayService import com.supwisdom.dlpay.api.service.DtlQueryResultService import com.supwisdom.dlpay.api.service.TransactionServiceProxy import com.supwisdom.dlpay.exception.TransactionException @@ -123,6 +124,9 @@ class AgentQueryResultTask { @Autowired private lateinit var systemUtilService: SystemUtilService + @Autowired + lateinit var consumePayService: ConsumePayService + @Async("queryAgentPayResult") fun queryResult(transaction: TTransactionMain, qcnt: Int) { try { @@ -148,31 +152,41 @@ class AgentQueryResultTask { transaction.sourceType + "Agent") logger.info("refno=[${transaction.refno}]开始第" + (qcnt + 1) + "次查询支付结果:") + var endFlag = false val resp = service.query(transaction) when (resp.code) { AgentCode.SUCCESS -> { //查询成功 when (resp.dtlStatus) { - DtlStatus.SUCCESS -> + DtlStatus.SUCCESS -> { + endFlag = true transactionService.success(transaction.refno, resp.agentRefno, false) //流水成功 + } DtlStatus.REFUND -> { //流水已退款 TODO:流水成功后才退款,无查询原成功流水逻辑 + endFlag = true logger.error("refno=[${transaction.refno}]查询结果为:已退款!!!") return } DtlStatus.PARTIAL_REFUND -> { //流水已部分退款 TODO:暂无逻辑 + endFlag = true logger.error("refno=[${transaction.refno}]查询结果为:已部分退款!!!") return } else -> { //流水失败 + endFlag = true transactionService.fail(transaction.refno, "查询流水状态为交易失败") } } } - AgentCode.REFNO_NOT_EXISTS -> - transactionService.fail(transaction.refno, "银行流水不存在") //银行返回流水不存在 + AgentCode.REFNO_NOT_EXISTS -> { + if(TradeDict.REVERSE_FLAG_NONE == transaction.reverseType) { + endFlag = true + transactionService.fail(transaction.refno, "银行流水不存在") //银行返回流水不存在 + } + } AgentCode.REQUIRE_QUERY -> { queryResult(transaction, qcnt + 1) //查询次数加1 } @@ -181,6 +195,36 @@ class AgentQueryResultTask { logger.error("查询refno=[${transaction.refno}]流水结果返回失败:code=[${resp.agentCode}],message=[${resp.agentMsg}]") } } + + //fixme: 撤销或退款可能不需要传商户订单号,必须查询原流水状态 + if (!endFlag && (TradeDict.REVERSE_FLAG_CANCEL == transaction.reverseType || TradeDict.REVERSE_FLAG_REFUND == transaction.reverseType)) { + consumePayService.getTransactionMainDtl(transaction.reverseRefno, null, null)?.let { + val origResp = service.query(it) //冲正流水,只能查询原始流水是否为退款状态 + when (origResp.code) { + AgentCode.SUCCESS ->{ + when (origResp.dtlStatus) { + DtlStatus.SUCCESS -> + transactionService.fail(transaction.refno, "查询确认退款失败!") //原始流水还是成功状态,则退款流水必定失败!!! + DtlStatus.REFUND -> { + //流水已退款 + transactionService.success(transaction.refno, origResp.agentRefno, false) //TODO: 确保这条原始流水只有一笔全额冲正流水成功!!! + } + DtlStatus.PARTIAL_REFUND -> { + //流水已部分退款 TODO:暂无逻辑 + } + else -> { + //流水失败 无逻辑 + } + } + } + else -> { + //其他都重新查 + queryResult(transaction, qcnt + 1) //查询次数加1 + } + } + } + } + } catch (ex: Exception) { ex.printStackTrace() } 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 c9a69459..8d8a26d0 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,12 +11,11 @@ import com.supwisdom.dlpay.api.bean.groups.ConfirmAction 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 -import com.supwisdom.dlpay.framework.tenant.TenantContext import com.supwisdom.dlpay.framework.util.* +import mu.KotlinLogging import org.apache.commons.lang3.StringUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.ApplicationContext @@ -62,6 +61,8 @@ class ConsumeAPIController { @Autowired private lateinit var ynrccNetPayBusinessService: YnrccNetPayBusinessService + private val logger = KotlinLogging.logger { } + /** * ============================================================================ * 消费流水结果查询统一接口 @@ -984,18 +985,91 @@ class ConsumeAPIController { } /** - * 直接调第三方查询流水结果 (方便测试) + * ============================================================================ + * 直接调第三方查询流水结果 (通用) + * ============================================================================ * */ @PostMapping("/thirdquery") - fun thirdToQueryResult(refno: String): ResponseEntity { - consumePayService.getTransactionMainDtl(refno, null, null)?.let { - agentQueryResultTask.queryResult(it, 0) - return ResponseEntity.ok( - ResponseBodyBuilder.create() - .success("已成功进入查询异步查询队列") - ) - } ?: return ResponseEntity.ok( - ResponseBodyBuilder.create() + fun queryDtlResultByThird(@Valid @RequestBody param: QueryDtlResultParam): ResponseEntity { + consumePayService.getTransactionMainDtl(param.refno, param.billno, param.shopaccno)?.let { + if (TradeDict.DTL_STATUS_SUCCESS == it.status || TradeDict.DTL_STATUS_FAIL == it.status) { + return ResponseEntity.ok(ResponseBodyBuilder.create() + .success(QueryTransDtlResponse(it.refno, it.outTradeNo, it.shopDtl.amount, + it.status, it.sourceType, it.personDtl.payinfo, it.reverseFlag, + it.shopDtl.transdesc,it.personDtl.remark), "查询成功")) + } + + //发往第三方查询 + var endFlag = false + val service = ApplicationUtil.findAgentPayService(applicationContext, it.sourceType + "Agent") + val resp = service.query(it) + when (resp.code) { + AgentCode.SUCCESS -> { + //查询成功 + when (resp.dtlStatus) { + DtlStatus.SUCCESS -> { + endFlag = true + transactionService.success(it.refno, resp.agentRefno, false) //流水成功 + } + DtlStatus.REFUND -> { + //流水已退款 TODO:流水成功后才退款,无查询原成功流水逻辑 + } + DtlStatus.PARTIAL_REFUND -> { + //流水已部分退款 TODO:暂无逻辑 + } + else -> { + //流水失败 + endFlag = true + transactionService.fail(it.refno, "查询流水状态为交易失败") + } + } + } + AgentCode.REFNO_NOT_EXISTS -> { + if(TradeDict.REVERSE_FLAG_NONE == it.reverseType){ + endFlag = true + transactionService.fail(it.refno, "银行流水不存在") //银行返回流水不存在 + } + } + AgentCode.REQUIRE_QUERY -> { + //流水无结果 TODO: 还需继续查询 + } + else -> { + //其他明确错误,查询失败 + logger.error("向第三方查询 refno=[${it.refno}]流水结果返回失败:code=[${resp.agentCode}],message=[${resp.agentMsg}]") + } + } + + //TODO: 撤销或退款可能不需要传商户订单号,必须查询原流水状态 + if (!endFlag && (TradeDict.REVERSE_FLAG_CANCEL == it.reverseType || TradeDict.REVERSE_FLAG_REFUND == it.reverseType)) { + consumePayService.getTransactionMainDtl(it.reverseRefno, null, null)?.let { origDtl -> + val origResp = service.query(origDtl) //有冲正流水,原流水必定是成功流水。判断原流水状态是否为退款状态 + when (origResp.code) { + AgentCode.SUCCESS -> { + when (origResp.dtlStatus) { + DtlStatus.SUCCESS -> + transactionService.fail(it.refno, "查询确认退款失败!") //原始流水还是成功状态,则退款流水必定失败!!! + DtlStatus.REFUND -> { + //流水已退款 + transactionService.success(it.refno, origResp.agentRefno, false) //TODO: 确保这条原始流水只有一笔全额冲正流水成功!!! + } + DtlStatus.PARTIAL_REFUND -> { + //流水已部分退款 TODO:暂无逻辑 + } + else -> { + //流水失败 无逻辑 + } + } + } + else -> { + logger.error("向第三方查询 refno=[${it.refno}]冲正流水结果,原始流水支付状态查询返回失败:code=[${origResp.agentCode}],message=[${origResp.agentMsg}]") + } + } + } + } + + return queryDtlResult(param) //直接查询逻辑 + + } ?: return ResponseEntity.ok(ResponseBodyBuilder.create() .fail(TradeErrorCode.TRANSACTION_NOT_EXISTS, "流水不存在") ) } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt index 9937a316..8292e633 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt @@ -1,6 +1,7 @@ package com.supwisdom.dlpay.api.service.impl import com.supwisdom.dlpay.agent.citizencard.YnrccUtil +import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile import com.supwisdom.dlpay.api.bean.YnrccChkfileBean import com.supwisdom.dlpay.api.dao.PersondtlDao import com.supwisdom.dlpay.api.dao.TransactionChkdtlDao @@ -9,6 +10,7 @@ import com.supwisdom.dlpay.api.dao.TransactionMainDao import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus import com.supwisdom.dlpay.api.domain.TTransactionChkdtl import com.supwisdom.dlpay.api.domain.TTransactionChkfile +import com.supwisdom.dlpay.api.domain.TTransactionMain import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionReconciliationService import com.supwisdom.dlpay.api.service.TransactionServiceProxy @@ -305,4 +307,72 @@ class TransactionReconciliationServiceImpl : TransactionReconciliationService { transactionChkfileDao.save(chkfile) return sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus) } + + override fun doBatchSaveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList): Boolean { + var idx=1 + for (bean in list) { + saveYnrccPayTransactionChkDtl(chkfile, bean, idx++) + } + return true + } + + override fun saveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, bean: YnrccPayChkfile, idx:Int): TTransactionChkdtl { + return when(bean.payFlag){ + YnrccPayChkfile.PAY->{ + transactionChkdtlDao.save(TTransactionChkdtl().apply { + this.chkfileId = chkfile.id + this.accdate = chkfile.accdate + this.sourcetype = chkfile.sourcetype + this.recordno = idx + this.amount = -1 * bean.amount.toDouble() + this.otherRefno = bean.agentRefno + this.localRefno = bean.billNo + this.otherAccdate = bean.clearDate + this.transtype = "pay" + this.otherStatus = "success" + this.remark = null + this.extdata = "农商行网关快捷支付" + this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_UNCHECK + this.resolved = ConstantUtil.CHKDTL_RESOLVED_NONE + this.lastsaved = systemUtilService.sysdatetime.sysdate + this.tenantid = TenantContext.getTenantSchema() + }) + } + YnrccPayChkfile.REFUND->{ + val refno = bean.agentRefno //商户订单号 + var localRefno = bean.billNo + val refundSuccessList = transactionMainDao.findByRefundSuccessTransdtl(refno) + if (null != refundSuccessList && refundSuccessList.size > 0) { + localRefno = refundSuccessList[0].refno //成功的退款流水 + } else { + val refundTransdtlList = transactionMainDao.findByRefundTransdtl(refno) + if (null != refundTransdtlList && refundTransdtlList.size > 0) { + localRefno = refundTransdtlList[0].refno //第一笔退款的流水 + } + } + + transactionChkdtlDao.save(TTransactionChkdtl().apply { + this.chkfileId = chkfile.id + this.accdate = chkfile.accdate + this.sourcetype = chkfile.sourcetype + this.recordno = idx + this.amount = bean.amount.toDouble() + this.otherRefno = bean.billNo + this.localRefno = localRefno + this.otherAccdate = bean.clearDate + this.transtype = "refund" + this.otherStatus = "success" + this.remark = null + this.extdata = "农商行网关快捷支付退款[${bean.agentRefno}]" + this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_UNCHECK + this.resolved = ConstantUtil.CHKDTL_RESOLVED_NONE + this.lastsaved = systemUtilService.sysdatetime.sysdate + this.tenantid = TenantContext.getTenantSchema() + }) + } + else -> { + throw TransactionException(97, "不能识别对账单明细中交易代码[${bean.payFlag}]!") + } + } + } } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt index d7ce27dd..5c4e1327 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt @@ -1,5 +1,6 @@ package com.supwisdom.dlpay.api.service +import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile import com.supwisdom.dlpay.api.bean.YnrccChkfileBean import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus import com.supwisdom.dlpay.api.domain.TTransactionChkdtl @@ -65,6 +66,10 @@ interface TransactionReconciliationService { @Transactional(rollbackFor = [Exception::class]) fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TSourceTypeCheckStatus + @Transactional(rollbackFor = [Exception::class]) + fun doBatchSaveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList): Boolean + @Transactional(rollbackFor = [Exception::class]) + fun saveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, bean: YnrccPayChkfile, idx: Int): TTransactionChkdtl } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt index a55845fb..c5e603c8 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt @@ -1,17 +1,24 @@ package com.supwisdom.dlpay.framework.controller +import com.google.gson.Gson import com.jcabi.manifests.Manifests +import com.supwisdom.dlpay.agent.service.YnrccNetPayService import com.supwisdom.dlpay.api.bean.ApiVersionResponse import com.supwisdom.dlpay.framework.ResponseBodyBuilder +import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api/common") class AboutController { + @Autowired + lateinit var ynrccNetPayService: YnrccNetPayService + @GetMapping("/version") fun version(): ResponseEntity { return try { @@ -24,4 +31,27 @@ class AboutController { .success(ApiVersionResponse("unknown"))) } } + + /** + * 查看农商行快捷支付对账单 + * */ + @GetMapping("/ynrccpaychkfile") + fun testYnrccpayChkfile( + @RequestParam("accdate") accdate: String, + @RequestParam("shopaccno") shopaccno: String + ): ResponseEntity { + return try { + val resp = ynrccNetPayService.doQueryYnrccPayChkfile(accdate, shopaccno) + + ResponseEntity.ok( + ResponseBodyBuilder.create() + .success(Gson().toJson(resp)) + ) + } catch (e: IllegalArgumentException) { + ResponseEntity.ok( + ResponseBodyBuilder.create() + .fail(99, "throw exception!") + ) + } + } } \ No newline at end of file -- 2.17.1