import com.supwisdom.dlpay.agent.AgentPayServiceContext
import com.supwisdom.dlpay.agent.DtlStatus
import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
+import com.supwisdom.dlpay.api.controller.TransactionContainer
import com.supwisdom.dlpay.api.domain.TDtlQuery
import com.supwisdom.dlpay.api.domain.TShopdtl
import com.supwisdom.dlpay.api.domain.TTransactionMain
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
+import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.retry.annotation.Backoff
import org.springframework.retry.annotation.Recover
import org.springframework.retry.annotation.Retryable
@Component
-class AgentQueryResultTask {
- private val logger = KotlinLogging.logger { }
-
- @Autowired
- lateinit var transactionService: TransactionServiceProxy
-
- @Autowired
- private lateinit var agentPayServiceContext: AgentPayServiceContext
+class AgentQueryResultTask(private val transactionService: TransactionServiceProxy,
+ private val agentPayServiceContext: AgentPayServiceContext,
+ private val dtlQueryResultService: DtlQueryResultService,
+ private val systemUtilService: SystemUtilService,
+ redisConnectionFactory: RedisConnectionFactory) {
- @Autowired
- private lateinit var dtlQueryResultService: DtlQueryResultService
+ private val logger = KotlinLogging.logger { }
- @Autowired
- private lateinit var systemUtilService: SystemUtilService
+ private val transactionContainer = TransactionContainer("qrcode.pay", redisConnectionFactory)
@Recover
fun quertRecover(ex: TaskRetryException, details: TenantDetails,
when (resp.code) {
AgentCode.SUCCESS -> {
+ transactionContainer.remove(transaction.refno)
//查询成功
when (resp.dtlStatus) {
DtlStatus.SUCCESS ->
}
}
}
- AgentCode.REFNO_NOT_EXISTS ->
+ AgentCode.REFNO_NOT_EXISTS -> {
+ transactionContainer.remove(transaction.refno)
transactionService.fail(transaction.refno, "银行流水不存在") //银行返回流水不存在
+ }
AgentCode.REQUIRE_QUERY -> {
throw TaskRetryException()
}
else -> {
+ transactionContainer.remove(transaction.refno)
//其他明确错误,查询失败
logger.error("查询refno=[${transaction.refno}]流水结果返回失败:code=[${resp.agentCode}],message=[${resp.agentMsg}]")
}
import com.supwisdom.dlpay.exception.TransactionCheckException
import com.supwisdom.dlpay.framework.ResponseBodyBuilder
import com.supwisdom.dlpay.framework.service.SystemUtilService
-import com.supwisdom.dlpay.framework.task.TaskExecuteTimes
import com.supwisdom.dlpay.framework.util.*
import com.supwisdom.multitenant.TenantContextHolder
import org.apache.commons.lang3.StringUtils
-import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.http.ResponseEntity
import org.springframework.validation.annotation.Validated
@RestController
@RequestMapping("/api/consume")
-class ConsumeAPIController {
- @Autowired
- lateinit var accountUtilServcie: AccountUtilServcie
- @Autowired
- lateinit var userService: UserService
- @Autowired
- lateinit var systemUtilService: SystemUtilService
- @Autowired
- lateinit var consumePayService: ConsumePayService
- @Autowired
- lateinit var transactionService: TransactionServiceProxy
- @Autowired
- lateinit var cardService: CardService
- @Autowired
- private lateinit var agentPayServiceContext: AgentPayServiceContext
-
- @Autowired
- private lateinit var sourceTypeService: SourceTypeService
- @Autowired
- lateinit var agentQueryResultTask: AgentQueryResultTask
-
- @Autowired
- private lateinit var agentServiceProxy: AgentServiceProxy
-
- @Autowired
- lateinit var redisTemplate: RedisTemplate<String, String>
-
- @Autowired
- private lateinit var qrCodeService: QRCodeService
+class ConsumeAPIController(private val qrCodeService: QRCodeService,
+ private val accountUtilServcie: AccountUtilServcie,
+ private val userService: UserService,
+ private val systemUtilService: SystemUtilService,
+ private val consumePayService: ConsumePayService,
+ private val transactionService: TransactionServiceProxy,
+ private val cardService: CardService,
+ private val agentPayServiceContext: AgentPayServiceContext,
+ private val sourceTypeService: SourceTypeService,
+ private val agentQueryResultTask: AgentQueryResultTask,
+ private val agentServiceProxy: AgentServiceProxy,
+ redisConnectionFactory: RedisConnectionFactory) {
+
+ private val redisTemplate = RedisTemplate<String, String>()
+
+ init {
+ redisTemplate.setConnectionFactory(redisConnectionFactory)
+ redisTemplate.afterPropertiesSet()
+ }
/**
* ============================================================================
package com.supwisdom.dlpay.api.controller
+import com.supwisdom.dlpay.api.TransactionBuilder
import com.supwisdom.dlpay.api.bean.InAppPayParam
import com.supwisdom.dlpay.api.bean.InAppPayResponse
import com.supwisdom.dlpay.api.bean.TransactionQueryResponse
+import com.supwisdom.dlpay.api.domain.TSourceType
+import com.supwisdom.dlpay.api.service.AccountUtilServcie
+import com.supwisdom.dlpay.api.service.SourceTypeService
import com.supwisdom.dlpay.api.service.TransactionServiceProxy
+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.TradeCode
+import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.framework.util.TradeErrorCode
import com.supwisdom.multitenant.TenantContextHolder
-import org.springframework.http.HttpStatus
+import com.supwisdom.multitenant.storage.TenantDetailsRegistrar
+import org.springframework.context.annotation.Lazy
+import org.springframework.data.redis.connection.RedisConnectionFactory
+import org.springframework.data.redis.core.RedisTemplate
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import javax.validation.Valid
+import kotlin.math.roundToInt
@RestController("/api/transaction")
class TransactionController(private val transactionServiceProxy: TransactionServiceProxy,
- private val systemUtilService: SystemUtilService) {
+ private val systemUtilService: SystemUtilService,
+ private val accountUtilServcie: AccountUtilServcie,
+ private val sourceTypeService: SourceTypeService,
+ redisConnectionFactory: RedisConnectionFactory) {
- @GetMapping("/query")
+ private val transactionContainer = TransactionContainer("inapp.pay", redisConnectionFactory)
+
+ @GetMapping("/inapp/query")
fun queryTransaction(refno: String): ResponseEntity<*> {
val tranaction = transactionServiceProxy.findTransactionByRefno(refno)
- ?: return ResponseBodyBuilder.fail(HttpStatus.NOT_FOUND, "未找到流水")
+ ?: return ResponseBodyBuilder.notFound("未找到流水")
if (tranaction.tenantid != TenantContextHolder.getContext().tenant.id) {
- return ResponseBodyBuilder.fail(HttpStatus.CONFLICT, "未找到流水")
+ return ResponseBodyBuilder.conflict("未找到流水")
}
+ if (tranaction.status != TradeDict.DTL_STATUS_SUCCESS) {
+ transactionContainer.add(refno)
+ // 查询第三方
+ transactionContainer.remove(refno)
+ }
return ResponseBodyBuilder.successEntity(TransactionQueryResponse().apply {
this.refno = tranaction.refno
accdate = tranaction.accdate
}, "成功")
}
+ private fun transSummary(sourceType: TSourceType, param: InAppPayParam): String {
+ return when (param.summary) {
+ "pay" -> "${sourceType.paydesc}消费"
+ "deposit" -> "${sourceType.paydesc}充值"
+ else -> throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "交易摘要类型错误")
+ }
+ }
+
@PostMapping("/inapp/payinit")
- fun inAppPayInit(@Valid param: InAppPayParam): ResponseEntity<InAppPayResponse> {
+ fun inAppPayInit(@Valid param: InAppPayParam): ResponseEntity<*> {
val response = InAppPayResponse()
+ if (transactionContainer.count() > 100) {
+ return ResponseBodyBuilder.serviceUnavailable("第三方请求异常")
+ }
+ val sourceType = sourceTypeService.getBySourceType(param.sourceType)
+ ?: return ResponseBodyBuilder.badRequest("source type <${param.sourceType}> 不存在")
+
+ val subject = accountUtilServcie.readSubject(sourceType.depositeSubjno)
+
+ val tradeCode = when (param.inAppType) {
+ "h5" -> TradeCode.TRANSCODE_H5PAY
+ "native" -> TradeCode.TRANSCODE_APPPAY
+ else -> return ResponseBodyBuilder.badRequest("app typ <${param.inAppType}> 不支持")
+ }
+
+ val builder = TransactionBuilder()
+ .setTransInfo(param.transDate, param.transTime, tradeCode, sourceType.sourceType)
+ .setOutTransInfo(TenantContextHolder.getContext().tenant.id, param.billno)
+ .apply {
+ description = param.description
+ }
+ val amount = param.totalAmount / 100.0
+ when (param.recipientType) {
+ "shop" -> {
+ // check shop acc
+ val shopacc = accountUtilServcie.readShopbyShopaccno(param.merchno)
+ builder.shop(shopacc)
+ .setAmount(amount, TradeDict.TRADE_FLAG_IN)
+ .setOpposite(subject.subjno, subject.subjname)
+ if (!param.isAnonymous) {
+ // 支付给商户,并且不是匿名支付
+ val account = accountUtilServcie.readAccount(param.userid)
+ builder.person(account)
+ .setAmount(amount, TradeDict.TRADE_FLAG_OUT)
+ .setOpposite(subject.subjno, subject.subjname)
+ .and()
+ .addDebitCreditRecord(subject.subjno, subject.subjno,
+ account.accno, account.subjno, amount,
+ transSummary(sourceType, param))
+ .addDebitCreditRecord(account.accno, account.subjno,
+ shopacc.shopaccno, shopacc.subjno, amount,
+ transSummary(sourceType, param))
+ } else {
+ // 匿名消费,直接到商户账户
+ builder.addDebitCreditRecord(subject.subjno, subject.subjno,
+ shopacc.shopaccno, shopacc.subjno, amount, transSummary(sourceType, param))
+ }
+ }
+ "person" -> {
+ // check person
+ val account = accountUtilServcie.readAccount(param.userid)
+ builder.person(account)
+ .setAmount(amount, TradeDict.TRADE_FLAG_IN)
+ .setOpposite(subject.subjno, subject.subjname)
+ .and()
+ .addDebitCreditRecord(subject.subjno, subject.subjno,
+ account.accno, account.subjno, amount, transSummary(sourceType, param))
+ }
+ else -> {
+ return ResponseBodyBuilder.badRequest("recipient type 错误")
+ }
+ }
+ val transaction = transactionServiceProxy.init(builder)
+ // 调用第三方完成交易初始化
+
+ transactionServiceProxy.wip(transaction.refno)
+ transactionContainer.add(transaction.refno)
+ response.apply {
+ when (param.recipientType) {
+ "shop" -> actuallyAmount = (transaction.shopDtl.amount * 100).roundToInt()
+ "person" -> actuallyAmount = (transaction.personDtl.amount * 100).roundToInt()
+ }
+ this.refno = transaction.refno
+ this.status = transaction.status
+ this.message = "初始化成功"
+ }
return ResponseBodyBuilder.successEntity(response)
}
}
+class TransactionContainer(private val name: String,
+ redisConnectionFactory: RedisConnectionFactory) {
+ private val redisTemplate: RedisTemplate<Any, Any> = RedisTemplate<Any, Any>()
+
+ init {
+ redisTemplate.setConnectionFactory(redisConnectionFactory)
+ redisTemplate.afterPropertiesSet()
+ }
+
+ private fun keyName(): String = "TRANS:REFNO:$name"
+
+ fun add(refno: String): Long {
+ redisTemplate.opsForSet().add(keyName(), refno)
+ return redisTemplate.opsForSet().size(keyName()) ?: 0L
+ }
+
+ fun has(refno: String): Boolean {
+ return redisTemplate.opsForSet().isMember(keyName(), refno) ?: false
+ }
+
+ fun remove(refno: String): Long {
+ redisTemplate.opsForSet().remove(keyName(), refno)
+ return redisTemplate.opsForSet().size(keyName()) ?: 0L
+ }
+
+ fun count(): Long {
+ return redisTemplate.opsForSet().size(keyName()) ?: 0L
+ }
+}
+
@RestController("/api/notify")
-class TransactionNotifyController {
+class TransactionNotifyController(@Lazy private val tenantDetailsRegistrar: TenantDetailsRegistrar,
+ private val transactionServiceProxy: TransactionServiceProxy) {
@PostMapping("/inapp/alipay/{tenant}")
- fun inAppPayCallback(@PathVariable("tenant") tenant: String): ResponseEntity<Any> {
- TODO("")
+ fun inAppPayCallback(@PathVariable("tenant") tenant: String,
+ refno: String): ResponseEntity<*> {
+ val tenantDetails = tenantDetailsRegistrar.getTenantDetailsById(tenant)
+ ?: return ResponseBodyBuilder.notFound("请求租户 <$tenant> 不存在")
+ TenantContextHolder.getContext().tenant = tenantDetails.get()
+ val transaction = transactionServiceProxy.success(refno)
+ return ResponseEntity.ok(ResponseBodyBuilder.create()
+ .data("refno", transaction.refno))
}
}
\ No newline at end of file