diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/wzj/soopin/order/controller/OrderController.java b/ruoyi-modules/ruoyi-order/src/main/java/com/wzj/soopin/order/controller/OrderController.java index 1ac45693f..f89d2310d 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/wzj/soopin/order/controller/OrderController.java +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/wzj/soopin/order/controller/OrderController.java @@ -31,7 +31,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author zcc * @date 2022-12-01 */ -//@Tag(name ="订单接口列表") @RestController @RequestMapping("/oms/order") @Slf4j @@ -69,7 +68,11 @@ public class OrderController extends BaseController { return R.ok(util.writeExcel(convert.toVO(list), "订单列表数据")); } - @Tag(name ="获取订单表详细信息") + /** + * 获取订单详细信息 + * @param id 订单id + * @return + */ @GetMapping(value = "/{id}") public R getInfo(@PathVariable("id") Long id) { return R.ok(orderService.selectById(id)); diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/config/EasypayConfig.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/config/EasypayConfig.java index a0a6ccd82..46054f30a 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/config/EasypayConfig.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/config/EasypayConfig.java @@ -40,8 +40,13 @@ public class EasypayConfig { private String merRsaPrivateKey; /** - * 易生异步回调通知地址 + * 支付结果异步回调通知地址 */ - private String backUrl; + private String tradeBackUrl; + + /** + * 分账结果异步回调通知地址 + */ + private String separateBackUrl; } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/controller/TransEasypayController.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/controller/TransEasypayController.java index 9c65830ec..ed55d1051 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/controller/TransEasypayController.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/controller/TransEasypayController.java @@ -2,23 +2,16 @@ package com.wzj.soopin.transaction.controller; import cn.dev33.satoken.annotation.SaIgnore; import com.wzj.soopin.transaction.domain.bo.PaymentBO; +import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO; import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest; -import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; -import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; +import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import com.wzj.soopin.transaction.service.IEasypayService; -import com.wzj.soopin.transaction.service.impl.WxAuthService; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.rmi.ServerException; @@ -61,6 +54,7 @@ public class TransEasypayController { */ @Log(title = "易生支付-发起支付", businessType = BusinessType.OTHER) @PostMapping("/trade") + @SaIgnore public R trade(@RequestBody PaymentBO paymentBO) throws ServerException { EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO); return R.ok(easypayPrePayVO); @@ -73,9 +67,10 @@ public class TransEasypayController { * @return */ @GetMapping("/paymentQuery/{orderId}") - public R paymentQuery(@PathVariable("orderId") Long orderId) throws ServerException { - EasypayPaymentResultVO easypayPaymentResultVO = easypayService.paymentQuery(orderId); - return R.ok(easypayPaymentResultVO); + @SaIgnore + public R paymentQuery(@PathVariable("orderId") Long orderId) throws ServerException { + EasypayTransResultVO easypayTransResultVO = easypayService.paymentQuery(orderId); + return R.ok(easypayTransResultVO); } /** @@ -86,8 +81,43 @@ public class TransEasypayController { */ @Log(title = "易生支付-实时退款", businessType = BusinessType.OTHER) @PostMapping("/refund/{orderId}") - public R refund(@PathVariable("orderId") Long orderId) throws ServerException { + @SaIgnore + public R refund(@PathVariable("orderId") Long orderId) throws ServerException { easypayService.refund(orderId); return R.ok(); } + + /** + * 查询退款结果 + * + * @param orderId 订单id + * @return + */ + @GetMapping("/refundQuery/{orderId}") + @SaIgnore + public R refundQuery(@PathVariable("orderId") Long orderId) throws ServerException { + EasypayTransResultVO easypayTransResultVO = easypayService.refundQuery(orderId); + return R.ok(easypayTransResultVO); + } + + + /** + * 处理易生分账结果通知回调 处理 + * + * @param easyPayRequest + * @return + */ + @SaIgnore + @Log(title = "易生支付-分账结果通知回调", businessType = BusinessType.UPDATE) + @PostMapping("/separate/callback") + public Map separateCallback(@RequestBody EasyPayRequest easyPayRequest) { + easypayService.handleSeparateCallback(easyPayRequest); + HashMap map = new HashMap<>(); + map.put("code", "000000"); + map.put("msg", "Success"); + return map; + } + + + } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateApplyBO.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateApplyBO.java new file mode 100644 index 000000000..83e756aa2 --- /dev/null +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateApplyBO.java @@ -0,0 +1,56 @@ +package com.wzj.soopin.transaction.domain.bo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.List; + +/** + * 分账请求参数 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SeparateApplyBO { + + /** + * 订单id + */ + private Long orderId; + + /** + * 订单id + */ + private Long payId; + + /** + * 支付完成时间 + */ + private Date transDate; + + /** + * 分账订单总金额.单位分 + */ + private Long transSumAmt; + + /** + * 分账订单总笔数 + */ + private Long transSumCount; + + /** + * 分账总单流水号 + */ + private String separateBatchTrace; + + /** + * 分账项 + */ + private List separateItemBOList; + + +} diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateItemBO.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateItemBO.java new file mode 100644 index 000000000..a7e9a5e72 --- /dev/null +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/SeparateItemBO.java @@ -0,0 +1,64 @@ +package com.wzj.soopin.transaction.domain.bo; + +import com.wzj.soopin.transaction.enums.easypay.SepaStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 分账项 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SeparateItemBO { + + + /** + * 分账子单流水号 + */ + private String separateTrade; + + /** + * 分账收款方商户号 + */ + private String receiveMchtCode; + + /** + * 分账金额,单位分 + */ + private Integer sepaTransAmount; + + /** + * 手续费对应本金(原交易订单)金额 + * 当前分账接收方拟承担的手续费本金金额,金额和比例2选1必填。 + * 当前收款方不承担,则送0; + * 当前收款方全额承担,则送原订单金额; + * 当前收款方仅承担自己接收金额的手续费,则送分账金额。。 + */ + private Integer sepaFeeAmount; + + +// =============== 分账结果参数 ============== + + /** + * 分账单状态 + */ + private SepaStatus sepaStatus; + + /** + * 分账平台应结金额,单位分 + */ + private Long sepaPlatStlmAmount; + + /** + * 分账实际结算金额,单位分 + */ + private Long sepaStlmAmount; + + + + +} diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/easypay/trade/jsapi/req/SeparateInfo.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/easypay/trade/jsapi/req/SeparateInfo.java index 30d0974de..39faa9b67 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/easypay/trade/jsapi/req/SeparateInfo.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/bo/easypay/trade/jsapi/req/SeparateInfo.java @@ -75,10 +75,6 @@ public class SeparateInfo { * 分账比例,分账金额和分账比例2选一必填,30代表30%,仅可上送整数 */ private Long sepaRatio; - /** - * 分账金额,分账金额和分账比例2选一必填,单位分 - */ - private Long sepaTransAmount; /** * 分账订单描述 */ diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/entity/WxAuthResponse.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/entity/WxAuthResponse.java index abf75a688..016a05b3e 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/entity/WxAuthResponse.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/entity/WxAuthResponse.java @@ -1,8 +1,12 @@ package com.wzj.soopin.transaction.domain.entity; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@AllArgsConstructor +@NoArgsConstructor public class WxAuthResponse { private String openid; private String session_key; diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideDetail.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideDetail.java index e5c872514..8b0c1947f 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideDetail.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideDetail.java @@ -85,9 +85,9 @@ public class DivideDetail extends BaseEntity { @ExcelProperty(value = "状态", order = 5) private Integer status; - @Schema(description = "账户id") - @ExcelProperty(value = "账户id", order = 6) - private Long accountId; + @Schema(description = "账户code") + @ExcelProperty(value = "账户code", order = 6) + private String accountCode; /** * 账户名称 * */ diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideRule.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideRule.java index e1ac46677..c9d5db0b6 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideRule.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/DivideRule.java @@ -4,6 +4,7 @@ package com.wzj.soopin.transaction.domain.po; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.wzj.soopin.transaction.enums.DivideRuleFeeType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.dromara.common.mybatis.core.domain.BaseEntity; @@ -46,6 +47,13 @@ public class DivideRule extends BaseEntity { private Long regionId; + /** + * 手续费承担方式类型见 {@link DivideRuleFeeType} + */ + @Schema(description = "手续费承担方式") + private Integer feeType; + + /** * 状态 */ diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/PayOrder.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/PayOrder.java index 4c5ac033d..2d12c8965 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/PayOrder.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/po/PayOrder.java @@ -83,11 +83,11 @@ public class PayOrder extends BaseAudit { private String unTrace; /** - * 是否支付完成 + * 交易状态 0->待支付,1->支付中,2->已支付,3->退款中,4->已退款 */ - @TableField(value = "trans_over") - @Schema(description = "是否支付完成") - private Boolean transOver; + @TableField(value = "trans_state") + @Schema(description = "交易状态 0->待支付,1->支付中,2->已支付,3->退款中,4->已退款") + private Integer transState; @TableField(value = "del_flag") private String delFlag; diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideDetailVO.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideDetailVO.java index 64b26cfd7..e60bbcc09 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideDetailVO.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideDetailVO.java @@ -74,9 +74,9 @@ public class DivideDetailVO extends BaseEntity { private Integer type; - @Schema(description = "账户id") - @ExcelProperty(value = "账户id", order = 6) - private Long accountId; + @Schema(description = "账户code") + @ExcelProperty(value = "账户code", order = 6) + private String accountCode; /** * 账户名称 * */ diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideRuleVO.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideRuleVO.java index 805bd3418..a5e9c2cc8 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideRuleVO.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/DivideRuleVO.java @@ -2,6 +2,7 @@ package com.wzj.soopin.transaction.domain.vo; import com.alibaba.excel.annotation.ExcelProperty; +import com.wzj.soopin.transaction.enums.DivideRuleFeeType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.dromara.common.mybatis.core.domain.BaseEntity; @@ -56,6 +57,12 @@ public class DivideRuleVO extends BaseEntity { @Schema(description = "区域id") private Long regionId; + /** + * 手续费承担方式类型见 {@link DivideRuleFeeType} + */ + @Schema(description = "手续费承担方式") + private Integer feeType; + /** * 类型 */ diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayPaymentResultVO.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayTransResultVO.java similarity index 78% rename from ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayPaymentResultVO.java rename to ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayTransResultVO.java index 57d6566a7..b5b28d02f 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayPaymentResultVO.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/domain/vo/EasypayTransResultVO.java @@ -14,7 +14,7 @@ import java.math.BigDecimal; @Builder @AllArgsConstructor @NoArgsConstructor -public class EasypayPaymentResultVO { +public class EasypayTransResultVO { /** * 订单id @@ -27,9 +27,9 @@ public class EasypayPaymentResultVO { public Long payId; /** - * 支付是否完成 + * 交易状态 0->待支付,1->已支付,2->退款中,3->已退款 */ - public boolean paymentComplete; + public int transState; /** * 订单总金额 diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleDetailType.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleDetailType.java index 8bc67b573..94803d047 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleDetailType.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleDetailType.java @@ -1,5 +1,13 @@ package com.wzj.soopin.transaction.enums; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 分账规则的商户类型 + */ +@Getter +@AllArgsConstructor public enum DivideRuleDetailType { @@ -15,25 +23,14 @@ public enum DivideRuleDetailType { * 代理 */ PROXY(3, "代理"), + /** + * 达人 + */ + REFERENCE(4, "达人"),; - REFERENCE(4, "推广"),; + private final int value; - private int value; - private String desc; - - DivideRuleDetailType(int value, String desc) { - this.value = value; - this.desc = desc; - - } - - public int getValue() { - return value; - } - - public String getDesc() { - return desc; - } + private final String desc; public static DivideRuleDetailType getEnum(int value) { for (DivideRuleDetailType type : DivideRuleDetailType.values()) { diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleFeeType.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleFeeType.java index 3f3eb69ed..a94c7d3ae 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleFeeType.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/DivideRuleFeeType.java @@ -1,33 +1,44 @@ package com.wzj.soopin.transaction.enums; +import lombok.AllArgsConstructor; import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; + +import java.util.Arrays; @Getter +@AllArgsConstructor public enum DivideRuleFeeType { /** * 按比例分配 */ - RATE(1, "按比例分配"), + RATE(0, "按比例分配"), /** * 商家承担 */ - SELLER(2, "商家承担"), + SELLER(1, "商家承担"), /** * 平台承担 */ - PLATFORM(3, "平台承担"), + PLATFORM(2, "平台承担"), + /** + * 平台代理承担 + */ + PROXY(3, "代理承担"), - - PROXY(4, "平台代理承担"); - - - private String desc; + /** + * 平台代理承担 + */ + REFERENCE(4, "达人承担"); private int value; - DivideRuleFeeType(int value, String desc) { - this.value = value; - this.desc = desc; + private String desc; + + public static DivideRuleFeeType getByValue(int value) { + return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElseThrow(() -> new ServiceException("手续费分配方式" + value + "不存在")); + } + } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/TransState.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/TransState.java new file mode 100644 index 000000000..92700b9e8 --- /dev/null +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/TransState.java @@ -0,0 +1,29 @@ +package com.wzj.soopin.transaction.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付单交易状态 + */ +@Getter +@AllArgsConstructor +public enum TransState { + + PENDING(0, "待支付"), + + Payment(1, "支付中"), + + PAID(2, "已支付"), + + REFUND_PENDING(3, "退款中"), + + REFUNDED(4, "已退款"); + + + private final int code; + + private final String desc; + + +} diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/easypay/SepaStatus.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/easypay/SepaStatus.java index 84f936b6c..5dc9d5328 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/easypay/SepaStatus.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/enums/easypay/SepaStatus.java @@ -1,27 +1,42 @@ package com.wzj.soopin.transaction.enums.easypay; -import java.io.IOException; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; + +import java.util.Arrays; + /** * 分账单状态,(处理中、成功、失败、已退款) */ +@Getter +@AllArgsConstructor public enum SepaStatus { - PENDING, REFUNDED, SUCCESS; - public String toValue() { - return switch (this) { - case PENDING -> "PENDING"; - case REFUNDED -> "REFUNDED"; - case SUCCESS -> "SUCCESS"; - }; - } + SUCCESS("SUCCESS", "成功"), + FAIL("FAIL", "失败"), + PENDING("PENDING", "处理中"), + PROCESSING("PROCESSING", "处理中"), + REFUNDED("REFUNDED", "已退款"); + + + /** + * 状态code + */ + private final String value; + + /** + * 状态描述 + */ + private final String info; + + public static SepaStatus getByValue(String value) { + return Arrays.stream(SepaStatus.values()) + .filter(e -> StrUtil.equals(e.getValue(), value)) + .findFirst() + .orElseThrow(() -> new ServiceException("分账状态不存在")); - public static SepaStatus forValue(String value) throws IOException { - return switch (value) { - case "PENDING" -> PENDING; - case "REFUNDED" -> REFUNDED; - case "SUCCESS" -> SUCCESS; - default -> throw new IOException("Cannot deserialize SepaStatus"); - }; } } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideDetailMapper.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideDetailMapper.java index a9088cf7f..0e0eb429a 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideDetailMapper.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideDetailMapper.java @@ -2,6 +2,7 @@ package com.wzj.soopin.transaction.mapper; import com.wzj.soopin.transaction.domain.po.DivideDetail; import com.wzj.soopin.transaction.domain.vo.DivideDetailVO; +import org.apache.ibatis.annotations.Mapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; /** @@ -9,6 +10,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; * * @author zcc */ +@Mapper public interface DivideDetailMapper extends BaseMapperPlus { } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideMapper.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideMapper.java index d5118eda3..b938304d2 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideMapper.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/DivideMapper.java @@ -3,13 +3,20 @@ package com.wzj.soopin.transaction.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.wzj.soopin.transaction.domain.po.Divide; import com.wzj.soopin.transaction.domain.vo.DivideVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; /** * 意见反馈Mapper接口 * * @author zcc */ -public interface DivideMapper extends BaseMapper { +@Mapper +public interface DivideMapper extends BaseMapper { - DivideVO getVOById( Long divideId); + DivideVO getVOById(Long divideId); + + @Select("select * from trans_divide where order_id = #{orderId}") + Divide selectByOrderId(@Param("orderId") Long orderId); } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/PayOrderMapper.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/PayOrderMapper.java index 0b6f1edd0..211634892 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/PayOrderMapper.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/mapper/PayOrderMapper.java @@ -3,8 +3,6 @@ package com.wzj.soopin.transaction.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.wzj.soopin.transaction.domain.po.PayOrder; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; @Mapper public interface PayOrderMapper extends BaseMapper { diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/IEasypayService.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/IEasypayService.java index 00b8cbd9f..948d0d0be 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/IEasypayService.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/IEasypayService.java @@ -2,9 +2,10 @@ package com.wzj.soopin.transaction.service; import com.wzj.soopin.transaction.domain.bo.PaymentBO; +import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO; import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest; import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO; -import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; +import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import java.math.BigDecimal; @@ -24,7 +25,7 @@ public interface IEasypayService { * @param orderId * @return */ - EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException; + EasypayTransResultVO paymentQuery(Long orderId) throws ServerException; /** * 处理易生支付结果通知回调 @@ -38,6 +39,27 @@ public interface IEasypayService { */ void refund(Long orderId); + /** + * 退款查询 + * @param orderId + * @return + */ + EasypayTransResultVO refundQuery(Long orderId); + + + /** + * 发起分账 + * @param separateApplyBO + * @return + */ + SeparateApplyBO separateApply(SeparateApplyBO separateApplyBO); + + /** + * 处理易生分账结果通知回调 + * @param easyPayRequest + */ + void handleSeparateCallback(EasyPayRequest easyPayRequest); + /** * 获取易生账户 * @param memberId diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideRuleServiceImpl.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideRuleServiceImpl.java index cf719dfc8..60131917b 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideRuleServiceImpl.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideRuleServiceImpl.java @@ -1,5 +1,6 @@ package com.wzj.soopin.transaction.service.impl; +import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.wzj.soopin.transaction.convert.DivideRuleConvert; @@ -104,15 +105,9 @@ public class DivideRuleServiceImpl extends ServiceImpl divideRuleList = baseMapper.selectList(new QueryWrapper().lambda() .eq(DivideRule::getType,orderType) .eq(DivideRule::getStatus,DivideRuleStatus.ON.getCode())); - if (CollectionUtils.isEmpty(divideRuleList)) { - return null; - } - if(divideRuleList.size()>1){ - throw new ServiceException("分账规则不唯一"); - } - + Assert.notEmpty(divideRuleList, () -> new ServiceException("未找到品类的分账规则")); + Assert.isTrue(divideRuleList.size() == 1, () -> new ServiceException("品类分账规则不唯一")); DivideRule divideRule = divideRuleList.get(0); - List detailList = detailMapper.selectList(new QueryWrapper().lambda() .eq(DivideRuleDetail::getRuleId, divideRule.getId())); DivideRuleVO vo = convert.toVO(divideRule); diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideServiceImpl.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideServiceImpl.java index 7571cc21a..054833ea4 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideServiceImpl.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/DivideServiceImpl.java @@ -1,30 +1,35 @@ package com.wzj.soopin.transaction.service.impl; +import cn.hutool.core.lang.Assert; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.wzj.soopin.member.domain.po.Member; -import com.wzj.soopin.member.domain.po.MemberAccount; import com.wzj.soopin.member.service.IMemberAccountService; import com.wzj.soopin.member.service.IMemberService; import com.wzj.soopin.order.domain.entity.Order; import com.wzj.soopin.order.service.OrderItemService; import com.wzj.soopin.order.service.OrderService; -import com.wzj.soopin.order.utils.StringUtils; import com.wzj.soopin.transaction.convert.DivideConvert; import com.wzj.soopin.transaction.convert.DivideDetailConvert; import com.wzj.soopin.transaction.domain.bo.DivideBO; +import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO; +import com.wzj.soopin.transaction.domain.bo.SeparateItemBO; import com.wzj.soopin.transaction.domain.po.Divide; import com.wzj.soopin.transaction.domain.po.DivideDetail; +import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.vo.DivideRuleDetailVO; import com.wzj.soopin.transaction.domain.vo.DivideRuleVO; import com.wzj.soopin.transaction.domain.vo.DivideVO; import com.wzj.soopin.transaction.enums.DivideRuleDetailType; +import com.wzj.soopin.transaction.enums.DivideRuleFeeType; import com.wzj.soopin.transaction.enums.DivideStatus; +import com.wzj.soopin.transaction.enums.TransState; import com.wzj.soopin.transaction.mapper.DivideDetailMapper; import com.wzj.soopin.transaction.mapper.DivideMapper; import com.wzj.soopin.transaction.service.IDivideRuleService; import com.wzj.soopin.transaction.service.IDivideService; import com.wzj.soopin.transaction.service.IEasypayService; +import com.wzj.soopin.transaction.service.PayOrderService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.exception.ServiceException; @@ -36,9 +41,8 @@ import org.springframework.transaction.annotation.Transactional; import java.io.Serializable; import java.math.BigDecimal; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * 分账服务实现类 @@ -56,23 +60,17 @@ public class DivideServiceImpl extends ServiceImpl impleme private final DivideConvert divideConvert; - private final OrderService orderService; + private final PayOrderService payOrderService; + private final IDivideRuleService ruleService; - private final OrderItemService orderItemService; - - - private final ConfigService configService; - private final IEasypayService easypayService; - private final IMemberAccountService accountService; - private final ISysTenantService sysTenantService; - private final IMemberService memberService; + @Override @Transactional(rollbackFor = Exception.class) @@ -138,177 +136,270 @@ public class DivideServiceImpl extends ServiceImpl impleme return baseMapper.selectOne(new QueryWrapper().lambda().eq(Divide::getOrderSn, orderNo)); } + /** + * 订单金额分账 + * 商家先从订单总金额中分走租户中配置的比例后,剩余的按分账规则分给平台和推广等账户 + * + * @param orderId + * @return + */ @Override @Transactional(rollbackFor = Exception.class) public boolean divide(Long orderId) { //获取订单信息 Order order = orderService.getById(orderId); - - if (order == null) { - throw new ServiceException("订单不存在"); - } - if (order.getStatus() != 3) { //订单状态为已完成 - throw new ServiceException("订单状态不正确"); - } - Divide divide = this.getById(orderId); - if (divide != null) { - throw new ServiceException("已存在分账记录,无需再次分账"); - } + Assert.notNull(order, () -> new ServiceException("订单不存在")); + Assert.isTrue(order.getStatus() == 3, () -> new ServiceException("订单未支付")); + PayOrder payOrder = payOrderService.getById(order.getPayId()); + Assert.notNull(payOrder, () -> new ServiceException("支付单不存在")); + Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付")); + Assert.isFalse(this.exists(Wrappers.lambdaQuery(Divide.class).eq(Divide::getOrderId, orderId)), () -> new ServiceException("已存在分账记录,无需再次分账")); //查找分账规则 DivideRuleVO rule = ruleService.getVOByOrderType(order.getType()); - if (rule == null) { - throw new ServiceException("未找到分账规则"); - } - //开始分账 + Integer feeType = rule.getFeeType(); + DivideRuleFeeType divideRuleFeeType = DivideRuleFeeType.getByValue(feeType); + List details = rule.getDetails(); + checkRule(details, divideRuleFeeType); //根据易生支付的合同查看手续费金额 BigDecimal totalAmount = order.getTotalAmount(); - BigDecimal totalFee = new BigDecimal(0); - String feeRate = configService.getConfigValue("transaction.divide.feeRate"); - if (StringUtils.isNotBlank(feeRate)) { - totalFee = totalAmount.multiply(new BigDecimal(feeRate)).divide(new BigDecimal(1000), 2, BigDecimal.ROUND_HALF_UP); - } - BigDecimal actualMoney = totalAmount.subtract(totalFee); + //计算订单可分配金额,订单只考虑整单退,不考虑单独退,所以直接计算订单金额 //先生成主表信息 - divide = Divide.builder() + Divide divide = Divide.builder() .ruleId(rule.getId()) .orderMoney(totalAmount) - .fee(totalFee) - .actualMoney(actualMoney) +// .fee(totalFee) +// .actualMoney(actualMoney) .orderSn(order.getOrderSn()) .status(DivideStatus.PENDING.getCode()) .build(); super.save(divide); - - //计算商户的手续费 + List detailList = new ArrayList<>(); + List separateItemBOList = new ArrayList<>(); + // 从租户信息得到商户的分账比例,先分给商户 SysTenantVo tenant = sysTenantService.queryById(order.getTenantId()); - if (tenant == null) { - throw new ServiceException("租户信息不存在"); - } - BigDecimal sellerFee = new BigDecimal(0); - BigDecimal platformFee = new BigDecimal(0); - if (tenant.getBearFeeFlag() == 1) { - //如果承担手续费,计算手续费 - sellerFee = totalFee; - } else { - platformFee = totalFee; - } + DivideDetail sellerDivideDetail = divideSeller(totalAmount, divide.getId(), tenant, divideRuleFeeType, detailList, separateItemBOList); + detailList.add(sellerDivideDetail); + // 商户分完后分给代理和达人,最后剩余都给平台 + rule.getDetails().stream() + .filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue()) + .findFirst() + .ifPresent(ruleDetailVO -> { + DivideDetail proxyDivideDetail = divideProxy(totalAmount, ruleDetailVO, divide.getId(), order.getTenantId(),divideRuleFeeType, detailList, separateItemBOList); + }); + rule.getDetails().stream() + .filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue()) + .findFirst() + .ifPresent(ruleDetailVO -> { + DivideDetail divideReference = divideReference(totalAmount, ruleDetailVO, divide.getId(), order.getMemberId(),divideRuleFeeType, detailList, separateItemBOList); + detailList.add(divideReference); + }); - //生成商户的分账明细 - BigDecimal sellerAmount = divideSeller(actualMoney, sellerFee, divide.getId(), tenant); - - - BigDecimal platformAmount = actualMoney.subtract(sellerAmount); - BigDecimal finalAmount = new BigDecimal(0); - //按规则进行分账 - //先分代理 然后达人 最后的剩余都给平台 - DivideRuleDetailVO divideRuleDetail = rule.getDetails().stream().filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue()).findFirst().orElse(null); - - if ((divideRuleDetail != null)) { - finalAmount = platformAmount.subtract(divideproxy(platformAmount, divideRuleDetail, divide.getId(), order.getTenantId())); - } - divideRuleDetail = rule.getDetails().stream().filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue()).findFirst().orElse(null); - - if ((divideRuleDetail != null)) { - finalAmount = finalAmount.subtract(divideDarren(platformAmount, divideRuleDetail, divide.getId(), order.getMemberId())); - } - dividePlatform(finalAmount, platformFee, divideRuleDetail, divide.getId(), tenant.getBearFeeFlag() == 1); + rule.getDetails().stream() + .filter(item -> item.getType() == DivideRuleDetailType.PLATFORM.getValue()) + .findFirst() + .ifPresent(ruleDetailVO -> { + DivideDetail dividePlatform = dividePlatform(totalAmount, ruleDetailVO, divide.getId(),divideRuleFeeType, detailList, separateItemBOList); + detailList.add(dividePlatform); + }); //同步订单状态 syncOrderStatus(order); - //同步易生支付 - syncYisheng(null); + // 调用易生支付实时分账 + SeparateApplyBO separateApplyBO = SeparateApplyBO.builder() + .orderId(order.getId()) + .payId(order.getPayId()) + .transDate(payOrder.getEndTransDate()) + .transSumAmt(payOrder.getTransAmount()) + .transSumCount((long) separateItemBOList.size()) + .separateBatchTrace(String.valueOf(divide.getId())) + .separateItemBOList(separateItemBOList) + .build(); + easypayService.separateApply(separateApplyBO); return true; } - private BigDecimal divideSeller(BigDecimal actualMoney, BigDecimal sellerFee, Long divideId, SysTenantVo tenant) { - //获取商户的账户信息 - Map account = getSellerAccount(tenant.getId()); - if (account == null) { - log.error("商户信息不存在,不参与分账"); + /** + * 校验分账规则详情和规则中的手续费承担类型是否一致 + * + * @param details + * @param divideRuleFeeType + */ + private void checkRule(List details, DivideRuleFeeType divideRuleFeeType) { + if (DivideRuleFeeType.SELLER == divideRuleFeeType) { + long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.SELLER.getValue()).count(); + Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置商户承担手续费,但未配置商户分账规则详情")); } + if (DivideRuleFeeType.PLATFORM == divideRuleFeeType) { + long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.PLATFORM.getValue()).count(); + Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置平台承担手续费,但未配置平台分账规则详情")); + } + if (DivideRuleFeeType.PROXY == divideRuleFeeType) { + long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue()).count(); + Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置代理承担手续费,但未配置代理分账规则详情")); + } + if (DivideRuleFeeType.REFERENCE == divideRuleFeeType) { + long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue()).count(); + Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置达人承担手续费,但未配置达人分账规则详情")); + } + + } + + /** + * 商户分账 + * + * @param totalAmount 总金额 + * @param divideId 分账总表id + * @param divideRuleFeeType + * @param tenant 商户所属租户 + * @param detailList + * @param separateItemBOList + * @return + */ + private DivideDetail divideSeller(BigDecimal totalAmount, Long divideId, SysTenantVo tenant, + DivideRuleFeeType divideRuleFeeType, List detailList, + List separateItemBOList) { + //获取商户的账户信息 + String mchtCode = getMchtCode(tenant.getId()); //计算商户的分账金额 BigDecimal divideRate = tenant.getDivideRate(); - Integer bearFeeFlag = tenant.getBearFeeFlag(); - //计算商户的分账金额 - BigDecimal sellerAmount = actualMoney.multiply(divideRate).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); - + BigDecimal sellerAmount = totalAmount.multiply(divideRate).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); //生成商户的分账明细 DivideDetail sellerDetail = DivideDetail.builder() .divideId(divideId) + .accountCode(mchtCode) .money(sellerAmount) - .fee(sellerFee) - .feePercent(bearFeeFlag == 1 ? new BigDecimal(100) : new BigDecimal(0)) .moneyPercent(divideRate) .type(DivideRuleDetailType.SELLER.getValue()).build(); - detailMapper.insert(sellerDetail); - - return sellerAmount; + detailList.add(sellerDetail); + Integer sepaFeeAmount = 0; + if (divideRuleFeeType == DivideRuleFeeType.SELLER) { + sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } else if (divideRuleFeeType == DivideRuleFeeType.RATE) { + sepaFeeAmount = sellerAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } + separateItemBOList.add(SeparateItemBO.builder() + .separateTrade(String.valueOf(sellerDetail.getId())) + .receiveMchtCode(mchtCode) + .sepaTransAmount(sellerAmount.multiply(BigDecimal.valueOf(100)).intValue()) + .sepaFeeAmount(sepaFeeAmount) + .build()); + return sellerDetail; } - private BigDecimal divideproxy(BigDecimal platformMoney, DivideRuleDetailVO rule, Long divideId, Long tenantId) { + /** + * 代理商分账 + * + * @param totalAmount 总金额 + * @param rule 代理商的分账规则详情 + * @param divideId 分账总表id + * @param tenantId 代理商所属租户id + * @param divideRuleFeeType + * @param detailList + * @param separateItemBOList + * @return + */ + private DivideDetail divideProxy(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, Long tenantId, DivideRuleFeeType divideRuleFeeType, List detailList, List separateItemBOList) { //获取代理人的账户信息 - Map account = getProxyAccount(tenantId); - if (account == null) { - log.error("代理人信息不存在,不参与分账"); - return new BigDecimal(0); - } + String mchtCode = getMchtCode(tenantId); //计算代理人的分账金额 - BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); + BigDecimal proxyAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); DivideDetail proxyDetail = DivideDetail.builder() .divideId(divideId) + .accountCode(mchtCode) .money(proxyAmount) - .fee(new BigDecimal(0)) - .feePercent(new BigDecimal(0)) .moneyPercent(rule.getMoneyPercent()) .type(DivideRuleDetailType.PROXY.getValue()) .build(); detailMapper.insert(proxyDetail); - return proxyAmount; + detailList.add(proxyDetail); + Integer sepaFeeAmount = 0; + if (divideRuleFeeType == DivideRuleFeeType.PROXY) { + sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } else if (divideRuleFeeType == DivideRuleFeeType.RATE) { + sepaFeeAmount = proxyAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } + separateItemBOList.add(SeparateItemBO.builder() + .separateTrade(String.valueOf(proxyDetail.getId())) + .receiveMchtCode(mchtCode) + .sepaTransAmount(proxyAmount.multiply(BigDecimal.valueOf(100)).intValue()) + .sepaFeeAmount(sepaFeeAmount) + .build()); + return proxyDetail; } - private BigDecimal divideDarren(BigDecimal platformMoney, DivideRuleDetailVO rule, Long divideId, Long tenantId) { + /** + * 达人分账 + * + * @param totalAmount + * @param rule + * @param divideId + * @param tenantId + * @param divideRuleFeeType + * @param detailList + * @param separateItemBOList + * @return + */ + private DivideDetail divideReference(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, Long tenantId, DivideRuleFeeType divideRuleFeeType, List detailList, List separateItemBOList) { //获取达人的账户信息 - Map account = getReferenceAccount(tenantId); - if (account == null) { - log.error("达人信息不存在,不参与分账"); - return new BigDecimal(0); - } + String mchtCode = getMchtCode(tenantId); //计算达人的分账金额 - BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); - DivideDetail proxyDetail = DivideDetail.builder() + BigDecimal proxyAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); + DivideDetail referenceDetail = DivideDetail.builder() .divideId(divideId) + .accountCode(mchtCode) .money(proxyAmount) - .fee(new BigDecimal(0)) - .feePercent(new BigDecimal(0)) .moneyPercent(rule.getMoneyPercent()) .type(DivideRuleDetailType.REFERENCE.getValue()) .build(); - detailMapper.insert(proxyDetail); - return proxyAmount; + detailMapper.insert(referenceDetail); + detailList.add(referenceDetail); + Integer sepaFeeAmount = 0; + if (divideRuleFeeType == DivideRuleFeeType.REFERENCE) { + sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } else if (divideRuleFeeType == DivideRuleFeeType.RATE) { + sepaFeeAmount = proxyAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } + separateItemBOList.add(SeparateItemBO.builder() + .separateTrade(String.valueOf(referenceDetail.getId())) + .receiveMchtCode(mchtCode) + .sepaTransAmount(proxyAmount.multiply(BigDecimal.valueOf(100)).intValue()) + .sepaFeeAmount(sepaFeeAmount) + .build()); + return referenceDetail; } - private void dividePlatform(BigDecimal platformMoney, BigDecimal fee, DivideRuleDetailVO rule, Long divideId, boolean feeFlag) { + private DivideDetail dividePlatform(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, DivideRuleFeeType divideRuleFeeType, List detailList, List separateItemBOList) { //获取平台的账户信息 - Map account = getPlatformAccount(); - if (account == null) { - log.error("平台信息不存在,不参与分账"); - } + String mchtCode = getMchtCode(1L); //计算平台的分账金额 - BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); - DivideDetail proxyDetail = DivideDetail.builder() + BigDecimal platformAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); + DivideDetail platformDetail = DivideDetail.builder() .divideId(divideId) - .money(proxyAmount) - .fee(fee) - .feePercent(feeFlag ? new BigDecimal(100) : new BigDecimal(0)) + .accountCode(mchtCode) + .money(platformAmount) .moneyPercent(rule.getMoneyPercent()) - .type(DivideRuleDetailType.REFERENCE.getValue()) + .type(DivideRuleDetailType.PLATFORM.getValue()) .build(); - detailMapper.insert(proxyDetail); + detailMapper.insert(platformDetail); + detailList.add(platformDetail); + Integer sepaFeeAmount = 0; + if (divideRuleFeeType == DivideRuleFeeType.PLATFORM) { + sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } else if (divideRuleFeeType == DivideRuleFeeType.RATE) { + sepaFeeAmount = platformAmount.multiply(BigDecimal.valueOf(100)).intValue(); + } + separateItemBOList.add(SeparateItemBO.builder() + .separateTrade(String.valueOf(platformDetail.getId())) + .receiveMchtCode(mchtCode) + .sepaTransAmount(platformAmount.multiply(BigDecimal.valueOf(100)).intValue()) + .sepaFeeAmount(sepaFeeAmount) + .build()); + return platformDetail; } @Override @@ -327,84 +418,22 @@ public class DivideServiceImpl extends ServiceImpl impleme } - private Map getPlatformAccount() { - return new HashMap<>() {{ - put("account", "123456"); - put("name", "平台"); - }}; - } - /** - * 获取达人的账户信息 + * 获取平台或商家或推广或达人在易生侧的商户号 * - * @param memberId + * @param tenantId * @return */ - private Map getReferenceAccount(Long memberId) { - Member member = memberService.getById(memberId); - if (member == null) { - return null; - } - //推广人 - Long inviteUserId = member.getSpreadUid(); - if (inviteUserId == null) { - return null; - } - MemberAccount account = accountService.getMemberAccount(inviteUserId); - if (account == null) { - return null; - } - return new HashMap<>() {{ - put("accountId", account.getId() + ""); - }}; - } - - /** - * 获取代理的账户信息 - * - * @param - * @return - */ - private Map getProxyAccount(Long memberId) { - if (memberId != null) { - return null; - } - MemberAccount account = accountService.getMemberAccount(memberId); - - if (account == null) { - return null; - } - return new HashMap<>() {{ - put("accountId", account.getId() + ""); - }}; - } - - /** - * 获取商家的账户信息 - * - * @param sellerId - * @return - */ - private Map getSellerAccount(Long sellerId) { - - SysTenantVo tenant = sysTenantService.queryById(sellerId); - if (tenant != null) { - return null; - } - MemberAccount account = accountService.getMemberAccount(tenant.getId()); - -// if (account == null) -// -// return null; -// } - return new HashMap<>() {{ - put("accountId", account.getId() + ""); - }}; + private String getMchtCode(Long tenantId) { + SysTenantVo tenant = sysTenantService.queryById(tenantId); + Assert.notNull(tenant, () -> new ServiceException("商户信息异常")); + // todo 需要从租户信息中获得商家在易生侧的商户号 + String mchtCode = ""; + Assert.notBlank(mchtCode, () -> new ServiceException("未维护商户在易生侧的商户号信息")); + return mchtCode; } private void syncOrderStatus(Order order) { } - private void syncYisheng(List divideDetails) { - } } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/EasypayServiceImpl.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/EasypayServiceImpl.java index 137f4194a..c653c4046 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/EasypayServiceImpl.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/EasypayServiceImpl.java @@ -18,17 +18,21 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.tencentcloudapi.dlc.v20210125.models.Asset; import com.wzj.soopin.order.domain.entity.Order; import com.wzj.soopin.order.emum.OrderStatusEnum; import com.wzj.soopin.order.mapper.OrderMapper; import com.wzj.soopin.transaction.config.EasypayConfig; import com.wzj.soopin.transaction.config.WechatMiniProgramConfig; import com.wzj.soopin.transaction.domain.bo.PaymentBO; +import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO; import com.wzj.soopin.transaction.domain.bo.easypay.*; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundApplyReqBody; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundReqOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.resp.RefundApplyRespBody; +import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateApplyReqBody; +import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateReqOrderInfo; +import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateApplyRespBody; +import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo; @@ -39,34 +43,30 @@ import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryR import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO; -import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; +import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; +import com.wzj.soopin.transaction.enums.TransState; import com.wzj.soopin.transaction.enums.easypay.DelaySettleFlag; import com.wzj.soopin.transaction.enums.easypay.PatnerSettleFlag; import com.wzj.soopin.transaction.enums.easypay.PayType; import com.wzj.soopin.transaction.enums.easypay.SplitSettleFlag; +import com.wzj.soopin.transaction.mapper.DivideDetailMapper; import com.wzj.soopin.transaction.mapper.DivideMapper; import com.wzj.soopin.transaction.mapper.PayOrderMapper; +import com.wzj.soopin.transaction.service.IDivideService; import com.wzj.soopin.transaction.service.IEasypayService; -import com.wzj.soopin.transaction.service.PayOrderService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.FormatsType; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.ServletUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.rmi.ServerException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; +import java.util.*; import static com.wzj.soopin.transaction.constans.EasypayConstants.*; @@ -80,18 +80,17 @@ import static com.wzj.soopin.transaction.constans.EasypayConstants.*; @Slf4j public class EasypayServiceImpl implements IEasypayService { - private EasypayConfig easypayConfig; + private final EasypayConfig easypayConfig; - private WechatMiniProgramConfig wechatMiniProgramConfig; + private final WechatMiniProgramConfig wechatMiniProgramConfig; - private WxAuthService wxAuthService; + private final WxAuthService wxAuthService; private final OrderMapper orderMapper; - private final DivideMapper divideMapper; - private final PayOrderMapper payOrderMapper; + /** * 生成易生接口请求头参数 * @@ -165,7 +164,7 @@ public class EasypayServiceImpl implements IEasypayService { */ private void verify(Object header, Object body, String signStr) { TreeMap hearMap = new TreeMap<>(JSONObject.from(header)); - TreeMapbodyMap = new TreeMap<>(JSONObject.from(body)); + TreeMap bodyMap = new TreeMap<>(JSONObject.from(body)); // 递归排序 sortMap(hearMap); sortMap(bodyMap); @@ -211,27 +210,32 @@ public class EasypayServiceImpl implements IEasypayService { .reqBody(apiReqBody) .reqSign(reqSign) .build(); - log.debug("调用易生发起支付接口请求:{}",JSONObject.toJSONString(easyPayRequest)); + log.debug("调用易生发起支付接口请求:{}", JSONObject.toJSONString(easyPayRequest)); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/jsapi").toString(); String body = HttpRequest.post(url) .timeout(3000) .body(JSON.toJSONString(easyPayRequest)) .execute() .body(); - log.debug("调用易生发起支付接口响应:{}",body); + log.debug("调用易生发起支付接口响应:{}", body); EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); JsApiRespBody jsApiRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), JsApiRespBody.class); RespStateInfo respStateInfo = jsApiRespBody.getRespStateInfo(); if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { - if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK,RSP_BODY_TRANS_PARTIALLY_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PROCESSING)) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_PARTIALLY_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PROCESSING)) { //保存支付单信息到数据库 JsapiRespOrderInfo respOrderInfo = jsApiRespBody.getRespOrderInfo(); payOrder.setPayType(paymentBO.getPayType().getValue()); payOrder.setStartTransDate(new Date()); payOrder.setEasypayTrace(respOrderInfo.getOutTrace()); + payOrder.setTransState(TransState.Payment.getCode()); payOrderMapper.updateById(payOrder); + //关联订单的最新支付单 + orderMapper.updateById(Order.builder().id(payOrder.getOrderId()).payId(payOrder.getId()).build()); + // 产生待分账记录 + generateDivideRecord(payOrder); // 生成返回前端的预支付信息 return generatePrePayVO(paymentBO.getPayType(), jsApiRespBody); } else { @@ -248,6 +252,14 @@ public class EasypayServiceImpl implements IEasypayService { } } + /** + * 生成待分账记录 + * @param payOrder + */ + private void generateDivideRecord(PayOrder payOrder) { + //TODO 待完善 + } + /** * 根据支付类型校验请求参数是否完整 * @@ -255,11 +267,14 @@ public class EasypayServiceImpl implements IEasypayService { */ private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException { switch (paymentBO.getPayType()) { - case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId")); - case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code")); - case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(), - paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(), - paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数")); + case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> + Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId")); + case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> + Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code")); + case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> + Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(), + paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(), + paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数")); default -> throw new ServerException("不支持的支付方式"); } } @@ -272,13 +287,14 @@ public class EasypayServiceImpl implements IEasypayService { */ private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException { switch (paymentBO.getPayType()) { - case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build()); + case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> + apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build()); case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> { WxAuthResponse wxAuthResponse = wxAuthService.getOpenIdByMiniProgramCode(paymentBO.getWxLoginCode()); - Assert.isTrue(StrUtil.equals(wxAuthResponse.getErrcode(), "0"), () -> new ServiceException("微信小程序登录异常")); apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(wxAuthResponse.getOpenid()).build()); } - case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class)); + case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> + apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class)); default -> throw new ServerException("不支持的支付方式"); } PayInfo payInfo = PayInfo.builder() @@ -297,27 +313,27 @@ public class EasypayServiceImpl implements IEasypayService { Order order = orderMapper.selectById(paymentBO.getOrderId()); Assert.notNull(order, () -> new ServiceException("订单不存在")); Assert.isTrue(Objects.equals(order.getStatus(), OrderStatusEnum.UNPAID.getValue()), () -> new ServiceException("订单已支付")); - PayOrder payOrder; - // 订单首次支付,创建支付订单记录 - if(order.getPayId() == null){ - payOrder = PayOrder.builder() - .orderId(order.getId()) - .transAmount(apiReqBody.getReqOrderInfo().getTransAmount()) - .startTransDate(new Date()) - .transOver(false) - .build(); - payOrderMapper.insert(payOrder); - orderMapper.updateById(Order.builder().id(order.getId()).payId(payOrder.getId()).build()); - }else { - payOrder = payOrderMapper.selectById(order.getPayId()); - Assert.notNull(payOrder, () -> new ServiceException("订单异常")); - } // 订单中的金额单位为元,需转换为分 long transAmount = order.getTotalAmount().multiply(BigDecimal.valueOf(100)).longValue(); + PayOrder payOrder; + // 订单已拉起过支付,支付中或已支付状态不可再次支付 + if (order.getPayId() != null) { + payOrder = payOrderMapper.selectById(order.getPayId()); + Assert.notNull(payOrder, () -> new ServiceException("订单异常")); + Assert.isTrue(Objects.equals(payOrder.getTransState(), TransState.PENDING.getCode()), () -> new ServiceException("订单支付中或已支付")); + } + payOrder = PayOrder.builder() + .orderId(order.getId()) + .transAmount(transAmount) + .startTransDate(new Date()) + .transState(TransState.PENDING.getCode()) + .payType(paymentBO.getPayType().getValue()) + .build(); + payOrderMapper.insert(payOrder); JsapiReqOrderInfo jsapiReqOrderInfo = JsapiReqOrderInfo.builder() .orgTrace(String.valueOf(payOrder.getId())) .transAmount(transAmount) - .backUrl(easypayConfig.getBackUrl()) + .backUrl(easypayConfig.getTradeBackUrl()) .timeout(String.valueOf(TRACE_TIMEOUT)) .orderSub("无终街支付订单") .orderDes("无终街支付订单:" + order.getMerchantNote()) @@ -357,23 +373,22 @@ public class EasypayServiceImpl implements IEasypayService { /** * 查询支付结果 + * * @param orderId * @return * @throws ServerException */ @Override - public EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException { + public EasypayTransResultVO paymentQuery(Long orderId) throws ServerException { Order order = orderMapper.selectById(orderId); Assert.notNull(order, () -> new ServiceException("订单不存在")); - PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class) - .eq(PayOrder::getOrderId, orderId) - .last("limit 1")); + PayOrder payOrder = payOrderMapper.selectById(order.getPayId()); Assert.notNull(payOrder, () -> new ServiceException("订单不存在")); - EasypayPaymentResultVO paymentResultVO = EasypayPaymentResultVO.builder() + EasypayTransResultVO paymentResultVO = EasypayTransResultVO.builder() .orderId(order.getId()) .payId(order.getPayId()) .totalAmount(order.getTotalAmount()) - .paymentComplete(false) + .transState(TransState.PENDING.getCode()) .build(); EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); TradeQueryReqBody queryReqBody = TradeQueryReqBody.builder() @@ -382,7 +397,7 @@ public class EasypayServiceImpl implements IEasypayService { .reqOrderInfo(TradeQueryReqBody.ReqOrderInfo.builder() .orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString()) .oriOrgTrace(String.valueOf(order.getPayId())) - .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD,payOrder.getStartTransDate())) + .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getStartTransDate())) .build()) .build(); String reqSign = getSignStr(reqHeader, queryReqBody); @@ -391,34 +406,34 @@ public class EasypayServiceImpl implements IEasypayService { .reqBody(queryReqBody) .reqSign(reqSign) .build(); - log.debug("调用易生查询支付接口请求:{}",JSONObject.toJSONString(easyRequest)); + log.debug("调用易生查询支付接口请求:{}", JSONObject.toJSONString(easyRequest)); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/tradeQuery").toString(); String body = HttpRequest.post(url) .timeout(3000) .body(JSON.toJSONString(easyRequest)) .execute() .body(); - log.debug("调用易生查询支付接口响应:{}",body); + log.debug("调用易生查询支付接口响应:{}", body); EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { - verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(),easyPayResponse.getRspSign()); + verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); TradeQueryRespBody tradeQueryRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), TradeQueryRespBody.class); RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo(); if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { // 支付完成 进入终态 - if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT,RSP_BODY_TRANS_PARTIALLY_OK)) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) { TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo(); // 更新支付订单信息到数据库 PayOrder payOrderUpdate = PayOrder.builder() .id(payOrder.getId()) - .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()),toString())) + .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()), toString())) .easypayTrace(respOrderInfo.getOutTrace()) .pcTrace(respOrderInfo.getPcTrace()) .unTrace(respOrderInfo.getUnTrace()) - .transOver(true) + .transState(TransState.PAID.getCode()) .build(); payOrderMapper.updateById(payOrderUpdate); - paymentResultVO.setPaymentComplete(true); + paymentResultVO.setTransState(TransState.PAID.getCode()); } } else { log.error("查询支付结果失败:{}", respStateInfo.getRespDesc()); @@ -434,25 +449,26 @@ public class EasypayServiceImpl implements IEasypayService { @Override @Transactional(rollbackFor = Exception.class) public void handleTradeCallback(EasyPayRequest easyPayRequest) { - verify(easyPayRequest.getReqHeader(),easyPayRequest.getReqBody(), easyPayRequest.getReqSign()); + log.debug("易生支付结果通知回调:{}", JSONObject.toJSONString(easyPayRequest)); + verify(easyPayRequest.getReqHeader(), easyPayRequest.getReqBody(), easyPayRequest.getReqSign()); TradeQueryRespBody tradeQueryRespBody = BeanUtil.toBean(easyPayRequest.getReqBody(), TradeQueryRespBody.class); RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo(); if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { // 支付完成 进入终态 - if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT,RSP_BODY_TRANS_PARTIALLY_OK)) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) { TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo(); PayOrder payOrder = payOrderMapper.selectById(String.valueOf(respOrderInfo.getOrgTrace())); - if(payOrder != null){ + if (payOrder != null) { Order order = orderMapper.selectById(payOrder.getOrderId()); - if(order != null){ + if (order != null) { // 更新支付订单信息 payOrderMapper.updateById(PayOrder.builder() .id(payOrder.getId()) - .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()),toString())) + .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()), toString())) .easypayTrace(respOrderInfo.getOutTrace()) .pcTrace(respOrderInfo.getPcTrace()) .unTrace(respOrderInfo.getUnTrace()) - .transOver(true) + .transState(TransState.PAID.getCode()) .build()); // 更新订单信息 orderMapper.updateById(Order.builder() @@ -461,10 +477,10 @@ public class EasypayServiceImpl implements IEasypayService { .paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss")) .payType(PayType.getByValue(payOrder.getPayType()).getChannel()) .build()); - }else{ + } else { log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace()); } - }else{ + } else { log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace()); } } @@ -474,11 +490,18 @@ public class EasypayServiceImpl implements IEasypayService { } @Override + @Transactional(rollbackFor = Exception.class) public void refund(Long orderId) { Order order = orderMapper.selectById(orderId); Assert.notNull(order, () -> new ServiceException("订单不存在")); - PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class).eq(PayOrder::getOrderId, orderId).last("limit 1")); + PayOrder payOrder = payOrderMapper.selectById(order.getPayId()); Assert.notNull(payOrder, () -> new ServiceException("订单不存在")); + Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付,不可发起退款")); + // 更新支付订单状态为退款中 + payOrderMapper.updateById(PayOrder.builder() + .id(payOrder.getId()) + .transState(TransState.REFUND_PENDING.getCode()) + .build()); EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder() .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build()) @@ -486,7 +509,7 @@ public class EasypayServiceImpl implements IEasypayService { .reqOrderInfo(RefundReqOrderInfo.builder() .orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString()) .oriOrgTrace(String.valueOf(order.getPayId())) - .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD,payOrder.getStartTransDate())) + .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate())) .refundAmount(payOrder.getTransAmount()) .build()) .build(); @@ -496,15 +519,165 @@ public class EasypayServiceImpl implements IEasypayService { .reqBody(refundApplyReqBody) .reqSign(reqSign) .build(); - log.debug("调用易生实时退款接口请求:{}",JSONObject.toJSONString(easyRequest)); + log.debug("调用易生实时退款接口请求:{}", JSONObject.toJSONString(easyRequest)); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/apply").toString(); String body = HttpRequest.post(url) .timeout(3000) .body(JSON.toJSONString(easyRequest)) .execute() .body(); - log.debug("调用易实时退款接口响应:{}",body); - RefundApplyRespBody refundApplyRespBody = JSONObject.parseObject(body, RefundApplyRespBody.class); + log.debug("调用易实时退款接口响应:{}", body); + EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); + if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { + verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); + RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class); + RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo(); + if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) { + // 更新支付单退款状态 + payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).build()); + } + } else { + log.error("易生退款失败:{}", respStateInfo.getRespDesc()); + payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build()); + throw new ServiceException("易生退款失败:" + respStateInfo.getRespDesc()); + } + } else { + log.error("易生退款通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo()); + payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build()); + throw new ServiceException("易生退款通讯失败:" + easyPayResponse.getRspHeader().getRspInfo()); + } + } + + @Override + public EasypayTransResultVO refundQuery(Long orderId) { + Order order = orderMapper.selectById(orderId); + Assert.notNull(order, () -> new ServiceException("订单不存在")); + PayOrder payOrder = payOrderMapper.selectById(order.getPayId()); + Assert.notNull(payOrder, () -> new ServiceException("订单不存在")); + EasypayTransResultVO easypayTransResultVO = EasypayTransResultVO.builder() + .orderId(order.getId()) + .payId(payOrder.getId()) + .totalAmount(order.getTotalAmount()) + .transState(payOrder.getTransState()) + .build(); + if (payOrder.getTransState() == TransState.REFUNDED.getCode()) { + return easypayTransResultVO; + } + Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode() || payOrder.getTransState() == TransState.REFUND_PENDING.getCode(), () -> new ServiceException("订单未支付")); + EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); + RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder() + .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build()) + .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build()) + .reqOrderInfo(RefundReqOrderInfo.builder() + .orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString()) + .oriOrgTrace(String.valueOf(order.getPayId())) + .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate())) + .refundAmount(payOrder.getTransAmount()) + .build()) + .build(); + String reqSign = getSignStr(reqHeader, refundApplyReqBody); + EasyPayRequest easyRequest = EasyPayRequest.builder() + .reqHeader(reqHeader) + .reqBody(refundApplyReqBody) + .reqSign(reqSign) + .build(); + log.debug("调用易生实时退款接口请求:{}", JSONObject.toJSONString(easyRequest)); + String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/query").toString(); + String body = HttpRequest.post(url) + .timeout(3000) + .body(JSON.toJSONString(easyRequest)) + .execute() + .body(); + log.debug("调用易实时退款接口响应:{}", body); + EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); + if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { + verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); + RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class); + RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo(); + if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) { + easypayTransResultVO.setTransState(TransState.REFUNDED.getCode()); + // 更新支付单退款状态 + payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).build()); + } + } else { + log.error("查询支付结果失败:{}", respStateInfo.getRespDesc()); + throw new ServiceException("查询支付结果失败:" + respStateInfo.getRespDesc()); + } + } else { + log.error("查询支付结果通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo()); + throw new ServiceException("查询支付结果通讯失败:" + easyPayResponse.getRspHeader().getRspInfo()); + } + return easypayTransResultVO; + } + + @Override + public SeparateApplyBO separateApply(SeparateApplyBO separateApplyBO) { + EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); + List separateOrderDetailListList = separateApplyBO.getSeparateItemBOList().stream().map(item -> BeanUtil.copyProperties(item, SeparateInfo.SeparateOrderDetailList.class)).toList(); + SeparateApplyReqBody separateApplyReqBody = SeparateApplyReqBody.builder() + .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build()) + .payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build()) + .reqOrderInfo(SeparateReqOrderInfo.builder() + .backUrl(easypayConfig.getSeparateBackUrl()) + .oriOrgTrace(String.valueOf(separateApplyBO.getPayId())) + .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, separateApplyBO.getTransDate())) + .separateBatchTrace(separateApplyBO.getSeparateBatchTrace()) + .transSumAmt(separateApplyBO.getTransSumAmt()) + .transSumCount(separateApplyBO.getSeparateItemBOList().size()) + .separateOrderDetailList(separateOrderDetailListList) + .build()) + .build(); + String reqSign = getSignStr(reqHeader, separateApplyReqBody); + EasyPayRequest easyRequest = EasyPayRequest.builder() + .reqHeader(reqHeader) + .reqBody(separateApplyReqBody) + .reqSign(reqSign) + .build(); + log.debug("调用易生请求分账接口请求:{}", JSONObject.toJSONString(easyRequest)); + String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/orderApply").toString(); + String body = HttpRequest.post(url) + .timeout(3000) + .body(JSON.toJSONString(easyRequest)) + .execute() + .body(); + log.debug("调用易生请求分账接口响应:{}", body); + EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); + if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { + verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); + SeparateApplyRespBody separateApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateApplyRespBody.class); + RespStateInfo respStateInfo = separateApplyRespBody.getRespStateInfo(); + if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { + if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) { + // 更新分账状态 + SeparateRespOrderInfo respOrderInfo = separateApplyRespBody.getRespOrderInfo(); + handleSeparateInfo(separateApplyBO, respOrderInfo); + } + } else { + log.error("请求分账失败:{}", respStateInfo.getRespDesc()); + throw new ServiceException("请求分账失败:" + respStateInfo.getRespDesc()); + } + } else { + log.error("易生请求分账通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo()); + throw new ServiceException("易生请求分账通讯失败:" + easyPayResponse.getRspHeader().getRspInfo()); + } + return separateApplyBO; + } + + @Override + public void handleSeparateCallback(EasyPayRequest easyPayRequest) { + // TODO 处理回调分账结果信息,更新分账详情表 + + } + + /** + * 处理分账结果信息,更新分账详情表 + * @param respOrderInfo + */ + private SeparateApplyBO handleSeparateInfo(SeparateApplyBO separateApplyBO, SeparateRespOrderInfo respOrderInfo) { + //TODO 处理分账结果信息,更新分账详情表 + return separateApplyBO; } diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/PayOrderServiceImpl.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/PayOrderServiceImpl.java index fbc5a3254..f3b00349e 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/PayOrderServiceImpl.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/PayOrderServiceImpl.java @@ -1,8 +1,6 @@ package com.wzj.soopin.transaction.service.impl; import org.springframework.stereotype.Service; -import org.springframework.beans.factory.annotation.Autowired; -import java.util.List; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.mapper.PayOrderMapper; diff --git a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/WxAuthService.java b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/WxAuthService.java index fccff7c0b..b32da2222 100644 --- a/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/WxAuthService.java +++ b/ruoyi-modules/ruoyi-transaction/src/main/java/com/wzj/soopin/transaction/service/impl/WxAuthService.java @@ -1,10 +1,15 @@ package com.wzj.soopin.transaction.service.impl; +import cn.hutool.core.lang.Assert; import cn.hutool.core.text.StrBuilder; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson2.JSONObject; import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; import com.wzj.soopin.transaction.config.WechatMiniProgramConfig; import com.wzj.soopin.transaction.wechat.WechatPayConfig; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -55,10 +60,10 @@ public class WxAuthService { .append(code) .append("&grant_type=authorization_code") .toString(); - WxAuthResponse response = restTemplate.getForObject(url, WxAuthResponse.class); - if (response == null || response.getOpenid() == null) { - throw new RuntimeException("Failed to get openid from WeChat"); - } + String body = HttpRequest.get(url).timeout(3000).execute().body(); + Assert.notBlank(body, () -> new ServiceException("微信小程序登录异常")); + WxAuthResponse response = JSONObject.parseObject(body, WxAuthResponse.class); + Assert.notBlank(response.getOpenid(), () -> new ServiceException("微信小程序登录异常:" + response.getErrmsg())); return response; }