diff --git a/_doc/Task.md b/_doc/Task.md index 1632efa2..af96c922 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -1,13 +1,15 @@ ## 单商户 2.0.8: 转账/撤销接口和系统优化 - [x] 支持撤销接口 -- [ ] 增加转账接口功能 +- [x] 增加转账接口功能 - [ ] 细分各种支付异常类和编码(部分) - [ ] 增加分账回调处理(支付宝) - [ ] 增加分账修复功能 - [ ] DEMO增加获取微信OpenID和支付宝OpenId功能 -- [ ] 支付宝微信等消息通知地址支持一键生成 +- [ ] DEMO增加转账演示功能 +- [x] 支付宝微信等消息通知地址支持一键生成 - [ ] 管理端界面支持扫码绑定对账接收方功能 +- [ ] 上下文对象进行优化精简 - [x] 请求IP参数增加正则校验 - [x] 支付接口公共参数添加随机数字段, 预防重放问题 - [x] 请求接口增加有效期校验, 超时后失效 diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/AlipayConfigController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/AlipayConfigController.java index 070dfa7b..2dd3fc53 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/AlipayConfigController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/AlipayConfigController.java @@ -53,4 +53,16 @@ public class AlipayConfigController { public ResResult readPem(MultipartFile file){ return Res.ok(new String(file.getBytes(), StandardCharsets.UTF_8)); } + + @Operation(summary = "生成异步通知地址") + @GetMapping("/generateNotifyUrl") + public ResResult generateNotifyUrl() { + return Res.ok(alipayConfigService.generateNotifyUrl()); + } + + @Operation(summary = "生成同步通知地址") + @GetMapping("/generateReturnUrl") + public ResResult generateReturnUrl() { + return Res.ok(alipayConfigService.generateReturnUrl()); + } } diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/UnionPayConfigController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/UnionPayConfigController.java index 1d7278a0..78f006db 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/UnionPayConfigController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/UnionPayConfigController.java @@ -54,4 +54,16 @@ public class UnionPayConfigController { public ResResult toBase64(MultipartFile file){ return Res.ok(Base64.encode(file.getBytes())); } + + @Operation(summary = "生成异步通知地址") + @GetMapping("/generateNotifyUrl") + public ResResult generateNotifyUrl() { + return Res.ok(unionPayConfigService.generateNotifyUrl()); + } + + @Operation(summary = "生成同步通知地址") + @GetMapping("/generateReturnUrl") + public ResResult generateReturnUrl() { + return Res.ok(unionPayConfigService.generateReturnUrl()); + } } diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WalletConfigController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WalletConfigController.java index 23895c01..db4feefe 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WalletConfigController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WalletConfigController.java @@ -39,9 +39,10 @@ public class WalletConfigController { return Res.ok(); } - @Operation(summary = "支付宝支持支付方式") + @Operation(summary = "钱包支持支付方式") @GetMapping("/findPayWays") public ResResult> findPayWays() { return Res.ok(service.findPayWays()); } + } diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WeChatPayConfigController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WeChatPayConfigController.java index 6977470a..a361b32b 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WeChatPayConfigController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/daxpay/single/admin/controller/channel/WeChatPayConfigController.java @@ -52,4 +52,16 @@ public class WeChatPayConfigController { public ResResult toBase64(MultipartFile file){ return Res.ok(Base64.encode(file.getBytes())); } + + @Operation(summary = "生成异步通知地址") + @GetMapping("/generateNotifyUrl") + public ResResult generateNotifyUrl() { + return Res.ok(weChatPayConfigService.generateNotifyUrl()); + } + + @Operation(summary = "生成同步通知地址") + @GetMapping("/generateReturnUrl") + public ResResult generateReturnUrl() { + return Res.ok(weChatPayConfigService.generateReturnUrl()); + } } diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/TransferStatusEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/TransferStatusEnum.java index 751381ae..1c3685d6 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/TransferStatusEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/TransferStatusEnum.java @@ -12,8 +12,6 @@ import lombok.Getter; @AllArgsConstructor public enum TransferStatusEnum { - - TRANSFERRING("transferring", "转账中"), SUCCESS("success", "转账成功"), FAIL("fail", "转账失败"), diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/PayReturnController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/PayReturnController.java index c2fd2323..cb6146f4 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/PayReturnController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/PayReturnController.java @@ -33,13 +33,13 @@ public class PayReturnController { return new ModelAndView("redirect:" + url); } - @Operation(summary = "微信同步通知") + @Operation(summary = "微信同步跳转连接") @GetMapping("/wechat") public ModelAndView wechat(){ return null; } - @Operation(summary = "云闪付同步通知") + @Operation(summary = "云闪付同步跳转连接") @PostMapping("/union") public ModelAndView union(UnionPayReturnParam param){ String url = payReturnService.union(param); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/CallbackLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/CallbackLocal.java index 3580aca5..a46c1660 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/CallbackLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/CallbackLocal.java @@ -27,7 +27,7 @@ public class CallbackLocal { private String tradeNo; /** - * 第三方支付平台交易号 + * 通道交易号 */ private String outTradeNo; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/ErrorInfoLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/ErrorInfoLocal.java new file mode 100644 index 00000000..049d5e86 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/ErrorInfoLocal.java @@ -0,0 +1,20 @@ +package cn.daxpay.single.service.common.context; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 错误信息 + * @author xxm + * @since 2024/6/14 + */ +@Data +@Accessors(chain = true) +public class ErrorInfoLocal { + + /** 错误码 */ + private String errorCode; + + /** 错误内容 */ + private String errorMsg; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PayLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PayLocal.java index 4d3013d2..c9a5c9e3 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PayLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PayLocal.java @@ -30,7 +30,4 @@ public class PayLocal { /** 支付参数体(通常用于发起支付的参数) */ private String payBody; - /** 订单超时时间, */ - private LocalDateTime expiredTime; - } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PaymentContext.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PaymentContext.java index 3d6aed75..b2448532 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PaymentContext.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/PaymentContext.java @@ -18,6 +18,9 @@ public class PaymentContext { /** 请求终端信息 */ private final ClientLocal clientInfo = new ClientLocal(); + /** 错误信息 */ + private final ErrorInfoLocal errorInfo = new ErrorInfoLocal(); + /** 支付相关信息 */ private final PayLocal payInfo = new PayLocal(); @@ -36,4 +39,7 @@ public class PaymentContext { /** 分账相关信息 */ private final AllocationLocal allocationInfo = new AllocationLocal(); + /** 转账相关信息 */ + private final TransferLocal transferInfo = new TransferLocal(); + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RefundLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RefundLocal.java index efab28d7..e4ec6ac3 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RefundLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RefundLocal.java @@ -1,7 +1,6 @@ package cn.daxpay.single.service.common.context; import cn.daxpay.single.code.RefundStatusEnum; -import cn.daxpay.single.service.core.order.refund.entity.RefundOrder; import lombok.Data; import lombok.experimental.Accessors; @@ -26,15 +25,6 @@ public class RefundLocal { */ private RefundStatusEnum status = RefundStatusEnum.SUCCESS; - /** 错误码 */ - private String errorCode; - - /** 错误内容 */ - private String errorMsg; - - /** 退款订单 */ - private RefundOrder refundOrder; - /** 退款完成时间 */ private LocalDateTime finishTime; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RepairLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RepairLocal.java index e7ed28dd..2af1d179 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RepairLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/RepairLocal.java @@ -20,7 +20,7 @@ public class RepairLocal { */ private PayRepairSourceEnum source; - /** 支付完成/退款时间 */ + /** 完成/退款时间 */ private LocalDateTime finishTime; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/TransferLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/TransferLocal.java new file mode 100644 index 00000000..51608e2a --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/common/context/TransferLocal.java @@ -0,0 +1,27 @@ +package cn.daxpay.single.service.common.context; + +import cn.daxpay.single.code.TransferStatusEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 转账相关信息 + * @author xxm + * @since 2024/6/14 + */ +@Data +@Accessors(chain = true) +public class TransferLocal { + + /** 通道转账订单号 */ + private String outTransferNo; + + /** 状态 */ + private TransferStatusEnum status = TransferStatusEnum.SUCCESS; + + /** 完成时间 */ + private LocalDateTime finishTime; +} + diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayConfigService.java index f922fe2f..030f1c22 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayConfigService.java @@ -9,6 +9,7 @@ import cn.daxpay.single.service.code.AliPayWay; import cn.daxpay.single.service.core.channel.alipay.dao.AliPayConfigManager; import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig; import cn.daxpay.single.service.core.system.config.service.PayChannelConfigService; +import cn.daxpay.single.service.core.system.config.service.PlatformConfigService; import cn.daxpay.single.service.param.channel.alipay.AliPayConfigParam; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; @@ -39,6 +40,7 @@ public class AliPayConfigService { private final static Long ID = 0L; private final AliPayConfigManager alipayConfigManager; private final PayChannelConfigService payChannelConfigService; + private final PlatformConfigService platformConfigService; /** * 修改 @@ -78,6 +80,19 @@ public class AliPayConfigService { return alipayConfig; } + /** + * 生成通知地址 + */ + public String generateNotifyUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/callback/pay/alipay"; + } + + /** + * 生成同步跳转地址 + */ + public String generateReturnUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/return/pay/alipay"; + } /** * 初始化IJPay服务 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java index 6ff8c826..6d05b51f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayRefundService.java @@ -3,6 +3,7 @@ package cn.daxpay.single.service.core.channel.alipay.service; import cn.daxpay.single.code.RefundStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.service.code.AliPayCode; +import cn.daxpay.single.service.common.context.ErrorInfoLocal; import cn.daxpay.single.service.common.context.RefundLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.refund.entity.RefundOrder; @@ -33,6 +34,7 @@ public class AliPayRefundService { */ public void refund(RefundOrder refundOrder) { RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); + ErrorInfoLocal errorInfo = PaymentContextLocal.get().getErrorInfo(); AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel(); refundModel.setOutTradeNo(refundOrder.getOrderNo()); refundModel.setOutRequestNo(refundOrder.getRefundNo()); @@ -44,8 +46,8 @@ public class AliPayRefundService { try { AlipayTradeRefundResponse response = AliPayApi.tradeRefundToResponse(refundModel); if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) { - refundInfo.setErrorMsg(response.getSubMsg()); - refundInfo.setErrorCode(response.getCode()); + errorInfo.setErrorMsg(response.getSubMsg()); + errorInfo.setErrorCode(response.getCode()); log.error("网关返回退款失败: {}", response.getSubMsg()); throw new PayFailureException(response.getSubMsg()); } @@ -61,8 +63,8 @@ public class AliPayRefundService { } catch (AlipayApiException e) { log.error("订单退款失败:", e); - refundInfo.setErrorMsg(e.getErrMsg()); - refundInfo.setErrorCode(e.getErrCode()); + errorInfo.setErrorMsg(e.getErrMsg()); + errorInfo.setErrorCode(e.getErrCode()); throw new PayFailureException("订单退款失败"); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java index 0e1c474e..5300547a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayService.java @@ -1,5 +1,6 @@ package cn.daxpay.single.service.core.channel.alipay.service; +import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.daxpay.single.code.PayMethodEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.param.channel.AliPayParam; @@ -25,6 +26,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.Date; import java.util.Objects; import java.util.Optional; @@ -252,11 +254,12 @@ public class AliPayService { model.setTotalAmount(amount); try { AlipayTradePayResponse response = AliPayApi.tradePayToResponse(model, alipayConfig.getNotifyUrl()); - // 支付成功处理 金额2000以下免密支付, 记录支付完成相关信息 if (Objects.equals(response.getCode(), AliPayCode.SUCCESS)) { + Date gmtPayment = response.getGmtPayment(); payInfo.setOutOrderNo(response.getTradeNo()) - .setComplete(true); + .setComplete(true) + .setCompleteTime(LocalDateTimeUtil.of(gmtPayment)); } // 非支付中响应码, 进行错误处理 if (!Objects.equals(response.getCode(), AliPayCode.INPROCESS)) { diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayTransferService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayTransferService.java index 3cf3381a..04f2d247 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayTransferService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayTransferService.java @@ -1,8 +1,17 @@ package cn.daxpay.single.service.core.channel.alipay.service; +import cn.bootx.platform.common.core.util.LocalDateTimeUtil; +import cn.daxpay.single.code.TransferStatusEnum; +import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.service.code.AliPayCode; +import cn.daxpay.single.service.common.context.TransferLocal; +import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig; import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder; -import cn.hutool.core.util.IdUtil; +import cn.daxpay.single.util.OrderNoGenerateUtil; +import cn.daxpay.single.util.PayUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.util.StrUtil; import com.alipay.api.domain.AlipayFundAccountQueryModel; import com.alipay.api.domain.AlipayFundTransToaccountTransferModel; import com.alipay.api.response.AlipayFundAccountQueryResponse; @@ -13,6 +22,9 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.Objects; + import static cn.daxpay.single.service.code.AliPayCode.QUERY_ACCOUNT_TYPE; @Slf4j @@ -20,8 +32,6 @@ import static cn.daxpay.single.service.code.AliPayCode.QUERY_ACCOUNT_TYPE; @RequiredArgsConstructor public class AliPayTransferService { - private final AliPayConfigService payConfigService; - /** * 余额查询接口 */ @@ -40,15 +50,28 @@ public class AliPayTransferService { @SneakyThrows public void transfer(TransferOrder order) { AlipayFundTransToaccountTransferModel model = new AlipayFundTransToaccountTransferModel(); -// model.setAmount(PayUtil.conversionAmount(order.getAmount()).toString()); - model.setAmount("1.00"); - model.setOutBizNo(IdUtil.getSnowflakeNextIdStr()); - model.setPayeeType("ALIPAY_USERID"); - model.setPayeeAccount("2088722032251651"); - model.setPayerShowName("易杯光年"); - model.setExtParam("{order_title: '订单标题'}"); - model.setRemark("易杯光年的备注"); + model.setAmount(PayUtil.conversionAmount(order.getAmount()).toString()); + model.setOutBizNo(OrderNoGenerateUtil.transfer()); + model.setPayeeType(order.getPayeeType()); + model.setPayeeAccount(order.getPayeeAccount()); + model.setPayerShowName(order.getPayerShowName()); + // 标题 + model.setExtParam(StrUtil.format("{order_title: '{}'}", order.getTitle())); + model.setRemark(order.getReason()); AlipayFundTransToaccountTransferResponse response = AliPayApi.transferToResponse(model); - System.out.println(response); + if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) { + log.error("网关返回退款失败: {}", response.getSubMsg()); + throw new PayFailureException(response.getSubMsg()); + } + TransferLocal transferInfo = PaymentContextLocal.get().getTransferInfo(); + // 通道转账号 + transferInfo.setOutTransferNo(response.getOrderId()); + // 有完成时间代表处理完成 + String payDate = response.getPayDate(); + if (StrUtil.isNotBlank(payDate)){ + LocalDateTime time = LocalDateTimeUtil.parse(payDate, DatePattern.NORM_DATETIME_PATTERN); + transferInfo.setFinishTime(time) + .setStatus(TransferStatusEnum.SUCCESS); + } } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayConfigService.java index 1a625740..4fac70ae 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/union/service/UnionPayConfigService.java @@ -6,7 +6,7 @@ import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.service.code.UnionPayWay; import cn.daxpay.single.service.core.channel.union.dao.UnionPayConfigManager; import cn.daxpay.single.service.core.channel.union.entity.UnionPayConfig; -import cn.daxpay.single.service.core.system.config.service.PayChannelConfigService; +import cn.daxpay.single.service.core.system.config.service.PlatformConfigService; import cn.daxpay.single.service.param.channel.union.UnionPayConfigParam; import cn.daxpay.single.service.sdk.union.api.UnionPayKit; import cn.hutool.core.bean.BeanUtil; @@ -37,7 +37,7 @@ public class UnionPayConfigService { /** 默认云闪付配置的主键ID */ private final static Long ID = 0L; private final UnionPayConfigManager unionPayConfigManager; - private final PayChannelConfigService payChannelConfigService; + private final PlatformConfigService platformConfigService; /** * 修改 @@ -77,6 +77,20 @@ public class UnionPayConfigService { return unionPayConfig; } + /** + * 生成通知地址 + */ + public String generateNotifyUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/callback/pay/union"; + } + + /** + * 生成同步跳转地址 + */ + public String generateReturnUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/return/pay/union"; + } + /** * 生成云闪付支付服务 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayConfigService.java index 6ebccb34..b17d4469 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayConfigService.java @@ -6,7 +6,7 @@ import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.service.code.WeChatPayWay; import cn.daxpay.single.service.core.channel.wechat.dao.WeChatPayConfigManager; import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; -import cn.daxpay.single.service.core.system.config.service.PayChannelConfigService; +import cn.daxpay.single.service.core.system.config.service.PlatformConfigService; import cn.daxpay.single.service.param.channel.wechat.WeChatPayConfigParam; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; @@ -31,7 +31,8 @@ public class WeChatPayConfigService { /** 默认微信支付配置的主键ID */ private final static Long ID = 0L; private final WeChatPayConfigManager weChatPayConfigManager; - private final PayChannelConfigService payChannelConfigService; + + private final PlatformConfigService platformConfigService; /** * 修改 @@ -73,4 +74,18 @@ public class WeChatPayConfigService { .collect(Collectors.toList()); } + /** + * 生成微信通知地址 + */ + public String generateNotifyUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/callback/pay/wechat"; + } + + /** + * 生成同步跳转地址 + */ + public String generateReturnUrl(){ + return platformConfigService.getConfig().getWebsiteUrl() + "/return/pay/wechat"; + } + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayService.java index 6771709f..2a0a3f17 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayService.java @@ -210,7 +210,11 @@ public class WeChatPayService { String errCode = result.get(WeChatPayCode.ERR_CODE); // 支付成功处理, if (Objects.equals(resultCode, WeChatPayCode.PAY_SUCCESS)) { - payInfo.setOutOrderNo(result.get(WeChatPayCode.TRANSACTION_ID)).setComplete(true); + String timeEnd = result.get(WeChatPayCode.TIME_END); + LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); + payInfo.setOutOrderNo(result.get(WeChatPayCode.TRANSACTION_ID)) + .setCompleteTime(time) + .setComplete(true); return; } // 支付中, 发起轮训同步 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayTransferService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayTransferService.java new file mode 100644 index 00000000..455fc838 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayTransferService.java @@ -0,0 +1,28 @@ +package cn.daxpay.single.service.core.channel.wechat.service; + +import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; +import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 微信转账到零钱 + * @author xxm + * @since 2024/6/14 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class WeChatPayTransferService { + + /** + * 转账接口 + */ + @SneakyThrows + public void transfer(TransferOrder order, WeChatPayConfig config) { + throw new PayFailureException("微信转账暂未实现"); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayRefundService.java index d8dbd7f5..e4b28921 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WechatPayRefundService.java @@ -2,6 +2,7 @@ package cn.daxpay.single.service.core.channel.wechat.service; import cn.daxpay.single.code.RefundStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.service.common.context.ErrorInfoLocal; import cn.daxpay.single.service.common.context.RefundLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; @@ -41,6 +42,7 @@ public class WechatPayRefundService { String totalFee = String.valueOf(refundOrder.getAmount()); // 设置退款信息 RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); + ErrorInfoLocal errorInfo = PaymentContextLocal.get().getErrorInfo(); Map params = RefundModel.builder() .appid(weChatPayConfig.getWxAppId()) .mch_id(weChatPayConfig.getWxMchId()) @@ -55,8 +57,8 @@ public class WechatPayRefundService { // 获取证书文件 if (StrUtil.isBlank(weChatPayConfig.getP12())){ String errorMsg = "微信p.12证书未配置,无法进行退款"; - refundInfo.setErrorMsg(errorMsg); - refundInfo.setErrorCode(RefundStatusEnum.FAIL.getCode()); + errorInfo.setErrorMsg(errorMsg); + errorInfo.setErrorCode(RefundStatusEnum.FAIL.getCode()); throw new PayFailureException(errorMsg); } byte[] fileBytes = Base64.decode(weChatPayConfig.getP12()); @@ -82,9 +84,9 @@ public class WechatPayRefundService { errorMsg = result.get(RETURN_MSG); } log.error("订单退款失败 {}", errorMsg); - RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); - refundInfo.setErrorMsg(errorMsg); - refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode)); + ErrorInfoLocal errorInfo = PaymentContextLocal.get().getErrorInfo(); + errorInfo.setErrorMsg(errorMsg); + errorInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode)); throw new PayFailureException(errorMsg); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/builder/PayBuilder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/builder/PayBuilder.java deleted file mode 100644 index 896c4c5a..00000000 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/builder/PayBuilder.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.daxpay.single.service.core.order.pay.builder; - -import cn.daxpay.single.code.PayOrderAllocStatusEnum; -import cn.daxpay.single.code.PayOrderRefundStatusEnum; -import cn.daxpay.single.code.PayStatusEnum; -import cn.daxpay.single.param.payment.pay.PayParam; -import cn.daxpay.single.service.common.local.PaymentContextLocal; -import cn.daxpay.single.service.core.order.pay.entity.PayOrder; -import cn.daxpay.single.util.OrderNoGenerateUtil; -import lombok.experimental.UtilityClass; - -import java.time.LocalDateTime; - -/** - * 支付对象构建器 - * - * @author xxm - * @since 2021/2/25 - */ -@UtilityClass -public class PayBuilder { - - /** - * 构建支付订单 - */ - public PayOrder buildPayOrder(PayParam payParam) { - // 订单超时时间 - LocalDateTime expiredTime = PaymentContextLocal.get() - .getPayInfo() - .getExpiredTime(); - // 构建支付订单对象 - PayOrder payOrder = new PayOrder() - .setBizOrderNo(payParam.getBizOrderNo()) - .setOrderNo(OrderNoGenerateUtil.pay()) - .setTitle(payParam.getTitle()) - .setDescription(payParam.getDescription()) - .setStatus(PayStatusEnum.PROGRESS.getCode()) - .setRefundStatus(PayOrderRefundStatusEnum.NO_REFUND.getCode()) - .setAllocation(payParam.getAllocation()) - .setAmount(payParam.getAmount()) - .setChannel(payParam.getChannel()) - .setMethod(payParam.getMethod()) - .setExpiredTime(expiredTime) - .setRefundableBalance(payParam.getAmount()) - .setClientIp(payParam.getClientIp()) - .setNotifyUrl(payParam.getNotifyUrl()) - .setReturnUrl(payParam.getReturnUrl()) - .setAttach(payParam.getAttach()) - .setReqTime(payParam.getReqTime()); - // 如果支持分账, 设置分账状态为代分账 - if (payOrder.getAllocation()) { - payOrder.setAllocStatus(PayOrderAllocStatusEnum.WAITING.getCode()); - } - return payOrder; - } -} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/convert/TransferOrderConvert.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/convert/TransferOrderConvert.java index 805e4291..08b5b0a8 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/convert/TransferOrderConvert.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/convert/TransferOrderConvert.java @@ -1,5 +1,7 @@ package cn.daxpay.single.service.core.order.transfer.convert; +import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder; +import cn.daxpay.single.service.dto.order.transfer.TransferOrderDto; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -12,4 +14,5 @@ import org.mapstruct.factory.Mappers; public interface TransferOrderConvert { TransferOrderConvert CONVERT = Mappers.getMapper(TransferOrderConvert.class); + TransferOrderDto convert(TransferOrder in); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/entity/TransferOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/entity/TransferOrder.java index ff2c61f9..38734f83 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/entity/TransferOrder.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/transfer/entity/TransferOrder.java @@ -1,5 +1,6 @@ package cn.daxpay.single.service.core.order.transfer.entity; +import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity; import cn.bootx.table.modify.annotation.DbColumn; import cn.bootx.table.modify.annotation.DbTable; @@ -7,6 +8,8 @@ import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex; import cn.daxpay.single.code.PayChannelEnum; import cn.daxpay.single.code.TransferPayeeTypeEnum; import cn.daxpay.single.code.TransferTypeEnum; +import cn.daxpay.single.service.core.order.transfer.convert.TransferOrderConvert; +import cn.daxpay.single.service.dto.order.transfer.TransferOrderDto; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; @@ -26,7 +29,7 @@ import java.time.LocalDateTime; @Accessors(chain = true) @DbTable(comment = "转账订单") @TableName("pay_transfer_order") -public class TransferOrder extends MpBaseEntity { +public class TransferOrder extends MpBaseEntity implements EntityBaseFunction { /** 商户转账号 */ @DbMySqlIndex(comment = "商户转账号索引") @@ -129,4 +132,8 @@ public class TransferOrder extends MpBaseEntity { @DbColumn(comment = "错误信息", length = 150) private String errorMsg; + @Override + public TransferOrderDto toDto() { + return TransferOrderConvert.CONVERT.convert(this); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java index 0d185cb8..53c6383f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java @@ -3,18 +3,20 @@ package cn.daxpay.single.service.core.payment.pay.service; import cn.bootx.platform.common.core.util.CollUtil; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.code.PayOrderAllocStatusEnum; import cn.daxpay.single.code.PayOrderRefundStatusEnum; +import cn.daxpay.single.code.PayStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.param.payment.pay.PayParam; import cn.daxpay.single.result.pay.PayResult; import cn.daxpay.single.service.common.context.PayLocal; import cn.daxpay.single.service.common.context.PlatformLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; -import cn.daxpay.single.service.core.order.pay.builder.PayBuilder; import cn.daxpay.single.service.core.order.pay.entity.PayOrder; import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService; import cn.daxpay.single.service.core.order.pay.service.PayOrderService; import cn.daxpay.single.service.core.payment.sync.service.PaySyncService; +import cn.daxpay.single.util.OrderNoGenerateUtil; import cn.daxpay.single.util.PayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; @@ -46,54 +48,37 @@ public class PayAssistService { private final PayOrderQueryService payOrderQueryService; - /** - * 初始化支付相关上下文 - */ - public void initPayContext(PayOrder order, PayParam payParam) { - // 初始化支付订单超时时间 - this.initExpiredTime(order, payParam); - } - - - /** - * 初始化支付订单超时时间 - * 1. 如果支付记录为空, 超时时间读取顺序 PayParam -> 平台设置 - * 2. 如果支付记录不为空, 超时时间通过支付记录进行反推 - */ - public void initExpiredTime(PayOrder order, PayParam payParam) { - // 钱包没有超时时间 - if (PayChannelEnum.WALLET.getCode() - .equals(payParam.getChannel())) { - return; - } - PayLocal payInfo = PaymentContextLocal.get() - .getPayInfo(); - PlatformLocal platform = PaymentContextLocal.get() - .getPlatformInfo(); - // 支付订单是非为空 - if (Objects.nonNull(order)) { - payInfo.setExpiredTime(order.getExpiredTime()); - return; - } - // 支付参数传入 - if (Objects.nonNull(payParam.getExpiredTime())) { - payInfo.setExpiredTime(payParam.getExpiredTime()); - return; - } - // 读取本地时间 - LocalDateTime paymentExpiredTime = PayUtil.getPaymentExpiredTime(platform.getOrderTimeout()); - payInfo.setExpiredTime(paymentExpiredTime); - } /** * 创建支付订单并保存, 返回支付订单 */ @Transactional(rollbackFor = Exception.class) public PayOrder createPayOrder(PayParam payParam) { - PayLocal payInfo = PaymentContextLocal.get() - .getPayInfo(); - // 构建支付订单并保存 - PayOrder order = PayBuilder.buildPayOrder(payParam); + // 订单超时时间 + LocalDateTime expiredTime = this.getExpiredTime(payParam); + // 构建支付订单对象 + PayOrder order = new PayOrder() + .setBizOrderNo(payParam.getBizOrderNo()) + .setOrderNo(OrderNoGenerateUtil.pay()) + .setTitle(payParam.getTitle()) + .setDescription(payParam.getDescription()) + .setStatus(PayStatusEnum.PROGRESS.getCode()) + .setRefundStatus(PayOrderRefundStatusEnum.NO_REFUND.getCode()) + .setAllocation(payParam.getAllocation()) + .setAmount(payParam.getAmount()) + .setChannel(payParam.getChannel()) + .setMethod(payParam.getMethod()) + .setExpiredTime(expiredTime) + .setRefundableBalance(payParam.getAmount()) + .setClientIp(payParam.getClientIp()) + .setNotifyUrl(payParam.getNotifyUrl()) + .setReturnUrl(payParam.getReturnUrl()) + .setAttach(payParam.getAttach()) + .setReqTime(payParam.getReqTime()); + // 如果支持分账, 设置分账状态为待分账 + if (order.getAllocation()) { + order.setAllocStatus(PayOrderAllocStatusEnum.WAITING.getCode()); + } payOrderService.save(order); return order; } @@ -187,12 +172,28 @@ public class PayAssistService { payResult.setStatus(payOrder.getStatus()); // 设置支付参数 - PayLocal payInfo = PaymentContextLocal.get() - .getPayInfo(); + PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); if (StrUtil.isNotBlank(payInfo.getPayBody())) { payResult.setPayBody(payInfo.getPayBody()); } - // 签名 return payResult; } + + /** + * 获取支付订单超时时间 + */ + private LocalDateTime getExpiredTime(PayParam payParam) { + // 钱包没有超时时间 + if (PayChannelEnum.WALLET.getCode() + .equals(payParam.getChannel())) { + return null; + } + PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo(); + // 支付参数传入 + if (Objects.nonNull(payParam.getExpiredTime())) { + return payParam.getExpiredTime(); + } + // 根据平台配置计算出 + return PayUtil.getPaymentExpiredTime(platform.getOrderTimeout()); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayService.java index f29c63d3..7a4b966f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayService.java @@ -11,7 +11,6 @@ import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService; import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService; import cn.daxpay.single.service.func.AbsPayStrategy; import cn.daxpay.single.service.util.PayStrategyFactory; -import cn.daxpay.single.util.PayUtil; import cn.hutool.extra.spring.SpringUtil; import com.baomidou.lock.LockInfo; import com.baomidou.lock.LockTemplate; @@ -66,8 +65,6 @@ public class PayService { try { // 查询并检查订单 PayOrder payOrder = payAssistService.getOrderAndCheck(payParam.getBizOrderNo()); - // 初始化支付上下文 - payAssistService.initPayContext(payOrder, payParam); // 走首次下单逻辑还是重复下档逻辑 if (Objects.isNull(payOrder)){ return this.firstPay(payParam); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java index 886fc2ab..5c701497 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java @@ -7,6 +7,7 @@ import cn.daxpay.single.code.RefundStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.param.payment.refund.RefundParam; import cn.daxpay.single.result.pay.RefundResult; +import cn.daxpay.single.service.common.context.ErrorInfoLocal; import cn.daxpay.single.service.common.context.PlatformLocal; import cn.daxpay.single.service.common.context.RefundLocal; import cn.daxpay.single.service.common.local.PaymentContextLocal; @@ -117,8 +118,9 @@ public class RefundAssistService { @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public void updateOrderByError(RefundOrder refundOrder){ RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); - refundOrder.setErrorCode(refundInfo.getErrorCode()); - refundOrder.setErrorMsg(refundInfo.getErrorMsg()); + ErrorInfoLocal errorInfo = PaymentContextLocal.get().getErrorInfo(); + refundOrder.setErrorCode(errorInfo.getErrorCode()); + refundOrder.setErrorMsg(errorInfo.getErrorMsg()); refundOrder.setStatus(refundInfo.getStatus().getCode()); refundOrderManager.updateById(refundOrder); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferAssistService.java index 5448f80d..25bcb15f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferAssistService.java @@ -3,6 +3,8 @@ package cn.daxpay.single.service.core.payment.transfer.service; import cn.daxpay.single.code.TransferStatusEnum; import cn.daxpay.single.param.payment.transfer.TransferParam; import cn.daxpay.single.result.transfer.TransferResult; +import cn.daxpay.single.service.common.context.ErrorInfoLocal; +import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.transfer.dao.TransferOrderManager; import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder; import cn.daxpay.single.util.OrderNoGenerateUtil; @@ -52,8 +54,11 @@ public class TransferAssistService { * 更新转账订单错误信息 */ public void updateOrderByError(TransferOrder order) { - order.setErrorMsg("") - .setErrorCode(""); + ErrorInfoLocal errorInfo = PaymentContextLocal.get().getErrorInfo(); + order.setStatus(TransferStatusEnum.FAIL.getCode()) + .setErrorMsg(errorInfo.getErrorMsg()) + .setErrorCode(errorInfo.getErrorCode()); + transferOrderManager.updateById(order); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferService.java index 179b410a..9df0f9d4 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/service/TransferService.java @@ -4,6 +4,7 @@ import cn.daxpay.single.code.RefundStatusEnum; import cn.daxpay.single.param.payment.transfer.TransferParam; import cn.daxpay.single.result.transfer.TransferResult; import cn.daxpay.single.service.common.local.PaymentContextLocal; +import cn.daxpay.single.service.core.order.transfer.dao.TransferOrderManager; import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder; import cn.daxpay.single.service.func.AbsTransferStrategy; import cn.daxpay.single.service.util.PayStrategyFactory; @@ -11,6 +12,8 @@ import cn.hutool.extra.spring.SpringUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; /** * 转账服务 @@ -24,6 +27,8 @@ public class TransferService { private final TransferAssistService transferAssistService; + private final TransferOrderManager transferOrderManager; + /** * 转账 */ @@ -56,8 +61,9 @@ public class TransferService { /** * 成功处理 */ + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void successHandler(TransferOrder order){ + order.setStatus(RefundStatusEnum.SUCCESS.getCode()); + transferOrderManager.updateById(order); } - - } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/AliPayTransferStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/AliPayTransferStrategy.java index 8124c2d0..30f5ae9f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/AliPayTransferStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/AliPayTransferStrategy.java @@ -1,7 +1,8 @@ package cn.daxpay.single.service.core.payment.transfer.strategy; import cn.daxpay.single.code.PayChannelEnum; -import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig; +import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.param.payment.transfer.TransferParam; import cn.daxpay.single.service.core.channel.alipay.service.AliPayConfigService; import cn.daxpay.single.service.core.channel.alipay.service.AliPayTransferService; import cn.daxpay.single.service.func.AbsTransferStrategy; @@ -10,10 +11,13 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; +import java.util.Arrays; + +import static cn.daxpay.single.code.TransferPayeeTypeEnum.*; import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; /** - * 支付宝转账测策略 + * 支付宝转账策略 * @author xxm * @since 2024/3/21 */ @@ -27,7 +31,6 @@ public class AliPayTransferStrategy extends AbsTransferStrategy { private final AliPayTransferService aliPayTransferService; - private AliPayConfig config; /** * 策略标识 @@ -37,15 +40,26 @@ public class AliPayTransferStrategy extends AbsTransferStrategy { return PayChannelEnum.ALI.getCode(); } + @Override + public void doValidateParam(TransferParam transferParam) { + // 转账接收方类型校验 + String payeeType = transferParam.getPayeeType(); + if (!Arrays.asList(ALI_USER_ID.getCode(), ALI_OPEN_ID.getCode(), ALI_LOGIN_NAME.getCode()).contains(payeeType)){ + throw new PayFailureException("支付宝不支持该类型收款人"); + } + } + /** * 转账前操作 */ @Override public void doBeforeHandler() { - this.config = payConfigService.getAndCheckConfig(); - payConfigService.initConfig(this.config); + payConfigService.initConfig(payConfigService.getAndCheckConfig()); } + /** + * 转账操作 + */ @Override public void doTransferHandler() { aliPayTransferService.transfer(this.getTransferOrder()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/WeChatTransferStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/WeChatTransferStrategy.java new file mode 100644 index 00000000..3f72e4bf --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/transfer/strategy/WeChatTransferStrategy.java @@ -0,0 +1,71 @@ +package cn.daxpay.single.service.core.payment.transfer.strategy; + +import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.param.payment.transfer.TransferParam; +import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; +import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService; +import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayTransferService; +import cn.daxpay.single.service.func.AbsTransferStrategy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +import static cn.daxpay.single.code.TransferPayeeTypeEnum.WX_PERSONAL; +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +/** + * 微信转账策略 + * @author xxm + * @since 2024/6/14 + */ +@Slf4j +@Service +@Scope(SCOPE_PROTOTYPE) +@RequiredArgsConstructor +public class WeChatTransferStrategy extends AbsTransferStrategy { + + private final WeChatPayConfigService weChatPayConfigService; + + private final WeChatPayTransferService weChatPayTransferService; + + private WeChatPayConfig weChatPayConfig; + + @Override + public String getChannel() { + return PayChannelEnum.WECHAT.getCode(); + } + + + /** + * 校验参数 + */ + @Override + public void doValidateParam(TransferParam transferParam) { + // 转账接收方类型校验 + String payeeType = transferParam.getPayeeType(); + if (!Objects.equals(WX_PERSONAL.getCode(), payeeType)){ + throw new PayFailureException("支付宝不支持该类型收款人"); + } + } + + /** + * 转账前操作 + */ + @Override + public void doBeforeHandler() { + this.weChatPayConfig = weChatPayConfigService.getConfig(); + } + + /** + * 转账操作 + */ + @Override + public void doTransferHandler() { + weChatPayTransferService.transfer(this.getTransferOrder(), weChatPayConfig); + } + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/pay/PayOrderDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/pay/PayOrderDto.java index 21e73bf8..1507d261 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/pay/PayOrderDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/pay/PayOrderDto.java @@ -105,13 +105,6 @@ public class PayOrderDto extends BaseDto { @Schema(description = "过期时间") private LocalDateTime expiredTime; - /** 错误码 */ - @Schema(description = "错误码") - private String errorCode; - - /** 错误信息 */ - @Schema(description = "错误信息") - private String errorMsg; /** 支付终端ip */ @Schema(description = "支付终端ip") @@ -128,4 +121,12 @@ public class PayOrderDto extends BaseDto { /** 请求时间,时间戳转时间, 以最后一次为准 */ @Schema(description = "请求时间,传输时间戳,以最后一次为准") private LocalDateTime reqTime; + + /** 错误码 */ + @Schema(description = "错误码") + private String errorCode; + + /** 错误信息 */ + @Schema(description = "错误信息") + private String errorMsg; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/transfer/TransferOrderDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/transfer/TransferOrderDto.java new file mode 100644 index 00000000..fb71952e --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/order/transfer/TransferOrderDto.java @@ -0,0 +1,125 @@ +package cn.daxpay.single.service.dto.order.transfer; + +import cn.bootx.platform.common.core.rest.dto.BaseDto; +import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.code.TransferPayeeTypeEnum; +import cn.daxpay.single.code.TransferTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 转账订单 + * @author xxm + * @since 2024/6/14 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "转账订单") +public class TransferOrderDto extends BaseDto { + + /** 商户转账号 */ + @Schema(description = "商户转账号") + private String bizTransferNo; + + /** 转账号 */ + @Schema(description = "收款人账号") + private String transferNo; + + /** 通道转账号 */ + @Schema(description = "通道转账号") + private String outTransferNo; + + /** + * 支付通道 + * @see PayChannelEnum + */ + @Schema(description = "支付通道") + private String channel; + + /** 转账金额 */ + @Schema(description = "转账金额") + private Integer amount; + + /** 标题 */ + @Schema(description = "标题") + private String title; + + /** 转账原因/备注 */ + @Schema(description = "转账原因/备注") + private String reason; + + /** + * 转账类型, 微信使用 + * @see TransferTypeEnum + */ + @Schema(description = "转账类型, 微信使用") + private String transferType; + + /** 付款方 */ + @Schema(description = "付款方") + private String payer; + + /** 付款方显示名称 */ + @Schema(description = "付款方显示名称") + private String payerShowName; + + /** + * 收款人类型 + * @see TransferPayeeTypeEnum + */ + @Schema(description = "收款人类型") + private String payeeType; + + /** 收款人账号 */ + @Schema(description = "收款人账号") + private String payeeAccount; + + /** 收款人姓名 */ + @Schema(description = "收款人姓名") + private String payeeName; + + /** + * 状态 + * @see cn.daxpay.single.code.TransferStatusEnum + */ + @Schema(description = "状态") + private String status; + + /** 成功时间 */ + @Schema(description = "成功时间") + private LocalDateTime successTime; + + + /** 异步通知地址 */ + @Schema(description = "异步通知地址") + private String notifyUrl; + + /** 商户扩展参数,回调时会原样返回, 以最后一次为准 */ + @Schema(description = "商户扩展参数") + private String attach; + + /** 请求时间,时间戳转时间 */ + @Schema(description = "请求时间,传输时间戳") + private LocalDateTime reqTime; + + /** 终端ip */ + @Schema(description = "支付终端ip") + private String clientIp; + + /** + * 错误码 + */ + @Schema(description = "错误码") + private String errorCode; + + /** + * 错误原因 + */ + @Schema(description = "错误原因") + private String errorMsg; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsTransferStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsTransferStrategy.java index 6e597d09..82e9d1a7 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsTransferStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsTransferStrategy.java @@ -20,6 +20,7 @@ public abstract class AbsTransferStrategy implements PayStrategy{ * 校验参数 */ public void doValidateParam(TransferParam transferParam) { + } /**