# Postgresql settings
spring.datasource.platform=postgresql
#spring.datasource.url=jdbc:postgresql://ykt.supwisdom.com:15432/payapidev
-spring.datasource.url=jdbc:postgresql://172.28.201.70:15432/payapidev
+spring.datasource.url=jdbc:postgresql://172.28.201.70:15432/touchorder
spring.datasource.username=payapi
spring.datasource.password=123456
spring.datasource.continue-on-error=true
spring.kafka.consumer.auto-commit-interval=100
# 指定消息key和消息体的编解码方式
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
-spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
\ No newline at end of file
+spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
+payapi.url=localhost:8080/payapi
\ No newline at end of file
@JsonProperty("transTime")
private String transTime;
- @JsonProperty("dlttype")
- private String dlttype;
+ @JsonProperty("dtltype")
+ private String dtltype;
@JsonProperty("amount")
private Integer amount;
this.transTime = transTime;
}
- public QrcodePayConfirmRequest dlttype(String dlttype) {
- this.dlttype = dlttype;
+ public QrcodePayConfirmRequest dtltype(String dtltype) {
+ this.dtltype = dtltype;
return this;
}
/**
- * Get dlttype
- * @return dlttype
+ * Get dtltype
+ * @return dtltype
*/
@ApiModelProperty(value = "")
- public String getDlttype() {
- return dlttype;
+ public String getDtltype() {
+ return dtltype;
}
- public void setDlttype(String dlttype) {
- this.dlttype = dlttype;
+ public void setDtltype(String dtltype) {
+ this.dtltype = dtltype;
}
public QrcodePayConfirmRequest amount(Integer amount) {
Objects.equals(this.shopaccno, qrcodePayConfirmRequest.shopaccno) &&
Objects.equals(this.transDate, qrcodePayConfirmRequest.transDate) &&
Objects.equals(this.transTime, qrcodePayConfirmRequest.transTime) &&
- Objects.equals(this.dlttype, qrcodePayConfirmRequest.dlttype) &&
+ Objects.equals(this.dtltype, qrcodePayConfirmRequest.dtltype) &&
Objects.equals(this.amount, qrcodePayConfirmRequest.amount) &&
Objects.equals(this.userid, qrcodePayConfirmRequest.userid) &&
Objects.equals(this.anonymous, qrcodePayConfirmRequest.anonymous) &&
@Override
public int hashCode() {
- return Objects.hash(billno, shopaccno, transDate, transTime, dlttype, amount, userid, anonymous, qrcode, qrcodeFormat);
+ return Objects.hash(billno, shopaccno, transDate, transTime, dtltype, amount, userid, anonymous, qrcode, qrcodeFormat);
}
@Override
sb.append(" shopaccno: ").append(toIndentedString(shopaccno)).append("\n");
sb.append(" transDate: ").append(toIndentedString(transDate)).append("\n");
sb.append(" transTime: ").append(toIndentedString(transTime)).append("\n");
- sb.append(" dlttype: ").append(toIndentedString(dlttype)).append("\n");
+ sb.append(" dtltype: ").append(toIndentedString(dtltype)).append("\n");
sb.append(" amount: ").append(toIndentedString(amount)).append("\n");
sb.append(" userid: ").append(toIndentedString(userid)).append("\n");
sb.append(" anonymous: ").append(toIndentedString(anonymous)).append("\n");
import feign.RequestInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Optional;
-@Component
+@Configuration
public class PayAPIRequestInterceptor {
private final JwtTokenClientManager manager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
@Configuration
@EnableTenantJwtClient
@EnableFeignClients(basePackages = "com.supwisdom.dlpay.paysdk")
+@Import({PayAPIRequestInterceptor.class})
public class PayAPISDKConfigure {
@Bean
public JwtTokenClientCallback jwtTokenClientCallback(ApiLoginProxy loginProxy) {
package com.supwisdom.dlpay.paysdk.proxy;
import com.supwisdom.dlpay.api.bean.*;
+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.mutlitenant.client.annotations.JwtMethod;
import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
@FeignClient(name = "consumePropxy", url = "${payapi.url}")
@RequestMapping("/api/consume")
public interface ConsumePropxy {
- @PostMapping("/qrcode/init")
- @JwtMethod
- QrcodePayResponse qrcodePayInit(@RequestBody QrcodePayParam param);
-
- @PostMapping("/qrcode/confirm")
- @JwtMethod
- QrcodePayResponse qrcodePayConfirm(@RequestBody QrcodePayParam param);
-
@PostMapping("/thirdpay/init")
@JwtMethod
ThirdPayResponse thirdpayInit(@RequestBody ThirdPayinitParam param);
@PostMapping("/qrcodequery")
@JwtMethod
DoorQrcodeResponse qrcodequery(@RequestBody DoorQRCodeParam param);
+
+
+ @RequestMapping(value = "/qrcode/init", method = RequestMethod.POST)
+ @JwtMethod
+ QrcodePayInitResponse qrcodePayInit(@RequestHeader(name = "X-TENANT-ID") String xTenantId,
+ @Valid @RequestBody QrcodePayInitRequest qrcodePayInitRequest);
+
+ @RequestMapping(value = "/qrcode/confirm", method = RequestMethod.POST)
+ @JwtMethod
+ QrcodePayConfirmResponse qrcodePayConfirm(@RequestHeader(name = "X-TENANT-ID") String xTenantId,
+ @Valid @RequestBody QrcodePayConfirmRequest qrcodePayConfirmRequest);
+
+ @RequestMapping(value = "/qrcode/query/{refno}", method = RequestMethod.GET)
+ @JwtMethod
+ QrcodePayConfirmResponse qrcodePayQuery(@RequestHeader(name = "X-TENANT-ID") String xTenantId,
+ @PathVariable("refno") String refno);
}
$ref: 'definitions.yaml#/components/schemas/TransDate'
transTime:
$ref: 'definitions.yaml#/components/schemas/TransTime'
- dlttype:
+ dtltype:
type: string
title: 交易类型
amount:
components:
headers:
TenantId:
- name: X-Tenant-Id
+ name: X-TENANT-ID
in: header
description: 租户ID
required: true
import com.supwisdom.dlpay.agent.domain.QrcodePayTrans;
import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import javax.persistence.LockModeType;
+import javax.persistence.QueryHint;
@Repository
QrcodePayTrans findByAgentMerchIdAndHostdateAndBillnoAndTenantid(String agentMerchId, String hostdate,
String billno, String tenantid);
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @Query("select t from QrcodePayTrans t where t.agentMerchId=:agentMerchId and t.hostdate=:hostdate and t.billno=:billno and t.tenantid=:tenantid")
+ @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
+ QrcodePayTrans findByQrcodePayTransForUpdate(@Param("agentMerchId") String agentMerchId,@Param("hostdate") String hostdate,
+ @Param("billno")String billno,@Param("tenantid") String tenantid);
+
void deleteByRefnoAndTenantid(String refno, String tenantid);
}
@Column(name = "tenantid", length = 20)
private String tenantid;
- @Column(name = "spid", length = 20)
+ @Column(name = "spip", length = 20)
private String spip;
public QrcodePayTrans() {
systemUtilService.getSysdatetime().getHostdate(), billno, TenantContextHolder.getContext().getTenant().getId());
}
+ public QrcodePayTrans findByQrcodePayTransForUpdate(String merchid, String billno) {
+ return qrcodeTransDao.findByQrcodePayTransForUpdate(merchid,
+ systemUtilService.getSysdatetime().getHostdate(), billno, TenantContextHolder.getContext().getTenant().getId());
+ }
+
public QrcodePayTrans qrcodePayTransSaveOrUpdate(@NotNull QrcodePayTrans bean) {
if (StringUtil.isEmpty(bean.getTenantid())) {
bean.setTenantid(TenantContextHolder.getContext().getTenant().getId());
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
- default ResponseEntity<QrcodePayInitResponse> qrcodePayInit(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "QrCode 初始化" ) @Valid @RequestBody QrcodePayInitRequest qrcodePayInitRequest) {
+ default ResponseEntity<QrcodePayInitResponse> qrcodePayInit(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-TENANT-ID", required=true) String xTenantId,@ApiParam(value = "QrCode 初始化" ) @Valid @RequestBody QrcodePayInitRequest qrcodePayInitRequest) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
@RequestMapping(value = "/consume/qrcode/query/{refno}",
produces = { "application/json" },
method = RequestMethod.GET)
- default ResponseEntity<QrcodePayConfirmResponse> qrcodePayQuery(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "系统交易参考号",required=true) @PathVariable("refno") String refno) {
+ default ResponseEntity<QrcodePayConfirmResponse> qrcodePayQuery(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-TENANT-ID", required=true) String xTenantId,@ApiParam(value = "系统交易参考号",required=true) @PathVariable("refno") String refno) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
import com.supwisdom.dlpay.agent.AgentCode
import com.supwisdom.dlpay.agent.AgentPayService
import com.supwisdom.dlpay.agent.AgentPayServiceContext
-import com.supwisdom.dlpay.agent.domain.QrcodePattern
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.service.*
+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.TransactionCheckException
import com.supwisdom.dlpay.framework.ResponseBodyBuilder
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.framework.util.TradeErrorCode
-import com.supwisdom.dlpay.payapi.model.QrcodePayInitRequest
-import com.supwisdom.dlpay.payapi.model.QrcodePayInitResponse
-import com.supwisdom.multitenant.TenantContextHolder
+import com.supwisdom.dlpay.payapi.model.*
import org.apache.commons.lang3.StringUtils
+import org.springframework.dao.PessimisticLockingFailureException
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
private val agentPayServiceContext: AgentPayServiceContext,
private val userService: UserService,
private val consumePayService: ConsumePayService,
- private val transactionService: TransactionServiceProxy,
- private val accountUtilServcie: AccountUtilServcie) : ConsumeApi {
+ private val transactionService: TransactionServiceProxy) : ConsumeApi {
override fun getRequest(): Optional<NativeWebRequest> {
return Optional.ofNullable(request)
}
override fun qrcodePayInit(xTenantId: String, @Valid param: QrcodePayInitRequest): ResponseEntity<QrcodePayInitResponse> {
- val apiResp = QrcodePayInitResponse()
- // 1. 检查 qrcode
- val qrcode: QrcodePattern
try {
- qrcode = agentServiceProxy.qrcodeMatch(param.qrcode)
+ val apiResp = QrcodePayInitResponse()
+ // 1. 检查 qrcode
+ val qrcode = agentServiceProxy.qrcodeMatch(param.qrcode)
?: throw BadRequestError("未识别的支付码")
- } catch (e: Exception) {
- throw BadRequestError(e.message!!)
- }
- val sourceType = sourceTypeService.getBySourceType(qrcode.sourceType)
- ?: throw BadRequestError("不支持的支付方式<${qrcode.sourceType}>")
- if (sourceType.paySubjno.isEmpty()
- || !StringUtils.isNumeric(sourceType.paySubjno)) {
- throw BadRequestError("支付方式<${qrcode.sourceType}>未配置科目号")
- }
- val systime = systemUtilService.sysdatetime
- // 2. 记录 qrcode 交易明细表
- val qrcodeTrans = agentServiceProxy.qrcodePayTransSaveOrUpdate(
- QrcodePayTrans().apply {
- this.agentMerchId = param.shopaccno
- this.billno = param.billno
- this.qrcode = param.qrcode
- this.createTime = systime.sysdate
- this.hostdate = systime.hostdate
- this.sourceType = sourceType.sourceType
- this.updateTime = systime.sysdate
- })
- // 3. 查询用户身份
- val service = createAgentService<QrcodePayTrans>(qrcode.sourceType)
- val agentResp = service.auth(param.shopaccno, param.billno)
- // 4. 重新读取 qrcode 交易明细表,以获取 service.auth 查询后的结果数据
- // 修改, 通过返回值来判断
+ val sourceType = sourceTypeService.getBySourceType(qrcode.sourceType)
+ ?: throw BadRequestError("不支持的支付方式<${qrcode.sourceType}>")
+ if (sourceType.paySubjno.isEmpty()
+ || !StringUtils.isNumeric(sourceType.paySubjno)) {
+ throw BadRequestError("支付方式<${qrcode.sourceType}>未配置科目号")
+ }
+ val systime = systemUtilService.sysdatetime
+ // 2. 记录 qrcode 交易明细表
+ val qrcodeTrans = agentServiceProxy.qrcodePayTransSaveOrUpdate(
+ QrcodePayTrans().apply {
+ this.agentMerchId = param.shopaccno
+ this.billno = param.billno
+ this.qrcode = param.qrcode
+ this.createTime = systime.sysdate
+ this.hostdate = systime.hostdate
+ this.sourceType = sourceType.sourceType
+ this.updateTime = systime.sysdate
+ this.isAnonymous = true
+ })
+ // 3. 查询用户身份
+ val service = createAgentService<QrcodePayTrans>(qrcode.sourceType)
+ val agentResp = service.auth(param.shopaccno, param.billno)
+ // 4. 重新读取 qrcode 交易明细表,以获取 service.auth 查询后的结果数据
+ // 修改, 通过返回值来判断
// val qrcodeTransResp = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(qrcodeTrans.agentMerchId,
// qrcodeTrans.billno)
- apiResp.also {
- it.sourcetype = sourceType.sourceType
- }
- return when (agentResp.code) {
- AgentCode.SUCCESS -> {
- val qrcodeTransResp = agentResp.payload
- if (!sourceType.anonymousEnable && qrcodeTransResp.isAnonymous) {
- throw BadRequestError("支付方式<${qrcode.sourceType}> 不支持匿名支付")
- } else {
- // 更新qrcode交易明细表 是否匿名 字段
- if (StringUtil.isEmpty(qrcodeTransResp.userid)) {
- qrcodeTransResp.isAnonymous = true
+ apiResp.also {
+ it.sourcetype = sourceType.sourceType
+ }
+ return when (agentResp.code) {
+ AgentCode.SUCCESS -> {
+ val qrcodeTransResp = agentResp.payload
+ if (!sourceType.anonymousEnable && qrcodeTransResp.isAnonymous) {
+ throw BadRequestError("支付方式<${qrcode.sourceType}> 不支持匿名支付")
} else {
- val person = userService.findByUseridOrThirdUniqueIdenty(qrcodeTransResp.userid, null)
- if (person == null) {
+ // 更新qrcode交易明细表 是否匿名 字段
+ if (StringUtil.isEmpty(qrcodeTransResp.userid)) {
qrcodeTransResp.isAnonymous = true
} else {
- apiResp.apply {
- qrcodeTransResp.isAnonymous = false
- this.userid = qrcodeTransResp.agentUserId
- this.username = person.name
+ val person = userService.findByUseridOrThirdUniqueIdenty(qrcodeTransResp.userid, null)
+ if (person == null) {
+ qrcodeTransResp.isAnonymous = true
+ } else {
+ apiResp.apply {
+ qrcodeTransResp.isAnonymous = false
+ this.userid = qrcodeTransResp.agentUserId
+ this.username = person.name
+ }
}
}
+ agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans.also {
+ it.isAnonymous = qrcodeTransResp.isAnonymous
+ })
+ ResponseBodyBuilder.ok(apiResp)
}
- agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans.also {
- it.isAnonymous = qrcodeTransResp.isAnonymous
- })
- ResponseBodyBuilder.ok(apiResp)
+ }
+ AgentCode.NOT_SUPPORT -> {
+ throw BadRequestError("系统或商户暂不支持当前的支付类型}")
+ }
+ else -> {
+ throw BadRequestError("第三方身份错误,<${agentResp.agentMsg}")
}
}
- AgentCode.NOT_SUPPORT -> {
- throw BadRequestError("系统或商户暂不支持当前的支付类型}")
- }
- else -> {
- throw BadRequestError("第三方身份错误,<${agentResp.agentMsg}")
+ } catch (e: Exception) {
+ when (e) {
+ is BadRequestError -> throw BadRequestError(e.message)
+ is PessimisticLockingFailureException -> throw ConflictError(e.message)
+ else -> {
+ e.printStackTrace()
+ println("二维码消费初始化异常:<${e.message}>")
+ throw InternalError(e.message)
+ }
}
}
}
"支付类型<$sourceType>未定义")
}
-// override fun qrcodePayConfirm(xTenantId: String, @Valid param: QrcodePayConfirmRequest): ResponseEntity<Void> {
-// //1. 交易检查
-// val apiResponse = QrcodePayResponse()
-// val qrcodeTrans = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(param.shopaccno,
-// param.billno) ?: return ResponseBodyBuilder.notFound(
-// "未找到billno") as ResponseEntity<Void>
-// if (!qrcodeTrans.refno.isNullOrEmpty()) {
-// return ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "该交易已确认,请查询结果"))
-// }
-//
-// if (qrcodeTrans.qrcode != param.qrcode && param.qrcode.isNotEmpty()) {
-// val qrcode = agentServiceProxy.qrcodeMatch(param.qrcode)
-// ?: return ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "未识别的支付码"))
-// if (qrcodeTrans.sourceType != qrcode.sourceType) {
-// return ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "支付码不符"))
-// }
-// qrcodeTrans.qrcode = param.qrcode
-// }
-//
-// val sourceType = sourceTypeService.getBySourceType(qrcodeTrans.sourceType)
-// ?: return ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
-// "不支持的支付方式<${qrcodeTrans.sourceType}>"))
-// if (!sourceType.anonymousEnable && qrcodeTrans.isAnonymous) {
-// return ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
-// "支付方式<${qrcodeTrans.sourceType}>不支持匿名支付"))
-// }
-// qrcodeTrans.spip = param.spip
-// val dtlType = consumePayService.getDtltypeDictionary(param.dtltype, Dictionary.DTLTYPES)
-// //2. 初始化交易流水
-// // sourcetype 资产类科目
-// val stSubject = accountUtilServcie.readSubject(sourceType.paySubjno)
-// // build 交易明细
-// val builder = TransactionBuilder().apply {
-// setTransInfo(param.transdate, param.transtime, TradeCode.TRANSCODE_QRCODE, qrcodeTrans.sourceType)
-// setOutTransInfo(qrcodeTrans.agentMerchId, qrcodeTrans.billno)
-// payinfo = qrcodeSummary(sourceType)
-// description = dtlType.dictcaption
-// dtltype = param.dtltype
-// }
-//
-// val shopacc = accountUtilServcie.readShopbyShopaccno(qrcodeTrans.agentMerchId)
-// val amount = param.amount / 100.0
-// builder.shop(shopacc).apply {
-// setAmount(amount, TradeDict.TRADE_FLAG_IN)
-// }
-// if (qrcodeTrans.isAnonymous) {
-// builder.anonymous().apply {
-// setAmount(amount, TradeDict.TRADE_FLAG_OUT)
-// setOpposite(shopacc.shopaccno, shopacc.shopname)
-// }.and() // 匿名消费时,借 科目 , 贷 商户
-// .addDebitCreditRecord(AccountProxy(stSubject), AccountProxy(shopacc),
-// amount, qrcodeSummary(sourceType))
-// } else {
-// val account = accountUtilServcie.readAccount(param.userid)
-// builder.person(account).apply {
-// setAmount(amount, TradeDict.TRADE_FLAG_OUT)
-// setOpposite(shopacc.shopaccno, shopacc.shopname)
-// }.and().shop().apply {
-// setOpposite(account.accno, account.accname)
-// }.and() // 实名消费时, 1. 借 科目, 贷 个人账户 ;2. 借 个人账户 贷 商户
-// .addDebitCreditRecord(AccountProxy(stSubject), AccountProxy(account),
-// amount, qrcodeSummary(sourceType))
-// .addDebitCreditRecord(AccountProxy(account), AccountProxy(shopacc),
-// amount, qrcodeSummary(sourceType))
-// }
-//
-// // 同一个客户ID + billno 是唯一索引,如果重复请求不能保存
-// val transaction = builder.init(transactionService)
-//
-// // qrcode 交易明细表记录 refno
-// qrcodeTrans.refno = transaction.refno
-// // 保存失败,可能客户端重复请求,导致前序交易已完成
-// agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans)
-//
-// //3. 调用第三方支付
-// transactionService.wip(transaction.refno)
-// val service = createAgentService<QrcodePayTrans>(qrcodeTrans.sourceType)
-// apiResponse.apply {
-// refno = transaction.refno
-// accdate = transaction.accdate
-// isRequireQuery = false
-// anonymous = qrcodeTrans.isAnonymous
-// }
-//
-// val response = service.pay(transaction)
-// return when (response.code) {
-// AgentCode.SUCCESS -> {
-// transactionService.success(transaction.refno, response.agentRefno)
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .success(apiResponse))
-// }
-// AgentCode.REQUIRE_QUERY -> {
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .success(apiResponse.also {
-// it.isRequireQuery = true
-// }))
-// }
-// else -> {
-// transactionService.fail(transaction.refno, response.agentMsg)
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
-// "第三方身份错误,<${response.agentMsg}"))
-// }
-// }
-// }
+ override fun qrcodePayConfirm(xTenantId: String, @Valid param: QrcodePayConfirmRequest): ResponseEntity<QrcodePayConfirmResponse> {
+ try {//1. 交易检查
+
+ val transaction = consumePayService.getTransactionMainByParam(param)
+ val systime = systemUtilService.sysdatetime
+ val apiResponse = QrcodePayConfirmResponse().apply {
+ this.refno = transaction.refno
+ this.hostDate = systime.hostdate
+ this.hostTime = systime.hosttime
+ }
+ 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
+ })
+ }
+ }
+ }
+
+ transactionService.qrcodeWip(param.billno, transaction)
+
+ val service = createAgentService<QrcodePayTrans>(transaction.sourceType)
+ val response = service.pay(transaction)
-// override fun qrcodePayQuery(xTenantId: String, refno: String): ResponseEntity<Void> {
-// return null
-// }
+ consumePayService.qrcodePayConfirmPostProcess(response, param.billno, transaction)
+ return when (response.code) {
+ AgentCode.SUCCESS -> {
+ ResponseBodyBuilder.ok(apiResponse.apply { this.result = TransResult.SUCCESS })
+ }
+ AgentCode.REQUIRE_QUERY -> {
+ ResponseBodyBuilder.ok(apiResponse.apply { this.result = TransResult.REQUIRE_QUERY })
+ }
+ else -> {
+ ResponseBodyBuilder.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()
+ println("二维码消费确认异常:<${e.message}>")
+ throw InternalError(e.message)
+ }
+ }
+ }
+ }
+
+ override fun qrcodePayQuery(xTenantId: String, refno: String): ResponseEntity<QrcodePayConfirmResponse> {
+ 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.result = when (it.status) {
+ TradeDict.DTL_STATUS_SUCCESS -> TransResult.SUCCESS
+ TradeDict.DTL_STATUS_WIP -> TransResult.REQUIRE_QUERY
+ TradeDict.DTL_STATUS_FAIL -> TransResult.FAILED
+ else -> TransResult.FAILED
+ }
+ })
+ } ?: throw BadRequestError("未找到refno")
+ } catch (e: Exception) {
+ when (e) {
+ is BadRequestError -> throw BadRequestError(e.message)
+ is PessimisticLockingFailureException -> throw ConflictError(e.message)
+ else -> {
+ e.printStackTrace()
+ println("二维码消费查询异常:<${e.message}>")
+ throw InternalError(e.message)
+ }
+ }
+ }
+ }
}
\ No newline at end of file
package com.supwisdom.dlpay.api.service
+import com.supwisdom.dlpay.agent.AgentResponse
+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 org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
fun getTransactionMainDtl(refno: String?, billno: String?, shopaccno: String?): TTransactionMain?
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
- fun getDtltypeDictionary(dictval:String, dicttype:String):TDictionary
+ fun getDtltypeDictionary(dictval: String, dicttype: String): TDictionary
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
fun checkCanReverse(sourcetype: String, shopaccno: String? = null): Boolean
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+ fun qrcodePayConfirmPostProcess(response: AgentResponse<QrcodePayTrans>, billno: String,transaction:TTransactionMain)
+
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+ fun getTransactionMainByParam(param: QrcodePayConfirmRequest): TTransactionMain
}
\ No newline at end of file
package com.supwisdom.dlpay.api.service.impl
+import com.supwisdom.dlpay.agent.AgentCode
+import com.supwisdom.dlpay.agent.AgentPayService
+import com.supwisdom.dlpay.agent.AgentPayServiceContext
+import com.supwisdom.dlpay.agent.AgentResponse
+import com.supwisdom.dlpay.agent.dao.QrcodePayTransDao
+import com.supwisdom.dlpay.agent.domain.QrcodePayTrans
+import com.supwisdom.dlpay.agent.service.AgentServiceProxy
+import com.supwisdom.dlpay.api.AccountProxy
+import com.supwisdom.dlpay.api.TransactionBuilder
import com.supwisdom.dlpay.api.dao.TransactionMainDao
+import com.supwisdom.dlpay.api.domain.TSourceType
import com.supwisdom.dlpay.api.domain.TTransactionMain
import com.supwisdom.dlpay.api.exception.RequestParamCheckException
+import com.supwisdom.dlpay.api.service.AccountUtilServcie
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.TransactionCheckException
import com.supwisdom.dlpay.framework.dao.DictionaryDao
import com.supwisdom.dlpay.framework.domain.TDictionary
-import com.supwisdom.dlpay.framework.util.StringUtil
+import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.*
+import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest
+import com.supwisdom.multitenant.TenantContextHolder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
lateinit var transactionMainDao: TransactionMainDao
@Autowired
lateinit var dictionaryDao: DictionaryDao
+ @Autowired
+ lateinit var agentServiceProxy: AgentServiceProxy
+ @Autowired
+ lateinit var accountUtilServcie:AccountUtilServcie
+ @Autowired
+ lateinit var transactionService:TransactionServiceProxy
+ @Autowired
+ lateinit var agentPayServiceContext: AgentPayServiceContext
+ @Autowired
+ lateinit var systemUtilService: SystemUtilService
+ @Autowired
+ lateinit var qrcodePayTransDao: QrcodePayTransDao
override fun checkShopPaytype(shopaccno: String, sourceType: String, anonymousflag: Boolean?): Boolean {
return sourceTypeService.checkShopSourceType(shopaccno, sourceType, true == anonymousflag)
sourceTypeService.checkShopCanReverse(sourcetype, shopaccno);
}
}
+
+ override fun qrcodePayConfirmPostProcess(response: AgentResponse<QrcodePayTrans>, billno: String,transaction:TTransactionMain) {
+ val qrcodeTrans = 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)
+ }
+ AgentCode.REQUIRE_QUERY -> {
+ return
+ }
+ else -> {
+ transactionService.qrcodeFail(transaction.refno, response.agentMsg,transaction)
+ }
+ }
+ }
+
+ private fun qrcodeSummary(st: TSourceType): String = st.paydesc + "扫码付"
+
+ fun <T> createAgentService(sourceType: String): AgentPayService<T> {
+ return agentPayServiceContext.findAgentPayService(sourceType)
+ ?: throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+ "支付类型<$sourceType>未定义")
+ }
+
+ override fun getTransactionMainByParam(param: QrcodePayConfirmRequest): TTransactionMain {
+ // 1.交易检查
+ val qrcodeTrans = qrcodePayTransDao.findByQrcodePayTransForUpdate(param.shopaccno, systemUtilService.sysdatetime.hostdate,
+ param.billno, TenantContextHolder.getContext().tenant.id)
+ ?: throw BadRequestError("未找到billno")
+
+ if (qrcodeTrans.qrcode != param.qrcode && param.qrcode.isNotEmpty()) {
+ val qrcode = agentServiceProxy.qrcodeMatch(param.qrcode)
+ ?: throw BadRequestError("未识别的支付码")
+ if (qrcodeTrans.sourceType != qrcode.sourceType) {
+ throw BadRequestError("支付码不符")
+ }
+ qrcodeTrans.qrcode = param.qrcode
+ }
+
+ val sourceType = sourceTypeService.getBySourceType(qrcodeTrans.sourceType)
+ ?: throw BadRequestError("不支持的支付方式<${qrcodeTrans.sourceType}>")
+ if (!sourceType.anonymousEnable && qrcodeTrans.isAnonymous) {
+ throw BadRequestError("支付方式<${qrcodeTrans.sourceType}>不支持匿名支付")
+ }
+
+ if (!qrcodeTrans.refno.isNullOrEmpty()) {
+ return getTransactionMainDtl(qrcodeTrans.refno, null, null)
+ ?: throw InternalError("未找到refno")
+ }
+
+ //2. 初始化交易流水
+ qrcodeTrans.spip = "192.168.43.90"
+ val dtlType = getDtltypeDictionary(param.dtltype, Dictionary.DTLTYPES)
+ // sourcetype 资产类科目
+ val stSubject = accountUtilServcie.readSubject(sourceType.paySubjno)
+ val builder = TransactionBuilder().apply {
+ setTransInfo(param.transDate, param.transTime, TradeCode.TRANSCODE_QRCODE, qrcodeTrans.sourceType)
+ setOutTransInfo(qrcodeTrans.agentMerchId, qrcodeTrans.billno)
+ payinfo = qrcodeSummary(sourceType)
+ description = dtlType.dictcaption
+ dtltype = param.dtltype
+ }
+
+ val shopacc = accountUtilServcie.readShopbyShopaccno(qrcodeTrans.agentMerchId)
+ val amount = param.amount / 100.0
+ builder.shop(shopacc).apply {
+ setAmount(amount, TradeDict.TRADE_FLAG_IN)
+ }
+ if (qrcodeTrans.isAnonymous) {
+ builder.anonymous().apply {
+ setAmount(amount, TradeDict.TRADE_FLAG_OUT)
+ setOpposite(shopacc.shopaccno, shopacc.shopname)
+ }.and() // 匿名消费时,借 科目 , 贷 商户
+ .addDebitCreditRecord(AccountProxy(stSubject), AccountProxy(shopacc),
+ amount, qrcodeSummary(sourceType))
+ } else {
+ val account = accountUtilServcie.readAccount(param.userid)
+ builder.person(account).apply {
+ setAmount(amount, TradeDict.TRADE_FLAG_OUT)
+ setOpposite(shopacc.shopaccno, shopacc.shopname)
+ }.and().shop().apply {
+ setOpposite(account.accno, account.accname)
+ }.and() // 实名消费时, 1. 借 科目, 贷 个人账户 ;2. 借 个人账户 贷 商户
+ .addDebitCreditRecord(AccountProxy(stSubject), AccountProxy(account),
+ amount, qrcodeSummary(sourceType))
+ .addDebitCreditRecord(AccountProxy(account), AccountProxy(shopacc),
+ amount, qrcodeSummary(sourceType))
+ }
+
+ // 同一个客户ID + billno 是唯一索引,如果重复请求不能保存
+ val transaction = builder.init(transactionService)
+ // qrcode 交易明细表记录 refno
+ qrcodeTrans.refno = transaction.refno
+ // 保存失败,可能客户端重复请求,导致前序交易已完成
+ agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans)
+
+ return transaction
+ }
}
\ No newline at end of file
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.TransactionMainDao
import com.supwisdom.dlpay.api.domain.*
@Autowired
private lateinit var sourceTypeService: SourceTypeService
+ @Autowired
+ private lateinit var qrcodePayTransDao: QrcodePayTransDao
+
/// 公共函数部分
private fun preCheck(builder: TransactionBuilder) {
return transaction
}
+ override fun qrcodeWip(billno: String,transaction:TTransactionMain): TTransactionMain {
+ val qrcodeTrans = qrcodePayTransDao.findByQrcodePayTransForUpdate(transaction.outId, systemUtilService.sysdatetime.hostdate,
+ billno, TenantContextHolder.getContext().tenant.id)
+ ?: throw InternalError("未找到billno")
+ if (transaction.status != TradeDict.DTL_STATUS_INIT) {
+ throw InternalError("流水<${transaction.refno}>状态错误")
+ }
+ updateRecordStatus(transaction, TradeDict.DTL_STATUS_WIP)
+ transactionMainDao.save(transaction)
+ return transaction
+ }
+
override fun fail(refno: String): TTransactionMain {
return fail(refno, "")
}
return transaction
}
+ override fun qrcodeFail(refno: String, remark: String, transaction: TTransactionMain): TTransactionMain {
+
+ val errorStatus = setOf(TradeDict.DTL_STATUS_SUCCESS, TradeDict.DTL_STATUS_CANCEL)
+ if (transaction.status in errorStatus) {
+ throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
+ }
+
+ updateRecordStatus(transaction, TradeDict.DTL_STATUS_FAIL)
+ if (transaction.person) {
+ transaction.personDtl.remark = remark
+ }
+ if (transaction.shop) {
+ transaction.shopDtl.remark = remark
+ }
+ if (transaction.reverseType != TradeDict.REVERSE_FLAG_NONE) {
+ val originTrans = transactionMainDao.findByRefnoNoLock(transaction.reverseRefno ?: "")
+ ?: throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+ "系统异常,无法处理退款流水")
+ originTrans.reverseFlag = TradeDict.REVERSE_FLAG_NONE
+ transactionMainDao.save(originTrans)
+ }
+ transaction.endTime = systemUtilService.sysdatetime.sysdate
+ transactionMainDao.save(transaction)
+ return transaction
+ }
+
//////////////////////////////////////////////////////////////////
// 成功
private fun transactionOnSuccess(transaction: TTransactionMain, sorcetypeRefno: String, overdraft: Boolean) {
return transaction
}
+ override fun qrcodeSuccess(refno: String, sourcetypeRefno: String, transaction: TTransactionMain, accdateUpdate: Boolean?): TTransactionMain {
+ val errorStatus = setOf(TradeDict.DTL_STATUS_SUCCESS, TradeDict.DTL_STATUS_CANCEL)
+ if (transaction.status in errorStatus) {
+ throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误,流水已结束")
+ }
+ transaction.status = TradeDict.DTL_STATUS_SUCCESS
+ if (true == accdateUpdate) {
+ transaction.accdate = systemUtilService.accdate
+ }
+ transactionOnSuccess(transaction, sourcetypeRefno, false)
+
+ if (transaction.reverseType != TradeDict.REVERSE_FLAG_NONE) {
+ val originTrans = transactionMainDao.findByRefnoNoLock(transaction.reverseRefno ?: "")
+ ?: throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+ "系统异常,无法处理退款流水")
+ originTrans.reverseFlag = transaction.reverseType
+ if (originTrans.person) {
+ originTrans.personDtl.reverseFlag = originTrans.reverseFlag
+ originTrans.personDtl.reverseAmount = originTrans.refundAmount
+ }
+ if (originTrans.shop) {
+ originTrans.shopDtl.reverseFlag = originTrans.reverseFlag
+ }
+ transactionMainDao.save(originTrans)
+ }
+
+ transaction.endTime = systemUtilService.sysdatetime.sysdate
+ transactionMainDao.save(transaction)
+ return transaction
+ }
+
override fun success(refno: String): TTransactionMain {
return success(refno, "", true)
}
@Transactional
fun wip(builder: TransactionBuilder): TTransactionMain
+ @Transactional
+ fun qrcodeWip(billno: String, transaction: TTransactionMain): TTransactionMain
+
@Transactional
fun fail(refno: String): TTransactionMain
@Transactional
fun fail(refno: String, remark: String): TTransactionMain
+ @Transactional
+ fun qrcodeFail(refno: String, remark: String, transaction: TTransactionMain): TTransactionMain
+
@Transactional
fun success(refno: String, sourcetypeRefno: String, accdateUpdate: Boolean? = true): TTransactionMain
+ @Transactional
+ fun qrcodeSuccess(refno: String, sourcetypeRefno: String, transaction: TTransactionMain, accdateUpdate: Boolean? = true): TTransactionMain
+
@Transactional
fun success(refno: String): TTransactionMain
return transactionService.wip(refno)
}
+ fun qrcodeWip(billno: String, transaction: TTransactionMain): TTransactionMain {
+ return transactionService.qrcodeWip(billno, transaction)
+ }
+
fun fail(refno: String): TTransactionMain {
return transactionService.fail(refno)
}
return transactionService.fail(refno, remark)
}
+ fun qrcodeFail(refno: String, remark: String, transaction: TTransactionMain): TTransactionMain {
+ return transactionService.qrcodeFail(refno, remark, transaction)
+ }
+
fun success(refno: String, sourcetypeRefno: String, accdateUpdate: Boolean? = true): TTransactionMain {
return transactionService.success(refno, sourcetypeRefno, accdateUpdate).also {
if (it.shop) {
}
}
+ fun qrcodeSuccess(refno: String, sourcetypeRefno: String, transaction: TTransactionMain, accdateUpdate: Boolean? = true): TTransactionMain {
+ return transactionService.qrcodeSuccess(refno, sourcetypeRefno, transaction, accdateUpdate).also {
+ if (it.shop) {
+ shopAccBalanceAsyncTask.updateShopBalance(TenantContextHolder.getContext().tenant, it.shopDtl)
+ }
+
+ if (it.person && !it.personDtl.userid.isNullOrEmpty()) {
+ kafkaSendMsgService.sendJpushMessage(it.personDtl.userid,
+ "交易提醒",
+ "你有一笔${it.personDtl.amount}元的支出,点击查看详情",
+ it.refno, mutableMapOf(), it.tenantid)
+ }
+ }
+ }
+
+
fun success(refno: String): TTransactionMain {
return success(refno, "")
}
multi-tenant.session.enableSessionScopedBean=false
multi-tenant.datasource.base-package=com.supwisdom.dlpay
multi-tenant.task.datacenter=default
-multi-tenant.environment=default
+multi-tenant.environment=touchorder
multi-tenant.task.worker.name=payapi
@Test
fun testHMACSHA256() {
- val token = "TJ3UlX9/tyunSZ2b"
- val secret = "5f788ce433ec44f299351cdf7f137e81"
+ val token = "ddCUUHAF/OOBTAZq"
+ val secret = "dc1d26c0d43e442588092c8d45c21bce"
val jwt = HmacUtil.HMACSHA256(token, secret)
println(jwt)
}