1、定时任务 商户进件状态查询
2、小程序下单统一支付提交
3、订单核销分账
4、订单退款
5、支付、退款回调
6、分账回调
17个文件已修改
5个文件已添加
1021 ■■■■ 已修改文件
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/poji/shop/ShopProportionVo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/miniapp/NotifyController.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/BackMessage.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderPayment.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderRefund.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/PaymentMessage.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/ProfitSharing.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/vo/ProfitSharingNotifyNewResult.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/RefundStatusEnum.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/WxPayNotifyEventTypeEnum.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/DictBean.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/IDict.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/BackMessageService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/OrderPaymentService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/PaymentMessageService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/ProfitSharingService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/BackMessageServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/OrderPaymentServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/PaymentMessageServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/ProfitSharingServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java 408 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/order/OrderService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/poji/shop/ShopProportionVo.java
@@ -1,4 +1,31 @@
package com.ruoyi.system.api.domain.poji.shop;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class ShopProportionVo {
    /**
     * 分成id
     */
    private Long proportionId;
    /**
     * 商户id
     */
    private Long shopId;
    /**
     * 商户类型
     */
    private Integer shopType;
    /**
     * 分成比例
     */
    private BigDecimal proportionPercent;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/miniapp/NotifyController.java
@@ -1,38 +1,34 @@
package com.ruoyi.order.controller.miniapp;
import com.ruoyi.common.core.domain.R;
import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyData;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.ProfitSharingV3Service;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.auth.Verifier;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.order.domain.dto.AppSureOrderDto;
import com.ruoyi.order.domain.dto.WXPayNotifyDto;
import com.ruoyi.order.domain.vo.AppSureOrderVo;
import com.ruoyi.order.domain.vo.ProfitSharingNotifyNewResult;
import com.ruoyi.order.enums.WxPayNotifyEventTypeEnum;
import com.ruoyi.order.service.account.BackMessageService;
import com.ruoyi.order.service.order.OrderService;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.bytebuddy.asm.Advice;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.security.GeneralSecurityException;
import java.util.Map;
import java.util.Objects;
/**
 * @program: ruoyi
@@ -44,88 +40,82 @@
@RestController
@RequestMapping("/app/notify")
public class NotifyController extends BaseController {
    /** 商户号 */
    public static String merchantId = "";
    /** 商户API私钥路径 */
    public static String privateKeyPath = "";
    /** 商户证书序列号 */
    public static String merchantSerialNumber = "";
    /** 微信支付平台证书路径 */
    public static String wechatPayCertificatePath = "";
    /** 商户APIV3密钥 */
    public static String apiV3key = "...";
    public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial";
    public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature";
    public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp";
    public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce";
    private static final Gson GSON = new GsonBuilder().create();
    @Resource
    private OrderService orderService;
    @RequestMapping(value = "/payNotify", method = RequestMethod.POST)
    @ApiOperation(value = "微信支付通知")
    public Map payNotify(HttpServletRequest request, HttpServletResponse response) {
        Config config =
                new RSAConfig.Builder()
                        .merchantId(merchantId)
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .wechatPayCertificatesFromPath(wechatPayCertificatePath)
                        .build();
    @Autowired
    private WxPayService wxService;
    @Resource
    private BackMessageService backMessageService;
        PrivacyEncryptor encryptor = config.createEncryptor();
        String wechatPayCertificateSerialNumber = encryptor.getWechatpaySerial();
    @PostMapping(value = "/payNotify")
    @ApiOperation(value = "微信支付/退款通知")
    public String payNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException {
        // 获取请求头
        SignatureHeader signatureHeader = getSignatureHeader(response);
        String nonoc=request.getHeader("Wechatpay-Nonce");
        String signature=request.getHeader("Wechatpay-Signature");
        String timestamp=request.getHeader("Wechatpay-Timestamp");
        String serial=request.getHeader("Wechatpay-Serial");
        String signatureType=request.getHeader("Wechatpay-Signature-Type");
        NotifyResponse responseData = GSON.fromJson(notifyData, NotifyResponse.class);
        String eventType = responseData.getEventType();
        String requestBody="";
        try {
            BufferedReader reader = request.getReader();
            String line = "";
            StringBuffer inputString = new StringBuffer();
            while ( (line = reader.readLine()) != null ) {
                inputString.append(line);
        int resultType = 1;
        String resultMessage = "";
        if(WxPayNotifyEventTypeEnum.TRANSACTION_SUCCESS.getCode().equals(eventType)){
            PartnerTransactionsNotifyResult notifyResult = wxService.getEcommerceService().parsePartnerNotifyResult(notifyData, signatureHeader);
            PartnerTransactionsResult result = notifyResult.getResult();
            // 支付通知回调
            if("SUCCESS".equals(result.getTradeState())){
                orderService.payBack(result);
            }
            if(inputString!=null && !"".equals(inputString)) {
                requestBody = inputString.toString();
            resultMessage = GSON.toJson(notifyResult);
        } else if(WxPayNotifyEventTypeEnum.REFUND_SUCCESS.getCode().equals(eventType)
            || WxPayNotifyEventTypeEnum.REFUND_ABNORMAL.getCode().equals(eventType)
            || WxPayNotifyEventTypeEnum.REFUND_CLOSED.getCode().equals(eventType)){
            RefundNotifyResult result = wxService.getEcommerceService().parseRefundNotifyResult(notifyData, signatureHeader);
            orderService.orderRefundBack(result);
            resultType = 2;
            resultMessage = GSON.toJson(result);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        // 保存支付/退款回调信息
        backMessageService.saveBackMessage(resultType, resultMessage);
        return WxPayNotifyV3Response.success("成功");
        }
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPayCertificateSerialNumber)
                .nonce(nonoc)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody)
                .build();
    @PostMapping(value = "/profitSharingNotify")
    @ApiOperation(value = "微信分账通知")
    public String profitSharingNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException {
        ProfitSharingV3Service sharingV3Service = wxService.getProfitSharingV3Service();
        // ProfitSharingNotifyResult
        NotificationConfig notifiConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3key)
                .build();
        NotificationParser parser = new NotificationParser(notifiConfig);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        // 获取请求头
        SignatureHeader signatureHeader = getSignatureHeader(response);
        String tradeStateEnum=transaction.getTradeState().toString();
        if(tradeStateEnum.equals("success")){
            orderService.payBack(transaction);
        ProfitSharingNotifyNewResult notifyResult = getProfitSharingNotifyData(notifyData, signatureHeader);
        ProfitSharingNotifyResult result = notifyResult.getResult();
        String eventType = notifyResult.getRawData().getEventType();
        if(WxPayNotifyEventTypeEnum.PROFIT_SHARING_SUCCESS.getCode().equals(eventType)){
            // 分账通知回调
            orderService.profitSharingBack(result);
        }
        Map map=new HashMap();
        map.put("code","SUCCESS");
        map.put("message","成功");
        String resultMessage = GSON.toJson(notifyResult);
        // 保存记录分账回调信息
        backMessageService.saveBackMessage(3, resultMessage);
        return map;
        return WxPayNotifyV3Response.success("成功");
    }
    @RequestMapping(value = "/shareNotify", method = RequestMethod.POST)
@@ -135,4 +125,49 @@
        return null;
    }
    public ProfitSharingNotifyNewResult getProfitSharingNotifyData(String notifyData, SignatureHeader header) throws WxPayException {
        if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) {
            throw new WxPayException("非法请求,头部信息验证失败");
        }
        ProfitSharingNotifyData response = GSON.fromJson(notifyData, ProfitSharingNotifyData.class);
        ProfitSharingNotifyData.Resource resource = response.getResource();
        String cipherText = resource.getCipherText();
        String associatedData = resource.getAssociatedData();
        String nonce = resource.getNonce();
        String apiV3Key = this.wxService.getConfig().getApiV3Key();
        try {
            String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key);
            ProfitSharingNotifyResult profitSharingResult = GSON.fromJson(result, ProfitSharingNotifyResult.class);
            ProfitSharingNotifyNewResult notifyResult = new ProfitSharingNotifyNewResult();
            notifyResult.setRawData(response);
            notifyResult.setResult(profitSharingResult);
            return notifyResult;
        } catch (GeneralSecurityException | IOException e) {
            throw new WxPayException("解析报文异常!", e);
        }
    }
    private boolean verifyNotifySign(SignatureHeader header, String data) throws WxPayException {
        String beforeSign = String.format("%s\n%s\n%s\n",
                header.getTimeStamp(),
                header.getNonce(),
                data);
        Verifier verifier = this.wxService.getConfig().getVerifier();
        if (verifier == null) {
            throw new WxPayException("证书检验对象为空");
        }
        return verifier.verify(header.getSerialNo(),
                beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned());
    }
    private SignatureHeader getSignatureHeader(HttpServletResponse response){
        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setSerialNo(response.getHeader(WECHAT_PAY_SERIAL));
        signatureHeader.setSigned(response.getHeader(WECHAT_PAY_SIGNATURE));
        signatureHeader.setNonce(response.getHeader(WECHAT_PAY_NONCE));
        signatureHeader.setTimeStamp(response.getHeader(WECHAT_PAY_TIMESTAMP));
        return signatureHeader;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/BackMessage.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -38,7 +39,7 @@
    @TableField("del_flag")
    private String delFlag;
    /**
     * 回调类型1支付回调2退款回调
     * 回调类型 1支付回调 2退款回调 3、分账
     */
    @TableField("result_type")
    private Integer resultType;
@@ -47,6 +48,11 @@
     */
    @TableField("result_message")
    private String resultMessage;
    /**
     * 回调时间
     */
    @TableField("create_time")
    private Date createTime;
    @Override
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderPayment.java
@@ -49,6 +49,12 @@
    @TableField("shop_id")
    private Long shopId;
    /**
     * 订单二级商户号
     */
    @TableField("sub_mch_id")
    private String subMchId;
    /**
     * 订单id
     */
    @TableField("order_id")
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/OrderRefund.java
@@ -99,7 +99,21 @@
     * 回调时间
     */
    @TableField("back_time")
    private Date backTime;
    private String backTime;
    /**
     * 微信退款订单id
     */
    @TableField("wx_refund_id")
    private String wxRefundId;
    /**
     * 1、发起退款 2、已退款
     */
    @TableField("refund_status")
    private Integer refundStatus;
    @Override
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/PaymentMessage.java
@@ -47,7 +47,7 @@
     * 发起关联id
     */
    @TableField("send_id")
    private Long sendId;
    private String sendId;
    /**
     * 发送报文
     */
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/pojo/account/ProfitSharing.java
@@ -93,7 +93,15 @@
     * 完成时间
     */
    @TableField("finish_time")
    private Date finishTime;
    private String finishTime;
    /**
     * 微信分账/回退单号
     */
    @TableField("wx_order_id")
    private String wxOrderId;
    /**
     * 分账失败原因
1、ACCOUNT_ABNORMAL : 分账接收账户异常
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/domain/vo/ProfitSharingNotifyNewResult.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.order.domain.vo;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyData;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
public class ProfitSharingNotifyNewResult implements Serializable {
    private static final long serialVersionUID = -6602962275015706689L;
    /**
     * 源数据
     */
    private ProfitSharingNotifyData rawData;
    /**
     * 解密后的数据
     */
    private ProfitSharingNotifyResult result;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/RefundStatusEnum.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.order.enums;
import com.ruoyi.order.enums.dict.IDict;
public enum RefundStatusEnum implements IDict<Integer> {
    /**
     * 退款状态,枚举值:
     * SUCCESS:退款成功
     * CLOSE:退款关闭
     * ABNORMAL:退款异常
     */
    SUCCESS(2, "SUCCESS"),
    CLOSE(3, "CLOSE"),
    ABNORMAL(4, "ABNORMAL");
    RefundStatusEnum(Integer code, String text){
        init(code, text);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/WxPayNotifyEventTypeEnum.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.order.enums;
import com.ruoyi.order.enums.dict.IDict;
public enum WxPayNotifyEventTypeEnum implements IDict<String> {
    /**
     * 微信回调通知
        支付成功通知的类型为 TRANSACTION.SUCCESS
        分账 PROFITSHARING.SUCCESS
     */
    TRANSACTION_SUCCESS("TRANSACTION.SUCCESS", "TRANSACTION.SUCCESS"),
    PROFIT_SHARING_SUCCESS("PROFITSHARING.SUCCESS", "PROFITSHARING.SUCCESS"),
    REFUND_SUCCESS("REFUND.SUCCESS", "REFUND.SUCCESS"),
    REFUND_ABNORMAL("REFUND.ABNORMAL", "REFUND.ABNORMAL"),
    REFUND_CLOSED("REFUND.CLOSED", "REFUND.CLOSED");
    WxPayNotifyEventTypeEnum(String code, String text){
        init(code, text);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/DictBean.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.order.enums.dict;
import lombok.Data;
/**
 * 字典bean
 * 只有code和text,可用于展示下拉框
 *
 * @author luozhan
 */
@Data
public class DictBean<T> implements IDict<T> {
    private final T code;
    private final String text;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/enums/dict/IDict.java
New file
@@ -0,0 +1,170 @@
package com.ruoyi.order.enums.dict;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * 字典接口
 * <p>
 * 自定义的字典枚举类实现本接口后可省略属性code和text,以及对应的get方法
 * 在构造方法中只需调用init方法即可初始化
 *
 * @author luozhan
 * @date 2021-12
 */
public interface IDict<T> {
    /**
     * 通过code获取value
     *
     * @param clazz 枚举class
     * @param code  code
     * @return text
     */
    static <T> String getTextByCode(Class<? extends IDict<T>> clazz, T code) {
        return Stream.of(clazz.getEnumConstants())
                .filter((IDict<T> e) -> e.getCode().equals(code))
                .map(IDict::getText)
                .findAny().orElse("");
    }
    /**
     * 通过text获取code
     *
     * @param clazz 枚举class
     * @param text  text
     * @return code
     */
    static <T> T getCodeByText(Class<? extends IDict<T>> clazz, String text) {
        return Stream.of(clazz.getEnumConstants())
                .filter((IDict<T> e) -> e.getText().equals(text))
                .map(IDict::getCode)
                .findAny().orElse(null);
    }
    /**
     * 通过code获取字典枚举实例
     *
     * @param clazz 枚举class
     * @param code  code
     * @param <T>   字典code类型
     * @param <R>   枚举类型
     * @return 字典枚举实例
     */
    @SuppressWarnings("unchecked")
    static <T, R extends IDict<T>> R getByCode(Class<? extends IDict<T>> clazz, T code) {
        return Stream.of(clazz.getEnumConstants())
                .filter((IDict<T> e) -> (e.getCode().equals(code)))
                .map(v -> (R) v)
                .findAny()
                .orElse(null);
    }
    /**
     * 获取给定的字典枚举项(常用下拉框数据请求)
     *
     * @param enums 可指定需要哪些项
     * @return List
     */
    @SafeVarargs
    static <T, E extends IDict<T>> List<IDict<T>> getItems(E... enums) {
        return Stream.of(enums)
                .map(DictPool::getDict)
                .collect(Collectors.toList());
    }
    /**
     * 获取所有字典枚举项,除开指定的枚举
     *
     * @param exclude 指定排除的枚举
     * @return List
     */
    @SafeVarargs
    @SuppressWarnings("unchecked")
    static <T, E extends IDict<T>> List<IDict<T>> getItemsExclude(E... exclude) {
        Class<IDict<T>> clazz = (Class<IDict<T>>) exclude.getClass().getComponentType();
        IDict<T>[] allEnum = clazz.getEnumConstants();
        List<IDict<T>> excludeList = Arrays.asList(exclude);
        return Stream.of(allEnum)
                .filter(e -> !excludeList.contains(e))
                .map(DictPool::getDict)
                .collect(Collectors.toList());
    }
    /**
     * 获取所有字典枚举项(常用下拉框数据请求)
     * 枚举值上标记@Deprecated的不会返回
     *
     * @param clazz 字典枚举类
     * @return List
     */
    static <T> List<IDict<T>> getAll(Class<? extends IDict<T>> clazz) {
        Map<String, Field> fieldCache = Arrays.stream(clazz.getDeclaredFields()).
                filter(Field::isEnumConstant).
                collect(Collectors.toMap(Field::getName, Function.identity()));
        IDict<T>[] allEnum = clazz.getEnumConstants();
        return Stream.of(allEnum)
                .filter(e -> !fieldCache.get(((Enum<?>) e).name()).isAnnotationPresent(Deprecated.class))
                .map(DictPool::getDict)
                .collect(Collectors.toList());
    }
    /**
     * 初始化
     *
     * @param code 字典编码
     * @param text 字典文本
     */
    default void init(T code, String text) {
        DictPool.putDict(this, code, text);
    }
    /**
     * 获取编码
     *
     * @return 编码
     */
    default T getCode() {
        return DictPool.getDict(this).getCode();
    }
    /**
     * 获取文本
     *
     * @return 文本
     */
    default String getText() {
        return DictPool.getDict(this).getText();
    }
    @SuppressWarnings("all")
    class DictPool {
        private static final Map<IDict, DictBean> DICT_MAP = new ConcurrentHashMap<>();
        private static final Map<String, Class<? extends IDict>> DICT_NAME_CLASS_MAP = new ConcurrentHashMap<>();
        static <T> void putDict(IDict<T> dict, T code, String text) {
            DICT_NAME_CLASS_MAP.put(dict.getClass().getName(), dict.getClass());
            DICT_MAP.put(dict, new DictBean<>(code, text));
        }
        public static List<IDict<Object>> getDict(String dictName) {
            Class<? extends IDict> aClass = DICT_NAME_CLASS_MAP.get(dictName);
            return IDict.getAll((Class<? extends IDict<Object>>) aClass);
        }
        static <K extends IDict<T>, T> DictBean<T> getDict(K dict) {
            return DICT_MAP.get(dict);
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/BackMessageService.java
@@ -13,4 +13,10 @@
 */
public interface BackMessageService extends IService<BackMessage> {
    /**
     * 保存回调记录
     * @param resultType
     * @param resultMessage
     */
    void saveBackMessage(int resultType, String resultMessage);
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/OrderPaymentService.java
@@ -28,7 +28,7 @@
     * @param goodsNames
     * @param prepayId
     */
    void saveOrderPayment(Long userId, Long shopId, String orderId, BigDecimal payMoney, Date endTime, String profitSharing, String openid, String goodsNames, String prepayId);
    void saveOrderPayment(Long userId, Long shopId, String subMchId, String orderId, BigDecimal payMoney, Date endTime, String profitSharing, String openid, String goodsNames, String prepayId);
    /**
     *
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/PaymentMessageService.java
@@ -20,5 +20,5 @@
     * @param sendMessage
     * @param resultMessage
     */
    void savePaymentMessage(String sendType, Long sendId, String sendMessage, String resultMessage);
    void savePaymentMessage(String sendType, String sendId, String sendMessage, String resultMessage);
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/account/ProfitSharingService.java
@@ -1,7 +1,10 @@
package com.ruoyi.order.service.account;
import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingResult;
import com.ruoyi.order.domain.pojo.account.ProfitSharing;
import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
/**
 * <p>
@@ -13,4 +16,12 @@
 */
public interface ProfitSharingService extends IService<ProfitSharing> {
    /**
     * 创建按分账记录
     * @param shopId
     * @param orderId
     * @param orderMoney
     * @param result
     */
    void saveProfitSharing(Long shopId, String orderId, BigDecimal orderMoney, ProfitSharingResult result);
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/BackMessageServiceImpl.java
@@ -6,6 +6,8 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
 * <p>
 * 回调报文 服务实现类
@@ -17,4 +19,13 @@
@Service
public class BackMessageServiceImpl extends ServiceImpl<BackMessageMapper, BackMessage> implements BackMessageService {
    @Override
    public void saveBackMessage(int resultType, String resultMessage) {
        BackMessage backMessage = new BackMessage();
        backMessage.setDelFlag("0");
        backMessage.setResultType(resultType);
        backMessage.setResultMessage(resultMessage);
        backMessage.setCreateTime(new Date());
        this.saveOrUpdate(backMessage);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/OrderPaymentServiceImpl.java
@@ -24,7 +24,7 @@
public class OrderPaymentServiceImpl extends ServiceImpl<OrderPaymentMapper, OrderPayment> implements OrderPaymentService {
    @Override
    public void saveOrderPayment(Long userId, Long shopId, String orderId, BigDecimal payMoney, Date endTime,
    public void saveOrderPayment(Long userId, Long shopId, String subMchId, String orderId, BigDecimal payMoney, Date endTime,
                                 String profitSharing, String openid, String goodsNames, String prepayId) {
        OrderPayment payment = new OrderPayment();
        payment.setPaymentId(IdUtils.simpleUUID());
@@ -33,6 +33,7 @@
        payment.setOrderId(orderId);
        payment.setPayMoney(payMoney);
        payment.setTimeExpire(endTime);
        payment.setSubMchId(subMchId);
        // 0 否 1、是
        payment.setProfitSharing("Y".equals(profitSharing) ? 1 : 0);
        payment.setUserOpenId(openid);
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/PaymentMessageServiceImpl.java
@@ -20,7 +20,7 @@
public class PaymentMessageServiceImpl extends ServiceImpl<PaymentMessageMapper, PaymentMessage> implements PaymentMessageService {
    @Override
    public void savePaymentMessage(String sendType, Long sendId, String sendMessage, String resultMessage) {
    public void savePaymentMessage(String sendType, String sendId, String sendMessage, String resultMessage) {
        PaymentMessage paymentMessage = new PaymentMessage();
        paymentMessage.setDelFlag("0");
        paymentMessage.setSendType(sendType);
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/account/ProfitSharingServiceImpl.java
@@ -1,10 +1,17 @@
package com.ruoyi.order.service.impl.account;
import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingResult;
import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.ruoyi.order.domain.pojo.account.ProfitSharing;
import com.ruoyi.order.mapper.account.ProfitSharingMapper;
import com.ruoyi.order.service.account.ProfitSharingService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
/**
 * <p>
@@ -17,4 +24,31 @@
@Service
public class ProfitSharingServiceImpl extends ServiceImpl<ProfitSharingMapper, ProfitSharing> implements ProfitSharingService {
    @Override
    public void saveProfitSharing(Long shopId, String orderId, BigDecimal orderMoney, ProfitSharingResult result) {
        ProfitSharing profitSharing = new ProfitSharing();
        profitSharing.setShareId(result.getOutOrderNo());
        profitSharing.setDelFlag(0);
        profitSharing.setShareStatus("FINISHED".equals(result.getStatus()) ? 2 : 1);
        profitSharing.setShopId(shopId);
        profitSharing.setOrderId(orderId);
        profitSharing.setOrderMoney(orderMoney);
        List<ProfitSharingResult.Receiver> receivers = result.getReceivers();
        if(null != receivers && !receivers.isEmpty()){
            ProfitSharingResult.Receiver receiver = receivers.get(0);
            profitSharing.setReceiverAccount(receiver.getReceiverMchid());
            BigDecimal receiverAmount = new BigDecimal(receiver.getAmount());
            receiverAmount = receiverAmount.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
            profitSharing.setReceiverAmount(receiverAmount);
            profitSharing.setSurpMoney(orderMoney.subtract(receiverAmount));
            profitSharing.setReceiverDescription(receiver.getDescription());
            profitSharing.setFinishFlag(0);
            profitSharing.setFailReason(receiver.getFailReason());
            profitSharing.setDetailId(receiver.getDetailId());
        }
        profitSharing.setCreateTime(new Date());
        this.saveOrUpdate(profitSharing);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java
@@ -1,16 +1,16 @@
package com.ruoyi.order.service.impl.order;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingRequest.Receiver;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.google.common.base.Joiner;
@@ -27,14 +27,19 @@
import com.ruoyi.order.domain.dto.*;
import com.ruoyi.order.domain.pojo.account.OrderPayment;
import com.ruoyi.order.domain.pojo.account.OrderRefund;
import com.ruoyi.order.domain.pojo.account.ProfitSharing;
import com.ruoyi.order.domain.pojo.order.ConsumerGoods;
import com.ruoyi.order.domain.pojo.order.Order;
import com.ruoyi.order.domain.pojo.order.OrderGoods;
import com.ruoyi.order.domain.pojo.order.PayRecord;
import com.ruoyi.order.domain.vo.*;
import com.ruoyi.order.enums.RefundStatusEnum;
import com.ruoyi.order.enums.dict.IDict;
import com.ruoyi.order.mapper.order.OrderMapper;
import com.ruoyi.order.service.account.OrderPaymentService;
import com.ruoyi.order.service.account.OrderRefundService;
import com.ruoyi.order.service.account.PaymentMessageService;
import com.ruoyi.order.service.account.ProfitSharingService;
import com.ruoyi.order.service.order.*;
import com.ruoyi.system.api.constant.AppErrorConstant;
import com.ruoyi.system.api.constant.DelayTaskEnum;
@@ -53,7 +58,7 @@
import com.ruoyi.system.api.domain.poji.sys.SysUser;
import com.ruoyi.system.api.domain.vo.*;
import com.ruoyi.system.api.service.*;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
@@ -62,6 +67,7 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
@@ -78,6 +84,7 @@
 * @since 2023-04-25
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Resource
@@ -133,6 +140,12 @@
    @Resource
    private OrderPaymentService orderPaymentService;
    @Resource
    private ProfitSharingService profitSharingService;
    @Resource
    private OrderRefundService orderRefundService;
   /**
     * @description: buyGoods
@@ -723,57 +736,16 @@
        appPlaceOrderVo.setOrderId(orderId);
        appPlaceOrderVo.setOrderNo(orderNo);
        try {
            // 创建支付订单
            Long shopId = appPlaceOrderDto.getShopId();
            R<String> resultMch = remoteShopService.getShopSubMchId(shopId);
            String subMchId = resultMch.getData();
            if(StringUtils.isEmpty(subMchId)){
                throw new ServiceException("获取微信商户号失败");
            }
            WxPayConfig config = wxService.getConfig();
            config.setSubMchId(subMchId);
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setTradeType(WxPayConstants.TradeType.JSAPI);
            request.setSubMchId(subMchId);
            // 商户订单号
            request.setOutTradeNo(orderNo);
            // 订单总金额
            Integer totalFee = order.getPayMoney().multiply(new BigDecimal(100)).intValue();
            request.setTotalFee(totalFee);
            request.setProductId(orderNo);
            request.setNotifyUrl(config.getNotifyUrl());
            request.setProfitSharing("Y");
            request.setOpenid(appPlaceOrderDto.getOpenid());
            request.setSpbillCreateIp(appPlaceOrderDto.getSpbillCreateIp());
            // 商品描述    body
            String body = "商品购买";
        String goodsName = "";
            if(null != goods){
                body = goods.getGoodsName() + "-" + body;
            goodsName = goods.getGoodsName();
            }
            request.setBody(body);
            request.checkAndSign(config);
        // 小程序微信下单支付
        createWxPayInfo(appPlaceOrderVo, userId, order.getShopId(), goodsName, orderNo,
                orderId, order.getPayMoney(), appPlaceOrderDto.getOpenid(),
                appPlaceOrderDto.getSpbillCreateIp(), goodsNameList);
            // 请求参数
            Gson gson = new Gson();
            String payRequestJson = gson.toJson(request);
            WxPayMpOrderResult result = wxService.createOrder(request);
            String payResponseJson = gson.toJson(request);
            // 支付相关信息返回
            appPlaceOrderVo.setAppId(result.getAppId());
            appPlaceOrderVo.setMchId(config.getMchId());
            appPlaceOrderVo.setTimeStamp(result.getTimeStamp());
            appPlaceOrderVo.setNonceStr(result.getNonceStr());
            appPlaceOrderVo.setPackageStr(result.getPackageValue());
            appPlaceOrderVo.setSignType(result.getSignType());
            appPlaceOrderVo.setPaySign(result.getPaySign());
            appPlaceOrderVo.setPrepayId(result.getPackageValue());
            //减去优惠券
            if(StringUtils.isNotBlank(memberCouponSJ.toString())){
@@ -793,16 +765,94 @@
            remoteConfigService.addDelayTask(delayTask);
            appPlaceOrderVo.setEndTime(DateUtils.addMinutes(order.getCreateTime(),delayTime));
        return appPlaceOrderVo;
    }
    /**
     * 小程序支付下单API
     * @param appPlaceOrderVo
     * @param userId
     * @param shopId
     * @param goodsName
     * @param orderNo
     * @param orderId
     * @param payMoney
     * @param openid
     * @param payerClientIp
     * @param goodsNameList
     */
    private void createWxPayInfo(AppPlaceOrderVo appPlaceOrderVo, Long userId, Long shopId,
                                 String goodsName, String orderNo,
                                 String orderId, BigDecimal payMoney,
                                 String openid, String payerClientIp, List<String> goodsNameList){
        try {
            // 创建支付订单
            R<String> resultMch = remoteShopService.getShopSubMchId(shopId);
            String subMchId = resultMch.getData();
            if (StringUtils.isEmpty(subMchId)) {
                throw new ServiceException("获取微信商户号失败");
            }
            WxPayConfig config = wxService.getConfig();
            config.setSubMchId(subMchId);
            PartnerTransactionsRequest request = new PartnerTransactionsRequest();
            request.setSpAppid(config.getAppId());
            request.setSpMchid(config.getMchId());
            request.setSubMchid(config.getSubMchId());
            //  商品描述    body
            String description = goodsName + "-商品购买";
            request.setDescription(description);
            request.setOutTradeNo(orderNo);
            request.setNotifyUrl(config.getNotifyUrl());
            // 结算信息
            PartnerTransactionsRequest.SettleInfo settleInfo = new PartnerTransactionsRequest.SettleInfo();
            settleInfo.setProfitSharing(true);
            settleInfo.setSubsidyAmount(BigDecimal.ZERO);
            request.setSettleInfo(settleInfo);
            // 订单总金额
            Integer totalFee = payMoney.multiply(new BigDecimal(100)).intValue();
            PartnerTransactionsRequest.Amount amount = new PartnerTransactionsRequest.Amount();
            amount.setTotal(totalFee);
            amount.setCurrency("CNY");
            request.setAmount(amount);
            // 支付者
            PartnerTransactionsRequest.Payer payer = new PartnerTransactionsRequest.Payer();
            payer.setSpOpenid(openid);
            request.setPayer(payer);
            // 场景信息
            PartnerTransactionsRequest.SceneInfo sceneInfo = new PartnerTransactionsRequest.SceneInfo();
            sceneInfo.setPayerClientIp(payerClientIp);
            request.setSceneInfo(sceneInfo);
            TransactionsResult.JsapiResult result = wxService.getEcommerceService().partnerTransactions(TradeTypeEnum.JSAPI, request);
            // 请求参数
            Gson gson = new Gson();
            String payRequestJson = gson.toJson(request);
            // 返回参数
            String payResponseJson = gson.toJson(result);
            // 支付相关信息返回
            appPlaceOrderVo.setAppId(result.getAppId());
            appPlaceOrderVo.setMchId(config.getMchId());
            appPlaceOrderVo.setTimeStamp(result.getTimeStamp());
            appPlaceOrderVo.setNonceStr(result.getNonceStr());
            appPlaceOrderVo.setPackageStr(result.getPackageValue());
            appPlaceOrderVo.setSignType(result.getSignType());
            appPlaceOrderVo.setPaySign(result.getPaySign());
            appPlaceOrderVo.setPrepayId(result.getPackageValue());
            // 保存支付订单统一下单日志
            paymentMessageService.savePaymentMessage("1", 0L, payRequestJson, payResponseJson);
            paymentMessageService.savePaymentMessage("1", orderId, payRequestJson, payResponseJson);
            // 保存支付订单统一下单支付记录
            orderPaymentService.saveOrderPayment(userId, shopId, orderId, order.getPayMoney(),
                    appPlaceOrderVo.getEndTime(), "Y", appPlaceOrderDto.getOpenid(),
            orderPaymentService.saveOrderPayment(userId, shopId, subMchId, orderId, payMoney,
                    appPlaceOrderVo.getEndTime(), "Y", openid,
                    Joiner.on(";").join(goodsNameList),result.getPackageValue());
            return appPlaceOrderVo;
        } catch (WxPayException e){
            throw new ServiceException(e.getMessage());
        }
@@ -1189,11 +1239,13 @@
    @Override
    public void cancelOrder(String orderId) {
        Order order = this.getById(orderId);
        // 订单状态-1.删除 0.已取消 1.待支付 2.待核销 3.已完成
        if (order.getOrderStatus() == 0) {
            throw new ServiceException(AppErrorConstant.CANCEL_CANCEL_ORDER);
        }else if (order.getOrderStatus() == 1) {
            this.autoCancelOrder(orderId);
        }else if(order.getOrderStatus() == 2){
            // 2.待核销 -> 订单退款
            this.refundOrder(orderId);
        }else{
            throw new ServiceException(AppErrorConstant.CANCEL_ERROR_ORDER);
@@ -1417,38 +1469,85 @@
        payRecord.setPayType(2);
        payRecordService.save(payRecord);
        submitProfitSharing(orderId, order.getOrderNo(), order.getShopId(), order.getOrderMoney());
        return merVerifyOrderVo;
    }
    private void submitProfitSharing(String orderId, String orderNo, Long shopId, BigDecimal orderMoney) {
        String sendMessage = "";
        String resultMessage = "";
        try {
        // TODO 核销完成 开始分账(平台收取服务费)
        OrderPayment payment = orderPaymentService.getByOrderId(orderId);
        String transactionId = payment.getTransactionId();
        String platformTyMacId = MyWxPayConfig.getPlatformTyMacId();
        R<String> resultMch = remoteShopService.getShopSubMchId(order.getShopId());
            R<String> resultMch = remoteShopService.getShopSubMchId(shopId);
        String subMchId = resultMch.getData();
        if(StringUtils.isEmpty(subMchId)){
            throw new ServiceException("获取微信商户号失败");
        }
            if (!StringUtils.isEmpty(subMchId)) {
        R<ShopProportionVo> resultShopProportion = remoteShopService.getShopProportion(order.getShopId());
                R<ShopProportionVo> resultShopProportion = remoteShopService.getShopProportion(shopId);
        ShopProportionVo shopProportion = resultShopProportion.getData();
        if(null == shopProportion){
            throw new ServiceException("获取商户分成失败");
                if (null != shopProportion) {
                    ProfitSharingRequest request = new ProfitSharingRequest();
                    request.setSubMchid(subMchId);
                    request.setTransactionId(transactionId);
                    request.setOutOrderNo(orderNo);
                    List<Receiver> receiverList = new ArrayList<>();
                    String description = "订单:" + orderNo + " 平台抽取佣金";
                    BigDecimal proportionPercent = shopProportion.getProportionPercent();
                    if (null == proportionPercent) {
                        proportionPercent = BigDecimal.ZERO;
        }
        ProfitSharingRequest request = new ProfitSharingRequest();
        request.setSubMchId(subMchId);
        request.setTransactionId(transactionId);
        request.setOutOrderNo(order.getOrderNo());
        List<ProfitSharingReceiver> receiverList = new ArrayList<>();
        ProfitSharingReceiver receiver = new ProfitSharingReceiver();
        // TODO 分账
        // receiver.set
        // request.setReceivers();
                    ProfitSharingResult result = new ProfitSharingResult();
                    result.setOutOrderNo(orderNo);
                    result.setStatus("FINISHED");
        // platformTyMacId
        // wxService.getProfitSharingV3Service().profitSharing()
                    // 计算分成金额
                    int amount = orderMoney.multiply(proportionPercent).setScale(0, RoundingMode.UP).intValue();
                    log.info("订单分账:{} 分账金额: {}", orderNo, amount);
                    if (amount > 0) {
                        // 分账创建
                        Receiver receiver = new Receiver();
                        receiver.setType("MERCHANT_ID");
                        receiver.setReceiverAccount(platformTyMacId);
                        receiver.setAmount(amount);
                        receiver.setDescription(description);
        return merVerifyOrderVo;
                        receiverList.add(receiver);
                        request.setReceivers(receiverList);
                        // 分账完成
                        request.setFinish(true);
                        result = wxService.getEcommerceService().profitSharing(request);
                    }
                    // 创建分账记录
                    profitSharingService.saveProfitSharing(shopId, orderId, orderMoney, result);
                    // 保存请求信息
                    sendMessage = JSONObject.toJSONString(request);
                    resultMessage = JSONObject.toJSONString(result);
                } else {
                    resultMessage = String.format("订单分账:%s 获取商户分成失败", orderNo);
                    log.info(resultMessage);
                }
            } else {
                resultMessage = String.format("订单分账:%s 获取微信商户号失败", orderNo);
                log.info(resultMessage);
            }
        } catch (WxPayException e) {
            resultMessage = String.format("订单分账:%s 分账失败:%s", orderNo, e.getMessage());
            log.info(resultMessage);
            e.printStackTrace();
        }
        // 保存分账信息
        paymentMessageService.savePaymentMessage("4", orderId, sendMessage, resultMessage);
    }
    /**
@@ -1546,11 +1645,11 @@
    /**
     * @param merVerifyPrizeDto
     * @return MerVerifyAwardVo
     * @description  确认核销奖品
     * @author  jqs
     * @date    2023/7/9 9:54
     * @param merVerifyPrizeDto
     * @return  MerVerifyAwardVo
     */
    @Override
    @Transactional
@@ -3048,12 +3147,12 @@
    }
    /**
     * @description  获取核销奖品
     * @author  jqs
     * @date    2023/7/8 17:46
     * @param verifyCode
     * @param shopId
     * @return  MerVerifyAwardVo
     * @description 获取核销奖品
     * @author jqs
     * @date 2023/7/8 17:46
     */
    @Override
    public MerVerifyAwardVo verifyPrize(String verifyCode,Long shopId){
@@ -3335,11 +3434,11 @@
    }
    /**
     * @param merOrderPageDto
     * @return MerTotalOrderVo
     * @description  商户端订单管理统计
     * @author  jqs
     * @date    2023/7/31 10:53
     * @param merOrderPageDto
     * @return  MerTotalOrderVo
     */
    @Override
    public MerTotalOrderVo totalMerOrder(MerOrderPageDto merOrderPageDto) {
@@ -3419,11 +3518,11 @@
    }
    /**
     * @param orderId
     * @return void
     * @description 自动取消订单
     * @author  jqs
     * @date    2023/7/13 17:15
     * @param orderId
     * @return  void
     */
    @Override
    @Transactional
@@ -3485,15 +3584,15 @@
    }
    /**
     * @param transaction
     * @return void
     * @description  订单支付回调
     * @author  jqs
     * @date    2023/7/13 17:57
     * @param transaction
     * @return  void
     */
    @Override
    @Transactional
    public void payBack(Transaction transaction) {
    public void payBack(PartnerTransactionsResult transaction) {
        // 更新订单状态
        String orderId=transaction.getOutTradeNo();
        Order order = this.getById(orderId);
@@ -3623,11 +3722,11 @@
    }
    /**
     * @param orderId
     * @return void
     * @description  订单退款
     * @author  jqs
     * @date    2023/7/13 18:36
     * @param orderId
     * @return  void
     */
    @Override
    @Transactional
@@ -3645,7 +3744,7 @@
        orderRefund.setOrderId(order.getOrderId());
        orderRefund.setUserId(order.getUserId());
        orderRefund.setShopId(order.getShopId());
        orderRefund.setRefundMoney(order.getPayMoney());
        orderRefund.setRefundMoney(order.getOnlinePayMoney());
        orderRefund.setOrderMoney(order.getPayMoney());
        orderRefund.setCreateTime(new Date());
        // 初始化各类商品的收款金额
@@ -3774,14 +3873,68 @@
            activityTotalChangeDto.setUserId(order.getUserId());
            remoteActivityService.changeActivityTotal(activityTotalChangeDto);
        }
        // 用户取消订单退款
        BigDecimal onlinePayMoney = order.getOnlinePayMoney();
        if(BigDecimal.ZERO.compareTo(onlinePayMoney) < 0){
            // 订单支付金额大于0,可发起退款
            orderWxApplyRefund(orderId, refundId, onlinePayMoney, orderRefund);
        }
        orderRefund.setRefundStatus(1);
        orderRefundService.saveOrUpdate(orderRefund);
    }
    /**
     * 申请退款API
     */
    private void orderWxApplyRefund(String orderId, String outRefundNo, BigDecimal payMoney, OrderRefund orderRefund){
        try {
            // 创建支付订单
            OrderPayment orderPayment = orderPaymentService.getByOrderId(orderId);
            if (null == orderPayment) {
                return;
            }
            String subMchId = orderPayment.getSubMchId();
            WxPayConfig config = wxService.getConfig();
            RefundsRequest request = new RefundsRequest();
            request.setSpAppid(config.getAppId());
            request.setSubMchid(subMchId);
            request.setTransactionId(orderPayment.getTransactionId());
            request.setOutRefundNo(outRefundNo);
            request.setReason("用户取消订单");
            // 订单金额
            int total = payMoney.multiply(new BigDecimal(100)).intValue();
            RefundsRequest.Amount amount = RefundsRequest.Amount.builder().refund(total).total(total).currency("CNY").build();
            request.setAmount(amount);
            request.setNotifyUrl(config.getNotifyUrl());
            RefundsResult result = wxService.getEcommerceService().refunds(request);
            // 微信退款id
            orderRefund.setWxRefundId(result.getRefundId());
            // 请求参数
            Gson gson = new Gson();
            String refundRequestJson = gson.toJson(request);
            // 返回参数
            String refundResponseJson = gson.toJson(result);
            // 保存支付订单统一下单日志
            paymentMessageService.savePaymentMessage("3", orderId, refundRequestJson, refundResponseJson);
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * @param staffTotalDto
     * @return StaffActivityOrderTotalVo
     * @description  员工端活动订单统计
     * @author  jqs
     * @date    2023/7/17 15:25
     * @param staffTotalDto
     * @return  StaffActivityOrderTotalVo
     */
    @Override
    public StaffActivityOrderTotalVo getStaffActivityOrderTotal(StaffTotalDto staffTotalDto){
@@ -3790,11 +3943,11 @@
    }
    /**
     * @param staffTotalDto
     * @return StaffActivityTotalVo
     * @description  员工端活动统计
     * @author  jqs
     * @date    2023/7/17 15:51
     * @param staffTotalDto
     * @return  StaffActivityTotalVo
     */
    @Override
    public StaffActivityTotalVo getStaffActivityTotal(StaffTotalDto staffTotalDto){
@@ -3803,11 +3956,11 @@
    }
    /**
     * @param staffTotalDto
     * @return MerOrderDistributionTotalVo
     * @description  员工端活动统计订单分布
     * @author  jqs
     * @date    2023/7/17 16:16
     * @param staffTotalDto
     * @return  MerOrderDistributionTotalVo
     */
    @Override
    public MerOrderDistributionTotalVo getStaffOrderDistributionTotal(StaffTotalDto staffTotalDto){
@@ -3870,11 +4023,11 @@
    /**
     * @param staffTotalDto
     * @return MerOrderTypeTotalVo
     * @description  员工端活动统计销售占比
     * @author  jqs
     * @date    2023/7/17 17:03
     * @param staffTotalDto
     * @return  MerOrderTypeTotalVo
     */
    @Override
    public MerOrderTypeTotalVo getStaffOrderTypeTotal(StaffTotalDto staffTotalDto){
@@ -3915,11 +4068,11 @@
    }
    /**
     * @param staffTotalDto
     * @return StaffActivityDateMemberTotalVo
     * @description  员工端获客人数
     * @author  jqs
     * @date    2023/7/17 18:42
     * @param staffTotalDto
     * @return  StaffActivityDateMemberTotalVo
     */
    @Override
    public StaffActivityDateMemberTotalVo getStaffActivityGetMemberTotal(StaffTotalDto staffTotalDto) {
@@ -3956,11 +4109,11 @@
    }
    /**
     * @param staffTotalDto
     * @return StaffActivityDateMemberTotalVo
     * @description  员工端获客人数
     * @author  jqs
     * @date    2023/7/17 18:42
     * @param staffTotalDto
     * @return  StaffActivityDateMemberTotalVo
     */
    @Override
    public StaffActivityDateMemberTotalVo getStaffActivityMemberTotal(StaffTotalDto staffTotalDto) {
@@ -3997,12 +4150,12 @@
    }
    /**
     * @description  获取时间段日期
     * @author  jqs
     * @date    2023/7/17 19:11
     * @param startDate
     * @param endDate
     * @return  List<String>
     * @description 获取时间段日期
     * @author jqs
     * @date 2023/7/17 19:11
     */
    public static List<String> getDateRange(String startDate, String endDate) {
        List<String> dateList = new ArrayList<>();
@@ -4016,11 +4169,11 @@
    }
    /**
     * @param
     * @return void
     * @description  检查订单状态定时任务
     * @author  jqs
     * @date    2023/7/25 14:40
     * @param
     * @return  void
     */
    @Override
    public void checkOrderStatus(){
@@ -4042,6 +4195,43 @@
        }
    }
    @Override
    public void profitSharingBack(ProfitSharingNotifyResult result) {
        try {
            String outOrderNo = result.getOutOrderNo();
            ProfitSharing profitSharing = profitSharingService.getById(outOrderNo);
            if(null != profitSharing){
                profitSharing.setWxOrderId(result.getOrderId());
                profitSharing.setFinishTime(result.getSuccessTime());
                profitSharing.setFinishFlag(1);
                profitSharingService.saveOrUpdate(profitSharing);
            }
        } catch (Exception e){
            log.error("==分账回成功回调操作====【{}】========={}", result.getOutOrderNo(), e.getMessage());
        }
    }
    @Override
    public void orderRefundBack(RefundNotifyResult result) {
        try {
            String outRefundNo = result.getOutRefundNo();
            String refundStatus = result.getRefundStatus();
            OrderRefund orderRefund = orderRefundService.getById(outRefundNo);
            if(null != orderRefund){
                orderRefund.setWxRefundId(result.getRefundId());
                orderRefund.setBackTime(result.getSuccessTime());
                orderRefund.setRefundStatus(IDict.getCodeByText(RefundStatusEnum.class, refundStatus));
                orderRefundService.saveOrUpdate(orderRefund);
            }
        } catch (Exception e){
            log.error("==退款回成功回调操作====【{}】========={}", result.getOutRefundNo(), e.getMessage());
        }
    }
    private void autoTimeCancelOrder(Order order){
        String orderId = order.getOrderId();
        order.setOrderStatus(0);
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/order/OrderService.java
@@ -2,6 +2,9 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.binarywang.wxpay.bean.ecommerce.PartnerTransactionsResult;
import com.github.binarywang.wxpay.bean.ecommerce.RefundNotifyResult;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult;
import com.ruoyi.order.domain.dto.*;
import com.ruoyi.order.domain.pojo.order.Order;
import com.ruoyi.order.domain.vo.*;
@@ -416,7 +419,7 @@
     * @param Transaction transaction
     * @return  void
     */
    void payBack(Transaction transaction);
    void payBack(PartnerTransactionsResult transaction);
    /**
     * 取消订单
@@ -489,4 +492,16 @@
     * @return  void
     */
    void checkOrderStatus();
    /**
     * 分账通知回调
     * @param result
     */
    void profitSharingBack(ProfitSharingNotifyResult result);
    /**
     * 订单退款回调
     * @param result
     */
    void orderRefundBack(RefundNotifyResult result);
}