import com.supwisdom.dlpay.agent.service.AgentServiceProxy
import com.supwisdom.dlpay.api.*
import com.supwisdom.dlpay.api.bean.*
+import com.supwisdom.dlpay.api.bean.groups.Confirm
+import com.supwisdom.dlpay.api.bean.groups.InitStep
+import com.supwisdom.dlpay.api.domain.TSourceType
import com.supwisdom.dlpay.api.service.*
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.*
+import org.apache.commons.lang3.StringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.http.ResponseEntity
-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.RestController
+import org.springframework.validation.annotation.Validated
+import org.springframework.web.bind.annotation.*
import javax.validation.Valid
@RestController
.fail(TradeErrorCode.BUSINESS_DEAL_ERROR, "交易扣费失败"))
}
- @PostMapping("/qrcode/init")
- fun qrcodePayAuth(@RequestBody param: QrcodePayParam): ResponseEntity<ApiResponse> {
+ @RequestMapping("/qrcode/init", method = [RequestMethod.POST, RequestMethod.GET])
+ fun qrcodePayInit(@Validated(InitStep::class) @RequestBody param: QrcodePayParam): ResponseEntity<ApiResponse> {
+ // 1. 检查 qrcode
val qrcode = agentServiceProxy.qrcodeMatch(param.qrcode)
?: return ResponseEntity.ok(ResponseBodyBuilder.create()
.fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR, "未识别的支付码"))
?: return ResponseEntity.ok(ResponseBodyBuilder.create()
.fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
"不支持的支付方式<${qrcode.sourceType}>"))
-
+ if (sourceType.assetSubjno.isEmpty()
+ || !StringUtils.isNumeric(sourceType.assetSubjno)) {
+ return ResponseEntity.ok(ResponseBodyBuilder.create()
+ .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ "支付方式<${qrcode.sourceType}>未配置科目号"))
+ }
+ // 2. 记录 qrcode 交易明细表
val qrcodeTrans = agentServiceProxy.qrcodePayTransSaveOrUpdate(
QrcodePayTrans().apply {
this.agentMerchId = param.shopaccno
this.qrcode = param.qrcode
})
+ // 3. 查询用户身份
val service = createAgentService(qrcode.sourceType)
-
val agentResp = service.auth(qrcodeTrans.agentMerchId, qrcodeTrans.billno)
-
+ // 4. 重新读取 qrcode 交易明细表,以获取 service.auth 查询后的结果数据
+ val qrcodeTransResp = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(qrcodeTrans.agentMerchId,
+ qrcodeTrans.billno)
+ val apiResp = QrcodePayResponse().also {
+ it.sourceType = sourceType.sourceType
+ it.paydesc = qrcodeSummary(sourceType)
+ }
return when (agentResp.code) {
AgentCode.SUCCESS -> {
- val qrcodeTransResp = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(qrcodeTrans.agentMerchId,
- qrcodeTrans.billno)
if (!sourceType.anonymousEnable && qrcodeTransResp.isAnonymous) {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResp, TradeErrorCode.BUSINESS_DEAL_ERROR,
"支付方式<${qrcode.sourceType}> 不支持匿名支付"))
} else {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .success(QrcodePayResponse().also {
+ .success(apiResp.also {
it.anonymous = qrcodeTransResp.isAnonymous
it.userid = qrcodeTransResp.agentUserId
}))
AgentCode.NOT_SUPPORT -> {
if (!sourceType.anonymousEnable) {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResp, TradeErrorCode.BUSINESS_DEAL_ERROR,
"支付方式<${qrcode.sourceType}> 不支持匿名支付"))
} else {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .success(QrcodePayResponse().also {
- it.anonymous = true
+ .success(apiResp.also {
+ it.anonymous = qrcodeTransResp.isAnonymous
}))
}
}
else -> {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResp, TradeErrorCode.BUSINESS_DEAL_ERROR,
"第三方身份错误,<${agentResp.agentMsg}"))
}
}
}
+ private fun qrcodeSummary(st: TSourceType): String = st.paydesc + "扫码付"
+
@PostMapping("/qrcodepay/confirm")
- fun qrcodePayInit(@Valid @RequestBody param: QrcodePayParam): ResponseEntity<ApiResponse> {
+ fun qrcodePayConfirm(@Validated(Confirm::class) @RequestBody param: QrcodePayParam): ResponseEntity<ApiResponse> {
//1. 交易检查
+ val apiResponse = QrcodePayResponse()
val qrcodeTrans = agentServiceProxy.qrcodePayTransFindByMerchIdAndBillno(param.shopaccno,
param.billno) ?: return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR, "未找到billno"))
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "未找到billno"))
if (qrcodeTrans.refno.isNotEmpty()) {
return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR, "该交易已确认,请查询结果"))
+ .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(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR, "未识别的支付码"))
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "未识别的支付码"))
if (qrcodeTrans.sourceType != qrcode.sourceType) {
return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR, "支付码不符"))
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR, "支付码不符"))
}
qrcodeTrans.qrcode = param.qrcode
}
val sourceType = sourceTypeService.getBySourceType(qrcodeTrans.sourceType)
?: return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
"不支持的支付方式<${qrcodeTrans.sourceType}>"))
if (!sourceType.anonymousEnable && qrcodeTrans.isAnonymous) {
return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
"支付方式<${qrcodeTrans.sourceType}>不支持匿名支付"))
}
//2. 初始化交易流水
+ // sourcetype 资产类科目
+ val stSubject = accountUtilServcie.readSubject(sourceType.assetSubjno)
+ // build 交易明细
val builder = TransactionBuilder().apply {
- setTransInfo(param.transdate, param.transtime, TradeCode.TRANSCODE_WECHAT, qrcodeTrans.sourceType)
+ setTransInfo(param.transdate, param.transtime, TradeCode.TRANSCODE_QRCODE, qrcodeTrans.sourceType)
setOutTransInfo(qrcodeTrans.agentMerchId, qrcodeTrans.billno)
}
val shopacc = accountUtilServcie.readShopbyShopaccno(qrcodeTrans.agentMerchId)
+ val amount = param.amount / 100.0
builder.shop(shopacc).apply {
- setAmount(param.amount / 100.0, TradeDict.TRADE_FLAG_IN)
+ setAmount(amount, TradeDict.TRADE_FLAG_IN)
}
if (qrcodeTrans.isAnonymous) {
builder.anonymous().apply {
- setAmount(param.amount / 100.0, TradeDict.TRADE_FLAG_OUT)
- }
+ 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(qrcodeTrans.userid)
builder.person(account).apply {
- setAmount(param.amount / 100.0, TradeDict.TRADE_FLAG_OUT)
+ 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))
}
- val transaction = builder.person().apply {
- setOpposite(shopacc.shopaccno, shopacc.shopname)
- }.and().init(transactionService)
+ // 同一个客户ID + billno 是唯一索引,如果重复请求不能保存
+ val transaction = builder.init(transactionService)
+
+ // qrcode 交易明细表记录 refno
qrcodeTrans.refno = transaction.refno
+ // 保存失败,可能客户端重复请求,导致前序交易已完成
agentServiceProxy.qrcodePayTransSaveOrUpdate(qrcodeTrans)
//3. 调用第三方支付
transactionService.wip(transaction.refno)
val service = createAgentService(qrcodeTrans.sourceType)
- val response = service.pay(transaction)
+ 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)
ResponseEntity.ok(ResponseBodyBuilder.create()
- .success(QrcodePayResponse().also {
- it.refno = transaction.refno
- }))
+ .success(apiResponse))
}
AgentCode.REQUIRE_QUERY -> {
ResponseEntity.ok(ResponseBodyBuilder.create()
- .success(QrcodePayResponse().also {
- it.refno = transaction.refno
+ .success(apiResponse.also {
+ it.isRequireQuery = true
}))
}
else -> {
transactionService.fail(transaction.refno, response.agentMsg)
ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(QrcodePayResponse(), TradeErrorCode.BUSINESS_DEAL_ERROR,
+ .fail(apiResponse, TradeErrorCode.BUSINESS_DEAL_ERROR,
"第三方身份错误,<${response.agentMsg}"))
}
}
}
-
-// ============================================== //
-//
-// @GetMapping("/account/payinit")
-// fun accountPayInit(userid: String, amount: Int, manageFee: Int): ResponseEntity<Any> {
-// val dtl = PersonTransBuilder.newBuilder(accountUtilServcie)
-// .setTransDatetime("20190411", "112311")
-// .enableOverdraft(false)
-// .addDetail(AccountHolder.person(userid),
-// AccountHolder.shop("12323"),
-// amount / 100.0, "")
-// .addDetail(AccountHolder.person(userid), AccountHolder.transType(301),
-// manageFee / 100.0, "")
-// .done(personBalancePayService, false)
-//
-// return ResponseEntity.ok(dtl)
-// }
-//
-// @GetMapping("/account/payfinish")
-// fun accountPayFinish(refno: String): ResponseEntity<Any> {
-// val dtl = PersonTransBuilder.newBuilder(accountUtilServcie)
-// .done(refno, TradeDict.DTL_STATUS_SUCCESS, personBalancePayService)
-// return ResponseEntity.ok(dtl)
-// }
-
-
-//
-// /**
-// * 微信支付
-// * wechattype
-// * qrcode-扫微信二维码支付
-// * app-原生app微信支付
-// * mp-微信公众号支付
-// * h5-微信h5支付
-// *
-// * */
-// @PostMapping("/wechat/payinit")
-// fun wechatPayInit(userid: String, amount: Int, manageFee: Int,
-// stuempno: String, shopid: String, transdate: String, transtime: String,
-// outtradeno: String, payinfo: String, feetype: String,
-// wechattype: String, realip: String?, qrcode: String?, openid: String?): ResponseEntity<Any> {
-// return try {
-// val paytype = paytypeService.getBySourceType(PaytypeUtil.WECHAT)
-// if (paytype == null || ConstantUtil.ENABLE_YES != paytype.enable) {
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(1, "支付方式未开启"))
-// }
-// val person = userService.findByThirdUniqueIdenty(stuempno)
-// val dtl = PersonTransBuilder.newBuilder(accountUtilServcie)
-// .setTransDatetime(transdate, transtime)
-// .selectPaytype(PaytypeUtil.WECHAT, payinfo)
-// .setOuttradeno(outtradeno)
-// .also {
-// if (null != person) it.setOwner(person)
-// }
-// .tryLock(true)
-// .setTransinfo(TradeCode.TRANSCODE_WECHAT, "微信支付")
-// .chooseTradetype(Tradetype.CONSUME)
-// .also {
-// when (feetype) {
-// TradeDict.FEETYPE_CONSUME_MEALER -> {
-// it.addDetail(AccountHolder.subject(Subject.SUBJNO_PAY_WECHAT),
-// AccountHolder.shop(shopid),
-// amount / 100.0, "微信支付")
-// .addDetail(AccountHolder.transType(TranstypeCode.TT_CONSUUME_MANAGE_FEE)
-// .with(AccountHolder.shop(shopid)),
-// manageFee / 100.0)
-// }
-// TradeDict.FEETYPE_CONSUME_DISCOUNT -> {
-// it.addDetail(AccountHolder.subject(Subject.SUBJNO_PAY_WECHAT),
-// AccountHolder.shop(shopid),
-// (amount - manageFee) / 100.0, "微信支付")
-// .addDetail(AccountHolder.subject(Subject.SUBJNO_CONSUME_DISCOUNT),
-// AccountHolder.shop(shopid),
-// manageFee / 100.0, "优惠折扣")
-// }
-// else -> {
-// it.addDetail(AccountHolder.subject(Subject.SUBJNO_PAY_WECHAT),
-// AccountHolder.shop(shopid),
-// amount / 100.0, "微信支付")
-// }
-// }
-// }.done(personBalancePayService, false)
-// val code = CallService.callWechatPay(paytypeService.getSourceTypeConfigBySourceType(PaytypeUtil.WECHAT),
-// dtl, DateUtil.getNow(), wechattype, realip, qrcode, openid)
-// if (code.retcode == "0") {
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .data("refno", dtl.refno)
-// .success())
-// } else {
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .fail(TradeErrorCode.TRANSACTION_NOT_EXISTS, "交易请求失败-${code.retcode}"))
-// }
-// } catch (e: TransactionException) {
-// ResponseEntity.ok(ResponseBodyBuilder.create()
-// .transException(e, "交易初始化异常"))
-// }
-// }
}
\ No newline at end of file
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('D3820D49DACA49199E36537F6719665F', 'citizenCard', 't', '大理市民卡', 't', 'f', 't', 'f', 't', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", "asset_subjno", "tenantid")
+VALUES ('D3820D49DACA49199E36537F6719665F', 'citizenCard', 't', '大理市民卡', 't', 'f', 't', 'f', 't', '112234', '{tenantid}');
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('0997477F40904AD1A2E37FD15345CE00', 'balance', 'f', '账户余额', 't', 'f', 't', 'f', 't', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", "asset_subjno","tenantid")
+VALUES ('0997477F40904AD1A2E37FD15345CE00', 'balance', 'f', '账户余额', 't', 'f', 't', 'f', 't', '-', '{tenantid}');
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('F0CA47ADC0F24DFCA0D95DF4136CC2D0', 'thirdpart', 'f', '其他第三方支付', 't', 't', 'f', 'f', 'f', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", ""asset_subjno",tenantid")
+VALUES ('F0CA47ADC0F24DFCA0D95DF4136CC2D0', 'thirdpart', 'f', '其他第三方支付', 't', 't', 'f', 'f', 'f','-', '{tenantid}');
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('F5B344726FA24BD896E70DEE3D3F46CA', 'swyktv5', 't', '一卡通支付', 't', 't', 't', 't', 't', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", "asset_subjno","tenantid")
+VALUES ('F5B344726FA24BD896E70DEE3D3F46CA', 'swyktv5', 't', '一卡通支付', 't', 't', 't', 't', 't','-', '{tenantid}');
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('28EE54CD3B044CC197D6C5B0E309F8B8', 'alipay', 't', '支付宝', 't', 't', 't', 't', 'f', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", "asset_subjno","tenantid")
+VALUES ('28EE54CD3B044CC197D6C5B0E309F8B8', 'alipay', 't', '支付宝', 't', 't', 't', 't', 'f', '112230', '{tenantid}');
INSERT INTO "tb_sourcetype" ("sourcetype_id", "sourcetype", "checkable", "paydesc", "enable", "charge_enable",
-"consume_enable", "anonymous_enable", "reversable", "tenantid")
-VALUES ('DAEF88B54B684347B2B83940C38C7671', 'wechat', 't', '微信支付', 't', 't', 't', 't', 'f', '{tenantid}');
+"consume_enable", "anonymous_enable", "reversable", "asset_subjno","tenantid")
+VALUES ('DAEF88B54B684347B2B83940C38C7671', 'wechat', 't', '微信支付', 't', 't', 't', 't', 'f', '112231', '{tenantid}');
-- 支付方式
INSERT INTO TB_SOURCETYPE_CONFIG (ID, SOURCETYPE,CONFIGID,CONFIG_NAME,CONFIG_VALUE,GLOBALFLAG, "tenantid")
INSERT INTO "tb_transcode" ("transcode", "transname", "tenantid")
VALUES (3010, '市民卡代扣', '{tenantid}');
INSERT INTO "tb_transcode" ("transcode", "transname", "tenantid")
+VALUES (1002, '支付码聚合付', '{tenantid}');
+INSERT INTO "tb_transcode" ("transcode", "transname", "tenantid")
VALUES (3500, '账户充值', '{tenantid}');
INSERT INTO "tb_dictionary" ("id", "dictval", "dicttype", "dictcaption", "dicttypename", "tenantid")
INSERT INTO "tb_dictionary" ("id", "dictval", "dicttype", "dictcaption", "dicttypename", "tenantid")
VALUES (30, 'shopmarket', 'dtltypeList', '商超消费', '流水类型', '{tenantid}');
-INSERT INTO QRCODE_PATTERN(ID, PATTERN, SOURCETYPE, TENANTID)
+INSERT INTO TB_QRCODE_PATTERN(ID, PATTERN, SOURCETYPE, TENANTID)
VALUES(1, '28\d{16}', 'alipay', '{tenantid}');
-INSERT INTO QRCODE_PATTERN(ID, PATTERN, SOURCETYPE, TENANTID)
-VALUES(1, '13\d{16}', 'wechatpay', '{tenantid}');
+INSERT INTO TB_QRCODE_PATTERN(ID, PATTERN, SOURCETYPE, TENANTID)
+VALUES(2, '13\d{16}', 'wechatpay', '{tenantid}');
----------------------------------------------------
commit;
\ No newline at end of file