From ce048d3747f077442ae47ca7d5a80514ed506d05 Mon Sep 17 00:00:00 2001 From: bootx Date: Mon, 11 Mar 2024 23:41:19 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E4=BA=91=E9=97=AA=E4=BB=98=E6=9D=A1?= =?UTF-8?q?=E7=A0=81=E6=94=AF=E4=BB=98/APP=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _doc/Task.md | 1 + .../controller/PayReturnController.java | 9 ++- .../daxpay/service/code/UnionPayCode.java | 15 +++-- .../service/UnionPayCallbackService.java | 2 +- .../union/service/UnionPayService.java | 67 ++++++++----------- .../union/service/UnionPaySyncService.java | 6 +- .../service/RefundCallbackService.java | 1 - .../notice/service/PayReturnService.java | 20 ++++++ .../payment/refund/service/RefundService.java | 8 +-- .../payment/sync/service/PaySyncService.java | 1 - .../channel/alipay/AliPayReturnParam.java | 4 +- .../channel/union/UnionPayReturnParam.java | 36 ++++++++++ 12 files changed, 113 insertions(+), 57 deletions(-) create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java diff --git a/_doc/Task.md b/_doc/Task.md index affe8267b..17843504b 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -20,6 +20,7 @@ - [ ] 支付流程涉及异步支付时, 更换支付方式需要控制预防客户重复付款 - [ ] 增加撤销功能,用于处理线下支付订单的情况 - [ ] 数据库表进行规则, 字段设置长度, 增加索引 +- [ ] 回调处理可以配置延迟时间, **任务池** - [x] 支付SDK编写 diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java index 2ce224af5..26a0ac221 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java @@ -3,10 +3,12 @@ package cn.bootx.platform.daxpay.gateway.controller; import cn.bootx.platform.common.core.annotation.IgnoreAuth; import cn.bootx.platform.daxpay.service.core.payment.notice.service.PayReturnService; import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayReturnParam; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayReturnParam; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @@ -38,8 +40,9 @@ public class PayReturnController { } @Operation(summary = "云闪付同步通知") - @GetMapping("/union") - public ModelAndView union(){ - return null; + @PostMapping("/union") + public ModelAndView union(UnionPayReturnParam param){ + String url = payReturnService.union(param); + return new ModelAndView("redirect:" + url); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java index 8566b01da..4d6b8591a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java @@ -10,12 +10,15 @@ import com.egzosn.pay.union.bean.SDKConstants; public interface UnionPayCode { - /** 成功状态 */ - String SUCCESS = "0"; - - /** 状态 00表示成功 */ + /** 应答码 00表示成功 */ String RESP_CODE = SDKConstants.param_respCode; + /** 原交易应答码 00表示成功 */ + String RESP_ORIG_CODE = SDKConstants.param_origRespCode; + + /** 应答码信息 */ + String RESP_MSG = SDKConstants.param_respMsg; + /** 业务结果 00表示成功 */ String RESP_SUCCESS = SDKConstants.OK_RESP_CODE; @@ -28,8 +31,8 @@ public interface UnionPayCode { /** 第三方订单号(本地订单号) */ String ORDER_ID = "orderId"; - /** 退款ID */ - String REFUND_ID = "refund_id"; + /** APP支付 银联订单号 */ + String PAY_APP_TN = SDKConstants.param_tn; /** 交易类型 支付 */ String TXN_TYPE_PAY = "01"; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java index 5fd7706a5..007629e19 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java @@ -110,7 +110,7 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { @Override public void resolveRefundData() { // 云闪付需要延迟半秒再进行处理, 不然会出现业务未处理完, 但回调已经到达的情况 - ThreadUtil.sleep(300); + ThreadUtil.sleep(100); CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java index f114e5a93..f61006c9f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java @@ -5,6 +5,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.channel.UnionPayParam; import cn.bootx.platform.daxpay.param.pay.PayChannelParam; import cn.bootx.platform.daxpay.service.code.AliPayWay; +import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; @@ -13,6 +14,8 @@ import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionPayOrder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.egzosn.pay.common.bean.NoticeParams; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -20,6 +23,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Date; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** @@ -89,8 +93,17 @@ public class UnionPayService { unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - Map app = unionPayKit.app(unionPayOrder); - return null; + Map result = unionPayKit.app(unionPayOrder); + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); + + // 支付失败 + if (!(Objects.equals(resultCode, UnionPayCode.RESP_SUCCESS))) { + log.warn("云闪付支付失败:{}", result); + String errMsg = MapUtil.getStr(result, UnionPayCode.RESP_MSG); + throw new PayFailureException(errMsg); + } + + return MapUtil.getStr(result, UnionPayCode.PAY_APP_TN); } /** @@ -100,31 +113,11 @@ public class UnionPayService { Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); UnionPayOrder unionPayOrder = new UnionPayOrder(); - unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); unionPayOrder.setSubject(payOrder.getTitle()); unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - return unionPayKit.getQrPay(unionPayOrder); - - -// Map params = UnifiedOrderModel.builder() -// .service(ServiceEnum.NATIVE.toString()) -// .mch_id(unionPayKit.getMachId()) -// .out_trade_no(String.valueOf(payOrder.getId())) -// .body(payOrder.getTitle()) -// .total_fee(amount) -// .time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime())) -// .mch_create_ip("127.0.0.1") -// .notify_url(unionPayKit.getNotifyUrl()) -// .nonce_str(WxPayKit.generateStr()) -// .build() -// .createSign(unionPayKit.getAppKey(), SignType.MD5); -// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); -// Map result = WxPayKit.xmlToMap(xmlResult); -// this.verifyErrorMsg(result); -// return result.get("code_url"); } /** @@ -134,28 +127,26 @@ public class UnionPayService { Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); UnionPayOrder unionPayOrder = new UnionPayOrder(); - unionPayOrder.setAuthCode(authCode); unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); unionPayOrder.setSubject(payOrder.getTitle()); unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - Map stringObjectMap = unionPayKit.microPay(unionPayOrder); + Map result = unionPayKit.microPay(unionPayOrder); -// Map params = MicroPayModel.builder() -// .service(ServiceEnum.MICRO_PAY.toString()) -// .mch_id(unionPayKit.getMachId()) -// .out_trade_no(WxPayKit.generateStr()) -// .body(payOrder.getTitle()) -// .total_fee(amount) -// .op_device_id("daxpay") -// .mch_create_ip("127.0.0.1") -// .auth_code(authCode) -// .nonce_str(WxPayKit.generateStr()) -// .build() -// .createSign(unionPayKit.getAppKey(), SignType.MD5); -// -// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); + if (!unionPayKit.verify(new NoticeParams(result))) { + log.warn("云闪付支付验签失败:{}", result); + throw new PayFailureException("云闪付支付验签失败"); + } + + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); + + // 支付失败 + if (!(Objects.equals(resultCode, UnionPayCode.RESP_SUCCESS))) { + log.warn("云闪付支付失败:{}", result); + String errMsg = MapUtil.getStr(result, UnionPayCode.RESP_MSG); + throw new PayFailureException(errMsg); + } } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java index b4b1bb1a6..6eba5be1d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java @@ -43,7 +43,6 @@ public class UnionPaySyncService { PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); AssistOrder query = new AssistOrder(); -// query.setOutTradeNo("1766811070348001280"); query.setOutTradeNo(String.valueOf(order.getId())); Map result = unionPayKit.query(query); @@ -87,6 +86,11 @@ public class UnionPaySyncService { return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); } + // 二维码支付无效 + if (Objects.equals(origRespCode, "86")) { + return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED).setErrorMsg("支付二维码无效"); + } + return syncResult; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java index c64a10ddd..cca9a6068 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java @@ -65,7 +65,6 @@ public class RefundCallbackService { RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_SUCCESS); callbackInfo.setPayRepairNo(repair.getRepairNo()); } else { - // 设置退款订单完成时间 RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_FAIL); callbackInfo.setPayRepairNo(repair.getRepairNo()); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java index 6acb90f0f..b0c54a146 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java @@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService; import cn.bootx.platform.daxpay.service.core.system.config.service.PlatformConfigService; import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayReturnParam; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayReturnParam; import cn.hutool.core.net.URLEncodeUtil; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; @@ -51,4 +52,23 @@ public class PayReturnService { // 跳转到默认页 return StrUtil.format("{}/result/success?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付成功...")); } + + public String union(UnionPayReturnParam param) { + PayOrderExtra payOrderExtra = payOrderExtraManager.findById(param.getOrderNo()).orElse(null); + PayOrder prOrder = payOrderQueryService.findById(Long.valueOf(param.getOrderNo())).orElse(null); + if (Objects.isNull(payOrderExtra) || Objects.isNull(prOrder)){ + return StrUtil.format("{}/result/error?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付订单有问题,请排查")); + } + + // 如果同步跳转参数为空, 获取系统配置地址, 系统配置如果也为空, 则返回默认地址 + String returnUrl = payOrderExtra.getReturnUrl(); + if (StrUtil.isBlank(returnUrl)){ + returnUrl = platformConfigService.getConfig().getReturnUrl(); + } + if (StrUtil.isNotBlank(returnUrl)){ + return StrUtil.format("{}?paymentId={}&businessNo={}", payOrderExtra.getReturnUrl(),prOrder.getId(),prOrder.getBusinessNo()); + } + // 跳转到默认页 + return StrUtil.format("{}/result/success?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付成功...")); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java index ffe428f0e..1a819f5d8 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service; +import cn.bootx.platform.common.core.annotation.CountTime; import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; import cn.bootx.platform.common.core.function.CollectorsFunction; @@ -67,6 +68,7 @@ public class RefundService { /** * 支付退款 */ + @CountTime @Transactional(rollbackFor = Exception.class ) public RefundResult refund(RefundParam param){ return this.refundAdapter(param,false); @@ -75,6 +77,7 @@ public class RefundService { /** * 简单退款 */ + @CountTime @Transactional(rollbackFor = Exception.class ) public RefundResult simpleRefund(SimpleRefundParam param){ ValidationUtil.validateParam(param); @@ -273,7 +276,7 @@ public class RefundService { this.successHandler(refundOrder, refundChannelOrders, payOrder); } catch (Exception e) { - // 5. 失败处理, 所有记录都会回滚, 可以调用重新 + // 5. 失败处理, 所有记录都会回滚, 可以调用退款重试 PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL); this.errorHandler(refundOrder); throw e; @@ -289,13 +292,10 @@ public class RefundService { int refundableBalance = refundOrder.getRefundableBalance(); // 设置支付订单状态 if (refundInfo.getStatus() == RefundStatusEnum.PROGRESS) { - // 设置为退款中 payOrder.setStatus(PayStatusEnum.REFUNDING.getCode()); } else if (refundableBalance == 0) { - // 退款完成 payOrder.setStatus(PayStatusEnum.REFUNDED.getCode()); } else { - // 部分退款 payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode()); } payOrderService.updateById(payOrder); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java index da3281664..de5cf3cbb 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java @@ -74,7 +74,6 @@ public class PaySyncService { } // 如果不是异步支付, 直接返回返回 if (!payOrder.isAsyncPay()){ -// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步"); throw new PayFailureException("订单没有异步支付方式,不需要同步"); } // 执行订单同步逻辑 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java index 306a026dc..84f597f92 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java @@ -6,13 +6,13 @@ import lombok.Data; import lombok.experimental.Accessors; /** - * 微信同步回调参数 + * 支付宝同步回调参数 * @author xxm * @since 2024/2/11 */ @Data @Accessors(chain = true) -@Schema(title = "微信同步回调参数") +@Schema(title = "支付宝同步回调参数") public class AliPayReturnParam { /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java new file mode 100644 index 000000000..f1917c19c --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java @@ -0,0 +1,36 @@ +package cn.bootx.platform.daxpay.service.param.channel.union; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 云闪付同步回调参数 + * @author xxm + * @since 2024/2/11 + */ +@Data +@Accessors(chain = true) +@Schema(title = "云闪付同步回调参数") +public class UnionPayReturnParam { + private String orderNo; + private String signature; + private String merName; + private String settleDate; + private String certId; + private String voucherNum; + private String version; + private String settleKey; + private String termId; + private String origReqType; + private String qrNo; + private String reqReserved; + private String reqType; + private String origRespMsg; + private String comInfo; + private String merId; + private String merCatCode; + private String currencyCode; + private String origRespCode; + private String txnAmt; +}