diff --git a/_doc/Task.md b/_doc/Task.md index 7ff41c514..ed7615a0a 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -16,14 +16,15 @@ - [x] 分类配置 - [x] 明细配置 - [x] 聚合支付配置 - - [ ] 支持通过订单信息生成多种类型的收银台链接, - - [ ] pc收银台 - - [ ] 扫码支付 - - [ ] 条码支付 - - [ ] 跳转支付 - - [ ] 聚合支付(生成扫码链接) - - [ ] H5收银台 - - [ ] 跳转支付 + - [x] 支持通过订单信息生成多种类型的收银台链接, + - [x] pc收银台 + - [x] 扫码支付 + - [x] 条码支付 + - [x] 跳转支付 + - [x] 聚合支付(生成扫码链接) + - [x] 聚合支付(条码支付) + - [x] H5收银台 + - [x] 跳转支付 - [x] 升级为聚合支付 - [ ] ~~APP调起支付~~ - [ ] ~~小程序支付~~ diff --git a/daxpay-single-channel/daxpay-single-alipay/src/main/java/org/dromara/daxpay/channel/alipay/strategy/AliPayCheckoutStrategy.java b/daxpay-single-channel/daxpay-single-alipay/src/main/java/org/dromara/daxpay/channel/alipay/strategy/AliPayCheckoutStrategy.java index ed582112f..66c9ca9e5 100644 --- a/daxpay-single-channel/daxpay-single-alipay/src/main/java/org/dromara/daxpay/channel/alipay/strategy/AliPayCheckoutStrategy.java +++ b/daxpay-single-channel/daxpay-single-alipay/src/main/java/org/dromara/daxpay/channel/alipay/strategy/AliPayCheckoutStrategy.java @@ -1,5 +1,6 @@ package org.dromara.daxpay.channel.alipay.strategy; +import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.daxpay.core.enums.ChannelEnum; @@ -15,6 +16,7 @@ import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class AliPayCheckoutStrategy extends AbsCheckoutStrategy { + /** * 策略标识, 可以自行进行扩展 * @@ -24,4 +26,13 @@ public class AliPayCheckoutStrategy extends AbsCheckoutStrategy { public String getChannel() { return ChannelEnum.ALI.getCode(); } + + /** + * 检测付款码 + */ + @Override + public boolean checkBarCode(String barCode){ + String[] ali = { "25", "26", "27", "28", "29", "30" }; + return StrUtil.startWithAny(barCode.substring(0, 2), ali); + } } diff --git a/daxpay-single-channel/daxpay-single-wechat/src/main/java/org/dromara/daxpay/channel/wechat/strategy/WechatCheckoutStrategy.java b/daxpay-single-channel/daxpay-single-wechat/src/main/java/org/dromara/daxpay/channel/wechat/strategy/WechatCheckoutStrategy.java index 802c3516b..302ee4d52 100644 --- a/daxpay-single-channel/daxpay-single-wechat/src/main/java/org/dromara/daxpay/channel/wechat/strategy/WechatCheckoutStrategy.java +++ b/daxpay-single-channel/daxpay-single-wechat/src/main/java/org/dromara/daxpay/channel/wechat/strategy/WechatCheckoutStrategy.java @@ -45,6 +45,14 @@ public class WechatCheckoutStrategy extends AbsCheckoutStrategy { return wechatAuthService.generateInnerAuthUrl(redirectUrl); } + /** + * 检测付款码 + */ + @Override + public boolean checkBarCode(String barCode){ + String[] wx = { "10", "11", "12", "13", "14", "15" }; + return StrUtil.startWithAny(barCode.substring(0, 2), wx); + } /** * 获取认证结果 */ diff --git a/daxpay-single/daxpay-single-core/src/main/java/org/dromara/daxpay/core/param/checkout/CheckoutAggregateBarPayParam.java b/daxpay-single/daxpay-single-core/src/main/java/org/dromara/daxpay/core/param/checkout/CheckoutAggregateBarPayParam.java new file mode 100644 index 000000000..6243a78af --- /dev/null +++ b/daxpay-single/daxpay-single-core/src/main/java/org/dromara/daxpay/core/param/checkout/CheckoutAggregateBarPayParam.java @@ -0,0 +1,31 @@ +package org.dromara.daxpay.core.param.checkout; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 收银台聚合条码支付参数 + * @author xxm + * @since 2024/11/26 + */ +@Data +@Accessors(chain = true) +@Schema(title = "收银台聚合支付参数") +public class CheckoutAggregateBarPayParam { + /** 支付订单号 */ + @NotBlank(message = "支付订单号不可为空") + @Size(max = 32, message = "支付订单号不可超过32位") + @Schema(description = "支付订单号") + private String orderNo; + + + /** 付款码 */ + @NotBlank(message = "付款码不可为空") + @Size(max = 256, message = "付款码不可超过256位") + @Schema(description = "付款码") + private String barCode; + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/controller/unipay/CheckoutController.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/controller/unipay/CheckoutController.java index d0baa6e38..fe3751581 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/controller/unipay/CheckoutController.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/controller/unipay/CheckoutController.java @@ -6,6 +6,7 @@ import cn.bootx.platform.core.rest.result.Result; import cn.bootx.platform.core.util.ValidationUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import org.dromara.daxpay.core.param.checkout.*; import org.dromara.daxpay.core.result.DaxResult; @@ -18,6 +19,7 @@ import org.dromara.daxpay.core.util.DaxRes; import org.dromara.daxpay.service.common.anno.PaymentVerify; import org.dromara.daxpay.service.service.checkout.CheckoutQueryService; import org.dromara.daxpay.service.service.checkout.CheckoutService; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; /** @@ -25,6 +27,7 @@ import org.springframework.web.bind.annotation.*; * @author xxm * @since 2024/11/26 */ +@Validated @IgnoreAuth @Tag(name = "收银台服务") @RestController @@ -48,7 +51,6 @@ public class CheckoutController { } - @Operation(summary = "获取收银台订单和配置信息") @GetMapping("/getOrderAndConfig") public Result getOrderAndConfig(String orderNo, String checkoutType){ @@ -84,7 +86,7 @@ public class CheckoutController { } - @Operation(summary = "发起支付(聚合)") + @Operation(summary = "发起支付(聚合扫码)") @PostMapping("/aggregatePay") public Result aggregatePay(@RequestBody CheckoutAggregatePayParam param){ ValidationUtil.validateParam(param); @@ -92,5 +94,19 @@ public class CheckoutController { } + @Operation(summary = "发起支付(聚合条码)") + @PostMapping("/aggregateBarPay") + public Result aggregateBarPay(@RequestBody CheckoutAggregateBarPayParam param){ + ValidationUtil.validateParam(param); + return Res.ok(checkoutService.aggregateBarPay(param)); + } + + + @Operation(summary = "查询订单状态") + @GetMapping("/findStatusByOrderNo") + public Result findStatusByOrderNo(@NotBlank(message = "订单号不能为空") String orderNo){ + return Res.ok(checkoutQueryService.findStatusByOrderNo(orderNo)); + } + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutQueryService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutQueryService.java index 0bd58d229..6eab138d7 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutQueryService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutQueryService.java @@ -4,6 +4,7 @@ import cn.bootx.platform.common.mybatisplus.base.MpIdEntity; import cn.bootx.platform.core.exception.DataNotExistException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.daxpay.core.enums.PayStatusEnum; import org.dromara.daxpay.core.result.checkout.*; import org.dromara.daxpay.service.convert.config.CheckoutAggregateConfigConvert; import org.dromara.daxpay.service.convert.config.CheckoutConfigConvert; @@ -13,6 +14,7 @@ import org.dromara.daxpay.service.dao.config.checkout.CheckoutAggregateConfigMan import org.dromara.daxpay.service.dao.config.checkout.CheckoutConfigManager; import org.dromara.daxpay.service.dao.config.checkout.CheckoutGroupConfigManager; import org.dromara.daxpay.service.dao.config.checkout.CheckoutItemConfigManager; +import org.dromara.daxpay.service.dao.order.pay.PayOrderManager; import org.dromara.daxpay.service.entity.config.checkout.CheckoutAggregateConfig; import org.dromara.daxpay.service.entity.config.checkout.CheckoutItemConfig; import org.dromara.daxpay.service.entity.order.pay.PayOrder; @@ -21,6 +23,7 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -37,6 +40,7 @@ public class CheckoutQueryService { private final CheckoutItemConfigManager checkoutItemConfigManager; private final CheckoutAggregateConfigManager checkoutAggregateConfigManager; private final CheckoutAssistService checkoutAssistService; + private final PayOrderManager payOrderManager; /** * 获取收银台配置 @@ -124,4 +128,14 @@ public class CheckoutQueryService { checkoutInfoResult.setAggregateConfig(CheckoutAggregateConfigConvert.CONVERT.toResult(aggregateConfig)); return checkoutInfoResult; } + + /** + * 查询订单状态 + */ + public Boolean findStatusByOrderNo(String orderNo) { + String status = payOrderManager.findByOrderNo(orderNo) + .map(PayOrder::getStatus) + .orElse(null); + return Objects.equals(status, PayStatusEnum.SUCCESS.getCode()); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutService.java index 3ef8db77b..f74c77068 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/checkout/CheckoutService.java @@ -9,6 +9,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.daxpay.core.enums.CheckoutCallTypeEnum; import org.dromara.daxpay.core.enums.CheckoutTypeEnum; +import org.dromara.daxpay.core.enums.PayMethodEnum; +import org.dromara.daxpay.core.exception.ChannelNotExistException; import org.dromara.daxpay.core.exception.ConfigNotExistException; import org.dromara.daxpay.core.exception.TradeProcessingException; import org.dromara.daxpay.core.exception.UnsupportedAbilityException; @@ -149,7 +151,7 @@ public class CheckoutService { } /** - * 聚合支付 + * 聚合支付(扫码) */ public PayResult aggregatePay(CheckoutAggregatePayParam param){ // 订单信息 @@ -177,6 +179,38 @@ public class CheckoutService { return payService.pay(payParam, payOrder); } + /** + * 聚合支付(条码支付) + */ + public PayResult aggregateBarPay(CheckoutAggregateBarPayParam param){ + // 订单信息 + PayOrder payOrder = checkoutAssistService.getOrderAndCheck(param.getOrderNo()); + var cashierStrategies = PaymentStrategyFactory.createGroup(AbsCheckoutStrategy.class); + // 获取策略 + var cashierStrategy = cashierStrategies.stream() + .filter(strategy -> strategy.checkBarCode(param.getBarCode())) + .findFirst() + .orElseThrow(() -> new ChannelNotExistException("未找到支持该付款码的支付通道")); + + // 获取聚合类型 + paymentAssistService.initMchApp(payOrder.getAppId()); + // 构建支付参数 + String clientIP = JakartaServletUtil.getClientIP(WebServletUtil.getRequest()); + PayParam payParam = new PayParam(); + payParam.setChannel(cashierStrategy.getChannel()); + payParam.setMethod(PayMethodEnum.BARCODE.getCode()); + payParam.setAppId(payOrder.getAppId()); + payParam.setClientIp(clientIP); + payParam.setReqTime(LocalDateTime.now()); + + // 进行参数预处理 + CheckoutPayParam cashierPayParam = new CheckoutPayParam() + .setBarCode(param.getBarCode()); + cashierStrategy.handlePayParam(cashierPayParam, payParam); + // 发起支付 + return payService.pay(payParam, payOrder); + } + /** * 生成授权链接跳转链接, 主要是微信类通道使用, 用于获取OpenId */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/strategy/AbsCheckoutStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/strategy/AbsCheckoutStrategy.java index 089d8a9e4..d0e72c650 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/strategy/AbsCheckoutStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/strategy/AbsCheckoutStrategy.java @@ -19,6 +19,13 @@ public abstract class AbsCheckoutStrategy implements PaymentStrategy{ return ""; } + /** + * 检测付款码 + */ + public boolean checkBarCode(String barCode){ + return false; + } + /** * 获取认证结果 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/util/PaymentStrategyFactory.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/util/PaymentStrategyFactory.java index 79be51590..fdb73cd45 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/util/PaymentStrategyFactory.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/util/PaymentStrategyFactory.java @@ -5,6 +5,9 @@ import org.dromara.daxpay.service.strategy.PaymentStrategy; import cn.hutool.extra.spring.SpringUtil; import lombok.experimental.UtilityClass; +import java.util.ArrayList; +import java.util.List; + /** * 策略工厂工具类 * @author xxm @@ -18,7 +21,7 @@ public class PaymentStrategyFactory { * @param channel 通道编码 * @param clazz 策略类型 * @return 策略类 - * @param 需要为 PayStrategy 的子类 + * @param 需要为 PaymentStrategy 的子类 */ public T create(String channel, Class clazz) { var beansOfType = SpringUtil.getBeansOfType(clazz); @@ -28,5 +31,16 @@ public class PaymentStrategyFactory { .orElseThrow(() -> new UnsupportedAbilityException("不支持的能力")); } + /** + * 根据指定类型获取策略组 + * @param clazz 策略类型 + * @return 策略类列表 + * @param 需要为 PaymentStrategy 的子类 + */ + public List createGroup(Class clazz) { + var beansOfType = SpringUtil.getBeansOfType(clazz); + return new ArrayList<>(beansOfType.values()); + } + }