Browse Source

维沙支付

plf 4 years ago
parent
commit
fe2d7625df

+ 38 - 0
src/main/java/com/caimei/config/CorsConfig.java

@@ -0,0 +1,38 @@
+package com.caimei.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+/**
+ * 跨域配置
+ *
+ * @author Administrator
+ */
+@Configuration
+public class CorsConfig {
+
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        CorsConfiguration config = new CorsConfiguration();
+        // 1 设置访问源地址
+        config.addAllowedOrigin("*");
+        config.setAllowCredentials(true);
+        // 3 设置访问源请求方法
+        config.addAllowedMethod("*");
+        // 2 设置访问源请求头
+        config.addAllowedHeader("*");
+        config.addExposedHeader("Content-Type");
+        config.addExposedHeader("X-Requested-With");
+        config.addExposedHeader("accept");
+        config.addExposedHeader("Origin");
+        config.addExposedHeader("Access-Control-Request-Method");
+        config.addExposedHeader("Access-Control-Request-Headers");
+        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
+        // 4 对接口配置跨域设置
+        configSource.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(configSource);
+    }
+}

+ 36 - 1
src/main/java/com/caimei/controller/PayOrderApi.java

@@ -14,6 +14,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpResponse;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.reactive.function.server.ServerRequest;
 
@@ -131,15 +132,36 @@ public class PayOrderApi {
         return payOrderService.payLink(orderPayLink);
     }
 
+    /**
+     * 支付链接重定向到页面
+     */
+    @GetMapping("/jumpPage")
+    public void jumpPage(String linkLogo, ServerHttpResponse response) {
+        payOrderService.jumpPage(linkLogo, response);
+    }
+
+    /**
+     * 链接数据
+     */
+    @GetMapping("/linkData")
+    public ResponseJson<Map<String, Object>> linkData(String linkLogo) {
+        if (StringUtils.isBlank(linkLogo)) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.linkData(linkLogo);
+    }
+
     /**
      * pc端支付,银联,支付宝
      */
     @ApiOperation("pc端支付,银联,支付宝支付")
     @PostMapping("/pcMallPay")
-    public ResponseJson<JSONObject> pcMallPay(@RequestBody PaymentDto payment) {
+    public ResponseJson<JSONObject> pcMallPay(@RequestBody PaymentDto payment, @RequestHeader HttpHeaders headers) {
         if (null == payment || StringUtils.isBlank(payment.getPayWay()) || StringUtils.isBlank(payment.getReturnUrl()) || payment.getPayAmount() == null) {
             return ResponseJson.error("参数异常", null);
         }
+        String clientIp = headers.getFirst("X-CLIENT-IP");
+        payment.setClientIp(clientIp);
         if ("UNIONPAY".equals(payment.getPayWay())) {
             //银联支付
             payment.setPayType("GATEWAY_UNIONPAY");
@@ -173,4 +195,17 @@ public class PayOrderApi {
         }
         return payOrderService.findOrderStatus(mbOrderId);
     }
+
+    /**
+     * 延时分账异步通知回调
+     */
+    @GetMapping("/delayedSplittingCallback")
+    public String delayedSplittingCallback(ServerRequest request) {
+        log.info("延时分账异步通知>>>>>>>start");
+        String data = request.pathVariable("data");
+        if (StringUtils.isBlank(data)) {
+            return "回调参数失败";
+        }
+        return payOrderService.delayedSplittingCallback(data);
+    }
 }

+ 106 - 1
src/main/java/com/caimei/mapper/PayOrderMapper.java

@@ -1,6 +1,9 @@
 package com.caimei.mapper;
 
-import com.caimei.model.po.*;
+import com.caimei.model.po.CmDiscernReceiptPo;
+import com.caimei.model.po.CmPayShopPo;
+import com.caimei.model.po.CmPayShopRecordPo;
+import com.caimei.model.po.CmReceiptOrderRelationPo;
 import com.caimei.model.vo.*;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -80,4 +83,106 @@ public interface PayOrderMapper {
      * 获取线上支付开关状态
      */
     Integer getPayOnLineSwitch();
+
+    /**
+     * 查询未分账已支付收款
+     *
+     * @param currentTime 延迟时间
+     * @return
+     */
+    List<CmReceiptOrderRelationPo> findUnallocatedAccount(String currentTime);
+
+    /**
+     * 查询已支付金额
+     *
+     * @param orderProductId
+     * @return
+     */
+    BigDecimal findPaidAmount(Integer orderProductId);
+
+    /**
+     * 查询子商户商编
+     *
+     * @param shopId
+     * @return
+     */
+    String findCommercialCode(Long shopId);
+
+    /**
+     * 查询已支付运费
+     *
+     * @param orderId
+     * @param shopId
+     * @return
+     */
+    BigDecimal findShipping(@Param("orderId") Integer orderId, @Param("shopId") Integer shopId);
+
+    /**
+     * 保存分账信息
+     *
+     * @param splitAccount
+     */
+    void insertSplitAccount(SplitAccountVo splitAccount);
+
+    /**
+     * 修改收款分账状态
+     *
+     * @param mbOrderId
+     */
+    void updateBySplitStatus(String mbOrderId);
+
+    /**
+     * 查询分账信息
+     *
+     * @param mbOrderId 米花平台交易流水号
+     * @return
+     */
+    List<SplitAccountVo> findByMbOrderId(String mbOrderId);
+
+    /**
+     * 查询已付供应商金额
+     *
+     * @param shopOrderId
+     * @return
+     */
+    BigDecimal findPaidShop(Integer shopOrderId);
+
+    /**
+     * 修改子订单付款状态及付款金额
+     *
+     * @param shopOrderId
+     * @param paidShop
+     * @param payStatus
+     */
+    void updateShopOrderByPayStatus(@Param("shopOrderId") Integer shopOrderId, @Param("paidShop") BigDecimal paidShop, @Param("payStatus") String payStatus);
+
+    /**
+     * 保存付款单
+     *
+     * @param payShop
+     */
+    void insertPayShop(CmPayShopPo payShop);
+
+    /**
+     * 保存付款记录
+     *
+     * @param shopRecord
+     */
+    void insertPayShopRecord(CmPayShopRecordPo shopRecord);
+
+    /**
+     * 修改主订单付款状态
+     *
+     * @param orderId
+     * @param payStatus
+     */
+    void updateOrderByPayStatus(@Param("orderId") Integer orderId, @Param("payStatus") String payStatus);
+
+    /**
+     * 查询网银链接数据
+     *
+     * @param linkLogo
+     * @return
+     */
+    OrderPayLinkVo getOrderPayLink(String linkLogo);
 }

+ 12 - 3
src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java

@@ -1,12 +1,14 @@
 package com.caimei.model.po;
 
+import lombok.Data;
+
 import java.io.Serializable;
 import java.math.BigDecimal;
-import lombok.Data;
 
 /**
  * cm_receipt_order_relation
- * @author 
+ *
+ * @author
  */
 @Data
 public class CmReceiptOrderRelationPo implements Serializable {
@@ -38,7 +40,7 @@ public class CmReceiptOrderRelationPo implements Serializable {
     private String mbOrderId;
 
     /**
-     *  商户唯一订单请求号(订单编号T随机时间戳)
+     * 商户唯一订单请求号(订单编号T随机时间戳)
      */
     private String orderRequestNo;
 
@@ -57,5 +59,12 @@ public class CmReceiptOrderRelationPo implements Serializable {
      */
     private String delFlag;
 
+    /**
+     * 付款类型:1建设银行7297、2广发银行0115、3中信银行7172、4中信银行0897、5中信银行0897-财付通、6中信银行0897-支付宝、
+     * 7线上-支付宝、8线上-微信支付、9线上-快钱支付、10口头返佣、11广发银行5461、12PC-B2B网银、13PC-微信支付、14PC-支付宝、
+     * 15小程序-微信支付、16余额抵扣
+     */
+    private String payType;
+
     private static final long serialVersionUID = 1L;
 }

+ 30 - 0
src/main/java/com/caimei/service/PayOrderService.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.caimei.model.ResponseJson;
 import com.caimei.model.dto.PaymentDto;
 import com.caimei.model.vo.OrderPayLinkVo;
+import org.springframework.http.server.reactive.ServerHttpResponse;
 
 import java.util.Map;
 
@@ -63,4 +64,33 @@ public interface PayOrderService {
      * @return
      */
     ResponseJson<JSONObject> findOrderStatus(String mbOrderId);
+
+    /**
+     * 延时分账
+     */
+    void delayedSplitting();
+
+    /**
+     * 分账异步通知
+     *
+     * @param data
+     * @return
+     */
+    String delayedSplittingCallback(String data);
+
+    /**
+     * 重定向跳转页面
+     *
+     * @param linkLogo
+     * @param response
+     */
+    void jumpPage(String linkLogo, ServerHttpResponse response);
+
+    /**
+     * 初始化网银支付数据
+     *
+     * @param linkLogo
+     * @return
+     */
+    ResponseJson<Map<String, Object>> linkData(String linkLogo);
 }

+ 408 - 7
src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java

@@ -1,18 +1,14 @@
 package com.caimei.service.impl;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.caimei.mapper.OrderMapper;
 import com.caimei.mapper.OrderSubmitMapper;
 import com.caimei.mapper.PayOrderMapper;
 import com.caimei.model.ResponseJson;
 import com.caimei.model.dto.PaymentDto;
-import com.caimei.model.po.CmDiscernReceiptPo;
-import com.caimei.model.po.CmReceiptOrderRelationPo;
-import com.caimei.model.po.UserPo;
-import com.caimei.model.vo.DiscernReceiptVo;
-import com.caimei.model.vo.OrderPayLinkVo;
-import com.caimei.model.vo.OrderProductVo;
-import com.caimei.model.vo.OrderVo;
+import com.caimei.model.po.*;
+import com.caimei.model.vo.*;
 import com.caimei.service.PayOrderService;
 import com.caimei.util.MathUtil;
 import com.caimei.util.Md5Util;
@@ -20,11 +16,15 @@ import com.caimei.util.PayUtils;
 import com.caimei.util.RandomCodeGenerator;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.math.BigDecimal;
+import java.net.URI;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
@@ -44,6 +44,11 @@ public class PayOrderServiceImpl implements PayOrderService {
     @Resource
     private OrderSubmitMapper orderSubmitMapper;
 
+    @Value("${caimei.delayedSplittingUrl}")
+    private String delayedSplittingUrl;
+    @Value("${caimei.linkPage}")
+    private String linkPage;
+
     /**
      * 商户标识
      */
@@ -60,6 +65,18 @@ public class PayOrderServiceImpl implements PayOrderService {
      * 用户编号
      */
     String merNo = "10002074";
+    /**
+     * 公账-专票,子商户商编
+     */
+    String publicAccountNo = "20001793";
+    /**
+     * 公账-普票,子商户商编
+     */
+    String commonInvoiceNo = "20001754";
+    /**
+     * 私账-无票,子商户商编
+     */
+    String privateAccountNo = "20001924";
 
     /**
      * 获取线上支付开关状态
@@ -411,4 +428,388 @@ public class PayOrderServiceImpl implements PayOrderService {
         }
         return ResponseJson.success(result);
     }
+
+    @Override
+    public void delayedSplitting() {
+        log.info("延时分账,每一小时执行一次");
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.DAY_OF_MONTH, -1);
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String currentTime = format.format(calendar.getTime());
+        List<CmReceiptOrderRelationPo> orderRelations = payOrderMapper.findUnallocatedAccount(currentTime);
+        if (orderRelations != null && orderRelations.size() > 0) {
+            for (CmReceiptOrderRelationPo orderRelation : orderRelations) {
+                log.info("订单id" + orderRelation.getOrderID() + ">>>进入延时分账");
+                OrderVo order = orderMapper.findOrder(orderRelation.getOrderID());
+                PaymentDto payment = new PaymentDto();
+                payment.setPayAmount(MathUtil.mul(orderRelation.getAssociateAmount(), 100).intValue());
+                if ("12".equals(orderRelation.getPayType())) {
+                    //网银支付
+                    payment.setPayWay("UNIONPAY");
+                }
+                List<SplitAccountVo> splitBillDetail = splitBillDetail(order, payment);
+                String parameters = ledgerParameters(splitBillDetail, order.getOrderId());
+                log.info("分账参数: " + parameters);
+
+                //第三方分账接口
+                JSONObject result = null;
+                try {
+                    // 时间戳
+                    long time = System.currentTimeMillis() / 1000;
+                    JSONObject json = new JSONObject();
+                    json.put("merAccount", merAccount);
+                    json.put("orderId", orderRelation.getOrderRequestNo());
+                    json.put("requestNo", orderRelation.getOrderRequestNo());
+                    json.put("mbOrderId", orderRelation.getMbOrderId());
+                    json.put("time", time);
+                    json.put("splitBillDetail", parameters);
+                    json.put("notifyUrl", delayedSplittingUrl);
+                    log.info("回调接口>>>" + delayedSplittingUrl);
+                    String sign = PayUtils.buildSign(json, merKey);
+                    json.put("sign", sign);
+                    String data = PayUtils.buildDataPrivate(json, merKey);
+                    result = PayUtils.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/splitOrder/v1", merAccount, data);
+                } catch (Exception e) {
+                    log.error("错误信息", e);
+                }
+                if (result != null) {
+                    String code = result.getString("code");
+                    if (!"000000".equals(code)) {
+                        String msg = result.getString("msg");
+                        log.info("第三方延迟分账失败>>>>>>>msg:" + msg);
+                    } else {
+                        //保存分账记录
+                        for (SplitAccountVo splitAccount : splitBillDetail) {
+                            splitAccount.setMbOrderId(orderRelation.getMbOrderId());
+                            splitAccount.setOrderRequestNo(orderRelation.getOrderRequestNo());
+                            splitAccount.setPayStatus("1");
+                            payOrderMapper.insertSplitAccount(splitAccount);
+                        }
+                        log.info("此订单分账结束");
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public String delayedSplittingCallback(String data) {
+        try {
+            //公钥解密
+            JSONObject json = PayUtils.decryptDataPublic(data, publicKey);
+            log.info("公钥解密>>>>>>" + json);
+            //公钥验签
+            String signaa = json.getString("sign");
+            json.remove("sign");
+            String signbb = PayUtils.buildSign(json, publicKey);
+            if (!signaa.equals(signbb)) {
+                return "验签失败";
+            }
+            String mbOrderId = json.getString("mbOrderId");
+            String status = json.getString("status");
+            log.info("分账状态>>>" + status);
+            if ("FAILED".equals(status)) {
+                return "分账失败";
+            }
+
+            //修改收款分账状态
+            payOrderMapper.updateBySplitStatus(mbOrderId);
+
+            Integer orderId = null;
+            List<SplitAccountVo> splitAccountList = payOrderMapper.findByMbOrderId(mbOrderId);
+            if (splitAccountList != null && splitAccountList.size() > 0) {
+                Integer shopOrderId = null;
+                String shopOrderNo = "";
+                for (SplitAccountVo account : splitAccountList) {
+                    log.info("保存应付付供应商>>>>" + account.getShopId());
+                    //本次付供应商金额(分账金额)
+                    BigDecimal splitAmount = account.getSplitAccount();
+                    orderId = account.getOrderId();
+                    Integer shopId = account.getShopId();
+                    List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(orderId);
+                    String payStatus = "";
+                    for (ShopOrderVo shopOrder : shopOrderList) {
+                        if (shopId.equals(shopOrder.getShopId())) {
+                            shopOrderId = shopOrder.getShopOrderId();
+                            shopOrderNo = shopOrder.getShopOrderNo();
+                            //已付供应商金额
+                            BigDecimal paidAmount = payOrderMapper.findPaidShop(shopOrderId);
+                            BigDecimal paidShop = MathUtil.add(paidAmount, splitAmount);
+                            if (MathUtil.compare(shopOrder.getShouldPayShopAmount(), paidShop) == 0) {
+                                payStatus = "3";
+                            } else {
+                                payStatus = "2";
+                            }
+                            //修改子订单付款状态及付款金额
+                            payOrderMapper.updateShopOrderByPayStatus(shopOrderId, paidShop, payStatus);
+                        }
+                    }
+
+                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                    String currentTime = format.format(new Date());
+                    //保存付供应商记录
+                    CmPayShopPo payShop = new CmPayShopPo();
+                    payShop.setShopID(shopId);
+                    payShop.setName("线上支付分账");
+                    payShop.setTotalAmount(splitAmount);
+                    payShop.setWipePayment(BigDecimal.ZERO);
+                    payShop.setPayType("6");
+                    payShop.setStatus("1");
+                    payShop.setDelFlag("0");
+                    payShop.setApplyTime(currentTime);
+                    payShop.setReviewTime(currentTime);
+                    payShop.setPayTime(currentTime);
+                    payOrderMapper.insertPayShop(payShop);
+
+                    CmPayShopRecordPo shopRecord = new CmPayShopRecordPo();
+                    shopRecord.setShopID(shopId);
+                    shopRecord.setShopOrderID(shopOrderId);
+                    shopRecord.setShopOrderNo(shopOrderNo);
+                    shopRecord.setPayAmount(splitAmount);
+                    shopRecord.setWipePayment(BigDecimal.ZERO);
+                    shopRecord.setPayType("6");
+                    shopRecord.setPayTime(currentTime);
+                    shopRecord.setPayShopID(payShop.getId().intValue());
+                    shopRecord.setStatus("1");
+                    shopRecord.setDelFlag("0");
+                    payOrderMapper.insertPayShopRecord(shopRecord);
+                }
+
+                //修改主订单付款状态
+                List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(orderId);
+                //子订单是否全部付款
+                boolean isPay = true;
+                for (ShopOrderVo shopOrder : shopOrderList) {
+                    if (!"3".equals(shopOrder.getPayStatus())) {
+                        isPay = false;
+                    }
+                }
+                if (isPay) {
+                    payOrderMapper.updateOrderByPayStatus(orderId, "3");
+                } else {
+                    payOrderMapper.updateOrderByPayStatus(orderId, "2");
+                }
+            }
+        } catch (Exception e) {
+            log.error("分账异步通知异常", e);
+            return "分账失败";
+        }
+        return "SUCCESS";
+    }
+
+    @Override
+    public void jumpPage(String linkLogo, ServerHttpResponse response) {
+        linkPage = linkPage + "?linkLogo=" + linkLogo;
+        response.setStatusCode(HttpStatus.FOUND);
+        response.getHeaders().setLocation(URI.create(linkPage));
+    }
+
+    @Override
+    public ResponseJson<Map<String, Object>> linkData(String linkLogo) {
+        Map<String, Object> map = new HashMap<>(3);
+        OrderPayLinkVo orderPayLink = payOrderMapper.getOrderPayLink(linkLogo);
+        //链接状态,0成功
+        int code = 0;
+        if (null == orderPayLink) {
+            //已重新生成链接
+            return ResponseJson.error("链接更新,请重新获取", null);
+        } else {
+            OrderVo order = orderMapper.findOrder(orderPayLink.getOrderId().intValue());
+            List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+            if (orderPayLink.getEffectiveTime().compareTo(new Date()) < 0) {
+                //链接失效
+                code = -3;
+            }
+            if (null != discernReceiptList && discernReceiptList.size() > 0) {
+                for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                    if ("2".equals(discernReceipt.getPayWay())) {
+                        // 已线下支付
+                        code = -2;
+                    }
+                }
+            }
+            if ("1".equals(orderPayLink.getPayStatus())) {
+                //链接已支付
+                code = 5;
+            }
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String time = dateFormat.format(orderPayLink.getEffectiveTime());
+            map.put("orderPayLink", orderPayLink);
+            map.put("code", code);
+            map.put("time", time);
+        }
+        return ResponseJson.success(map);
+    }
+
+    /**
+     * 分账详情
+     */
+    private List<SplitAccountVo> splitBillDetail(OrderVo order, PaymentDto payment) {
+        List<SplitAccountVo> list = new ArrayList<>();
+        //本次支付金额,单位/元
+        BigDecimal payAmount = MathUtil.div(payment.getPayAmount(), 100);
+        //待分账总金额
+        BigDecimal splitAmount = payAmount;
+        //总手续费
+        BigDecimal procedureFee = BigDecimal.ZERO;
+        if ("UNIONPAY".equals(payment.getPayWay())) {
+            procedureFee = new BigDecimal(8);
+        } else {
+            //手续费
+            procedureFee = MathUtil.mul(payAmount, 0.0038, 2);
+            if (MathUtil.compare(procedureFee, 0) == 0) {
+                procedureFee = new BigDecimal("0.01");
+            }
+        }
+        splitAmount = MathUtil.sub(splitAmount, procedureFee);
+        List<OrderProductVo> orderProductList = payOrderMapper.findAllOrderProduct(order.getOrderId());
+        for (OrderProductVo orderProduct : orderProductList) {
+            BigDecimal costPrice = MathUtil.mul(orderProduct.getCostPrice(), orderProduct.getNum());
+            //不含税能开发票
+            if ("0".equals(orderProduct.getIncludedTax()) && !"3".equals(orderProduct.getInvoiceType())) {
+                //应付总税费
+                BigDecimal payableTax = MathUtil.mul(orderProduct.getSingleShouldPayTotalTax(), orderProduct.getNum());
+                costPrice = MathUtil.add(costPrice, payableTax);
+            }
+            //判断是否支付过
+            BigDecimal paidAmount = payOrderMapper.findPaidAmount(orderProduct.getOrderProductId());
+            if (paidAmount == null || MathUtil.compare(paidAmount, costPrice) < 0) {
+                if (paidAmount != null && MathUtil.compare(paidAmount, 0) > 0) {
+                    costPrice = MathUtil.sub(costPrice, paidAmount);
+                }
+                //待分账金额>=本次待分账金额
+                if (MathUtil.compare(splitAmount, costPrice) > -1) {
+                    splitAmount = MathUtil.sub(splitAmount, costPrice);
+                } else {
+                    costPrice = splitAmount;
+                    splitAmount = BigDecimal.ZERO;
+                }
+                String commercialCode = payOrderMapper.findCommercialCode(orderProduct.getShopId());
+                SplitAccountVo splitAccount = new SplitAccountVo();
+                splitAccount.setOrderId(order.getOrderId());
+                splitAccount.setOrderProductId(orderProduct.getOrderProductId());
+                splitAccount.setShopId(orderProduct.getShopId().intValue());
+                splitAccount.setSplitAccount(costPrice);
+                splitAccount.setProductType("1");
+                if (StringUtils.isNotBlank(commercialCode)) {
+                    //供应商拥有子商户号
+                    splitAccount.setType("4");
+                    splitAccount.setSubUserNo(commercialCode);
+                } else {
+                    if ("3".equals(orderProduct.getInvoiceType())) {
+                        //不能开票,则分账到私账-无票
+                        splitAccount.setType("2");
+                        splitAccount.setSubUserNo(privateAccountNo);
+                    } else if ("1".equals(orderProduct.getInvoiceType())) {
+                        //开增值税发票,则分账到公账-专票
+                        splitAccount.setType("1");
+                        splitAccount.setSubUserNo(publicAccountNo);
+                    } else if ("2".equals(orderProduct.getInvoiceType())) {
+                        //开普通发票,则分账到公账-普票
+                        splitAccount.setType("3");
+                        splitAccount.setSubUserNo(commonInvoiceNo);
+                    }
+                }
+                list.add(splitAccount);
+                if (MathUtil.compare(splitAmount, 0) == 0) {
+                    break;
+                }
+            }
+        }
+
+        //付供应商运费,是以供应商为单位的
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(order.getOrderId());
+            for (ShopOrderVo shopOrder : shopOrderList) {
+                //运费
+                BigDecimal shopPostFee = shopOrder.getShopPostFee();
+                if (MathUtil.compare(shopPostFee, 0) > 0) {
+                    BigDecimal shipping = payOrderMapper.findShipping(order.getOrderId(), shopOrder.getShopId());
+                    shopPostFee = MathUtil.sub(shopPostFee, shipping);
+                    if (MathUtil.compare(splitAmount, shopPostFee) > -1) {
+                        splitAmount = MathUtil.sub(splitAmount, shipping);
+                    } else {
+                        shopPostFee = splitAmount;
+                        splitAmount = BigDecimal.ZERO;
+                    }
+                    String commercialCode = payOrderMapper.findCommercialCode(Long.valueOf(shopOrder.getShopId()));
+                    SplitAccountVo splitAccount = new SplitAccountVo();
+                    splitAccount.setOrderId(order.getOrderId());
+                    splitAccount.setShopId(shopOrder.getShopId());
+                    splitAccount.setSplitAccount(shopPostFee);
+                    splitAccount.setProductType("2");
+                    if (StringUtils.isNotBlank(commercialCode)) {
+                        //供应商拥有子商户号
+                        splitAccount.setType("4");
+                        splitAccount.setSubUserNo(commercialCode);
+                    } else {
+                        //私账
+                        splitAccount.setType("2");
+                        splitAccount.setSubUserNo(privateAccountNo);
+                    }
+                    list.add(splitAccount);
+                }
+            }
+        }
+
+        //佣金,公账
+        if (MathUtil.compare(splitAmount, 0) > 0) {
+            SplitAccountVo splitAccount = new SplitAccountVo();
+            splitAccount.setOrderId(order.getOrderId());
+            splitAccount.setSplitAccount(splitAmount);
+            splitAccount.setProductType("3");
+            splitAccount.setType("1");
+            splitAccount.setSubUserNo(publicAccountNo);
+            list.add(splitAccount);
+        }
+        return list;
+    }
+
+    /**
+     * 整理第三方支付详情参数
+     */
+    private String ledgerParameters(List<SplitAccountVo> splitBillDetail, Integer orderId) {
+        List<Map<String, String>> maps = new ArrayList<>();
+        List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(orderId);
+        //供应商子商户总金额
+        for (ShopOrderVo shopOrder : shopOrderList) {
+            BigDecimal shopTotalAmount = BigDecimal.ZERO;
+            String subUserNo = "";
+            for (SplitAccountVo account : splitBillDetail) {
+                if ("4".equals(account.getType()) && shopOrder.getShopId().equals(account.getShopId())) {
+                    shopTotalAmount = MathUtil.add(shopTotalAmount, account.getSplitAccount());
+                    subUserNo = account.getSubUserNo();
+                }
+            }
+            addMaps(maps, shopTotalAmount, subUserNo);
+        }
+        //公账-专票总金额,私账-无票总金额,公账-普票总金额
+        BigDecimal totalAmount1 = BigDecimal.ZERO;
+        BigDecimal totalAmount2 = BigDecimal.ZERO;
+        BigDecimal totalAmount3 = BigDecimal.ZERO;
+        for (SplitAccountVo account : splitBillDetail) {
+            if ("1".equals(account.getType())) {
+                totalAmount1 = MathUtil.add(totalAmount1, account.getSplitAccount());
+            } else if ("2".equals(account.getType())) {
+                totalAmount2 = MathUtil.add(totalAmount2, account.getSplitAccount());
+            } else if ("3".equals(account.getType())) {
+                totalAmount3 = MathUtil.add(totalAmount3, account.getSplitAccount());
+            }
+        }
+        addMaps(maps, totalAmount1, publicAccountNo);
+        addMaps(maps, totalAmount2, privateAccountNo);
+        addMaps(maps, totalAmount3, commonInvoiceNo);
+        return JSON.toJSONString(maps);
+    }
+
+    private void addMaps(List<Map<String, String>> maps, BigDecimal shopTotalAmount, String subUserNo) {
+        if (MathUtil.compare(shopTotalAmount, 0) > 0) {
+            Map<String, String> map = new HashMap<>(3);
+            map.put("subUserNo", subUserNo);
+            map.put("splitBillType", "2");
+            map.put("splitBillValue", MathUtil.mul(shopTotalAmount, 100).toString());
+            maps.add(map);
+        }
+    }
 }

+ 35 - 0
src/main/java/com/caimei/task/SplitAccountTask.java

@@ -0,0 +1,35 @@
+package com.caimei.task;
+
+import com.caimei.service.PayOrderService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * Description
+ * 延时分账定时器
+ *
+ * @author : plf
+ * @date : 2021/3/3
+ */
+@Component
+@EnableScheduling
+public class SplitAccountTask {
+    @Resource
+    private PayOrderService payOrderService;
+
+    @Value("${caimei.delayedSplittingUrl}")
+    private String delayedSplittingUrl;
+
+    /**
+     * 延时分账,每一小时执行一次
+     */
+    @Scheduled(cron = "0 0 * * * ?")
+    //@Scheduled(cron = "0 */1 * * * ?")
+    public void delayedSplitting() {
+        payOrderService.delayedSplitting();
+    }
+}

+ 5 - 1
src/main/resources/config/beta/application-beta.yml

@@ -47,4 +47,8 @@ caimei:
   #支付异步回调地址
   notifyUrl: https://mall2c-b.caimei365.com/PayOrder/paymentCallback
   #支付链接重定向地址
-  redirectLink: https://spi-b.caimei365.com/PayOrder/jumpPage
+  redirectLink: https://mall2c-b.caimei365.com/PayOrder/jumpPage
+  #延时分账异步回调地址
+  delayedSplittingUrl: https://mall2c-b.caimei365.com/PayOrder/delayedSplittingCallback
+  #链接页面
+  linkPage: https://www-b.caimei365.com/pay/caimei-wisapay.html

+ 6 - 2
src/main/resources/config/dev/application-dev.yml

@@ -45,6 +45,10 @@ wx:
 caimei:
   oldapi: http://localhost:8100
   #支付异步回调地址
-  notifyUrl: https://mall2c-b.caimei365.com/PayOrder/paymentCallback
+  notifyUrl: https://localhost:8010/PayOrder/paymentCallback
   #支付链接重定向地址
-  redirectLink: https://spi-b.caimei365.com/PayOrder/jumpPage
+  redirectLink: https://localhost:8010/PayOrder/jumpPage
+  #延时分账异步回调地址
+  delayedSplittingUrl: http://localhost:8010/PayOrder/delayedSplittingCallback
+  #链接页面
+  linkPage: http://192.168.2.56:8009/pay/caimei-wisapay.html

+ 5 - 1
src/main/resources/config/prod/application-prod.yml

@@ -48,4 +48,8 @@ caimei:
   #支付异步回调地址
   notifyUrl: https://mall2c.caimei365.com/PayOrder/paymentCallback
   #支付链接重定向地址
-  redirectLink: https://spi.caimei365.com/PayOrder/jumpPage
+  redirectLink: https://mall2c.caimei365.com/PayOrder/jumpPage
+  #延时分账异步回调地址
+  delayedSplittingUrl: https://mall2c.caimei365.com/PayOrder/delayedSplittingCallback
+  #链接页面
+  linkPage: https://www.caimei365.com/pay/caimei-wisapay.html

+ 2 - 0
src/main/resources/mapper/OrderMapper.xml

@@ -74,6 +74,8 @@
         cso.itemCount,
         cso.totalAmount,
         cso.promotionFullReduction,
+        cso.shopPostFee,
+        cso.payStatus,
         s.name AS shopName,
         s.logo AS shopLogo
         FROM

+ 182 - 0
src/main/resources/mapper/PayOrderMapper.xml

@@ -389,4 +389,186 @@
     <select id="getPayOnLineSwitch" resultType="java.lang.Integer">
         select status from cm_pay_online_switch where id=1
     </select>
+
+    <select id="findUnallocatedAccount" resultType="com.caimei.model.po.CmReceiptOrderRelationPo">
+        SELECT
+          cror.id,
+          cror.relationType,
+          cror.receiptID,
+          cror.orderID,
+          cror.mbOrderId,
+          cror.splitStatus,
+          cror.associateAmount,
+          cdr.payType
+        FROM
+          cm_receipt_order_relation cror
+          LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+          LEFT JOIN cm_order co ON cror.orderID = co.orderID
+        WHERE
+          cror.relationType = 2
+          AND cdr.payWay = 1
+          AND cror.delFlag = 0
+          AND cror.mbOrderId IS NOT NULL
+          AND cror.splitStatus = 0
+          AND cdr.receiptDate <![CDATA[  <=  ]]> #{currentTime}
+          AND co.organizeID <![CDATA[  >=  ]]> 2
+    </select>
+
+    <select id="findPaidAmount" resultType="java.math.BigDecimal">
+        SELECT
+          SUM(splitAccount)
+        FROM
+          cm_split_account
+        WHERE
+          orderProductId = #{orderProductId}
+          AND payStatus = 1
+          AND productType = 1
+    </select>
+
+    <select id="findCommercialCode" resultType="string">
+        SELECT commercialCode FROM shop WHERE shopID = #{shopId}
+    </select>
+
+    <select id="findShipping" resultType="java.math.BigDecimal">
+        SELECT
+          SUM(splitAccount)
+        FROM
+          cm_split_account
+        WHERE
+          orderId = #{orderId}
+          AND shopId = #{shopId}
+          AND productType = 2
+          AND payStatus = 1
+    </select>
+
+    <insert id="insertSplitAccount" keyColumn="id" keyProperty="id" parameterType="com.caimei.model.vo.SplitAccountVo" useGeneratedKeys="true">
+        INSERT INTO `cm_split_account` (
+          `orderId`, `productId`, `orderProductId`,
+          `shopId`, `type`, `subUserNo`, `splitAccount`,
+          `mbOrderId`, `orderRequestNo`,
+          `payStatus`, `productType`, splitTime
+        )
+        VALUES
+          (
+            #{orderId}, #{productId}, #{orderProductId},
+            #{shopId}, #{type}, #{subUserNo}, #{splitAccount},
+            #{mbOrderId}, #{orderRequestNo},
+            #{payStatus}, #{productType}, NOW()
+          )
+    </insert>
+
+    <update id="updateBySplitStatus">
+        UPDATE cm_receipt_order_relation SET splitStatus = 1 WHERE mbOrderId = #{mbOrderId}
+    </update>
+
+    <select id="findByMbOrderId" resultType="com.caimei.model.vo.SplitAccountVo">
+        SELECT
+          orderId,
+          productId,
+          orderProductId,
+          shopId,
+          type,
+          subUserNo,
+          SUM(splitAccount) AS "splitAccount",
+          mbOrderId,
+          orderRequestNo,
+          payStatus,
+          productType
+        FROM
+          cm_split_account
+        WHERE
+          TYPE = 4
+          AND payStatus = 1
+          AND splitAccount > 0
+          AND mbOrderId = #{mbOrderId}
+        GROUP BY
+          shopID
+    </select>
+
+    <select id="findPaidShop" resultType="java.math.BigDecimal">
+        SELECT
+          SUM(payAmount)
+        FROM
+          cm_pay_shop_record
+        WHERE
+          STATUS = 1
+          AND delFlag = 0
+          AND shopOrderID = #{shopOrderId}
+    </select>
+
+    <update id="updateShopOrderByPayStatus">
+        UPDATE
+          cm_shop_order
+        SET
+          payStatus = #{payStatus},
+          payedShopAmount = #{paidShop}
+        WHERE
+          shopOrderID = #{shopOrderId}
+    </update>
+
+    <insert id="insertPayShop" useGeneratedKeys="true" keyProperty="id" keyColumn="id" parameterType="com.caimei.model.po.CmPayShopPo">
+        INSERT INTO `cm_pay_shop` (
+          `shopID`, `name`, `bankAccountName`,
+          `bankAccount`, `bankName`, `type`,
+          `totalAmount`, `balancePayFee`,
+          `transferPayFee`, `payType`, `wipePayment`,
+          `wipeRemarks`, `wipeRemarkImages`,
+          `wipeTime`, `applicant`, `applyTime`,
+          `reviewer`, `reviewTime`, `payTime`,
+          `status`, `reason`, `delFlag`
+        )
+        VALUES
+          (
+            #{shopID}, #{name}, #{bankAccountName},
+            #{bankAccount}, #{bankName}, #{type},
+            #{totalAmount}, #{balancePayFee},
+            #{transferPayFee}, #{payType}, #{wipePayment},
+            #{wipeRemarks}, #{wipeRemarkImages},
+            #{wipeTime}, #{applicant}, #{applyTime},
+            #{reviewer}, #{reviewTime}, #{payTime},
+            #{status}, #{reason}, #{delFlag}
+          )
+    </insert>
+
+    <insert id="insertPayShopRecord">
+        INSERT INTO `cm_pay_shop_record` (
+          `shopID`, `shopOrderID`, `shopOrderNo`,
+          `payAmount`, `wipePayment`, `payType`,
+          `payTime`, `payShopID`,
+          `status`, `delFlag`
+        )
+        VALUES
+          (
+            #{shopID}, #{shopOrderID}, #{shopOrderNo},
+            #{payAmount}, #{wipePayment}, #{payType},
+            #{payTime}, #{payShopID},
+            #{status}, #{delFlag}
+          )
+    </insert>
+
+    <update id="updateOrderByPayStatus">
+        UPDATE
+          cm_order
+        SET
+          payStatus = #{payStatus}
+        WHERE
+          orderID = #{orderId}
+    </update>
+
+    <select id="getOrderPayLink" resultType="com.caimei.model.vo.OrderPayLinkVo">
+        SELECT
+            `id`,
+          `orderId`,
+          `linkLogo`,
+          `unpaidAmount`,
+          `generateTime`,
+          `effectiveTime`,
+          `payStatus`,
+          `payType`
+        FROM
+          cm_order_pay_link
+        WHERE
+          linkLogo = #{linkLogo}
+          AND delFlag = '0'
+    </select>
 </mapper>