diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/code/pay/PayStatusCode.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/code/pay/PayStatusCode.java index 0962ff5f..1a593768 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/code/pay/PayStatusCode.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/code/pay/PayStatusCode.java @@ -24,7 +24,7 @@ public interface PayStatusCode { /** 支付取消(超时/手动取消/订单已经关闭,撤销支付单) */ String TRADE_CANCEL = "trade_cancel"; - /** 退款中 */ + /** 退款中(部分退款) */ String TRADE_REFUNDING = "trade_refunding"; /** 已退款 */ diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/controller/CashierController.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/controller/CashierController.java index 61755896..65ad023b 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/controller/CashierController.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/controller/CashierController.java @@ -43,11 +43,11 @@ public class CashierController { return Res.ok(cashierService.combinationPay(param)); } - @Operation(summary = "扫码聚合支付(单渠道)") - @GetMapping("/aggregatePay/{mchAppCode}") - public ModelAndView aggregatePay(String key,@PathVariable String mchAppCode, @RequestHeader(USER_AGENT) String ua) { + @Operation(summary = "扫码聚合支付(单通道)") + @GetMapping("/aggregatePay/{mchCode}/{mchAppCode}") + public ModelAndView aggregatePay(String key,@PathVariable String mchCode,@PathVariable String mchAppCode, @RequestHeader(USER_AGENT) String ua) { try { - String url = cashierService.aggregatePay(key, mchAppCode, ua); + String url = cashierService.aggregatePay(key, mchCode, mchAppCode, ua); return new ModelAndView("redirect:" + url); } catch (PayUnsupportedMethodException e) { @@ -56,9 +56,9 @@ public class CashierController { } @Operation(summary = "微信jsapi支付(回调)") - @GetMapping("/wxJsapiPay/{mchAppCode}") - public ModelAndView wxJsapiPay(String code, @PathVariable String mchAppCode, String state) { - Map map = cashierService.wxJsapiPay(code,mchAppCode,state); + @GetMapping("/wxJsapiPay/{mchCode}/{mchAppCode}") + public ModelAndView wxJsapiPay(String code, @PathVariable String mchCode, @PathVariable String mchAppCode, String state) { + Map map = cashierService.wxJsapiPay(code,mchCode,mchAppCode,state); // 跳转页面, 调起微信jsapi支付 return new ModelAndView("wechatJsapiPay").addAllObjects(map); } diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/cashier/service/CashierService.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/cashier/service/CashierService.java index c0fdb0d8..68af7451 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/cashier/service/CashierService.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/cashier/service/CashierService.java @@ -12,6 +12,7 @@ import cn.bootx.platform.daxpay.core.aggregate.entity.AggregatePayInfo; import cn.bootx.platform.daxpay.core.aggregate.service.AggregateService; import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager; import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig; +import cn.bootx.platform.daxpay.core.merchant.dao.MchAppManager; import cn.bootx.platform.daxpay.core.pay.service.PayService; import cn.bootx.platform.daxpay.dto.pay.PayResult; import cn.bootx.platform.daxpay.exception.payment.PayFailureException; @@ -22,6 +23,7 @@ import cn.bootx.platform.daxpay.param.pay.PayParam; import cn.bootx.platform.daxpay.param.pay.PayWayParam; import cn.bootx.platform.daxpay.util.PayWaylUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import com.ijpay.core.enums.SignType; import com.ijpay.core.kit.WxPayKit; import lombok.RequiredArgsConstructor; @@ -54,13 +56,14 @@ public class CashierService { private final WeChatPayConfigManager weChatPayConfigManager; + private final MchAppManager mchAppManager; + private final SystemParamManager systemParamManager; /** * 发起支付(单渠道支付) */ public PayResult singlePay(CashierSinglePayParam param) { - // 如果是聚合支付,存在付款码时特殊处理(聚合扫码支付不用额外处理) if (Objects.equals(PayChannelEnum.AGGREGATION.getCode(), param.getPayChannel())) { String payChannel = aggregateService.getPayChannel(param.getAuthCode()).getCode(); @@ -96,8 +99,9 @@ public class CashierService { /** * 扫码发起自动支付 */ - public String aggregatePay(String key, String mchAppCode, String ua) { + public String aggregatePay(String key, String mchCode, String mchAppCode, String ua) { CashierSinglePayParam cashierSinglePayParam = new CashierSinglePayParam() + .setMchCode(mchCode) .setMchAppCode(mchAppCode) .setPayWay(PayWayEnum.QRCODE.getCode()); // 判断是哪种支付方式 @@ -106,7 +110,7 @@ public class CashierService { } else if (ua.contains(PayChannelEnum.UA_WECHAT_PAY)) { // 跳转微信授权页面, 调用jsapi进行支付 - return this.wxJsapiAuth(key,mchAppCode); + return this.wxJsapiAuth(key,mchCode,mchAppCode); } else { throw new PayUnsupportedMethodException(); @@ -123,26 +127,27 @@ public class CashierService { /** * 微信jsapi支付 - 跳转到授权页面 */ - private String wxJsapiAuth(String key, String mchAppCode) { + private String wxJsapiAuth(String key, String mchCode, String mchAppCode) { WeChatPayConfig config = weChatPayConfigManager.findByMchAppCode(mchAppCode) .orElseThrow(() -> new PayFailureException("未找到启用的微信支付配置")); WxMpService wxMpService = getWxMpService(config.getWxAppId(), config.getAppSecret()); // 回调地址为 结算台微信jsapi支付的回调地址 SystemParameter systemParameter = systemParamManager.findByParamKey(WeChatPayCode.JSAPI_REDIRECT_URL) .orElseThrow(() -> new PayFailureException("微信支付回调地址参数不存在")); - String url = systemParameter.getValue() + "cashier/wxJsapiPay"; + String url = StrUtil.format("{}cashier/wxJsapiPay/{}/{}",systemParameter.getValue(),mchCode,mchAppCode); return wxMpService.getOAuth2Service().buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_BASE, key); } /** * 微信jsapi支付 - 回调发起预支付, 同时调起微信页面jsapi支付 * @param code 微信授权码, 用来获取id + * @param mchCode 商户编码 * @param mchAppCode 商户应用编码 * @param state 聚合支付参数记录的key * @return 页面中调起jsapi支付的参数 */ @SneakyThrows - public Map wxJsapiPay(String code, String mchAppCode, String state) { + public Map wxJsapiPay(String code, String mchCode, String mchAppCode, String state) { WeChatPayConfig config = weChatPayConfigManager.findByMchAppCode(mchAppCode) .orElseThrow(() -> new PayFailureException("未找到启用的微信支付配置")); WxMpService wxMpService = this.getWxMpService(config.getWxAppId(), config.getAppSecret()); @@ -153,6 +158,8 @@ public class CashierService { CashierSinglePayParam cashierSinglePayParam = new CashierSinglePayParam() .setPayChannel(PayChannelEnum.WECHAT.getCode()) .setPayWay(PayWayEnum.JSAPI.getCode()) + .setMchCode(mchCode) + .setMchAppCode(mchAppCode) .setTitle(aggregatePayInfo.getTitle()) .setAmount(aggregatePayInfo.getAmount()) .setOpenId(openId) @@ -188,6 +195,8 @@ public class CashierService { } // 发起支付 PayParam payParam = new PayParam().setTitle(param.getTitle()) + .setMchCode(param.getMchCode()) + .setMchAppCode(param.getMchAppCode()) .setBusinessId(param.getBusinessId()) .setPayWayList(param.getPayWayList()); PayResult payResult = payService.pay(payParam); diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/channel/wechat/service/WeChatPayCancelService.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/channel/wechat/service/WeChatPayCancelService.java index 0a7a3741..a854e2d5 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/channel/wechat/service/WeChatPayCancelService.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/channel/wechat/service/WeChatPayCancelService.java @@ -23,8 +23,11 @@ import org.springframework.stereotype.Service; import java.io.ByteArrayInputStream; import java.math.BigDecimal; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import static cn.bootx.platform.daxpay.code.pay.PayStatusCode.REFUND_PROCESS_FAIL; + /** * 微信支付关闭和退款 * @@ -76,6 +79,12 @@ public class WeChatPayCancelService { .build() .createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256); // 获取证书文件流 + if (Objects.isNull(weChatPayConfig.getP12())){ + String errorMsg = "微信p.12证书未配置,无法进行退款"; + AsyncRefundLocal.setErrorMsg(errorMsg); + AsyncRefundLocal.setErrorCode(REFUND_PROCESS_FAIL); + throw new PayFailureException(errorMsg); + } byte[] fileBytes = uploadService.getFileBytes(weChatPayConfig.getP12()); ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes); // 证书密码为 微信商户号 diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/pay/builder/PaymentBuilder.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/pay/builder/PaymentBuilder.java index c5b08784..c233fca8 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/pay/builder/PaymentBuilder.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/pay/builder/PaymentBuilder.java @@ -41,27 +41,28 @@ public class PaymentBuilder { String ip = ServletUtil.getClientIP(request); // 基础信息 payment.setBusinessId(payParam.getBusinessId()) - .setMchCode(payParam.getMchCode()) - .setMchAppCode(payment.getMchAppCode()) - .setTitle(payParam.getTitle()) - .setDescription(payParam.getDescription()); + .setMchCode(payParam.getMchCode()) + .setMchAppCode(payParam.getMchAppCode()) + .setTitle(payParam.getTitle()) + .setDescription(payParam.getDescription()); // 支付方式和状态 List payTypeInfos = buildPayTypeInfo(payParam.getPayWayList()); List refundableInfos = buildRefundableInfo(payParam.getPayWayList()); // 计算总价 BigDecimal sumAmount = payTypeInfos.stream() - .map(PayChannelInfo::getAmount) - .filter(Objects::nonNull) - .reduce(BigDecimal::add) - .orElse(BigDecimal.ZERO); + .map(PayChannelInfo::getAmount) + .filter(Objects::nonNull) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); // 支付通道信息 payment.setPayChannelInfo(payTypeInfos) - .setRefundableInfo(refundableInfos) - .setPayStatus(PayStatusCode.TRADE_PROGRESS) - .setAmount(sumAmount) - .setClientIp(ip) - .setRefundableBalance(sumAmount); + .setRefundableInfo(refundableInfos) + .setPayStatus(PayStatusCode.TRADE_PROGRESS) + .setAmount(sumAmount) + .setCombinationPayMode(payTypeInfos.size()>1) + .setClientIp(ip) + .setRefundableBalance(sumAmount); return payment; } @@ -88,16 +89,17 @@ public class PaymentBuilder { PayParam payParam = new PayParam(); // 恢复 payModeList List payWayParams = payment.getPayChannelInfo() - .stream() - .map(payTypeInfo -> new PayWayParam().setAmount(payTypeInfo.getAmount()) - .setPayChannel(payTypeInfo.getPayChannel()) - .setExtraParamsJson(payTypeInfo.getExtraParamsJson())) - .collect(Collectors.toList()); + .stream() + .map(payTypeInfo -> new PayWayParam().setAmount(payTypeInfo.getAmount()) + .setPayChannel(payTypeInfo.getPayChannel()) + .setExtraParamsJson(payTypeInfo.getExtraParamsJson())) + .collect(Collectors.toList()); payParam.setPayWayList(payWayParams) - .setBusinessId(payment.getBusinessId()) - .setTitle(payment.getTitle()) - .setTitle(payment.getTitle()) - .setDescription(payment.getDescription()); + .setBusinessId(payment.getBusinessId()) + .setTitle(payment.getTitle()) + .setMchCode(payment.getMchCode()) + .setMchAppCode(payment.getMchAppCode()) + .setDescription(payment.getDescription()); return payParam; } @@ -112,21 +114,21 @@ public class PaymentBuilder { paymentResult = new PayResult(); // 异步支付信息 paymentResult.setAsyncPayChannel(payment.getAsyncPayChannel()) - .setAsyncPayMode(payment.isAsyncPayMode()) - .setPayStatus(payment.getPayStatus()); + .setAsyncPayMode(payment.isAsyncPayMode()) + .setPayStatus(payment.getPayStatus()); List channelInfos = payment.getPayChannelInfo(); // 设置异步支付参数 List moneyPayTypeInfos = channelInfos.stream() - .filter(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getPayChannel())) - .collect(Collectors.toList()); + .filter(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getPayChannel())) + .collect(Collectors.toList()); if (!CollUtil.isEmpty(moneyPayTypeInfos)) { paymentResult.setAsyncPayInfo(AsyncPayInfoLocal.get()); } - // 清空线程变量 } finally { + // 清空线程变量 AsyncPayInfoLocal.clear(); } return paymentResult; diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/payment/entity/Payment.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/payment/entity/Payment.java index 2c57e770..b1102a0c 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/payment/entity/Payment.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/core/payment/entity/Payment.java @@ -4,7 +4,9 @@ import cn.bootx.mybatis.table.modify.annotation.DbColumn; import cn.bootx.mybatis.table.modify.annotation.DbComment; import cn.bootx.mybatis.table.modify.annotation.DbTable; import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType; +import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex; import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum; +import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlIndexType; import cn.bootx.platform.common.core.annotation.BigField; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity; @@ -41,7 +43,8 @@ import java.util.List; public class Payment extends MpBaseEntity implements EntityBaseFunction { /** 关联的业务id */ - @DbColumn(comment = "商户编码") + @DbMySqlIndex(comment = "业务Id索引",type = MySqlIndexType.UNIQUE) + @DbColumn(comment = "关联的业务id") private String businessId; /** 商户编码 */ @@ -53,30 +56,42 @@ public class Payment extends MpBaseEntity implements EntityBaseFunction payChannelInfo; /** @@ -95,21 +111,26 @@ public class Payment extends MpBaseEntity implements EntityBaseFunction refundableInfo; /** * 支付状态 * @see PayStatusCode#TRADE_PROGRESS */ + @DbColumn(comment = "支付状态") private String payStatus; /** 支付时间 */ + @DbColumn(comment = "支付时间") private LocalDateTime payTime; /** 支付终端ip */ + @DbColumn(comment = "支付终端ip") private String clientIp; /** 过期时间 */ + @DbColumn(comment = "过期时间") private LocalDateTime expiredTime; @Override diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/notify/PayNotifyRecordDto.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/notify/PayNotifyRecordDto.java index dee73197..9adb5afb 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/notify/PayNotifyRecordDto.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/notify/PayNotifyRecordDto.java @@ -28,10 +28,10 @@ public class PayNotifyRecordDto extends BaseDto implements Serializable { private String notifyInfo; @Schema(description = "支付通道") - private Integer payChannel; + private String payChannel; @Schema(description = "处理状态") - private Integer status; + private String status; @Schema(description = "提示信息") private String msg; diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/payment/PaymentDto.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/payment/PaymentDto.java index 8e76e44f..001600f9 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/payment/PaymentDto.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/payment/PaymentDto.java @@ -42,6 +42,9 @@ public class PaymentDto extends BaseDto implements Serializable { @Schema(description = "是否是异步支付") private boolean asyncPayMode; + @Schema(description = "是否是组合支付") + private boolean combinationPayMode; + /** * @see cn.bootx.platform.daxpay.code.pay.PayChannelEnum#ASYNC_TYPE_CODE */ diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/refund/RefundRecordDto.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/refund/RefundRecordDto.java index 8183875e..6343cf6f 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/refund/RefundRecordDto.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/dto/refund/RefundRecordDto.java @@ -58,7 +58,7 @@ public class RefundRecordDto extends BaseDto { * @see PayStatusCode#REFUND_PROCESS_FAIL */ @Schema(description = "退款状态") - private int refundStatus; + private String refundStatus; @Schema(description = "错误码") private String errorCode; diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/cashier/CashierCombinationPayParam.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/cashier/CashierCombinationPayParam.java index 0543349c..fb6fed4a 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/cashier/CashierCombinationPayParam.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/cashier/CashierCombinationPayParam.java @@ -19,6 +19,9 @@ import java.util.List; @Schema(title = "结算台组合支付参数") public class CashierCombinationPayParam { + @Schema(description = "商户编码") + private String mchCode; + @Schema(description = "商户应用编码") private String mchAppCode; diff --git a/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/channel/wechat/WeChatPayConfigParam.java b/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/channel/wechat/WeChatPayConfigParam.java index 407d7f59..5232d9fc 100644 --- a/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/channel/wechat/WeChatPayConfigParam.java +++ b/dax-pay/src/main/java/cn/bootx/platform/daxpay/param/channel/wechat/WeChatPayConfigParam.java @@ -1,7 +1,6 @@ package cn.bootx.platform.daxpay.param.channel.wechat; import cn.bootx.platform.common.core.annotation.QueryParam; -import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -38,12 +37,6 @@ public class WeChatPayConfigParam { @Schema(description = "微信应用appId") private String wxAppId; - /** - * @see WeChatPayCode#API_V2 - */ - // @Schema(description = "api版本") - // private String apiVersion; - @Schema(description = "商户平台「API安全」中的 APIv2 密钥") private String apiKeyV2;