From: sijun.li Date: Fri, 20 Mar 2020 09:38:41 +0000 (+0800) Subject: 完善消费退款接口 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F56%2F11356%2F1;p=epayment%2Ffood_payapi.git 完善消费退款接口 --- diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmResponse.java b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmResponse.java index e6944c4a..ea6b9fc1 100644 --- a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmResponse.java +++ b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmResponse.java @@ -1,12 +1,14 @@ package com.supwisdom.dlpay.payapi.model; import java.util.Objects; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonCreator; import com.supwisdom.dlpay.payapi.model.TransResult; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import org.openapitools.jackson.nullable.JsonNullable; + import javax.validation.Valid; import javax.validation.constraints.*; @@ -15,7 +17,7 @@ import javax.validation.constraints.*; */ @javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-17T08:49:02.541+08:00[Asia/Shanghai]") -public class QrcodePayConfirmResponse { +public class QrcodePayConfirmResponse { @JsonProperty("refno") private String refno; @@ -31,6 +33,9 @@ public class QrcodePayConfirmResponse { @JsonProperty("result") private TransResult result; + @JsonProperty("amount") + private Integer amount; + public QrcodePayConfirmResponse refno(String refno) { this.refno = refno; return this; @@ -38,11 +43,13 @@ public class QrcodePayConfirmResponse { /** * Get refno + * * @return refno - */ + */ @ApiModelProperty(value = "") -@Pattern(regexp="[0-9]*") @Size(min=16) + @Pattern(regexp = "[0-9]*") + @Size(min = 16) public String getRefno() { return refno; } @@ -58,11 +65,13 @@ public class QrcodePayConfirmResponse { /** * Get hostDate + * * @return hostDate - */ + */ @ApiModelProperty(value = "") -@Pattern(regexp="[0-9]*") @Size(min=8,max=8) + @Pattern(regexp = "[0-9]*") + @Size(min = 8, max = 8) public String getHostDate() { return hostDate; } @@ -78,11 +87,13 @@ public class QrcodePayConfirmResponse { /** * Get hostTime + * * @return hostTime - */ + */ @ApiModelProperty(value = "") -@Pattern(regexp="[0-9]*") @Size(min=6,max=6) + @Pattern(regexp = "[0-9]*") + @Size(min = 6, max = 6) public String getHostTime() { return hostTime; } @@ -98,8 +109,9 @@ public class QrcodePayConfirmResponse { /** * Get description + * * @return description - */ + */ @ApiModelProperty(value = "") @@ -118,8 +130,9 @@ public class QrcodePayConfirmResponse { /** * Get result + * * @return result - */ + */ @ApiModelProperty(value = "") @Valid @@ -132,6 +145,26 @@ public class QrcodePayConfirmResponse { this.result = result; } + public QrcodePayConfirmResponse amount(Integer amount) { + this.amount = amount; + return this; + } + + /** + * Get amount + * @return amount + */ + @ApiModelProperty(value = "") + + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + @Override public boolean equals(java.lang.Object o) { @@ -146,24 +179,26 @@ public class QrcodePayConfirmResponse { Objects.equals(this.hostDate, qrcodePayConfirmResponse.hostDate) && Objects.equals(this.hostTime, qrcodePayConfirmResponse.hostTime) && Objects.equals(this.description, qrcodePayConfirmResponse.description) && - Objects.equals(this.result, qrcodePayConfirmResponse.result); + Objects.equals(this.result, qrcodePayConfirmResponse.result) && + Objects.equals(this.amount, qrcodePayConfirmResponse.amount); } @Override public int hashCode() { - return Objects.hash(refno, hostDate, hostTime, description, result); + return Objects.hash(refno, hostDate, hostTime, description, result, amount); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class QrcodePayConfirmResponse {\n"); - + sb.append(" refno: ").append(toIndentedString(refno)).append("\n"); sb.append(" hostDate: ").append(toIndentedString(hostDate)).append("\n"); sb.append(" hostTime: ").append(toIndentedString(hostTime)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); sb.append(" result: ").append(toIndentedString(result)).append("\n"); + sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/RefundRequest.java b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/RefundRequest.java index d1494125..71c9915f 100644 --- a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/RefundRequest.java +++ b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/RefundRequest.java @@ -1,13 +1,12 @@ package com.supwisdom.dlpay.payapi.model; -import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonCreator; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import org.openapitools.jackson.nullable.JsonNullable; -import javax.validation.Valid; -import javax.validation.constraints.*; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.Objects; /** * RefundRequest @@ -30,6 +29,9 @@ public class RefundRequest { @JsonProperty("amount") private Integer amount; + @JsonProperty("shopaccno") + private String shopaccno; + public RefundRequest billno(String billno) { this.billno = billno; return this; @@ -42,7 +44,7 @@ public class RefundRequest { @ApiModelProperty(required = true, value = "") @NotNull -@Pattern(regexp="[0-9]*") @Size(min=16) +@Pattern(regexp="[0-9]*") @Size(min=16) public String getBillno() { return billno; } @@ -63,7 +65,7 @@ public class RefundRequest { @ApiModelProperty(required = true, value = "") @NotNull -@Pattern(regexp="[0-9]*") @Size(min=8,max=8) +@Pattern(regexp="[0-9]*") @Size(min=8,max=8) public String getTransDate() { return transDate; } @@ -84,7 +86,7 @@ public class RefundRequest { @ApiModelProperty(required = true, value = "") @NotNull -@Pattern(regexp="[0-9]*") @Size(min=6,max=6) +@Pattern(regexp="[0-9]*") @Size(min=6,max=6) public String getTransTime() { return transTime; } @@ -105,7 +107,7 @@ public class RefundRequest { @ApiModelProperty(required = true, value = "") @NotNull -@Pattern(regexp="[0-9]*") @Size(min=16) +@Pattern(regexp="[0-9]*") @Size(min=16) public String getRefno() { return refno; } @@ -135,6 +137,26 @@ public class RefundRequest { this.amount = amount; } + public RefundRequest shopaccno(String shopaccno) { + this.shopaccno = shopaccno; + return this; + } + + /** + * Get shopaccno + * @return shopaccno + */ + @ApiModelProperty(required = true, value = "") + @NotNull + + @Pattern(regexp="[0-9]*") + public String getShopaccno() { + return shopaccno; + } + + public void setShopaccno(String shopaccno) { + this.shopaccno = shopaccno; + } @Override public boolean equals(java.lang.Object o) { @@ -149,24 +171,26 @@ public class RefundRequest { Objects.equals(this.transDate, refundRequest.transDate) && Objects.equals(this.transTime, refundRequest.transTime) && Objects.equals(this.refno, refundRequest.refno) && - Objects.equals(this.amount, refundRequest.amount); + Objects.equals(this.amount, refundRequest.amount) && + Objects.equals(this.shopaccno, refundRequest.shopaccno); } @Override public int hashCode() { - return Objects.hash(billno, transDate, transTime, refno, amount); + return Objects.hash(billno, transDate, transTime, refno, amount, shopaccno); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class RefundRequest {\n"); - + sb.append(" billno: ").append(toIndentedString(billno)).append("\n"); sb.append(" transDate: ").append(toIndentedString(transDate)).append("\n"); sb.append(" transTime: ").append(toIndentedString(transTime)).append("\n"); sb.append(" refno: ").append(toIndentedString(refno)).append("\n"); sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); + sb.append(" shopaccno: ").append(toIndentedString(shopaccno)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/payapi-spec/consumeapi.yaml b/payapi-spec/consumeapi.yaml index a9b91b94..cbe75831 100644 --- a/payapi-spec/consumeapi.yaml +++ b/payapi-spec/consumeapi.yaml @@ -86,6 +86,10 @@ components: result: title: 交易结果 $ref: 'definitions.yaml#/components/schemas/TransResult' + amount: + type: integer + format: int32 + title: 消费金额 RefundRequest: type: object title: 退款申请 @@ -95,6 +99,7 @@ components: - transTime - refno - amount + - shopaccno properties: billno: title: 退款申请订单号 @@ -106,6 +111,8 @@ components: refno: title: 退款申请原始订单交易参考号 $ref: 'definitions.yaml#/components/schemas/Refno' + shopaccno: + $ref: 'definitions.yaml#/components/schemas/ShopAccNo' amount: type: integer title: 退款金额, 正式 diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java index 4625ca91..8b39fce2 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java @@ -5,32 +5,16 @@ */ package com.supwisdom.dlpay.api; -import com.supwisdom.dlpay.payapi.model.ErrorResponse; -import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest; -import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmResponse; -import com.supwisdom.dlpay.payapi.model.QrcodePayInitRequest; -import com.supwisdom.dlpay.payapi.model.QrcodePayInitResponse; -import com.supwisdom.dlpay.payapi.model.RefundRequest; -import com.supwisdom.dlpay.payapi.model.RefundResponse; +import com.supwisdom.dlpay.payapi.model.*; import io.swagger.annotations.*; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; -import javax.validation.constraints.*; -import java.util.List; -import java.util.Map; import java.util.Optional; @javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-17T08:49:02.541+08:00[Asia/Shanghai]") diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/RefundDtlDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/RefundDtlDao.java new file mode 100644 index 00000000..3aaeaa80 --- /dev/null +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/RefundDtlDao.java @@ -0,0 +1,8 @@ +package com.supwisdom.dlpay.api.dao; + +import com.supwisdom.dlpay.api.domain.TRefundDtl; +import org.springframework.data.repository.CrudRepository; + +public interface RefundDtlDao extends CrudRepository { + TRefundDtl findFirstByOriginrefnoAndTenantidOrderBySeqnoDesc(String originrefno, String tenantid); +} 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 e6a87341..446c128b 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 { @@ -30,4 +31,6 @@ public interface TransactionMainDao extends CrudRepository findByReverseRefno(String refno); } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java index 1c7f5d1f..39d03f62 100644 --- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java @@ -28,9 +28,6 @@ public class TPersondtl { @Column(name = "ACCNO", length = 32) private String accountNo; - @Column(name = "accname", length = 32) - private String accountName; // 账户名 - @Column(name = "USERNAME", length = 200) private String userName; @@ -324,12 +321,4 @@ public class TPersondtl { public void setAftbal(Double aftbal) { this.aftbal = aftbal; } - - public String getAccountName() { - return accountName; - } - - public void setAccountName(String accountName) { - this.accountName = accountName; - } } diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TRefundDtl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TRefundDtl.java new file mode 100644 index 00000000..f59fee3d --- /dev/null +++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TRefundDtl.java @@ -0,0 +1,130 @@ +package com.supwisdom.dlpay.api.domain; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +@Table(name = "TB_REFUNDDTL", + indexes = {@Index(name = "refunddtl_idx",columnList = "originrefno,seqno,tenantid",unique = true), + @Index(name = "refunddtl_idx2",columnList = "outid,outtradeno,tenantid",unique = true)}) +public class TRefundDtl { + /** + * refno 退款流水交易参考号 + */ + @Id + @Column(name = "refno",length = 32) + private String refno; + + /** + * originrefno 交易主表流水号 + */ + @NotNull + @Column(name = "originrefno",length = 32) + private String originrefno; + + @NotNull + @Column(name = "amount",precision = 9,scale = 2) + private Double amount; + + /** + * afteramount 原流水退款剩余可退金额 + */ + @NotNull + @Column(name = "afteramount",precision = 9,scale = 2) + private Double afteramount; + + /** + * 商户号 + */ + @Column(name = "outid",length = 60) + private String outid; + + /** + * 退款商户billno + */ + @Column(name = "outtradeno",length = 60) + private String outtradeno; + + @Column(name = "seqno") + private Integer seqno; + + @NotNull + @Column(name = "tenantid",length = 20) + private String tenantid; + + @NotNull + @Column(name = "tradeflag", length = 10) + private String tradeflag; + + public String getRefno() { + return refno; + } + + public void setRefno(String refno) { + this.refno = refno; + } + + public String getOriginrefno() { + return originrefno; + } + + public void setOriginrefno(String originrefno) { + this.originrefno = originrefno; + } + + public Double getAmount() { + return amount; + } + + public void setAmount(Double amount) { + this.amount = amount; + } + + public Double getAfteramount() { + return afteramount; + } + + public void setAfteramount(Double afteramount) { + this.afteramount = afteramount; + } + + public String getOutid() { + return outid; + } + + public void setOutid(String outid) { + this.outid = outid; + } + + public String getOuttradeno() { + return outtradeno; + } + + public void setOuttradeno(String outtradeno) { + this.outtradeno = outtradeno; + } + + public Integer getSeqno() { + return seqno; + } + + public void setSeqno(Integer seqno) { + this.seqno = seqno; + } + + public String getTenantid() { + return tenantid; + } + + public void setTenantid(String tenantid) { + this.tenantid = tenantid; + } + + public String getTradeflag() { + return tradeflag; + } + + public void setTradeflag(String tradeflag) { + this.tradeflag = tradeflag; + } +} diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/wechat_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/wechat_service_impl.kt index 6084118a..eba94480 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/wechat_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/wechat_service_impl.kt @@ -12,15 +12,26 @@ import com.supwisdom.dlpay.api.domain.TTransactionMain import com.supwisdom.dlpay.api.service.ConsumePayService import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionServiceProxy +import com.supwisdom.dlpay.exception.InternalServerError import com.supwisdom.dlpay.framework.util.* import com.supwisdom.dlpay.framework.util.RandomUtils.getRandomString import com.supwisdom.dlpay.util.PaytypeUtil import mu.KotlinLogging import org.apache.commons.lang.StringUtils +import org.apache.http.client.methods.HttpPost +import org.apache.http.conn.ssl.SSLConnectionSocketFactory +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.HttpClients +import org.apache.http.ssl.SSLContexts +import org.apache.http.util.EntityUtils import org.springframework.http.converter.StringHttpMessageConverter import org.springframework.stereotype.Service import org.springframework.web.client.RestTemplate +import java.io.File +import java.io.FileInputStream import java.nio.charset.StandardCharsets +import java.security.KeyStore +import kotlin.math.abs @Service @@ -264,36 +275,73 @@ class WechatServiceImpl(val sourceTypeService: SourceTypeService, override fun doRefund(transaction: TTransactionMain): AgentResponse { val agentResponse = AgentResponse() + val originTrans = transactionService.findTransactionByRefno(transaction.reverseRefno) + ?: throw InternalServerError("被退款流水未找到") val config = sourceTypeService.getConsumePaytypeConfig(TradeDict.PAYTYPE_WECHAT, transaction.shopDtl.shopaccno, false, false) if (checkCfg(config, agentResponse)) { + // 生成请求参数 val bean = WechatReqResp().apply { this.appid = config[PaytypeUtil.CFG_WECHAT_APPID] this.mch_id = config[PaytypeUtil.CFG_WECHAT_MECHID] this.key = config[PaytypeUtil.CFG_WECHAT_MECHKEY] - this.out_trade_no = transaction.refno - this.total_fee = MoneyUtil.YuanToFen(transaction.shopDtl.amount) - this.refund_fee = MoneyUtil.YuanToFen(transaction.refundAmount) - this.out_refund_no = transaction.reverseRefno + this.out_trade_no = transaction.reverseRefno + this.total_fee = abs(MoneyUtil.YuanToFen(originTrans.personDtl.amount)) + this.refund_fee = abs(MoneyUtil.YuanToFen(transaction.personDtl.amount)) + this.out_refund_no = transaction.refno this.notify_url = StringUtil.urlAppend(config[PaytypeUtil.CFG_WECHAT_NOTIFY], transaction.tenantid) } bean.generaReverseSign() val xml = bean.generaReverseXML() - val res = restTemplate.postForEntity(PaytypeUtil.CFG_WECHAT_REFUND, xml, String::class.java) - val eleMap = XmlUtils.parseXml(res.body) - val retcode = eleMap["return_code"] - val resultCode = eleMap["result_code"] - if (!retcode.isNullOrEmpty() && "SUCCESS" == retcode - && !resultCode.isNullOrEmpty() && "SUCCESS" == resultCode) { - agentResponse.code = AgentCode.REQUIRE_QUERY - agentResponse.agentCode = resultCode - agentResponse.agentRefno = eleMap["refund_id"] - agentResponse.agentMsg = eleMap["return_msg"] - } else { - logger.error { "code=${eleMap["err_code"]},des=${eleMap["err_code_des"]}" } - agentResponse.code = AgentCode.FAIL - agentResponse.agentCode = eleMap["err_code"] - agentResponse.agentMsg = eleMap["err_code_des"] + // 设置证书 + val password = config[PaytypeUtil.CFG_WECHAT_MECHID]!!.toCharArray() + val certStream = FileInputStream(File(config[PaytypeUtil.CFG_WECHAT_REFUNDCERT]!!)) + val keyStore = KeyStore.getInstance("PKCS12") + certStream.use { + keyStore.load(it, password) } + val sslContext = SSLContexts.custom() + //这里也是写密码的 + .loadKeyMaterial(keyStore, password) + .build() + val socketFactory = SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier()) + val httpClient = HttpClients.custom() + .setSSLSocketFactory(socketFactory) + .build() + + httpClient.use { + // 发送请求 + val httpPost = HttpPost(PaytypeUtil.CFG_WECHAT_REFUND) + httpPost.entity = StringEntity(xml, "UTF-8") + val response = httpClient.execute(httpPost) + // 解析响应 + response.use { + val entity = EntityUtils.toString(response.entity, "UTF-8") + val eleMap = XmlUtils.parseXml(entity) + + val retcode = eleMap["return_code"] + val resultCode = eleMap["result_code"] + if (!retcode.isNullOrEmpty() && "SUCCESS" == retcode) { + if (!resultCode.isNullOrEmpty() && "SUCCESS" == resultCode) { + agentResponse.code = AgentCode.REQUIRE_QUERY + agentResponse.agentCode = resultCode + agentResponse.agentRefno = eleMap["refund_id"] + agentResponse.agentMsg = eleMap["return_msg"] + } else { + logger.error { "code=${eleMap["err_code"]},des=${eleMap["err_code_des"]}" } + agentResponse.code = AgentCode.FAIL + agentResponse.agentCode = eleMap["err_code"] + agentResponse.agentMsg = eleMap["err_code_des"] + } + } else { + logger.error { "微信退款请求通信失败:<${eleMap["return_msg"]}>" } + agentResponse.code = AgentCode.FAIL + agentResponse.agentCode = eleMap["err_code"] + agentResponse.agentMsg = eleMap["return_msg"] + } + + } + } + } return agentResponse } 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 f5003275..cfd3797a 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 @@ -334,7 +334,7 @@ class DaliConsumeAPIController(private val qrCodeService: QRCodeService, setOutTransInfo(mainDtl.outId, param.requestbillno) } val refundTrans = builder.refundInit(mainDtl.refno, - param.refundAmount / 100.0, transactionService) + param.refundAmount / 100.0, transactionService,null,null) transactionService.wip(refundTrans.refno) val service = createAgentService(mainDtl.sourceType) val resp = service.refund(refundTrans) diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/k12_consume_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/k12_consume_api_controller.kt index 7bf36cd6..f235fe5b 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/k12_consume_api_controller.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/k12_consume_api_controller.kt @@ -6,19 +6,23 @@ import com.supwisdom.dlpay.agent.AgentPayServiceContext import com.supwisdom.dlpay.agent.domain.QrcodePayTrans import com.supwisdom.dlpay.agent.service.AgentServiceProxy import com.supwisdom.dlpay.api.ConsumeApi +import com.supwisdom.dlpay.api.domain.TTransactionMain import com.supwisdom.dlpay.api.service.ConsumePayService import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionServiceProxy import com.supwisdom.dlpay.api.service.UserService import com.supwisdom.dlpay.exception.BadRequestError import com.supwisdom.dlpay.exception.ConflictError +import com.supwisdom.dlpay.exception.InternalServerError import com.supwisdom.dlpay.exception.TransactionCheckException import com.supwisdom.dlpay.framework.ResponseBodyBuilder import com.supwisdom.dlpay.framework.service.SystemUtilService +import com.supwisdom.dlpay.framework.util.MoneyUtil import com.supwisdom.dlpay.framework.util.StringUtil import com.supwisdom.dlpay.framework.util.TradeDict import com.supwisdom.dlpay.framework.util.TradeErrorCode import com.supwisdom.dlpay.payapi.model.* +import mu.KotlinLogging import org.apache.commons.lang3.StringUtils import org.springframework.dao.PessimisticLockingFailureException import org.springframework.http.ResponseEntity @@ -27,6 +31,7 @@ import org.springframework.web.bind.annotation.RestController import org.springframework.web.context.request.NativeWebRequest import java.util.* import javax.validation.Valid +import kotlin.math.abs @RestController @RequestMapping("\${openapi.aPITitle.base-path:/api}") @@ -40,6 +45,8 @@ constructor(private val request: NativeWebRequest, private val consumePayService: ConsumePayService, private val transactionService: TransactionServiceProxy) : ConsumeApi { + private val logger = KotlinLogging.logger { } + override fun getRequest(): Optional { return Optional.ofNullable(request) } @@ -72,6 +79,7 @@ constructor(private val request: NativeWebRequest, // 3. 查询用户身份 val service = createAgentService(qrcode.sourceType) val agentResp = service.auth(param.shopaccno, param.billno) + // 4. 重新读取 qrcode 交易明细表,以获取 service.auth 查询后的结果数据 // 修改, 通过返回值来判断 // val qrcodeTransResp = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(qrcodeTrans.agentMerchId, @@ -93,13 +101,16 @@ constructor(private val request: NativeWebRequest, if (person == null) { qrcodeTransResp.isAnonymous = true } else { + qrcodeTransResp.isAnonymous = false apiResp.apply { - qrcodeTransResp.isAnonymous = false - this.userid = qrcodeTransResp.agentUserId + this.userid = qrcodeTransResp.userid this.username = person.name } } } + apiResp.apply { + this.anonymous = qrcodeTransResp.isAnonymous + } agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans.also { it.isAnonymous = qrcodeTransResp.isAnonymous }) @@ -119,8 +130,8 @@ constructor(private val request: NativeWebRequest, is PessimisticLockingFailureException -> throw ConflictError(e.message) else -> { e.printStackTrace() - println("二维码消费初始化异常:<${e.message}>") - throw InternalError(e.message) + logger.error { "二维码消费初始化异常:<${e.message}>" } + throw InternalServerError(e.message) } } } @@ -141,30 +152,12 @@ constructor(private val request: NativeWebRequest, this.refno = transaction.refno this.hostDate = systime.hostdate this.hostTime = systime.hosttime + this.amount = abs(MoneyUtil.YuanToFen(transaction.personDtl.amount)) } if (TradeDict.DTL_STATUS_INIT != transaction.status) { - return when (transaction.status) { - TradeDict.DTL_STATUS_WIP -> { - ResponseBodyBuilder.ok(apiResponse.apply { - this.result = TransResult.REQUIRE_QUERY - }) - } - TradeDict.DTL_STATUS_FAIL -> { - ResponseBodyBuilder.ok(apiResponse.apply { - this.result = TransResult.FAILED - }) - } - TradeDict.DTL_STATUS_SUCCESS -> { - ResponseBodyBuilder.ok(apiResponse.apply { - this.result = TransResult.ALREADY_SUCCESS - }) - } - else -> { - ResponseBodyBuilder.ok(apiResponse.apply { - this.result = TransResult.FAILED - }) - } - } + return ResponseBodyBuilder.ok(apiResponse.apply { + this.result = checkAlreadyTransStatus(transaction.status) + }) } transactionService.qrcodeWip(param.billno, transaction) @@ -181,7 +174,10 @@ constructor(private val request: NativeWebRequest, ResponseBodyBuilder.ok(apiResponse.apply { this.result = TransResult.REQUIRE_QUERY }) } else -> { - ResponseBodyBuilder.ok(apiResponse.apply { this.result = TransResult.FAILED }) + ResponseBodyBuilder.ok(apiResponse.apply { + this.description = response.agentMsg + this.result = TransResult.FAILED + }) } } } catch (e: Exception) { @@ -190,8 +186,8 @@ constructor(private val request: NativeWebRequest, is PessimisticLockingFailureException -> throw ConflictError(e.message) else -> { e.printStackTrace() - println("二维码消费确认异常:<${e.message}>") - throw InternalError(e.message) + logger.error { "二维码消费确认异常:<${e.message}>" } + throw InternalServerError(e.message) } } } @@ -200,11 +196,13 @@ constructor(private val request: NativeWebRequest, override fun qrcodePayQuery(xTenantId: String, refno: String): ResponseEntity { try { val systime = systemUtilService.sysdatetime + consumePayService.getTransactionMainDtl(refno, null, null)?.let { return ResponseBodyBuilder.ok(QrcodePayConfirmResponse().apply { this.refno = it.refno this.hostDate = systime.hostdate this.hostTime = systime.hosttime + this.amount = MoneyUtil.YuanToFen(it.personDtl.amount) this.result = when (it.status) { TradeDict.DTL_STATUS_SUCCESS -> TransResult.SUCCESS TradeDict.DTL_STATUS_WIP -> TransResult.REQUIRE_QUERY @@ -219,9 +217,98 @@ constructor(private val request: NativeWebRequest, is PessimisticLockingFailureException -> throw ConflictError(e.message) else -> { e.printStackTrace() - println("二维码消费查询异常:<${e.message}>") - throw InternalError(e.message) + logger.error { "二维码消费查询异常:<${e.message}>" } + throw InternalServerError(e.message) + } + } + } + } + + override fun refund(xTenantId: String, param: RefundRequest): ResponseEntity { + try { + // 查询是否重试 + val alreadyTrans = consumePayService.queryRefundRetry(param.billno, param.shopaccno) + val systime = systemUtilService.sysdatetime + val apiResponse = RefundResponse().apply { + this.billno = param.billno + this.hostDate = systime.hostdate + this.hostTime = systime.hosttime + } + + val refundTrans:TTransactionMain + if (alreadyTrans != null) { + val alreadyStatus = checkAlreadyTransStatus(alreadyTrans.status) + // 判断已有的流水是否是init状态 + if (alreadyStatus != null) { + return ResponseBodyBuilder.ok(apiResponse.apply { + this.refno = alreadyTrans.refno + this.result = alreadyStatus + }) + } else { + // 直接发起第三方重试 + refundTrans = alreadyTrans } + } else { + // 先进行流水初始化 + refundTrans = consumePayService.initRefundTransactionMain(param) + } + + val service = createAgentService(refundTrans.sourceType) + val resp = service.refund(refundTrans) + + apiResponse.apply { + this.refno = refundTrans.refno + } + when (resp.code) { + AgentCode.SUCCESS -> { + transactionService.success(refundTrans.refno, resp.agentRefno, false) + return ResponseEntity.ok(apiResponse.apply { this.result = TransResult.SUCCESS }) + } + AgentCode.REQUIRE_QUERY -> { + //待查询 + //agentQueryResultTask.queryResult(TenantContextHolder.getContext().tenant, refundTrans) + transactionService.wip(refundTrans.refno) + return ResponseEntity.ok(apiResponse.apply { this.result = TransResult.REQUIRE_QUERY }) + } + else -> transactionService.fail(refundTrans.refno, + "${resp.agentCode}-${resp.agentMsg}").let { + logger.error { "退款业务失败:<${resp.agentMsg}>" } + return ResponseEntity.ok(apiResponse.apply { this.result = TransResult.FAILED }) + } + } + } catch (e: Exception) { + when (e) { + is BadRequestError -> throw BadRequestError(e.message) + is PessimisticLockingFailureException -> throw ConflictError(e.message) + else -> { + e.printStackTrace() + logger.error { "退款交易业务异常:<${e.message}>" } + throw InternalServerError(e.message) + } + } + } + } + +// override fun refundQuery(xTenantId: String, billno: String): ResponseEntity { +// +// } + + fun checkAlreadyTransStatus(status: String): TransResult? { + return when (status) { + TradeDict.DTL_STATUS_WIP -> { + TransResult.REQUIRE_QUERY + } + TradeDict.DTL_STATUS_FAIL -> { + TransResult.FAILED + } + TradeDict.DTL_STATUS_SUCCESS -> { + TransResult.ALREADY_SUCCESS + } + TradeDict.DTL_STATUS_INIT -> { + null + } + else -> { + TransResult.FAILED } } } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt index ae4c7a72..8475a22c 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt @@ -323,7 +323,7 @@ class UserAPIController { this.setOutTransInfo(request.operid, request.operSeqno) } val transaction = builder.refundInit(request.originRefno, request.totalAmount / 100.0, - transactionService) + transactionService,null,null) result.apply { refno = transaction.refno diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt index 832cf5ce..c5e0daae 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt @@ -5,6 +5,7 @@ import com.supwisdom.dlpay.agent.domain.QrcodePayTrans import com.supwisdom.dlpay.api.domain.TTransactionMain import com.supwisdom.dlpay.framework.domain.TDictionary import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest +import com.supwisdom.dlpay.payapi.model.RefundRequest import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional @@ -32,4 +33,10 @@ interface ConsumePayService { @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class]) fun getTransactionMainByParam(param: QrcodePayConfirmRequest): TTransactionMain + + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class]) + fun initRefundTransactionMain(param: RefundRequest): TTransactionMain + + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class]) + fun queryRefundRetry(billno: String,shopaccno:String):TTransactionMain? } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt index 9d8b5c44..9fdfaea8 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt @@ -18,12 +18,14 @@ import com.supwisdom.dlpay.api.service.ConsumePayService import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionServiceProxy import com.supwisdom.dlpay.exception.BadRequestError +import com.supwisdom.dlpay.exception.InternalServerError import com.supwisdom.dlpay.exception.TransactionCheckException import com.supwisdom.dlpay.framework.dao.DictionaryDao import com.supwisdom.dlpay.framework.domain.TDictionary import com.supwisdom.dlpay.framework.service.SystemUtilService import com.supwisdom.dlpay.framework.util.* import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest +import com.supwisdom.dlpay.payapi.model.RefundRequest import com.supwisdom.multitenant.TenantContextHolder import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service @@ -39,9 +41,9 @@ class ConsumePayServiceImpl : ConsumePayService { @Autowired lateinit var agentServiceProxy: AgentServiceProxy @Autowired - lateinit var accountUtilServcie:AccountUtilServcie + lateinit var accountUtilServcie: AccountUtilServcie @Autowired - lateinit var transactionService:TransactionServiceProxy + lateinit var transactionService: TransactionServiceProxy @Autowired lateinit var agentPayServiceContext: AgentPayServiceContext @Autowired @@ -82,19 +84,19 @@ class ConsumePayServiceImpl : ConsumePayService { } } - override fun qrcodePayConfirmPostProcess(response: AgentResponse, billno: String,transaction:TTransactionMain) { - val qrcodeTrans = qrcodePayTransDao.findByQrcodePayTransForUpdate(transaction.outId, systemUtilService.sysdatetime.hostdate, + override fun qrcodePayConfirmPostProcess(response: AgentResponse, billno: String, transaction: TTransactionMain) { + qrcodePayTransDao.findByQrcodePayTransForUpdate(transaction.outId, systemUtilService.sysdatetime.hostdate, billno, TenantContextHolder.getContext().tenant.id) ?: throw BadRequestError("未找到billno") when (response.code) { AgentCode.SUCCESS -> { - transactionService.qrcodeSuccess(transaction.refno, response.agentRefno,transaction) + transactionService.qrcodeSuccess(transaction.refno, response.agentRefno, transaction) } AgentCode.REQUIRE_QUERY -> { return } else -> { - transactionService.qrcodeFail(transaction.refno, response.agentMsg,transaction) + transactionService.qrcodeFail(transaction.refno, response.agentMsg, transaction) } } } @@ -130,7 +132,7 @@ class ConsumePayServiceImpl : ConsumePayService { if (!qrcodeTrans.refno.isNullOrEmpty()) { return getTransactionMainDtl(qrcodeTrans.refno, null, null) - ?: throw InternalError("未找到refno") + ?: throw InternalServerError("未找到refno") } //2. 初始化交易流水 @@ -181,4 +183,30 @@ class ConsumePayServiceImpl : ConsumePayService { return transaction } + + override fun initRefundTransactionMain(param: RefundRequest): TTransactionMain { + transactionMainDao.findByRefnoForUpdate(param.refno)?.let { mainDtl -> + if (mainDtl.sourceType.isNotEmpty()) { + //判断能否冲正 + if (mainDtl.shop) { + checkCanReverse(mainDtl.sourceType, mainDtl.shopDtl.shopaccno) + } else { + checkCanReverse(mainDtl.sourceType) + } + } else { + throw BadRequestError("该笔交易未定义sourcetype, 不支持退款") + } + + val builder = TransactionBuilder().apply { + setTransInfo(param.transDate, param.transTime, mainDtl.transCode, mainDtl.sourceType) + setOutTransInfo(mainDtl.outId, param.billno) + } + return builder.refundInit(mainDtl.refno, + param.amount / 100.0, transactionService, param.billno,param.shopaccno) + } ?: throw BadRequestError("未找到refno") + } + + override fun queryRefundRetry(billno: String,shopaccno:String): TTransactionMain? { + return transactionMainDao.findByBillno(billno, shopaccno) + } } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt index 20611448..ae24458c 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt @@ -2,20 +2,28 @@ package com.supwisdom.dlpay.api.service.impl import com.supwisdom.dlpay.agent.dao.QrcodePayTransDao import com.supwisdom.dlpay.api.TransactionBuilder +import com.supwisdom.dlpay.api.dao.RefundDtlDao import com.supwisdom.dlpay.api.dao.TransactionMainDao import com.supwisdom.dlpay.api.domain.* import com.supwisdom.dlpay.api.repositories.AccountService import com.supwisdom.dlpay.api.service.AccountUtilServcie import com.supwisdom.dlpay.api.service.SourceTypeService import com.supwisdom.dlpay.api.service.TransactionService +import com.supwisdom.dlpay.exception.BadRequestError +import com.supwisdom.dlpay.exception.InternalServerError import com.supwisdom.dlpay.exception.TransactionCheckException import com.supwisdom.dlpay.exception.TransactionProcessException import com.supwisdom.dlpay.framework.service.SystemUtilService -import com.supwisdom.dlpay.framework.util.* +import com.supwisdom.dlpay.framework.util.DateUtil +import com.supwisdom.dlpay.framework.util.Subject +import com.supwisdom.dlpay.framework.util.TradeDict +import com.supwisdom.dlpay.framework.util.TradeErrorCode import com.supwisdom.multitenant.TenantContextHolder import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service +import java.math.BigDecimal import java.sql.SQLException +import kotlin.math.abs import kotlin.math.absoluteValue @@ -39,6 +47,9 @@ class TransactionServiceImpl : TransactionService { @Autowired private lateinit var qrcodePayTransDao: QrcodePayTransDao + @Autowired + private lateinit var refundDtlDao: RefundDtlDao + /// 公共函数部分 private fun preCheck(builder: TransactionBuilder) { @@ -297,12 +308,12 @@ class TransactionServiceImpl : TransactionService { return transaction } - override fun qrcodeWip(billno: String,transaction:TTransactionMain): TTransactionMain { - val qrcodeTrans = qrcodePayTransDao.findByQrcodePayTransForUpdate(transaction.outId, systemUtilService.sysdatetime.hostdate, + override fun qrcodeWip(billno: String, transaction: TTransactionMain): TTransactionMain { + qrcodePayTransDao.findByQrcodePayTransForUpdate(transaction.outId, systemUtilService.sysdatetime.hostdate, billno, TenantContextHolder.getContext().tenant.id) - ?: throw InternalError("未找到billno") + ?: throw InternalServerError("未找到billno") if (transaction.status != TradeDict.DTL_STATUS_INIT) { - throw InternalError("流水<${transaction.refno}>状态错误") + throw InternalServerError("流水<${transaction.refno}>状态错误") } updateRecordStatus(transaction, TradeDict.DTL_STATUS_WIP) transactionMainDao.save(transaction) @@ -411,9 +422,9 @@ class TransactionServiceImpl : TransactionService { transactionOnSuccess(transaction, sourcetypeRefno, false) if (transaction.reverseType != TradeDict.REVERSE_FLAG_NONE) { - val originTrans = transactionMainDao.findByRefnoNoLock(transaction.reverseRefno ?: "") + val originTrans = transactionMainDao.findByRefnoForUpdate(transaction.reverseRefno ?: "") ?: throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR, - "系统异常,无法处理退款流水") + "系统异常,被退款流水未找到") originTrans.reverseFlag = transaction.reverseType if (originTrans.person) { originTrans.personDtl.reverseFlag = originTrans.reverseFlag @@ -471,10 +482,13 @@ class TransactionServiceImpl : TransactionService { ////////////////////////////////////////////////////////////////// // 回退业务接口,包括撤销和退款 - override fun refundInit(originRefno: String, refundAmount: Double, builder: TransactionBuilder): TTransactionMain { + override fun refundInit(originRefno: String, refundAmount: Double, builder: TransactionBuilder, billno: String?, shopaccno: String?): TTransactionMain { val originTrans = transactionMainDao.findByRefnoForUpdate(originRefno) ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS , "被退款流水不存在") + if (!originTrans.person) { + throw BadRequestError("无法退款非个人流水") + } if (TradeDict.REVERSE_FLAG_NONE != originTrans.reverseType) { throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, "原始流水为撤销或退款流水,不能再被退款") @@ -484,24 +498,80 @@ class TransactionServiceImpl : TransactionService { throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, "原流水不是成功状态") } - if (originTrans.reverseFlag != TradeDict.REVERSE_FLAG_NONE) { - throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, - "原流水已被退款或正在处理退款中") - } - doReversePrepareAndCheck(originTrans, builder, refundAmount) - //fixme: 判断退款金额 check 不支持部分退款 - if (originTrans.person) { - if (!MoneyUtil.moneyEqual(Math.abs(originTrans.personDtl.amount), refundAmount)) { - throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR - , "暂不支持部分退款") + val tenantid = TenantContextHolder.getContext().tenant.id + val availableAmount: Double + val seqno: Int + // 校验原流水是否已被退款或撤销 + when (originTrans.reverseFlag) { + TradeDict.REVERSE_FLAG_CANCEL -> { + throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, + "原流水已被撤销") + } + TradeDict.REVERSE_FLAG_REFUND -> { + // 查询原流水最后一笔退款流水 + val lastRefundDtl = refundDtlDao.findFirstByOriginrefnoAndTenantidOrderBySeqnoDesc(originRefno, tenantid) + ?: throw InternalServerError("原流水refno:<${originRefno}>的最后一笔退款流水未找到") + val lastTransMain = transactionMainDao.findByRefno(lastRefundDtl.refno) + ?: throw InternalServerError("原流水refno:<${originRefno}>的最后一笔退款主流水未找到") + + when (lastTransMain.status) { + TradeDict.DTL_STATUS_WIP -> { + throw BadRequestError("原流水refno:<${originRefno}>有一笔正在处理中的退款请求") + } + TradeDict.DTL_STATUS_INIT -> { + lastTransMain.status = TradeDict.DTL_STATUS_CANCEL + transactionMainDao.save(lastTransMain) + throw InternalServerError("原流水refno:<${originRefno}>最后一笔退款流水已取消") + } + TradeDict.DTL_STATUS_SUCCESS -> { + availableAmount = lastRefundDtl.afteramount + } + TradeDict.DTL_STATUS_FAIL -> { + availableAmount = (BigDecimal(lastRefundDtl.afteramount) + + BigDecimal(lastRefundDtl.amount)).toDouble() + } + else -> { + throw InternalServerError("原流水refno:<${originRefno}>最后一笔退款流水状态异常") + } + } + seqno = lastRefundDtl.seqno + 1 + } + TradeDict.REVERSE_FLAG_NONE -> { + availableAmount = abs(originTrans.personDtl.amount) + seqno = 1 } + else -> { + throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, + "原流水的退款或撤销状态异常") + } + } + + if (refundAmount > availableAmount) { + throw BadRequestError("退款金额不能超过原流水剩余金额") } - val transaction = doReverseProcess(originTrans, builder) - originTrans.reverseFlag = TradeDict.REVERSE_FLAG_WIP - originTrans.refundAmount = refundAmount + doReversePrepareAndCheck(originTrans, builder, refundAmount) + + // 交易主表生成一笔退款流水 + val transaction = doReverseProcess(originTrans, builder, refundAmount) + // 标记原流水为已有退款 + originTrans.reverseFlag = TradeDict.REVERSE_FLAG_REFUND transactionMainDao.save(originTrans) + // 记录退款流水表 + val refundDtl = TRefundDtl().apply { + this.refno = transaction.refno + this.originrefno = originRefno + this.amount = refundAmount + this.afteramount = (BigDecimal(availableAmount) - BigDecimal(refundAmount)).toDouble() + this.seqno = seqno + this.outid = shopaccno + this.outtradeno = billno + this.tenantid = tenantid + this.tradeflag = transaction.personDtl.tradeflag + } + refundDtlDao.save(refundDtl) + return transaction } @@ -524,9 +594,9 @@ class TransactionServiceImpl : TransactionService { } doReversePrepareAndCheck(originTrans, builder, 0.0) - val transaction = doReverseProcess(originTrans, builder) + val transaction = doReverseProcess(originTrans, builder, 0.0) - originTrans.reverseFlag = TradeDict.REVERSE_FLAG_WIP +// originTrans.reverseFlag = TradeDict.REVERSE_FLAG_WIP transactionMainDao.save(originTrans) return transaction } @@ -548,11 +618,11 @@ class TransactionServiceImpl : TransactionService { } } - private fun doReverseProcess(originTrans: TTransactionMain, builder: TransactionBuilder): TTransactionMain { + private fun doReverseProcess(originTrans: TTransactionMain, builder: TransactionBuilder, amount: Double): TTransactionMain { val summarySuffix = getReverseSuffix(builder.reverseType) if (originTrans.person) { builder.person().apply { - setAmount(-originTrans.personDtl.amount, getOppositeTradeFlag(originTrans.personDtl.tradeflag)) + setAmount(-amount, getOppositeTradeFlag(originTrans.personDtl.tradeflag)) setOpposite(originTrans.personDtl.oppositeAccNo, originTrans.personDtl.oppositeAccName) } builder.description = "${originTrans.personDtl.transdesc}$summarySuffix" @@ -560,7 +630,7 @@ class TransactionServiceImpl : TransactionService { } if (originTrans.shop) { builder.shop().apply { - setAmount(-originTrans.shopDtl.amount, getOppositeTradeFlag(originTrans.shopDtl.tradeflag)) + setAmount(-amount, getOppositeTradeFlag(originTrans.shopDtl.tradeflag)) setOpposite(originTrans.shopDtl.oppositeAccNo, originTrans.shopDtl.oppositeAccName) } builder.description = "${originTrans.shopDtl.transdesc}$summarySuffix" @@ -569,7 +639,7 @@ class TransactionServiceImpl : TransactionService { originTrans.details.forEach { builder.addDebitCreditRecord(it.draccno, it.drsubjno, it.craccno, it.crsubjno, - -it.amount, "${it.summary}$summarySuffix") + -amount, "${it.summary}$summarySuffix") } builder.dtltype = originTrans.dtltype if (builder.operId.isNullOrEmpty()) { @@ -583,14 +653,14 @@ class TransactionServiceImpl : TransactionService { if (originTrans.person) { val account = accountUtilService.readAccount(originTrans.personDtl.userid) builder.person(account).apply { - setAmount(-originTrans.personDtl.amount, + setAmount(-amount, getOppositeTradeFlag(originTrans.personDtl.tradeflag)) } } if (originTrans.shop) { val shopacc = accountUtilService.readShopbyShopaccno(originTrans.shopDtl.shopaccno) builder.shop(shopacc).apply { - setAmount(-originTrans.shopDtl.amount, + setAmount(-amount, getOppositeTradeFlag(originTrans.shopDtl.tradeflag)) } } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt index dd9dff40..6fddee84 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt @@ -50,7 +50,7 @@ interface TransactionService { * 退款类业务 */ @Transactional - fun refundInit(originRefno: String, refundAmount: Double, builder: TransactionBuilder): TTransactionMain + fun refundInit(originRefno: String, refundAmount: Double, builder: TransactionBuilder,billno: String?,shopaccno:String?): TTransactionMain /** * 撤销业务 @@ -161,8 +161,8 @@ class TransactionServiceProxy { return trans } - fun refund(originRefno: String, refundAmount: Double, builder: TransactionBuilder, confirm: Boolean): TTransactionMain { - val trans = transactionService.refundInit(originRefno, refundAmount, builder) + fun refund(originRefno: String, refundAmount: Double,billno:String?,shopaccno:String?, builder: TransactionBuilder, confirm: Boolean): TTransactionMain { + val trans = transactionService.refundInit(originRefno, refundAmount, builder,billno,shopaccno) if (confirm) { return success(trans.refno) } @@ -185,10 +185,6 @@ class TransactionServiceProxy { return transactionMainDao.findByRefno(refno) } - fun findTransactionByRefnoForUpdate(refno: String): TTransactionMain { - return transactionMainDao.findByRefnoForUpdate(refno) - } - fun findByOutTradeNo(refno: String): TTransactionMain? { return transactionMainDao.findByOutTradeNo(refno) } diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt index 85ed71a0..bfeedb5e 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt @@ -417,13 +417,13 @@ class TransactionBuilder { fun refund(originRefno: String, amount: Double, transactionService: TransactionServiceProxy): TTransactionMain { this.reverseType = TradeDict.REVERSE_FLAG_REFUND this.reverseRefno = originRefno - return transactionService.refund(originRefno, amount, this, true) + return transactionService.refund(originRefno, amount, null, null, this, true) } - fun refundInit(originRefno: String, amount: Double, transactionService: TransactionServiceProxy): TTransactionMain { + fun refundInit(originRefno: String, amount: Double, transactionService: TransactionServiceProxy, billno: String?, shopaccno: String?): TTransactionMain { this.reverseType = TradeDict.REVERSE_FLAG_REFUND this.reverseRefno = originRefno - return transactionService.refund(originRefno, amount, this, false) + return transactionService.refund(originRefno, amount, billno, shopaccno, this, false) } fun cancel(originRefno: String, transactionService: TransactionServiceProxy): TTransactionMain { diff --git a/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt b/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt index 0d4f8085..60983ee2 100644 --- a/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt +++ b/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt @@ -125,7 +125,7 @@ class SecurityControllerTest : MvcBaseTest() { @Test fun testHMACSHA256() { - val token = "ddCUUHAF/OOBTAZq" + val token = "nR64NdrF3tjkcIHb" val secret = "dc1d26c0d43e442588092c8d45c21bce" val jwt = HmacUtil.HMACSHA256(token, secret) println(jwt)