TRX 1 рік тому
батько
коміт
c3f5e88860

+ 25 - 0
FullCardServer/src/main/java/com/zhongshu/card/server/core/dao/payment/PaymentProcessDao.java

@@ -0,0 +1,25 @@
+package com.zhongshu.card.server.core.dao.payment;
+
+import com.github.microservice.components.data.mongo.mongo.dao.MongoDao;
+import com.github.microservice.types.payment.PaymentType;
+import com.zhongshu.card.server.core.dao.payment.extend.ExpenseFlowDaoExtend;
+import com.zhongshu.card.server.core.domain.payment.ExpenseFlow;
+import com.zhongshu.card.server.core.domain.payment.PaymentProcess;
+
+import java.util.List;
+
+/**
+ * 订单支付过程
+ *
+ * @author TRX
+ * @date 2024/3/21
+ */
+public interface PaymentProcessDao extends MongoDao<PaymentProcess> {
+
+    PaymentProcess findTopById(String id);
+
+    List<PaymentProcess> findByPaymentNoOrderByCreateTimeAsc(String paymentNo);
+
+    PaymentProcess findTopByPaymentNoAndPaymentTypeOrderByCreateTimeDesc(String paymentNo, PaymentType paymentType);
+
+}

+ 1 - 0
FullCardServer/src/main/java/com/zhongshu/card/server/core/dao/projectAbout/ProjectPaySettingDao.java

@@ -17,4 +17,5 @@ public interface ProjectPaySettingDao extends MongoDao<ProjectPaySetting> {
 
     ProjectPaySetting findTopByProjectOidAndState(String projectOid, DataState state);
 
+    List<ProjectPaySetting> findByProjectOidAndState(String projectOid, DataState state);
 }

+ 6 - 3
FullCardServer/src/main/java/com/zhongshu/card/server/core/domain/payment/ExpenseFlow.java

@@ -56,7 +56,7 @@ public class ExpenseFlow extends SuperMain {
     private String paymentWay;
 
     @Schema(description = "支付方式")
-    private PaymentType paymentType = PaymentType.WxQrCode;
+    private PaymentType paymentType;
 
     @Schema(description = "消费的用户userId")
     private String userId;
@@ -152,7 +152,7 @@ public class ExpenseFlow extends SuperMain {
     @Schema(description = "支付设备和方式")
     private PaymentDeviceType paymentDeviceType;
 
-    @Schema(description = "验证参数是否符合要求")
+    @Schema(description = "验证参数是否符合要求,可以发起支付")
     private Boolean verifyParamIsSuccess = Boolean.TRUE;
 
     @Schema(description = "验证参数的提示")
@@ -180,6 +180,9 @@ public class ExpenseFlow extends SuperMain {
     @Schema(description = "支付是否成功")
     private Boolean isPaySuccess;
 
+    @Schema(description = "是否已支付")
+    private Boolean isPaid = Boolean.FALSE;
+
     @Schema(description = "支付结果备注,返回给设备展示")
     private String payRemark;
 
@@ -237,5 +240,5 @@ public class ExpenseFlow extends SuperMain {
 
     @Schema(description = "结算时间")
     private Long settlementTime;
-    
+
 }

+ 54 - 0
FullCardServer/src/main/java/com/zhongshu/card/server/core/domain/payment/PaymentProcess.java

@@ -0,0 +1,54 @@
+package com.zhongshu.card.server.core.domain.payment;
+
+import com.github.microservice.types.payment.PaymentType;
+import com.zhongshu.card.server.core.domain.base.SuperMain;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.checkerframework.common.aliasing.qual.NonLeaked;
+import org.springframework.data.mongodb.core.mapping.DBRef;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @author TRX
+ * @date 2024/11/13
+ */
+@Data
+@Document
+@AllArgsConstructor
+@NoArgsConstructor
+@NonLeaked
+public class PaymentProcess extends SuperMain {
+
+    @Schema(description = "订单数据")
+    @DBRef(lazy = true)
+    private ExpenseFlow expenseFlow;
+
+    @Schema(description = "订单号")
+    private String paymentNo;
+
+    @Schema(description = "支付渠道")
+    private PaymentType paymentType;
+
+    @Schema(description = "是否在支付中")
+    private Boolean isPaying = Boolean.FALSE;
+
+    @Schema(description = "支付是否成功")
+    private Boolean isPaySuccess;
+
+    @Schema(description = "支付结果备注,返回给设备展示")
+    private String payRemark;
+
+    @Schema(description = "支付开始时间")
+    private Long payStartTime;
+
+    @Schema(description = "支付结束时间")
+    private Long payEndTime;
+
+    @Schema(description = "支付耗用的时间")
+    private Long payMillis = 0L;
+
+    @Schema(description = "可度时间")
+    private String timeStr;
+}

+ 50 - 5
FullCardServer/src/main/java/com/zhongshu/card/server/core/service/paySetting/PayOrderSettingService.java

@@ -18,10 +18,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -61,7 +58,13 @@ public class PayOrderSettingService extends SuperService {
         return ResultContent.buildSuccess(models);
     }
 
-
+    /**
+     * 得到用户的支付渠道排序数据
+     *
+     * @param userId
+     * @param projectOid
+     * @return
+     */
     public List<PayOrderSetting> getUserPayOrderSettings(String userId, String projectOid) {
         // 已有的数据
         List<PayOrderSetting> payOrderSettings = payOrderSettingDao.findByUserIdAndProjectOidOrderBySortAsc(userId, projectOid);
@@ -76,6 +79,7 @@ public class PayOrderSettingService extends SuperService {
                 map.put(temp.getPaymentType(), temp);
             }
         }
+
         // 所有的支付渠道
         List<PaymentType> paymentTypes = getProjectPayment(projectOid);
         List<PayOrderSetting> newList = new ArrayList<>();
@@ -101,6 +105,41 @@ public class PayOrderSettingService extends SuperService {
         return newList;
     }
 
+    public List<PaymentType> getUserPayment(String userId, String projectOid) {
+        // 得到项目支持的付款方式
+        List<PaymentType> supportTypes = getProjectPayment(projectOid);
+        if (ObjectUtils.isNotEmpty(supportTypes)) {
+            List<PayOrderSetting> payOrderSettings = payOrderSettingDao.findByUserIdAndProjectOidOrderBySortAsc(userId, projectOid);
+            if (ObjectUtils.isNotEmpty(payOrderSettings)) {
+                Map<PaymentType, Long> map = payOrderSettings.stream().collect(Collectors.toMap(it -> it.getPaymentType(), it -> it.getSort()));
+                long defaultSort = 0;
+                // 排序
+                Collections.sort(supportTypes, new Comparator<PaymentType>() {
+                    @Override
+                    public int compare(PaymentType o1, PaymentType o2) {
+                        long sort1 = map.get(o1) != null ? map.get(o1) : defaultSort;
+                        long sort2 = map.get(o2) != null ? map.get(o2) : defaultSort;
+                        return (int) (sort1 - sort2);
+                    }
+                });
+            }
+        }
+
+        //
+        List<PaymentType> userPaymentTypes = new ArrayList<>();
+        if (ObjectUtils.isNotEmpty(supportTypes)) {
+            for (PaymentType paymentType : supportTypes) {
+                if (paymentType == PaymentType.WeChat) {
+                    paymentType = PaymentType.UserWallet;
+                }
+                if (!userPaymentTypes.contains(paymentType)) {
+                    userPaymentTypes.add(paymentType);
+                }
+            }
+        }
+        return userPaymentTypes;
+    }
+
     public List<PaymentType> getProjectPayment(String projectOid) {
         List<PaymentType> paymentTypes = new ArrayList<PaymentType>();
         List<ProjectPaySetting> projectPaySettings = projectPaySettingDao.findByProjectOid(projectOid);
@@ -116,6 +155,12 @@ public class PayOrderSettingService extends SuperService {
         return paymentTypes;
     }
 
+    /**
+     * 保存排序信息
+     *
+     * @param param
+     * @return
+     */
     public ResultContent saveOrder(PayOrderSettingParam param) {
         String userId = getCurrentUserId();
         String projectOid = getCurrentProjectOid();

+ 1 - 17
FullCardServer/src/main/java/com/zhongshu/card/server/core/service/payment/ExpenseFlowServiceImpl.java

@@ -173,10 +173,10 @@ public class ExpenseFlowServiceImpl extends SuperService implements ExpenseFlowS
 
         if (entity.getMode() != null) {
             if (entity.getMode() == 0) {
+                // 支付模式
                 entity.setOrderModeType(OrderModeType.Consume);
                 // 支付
                 payCallService.payOrder(entity);
-                expenseFlowDao.save(entity);
             } else if (entity.getMode() == 2) {
                 // 查询余额模式
                 entity.setOrderModeType(OrderModeType.BalanceInquiry);
@@ -281,18 +281,6 @@ public class ExpenseFlowServiceImpl extends SuperService implements ExpenseFlowS
             return;
         }
 
-        // 确定项目的付款方式
-        ResultContent<PaymentType> resultContent = projectPaySettingService.getProjectPayment(entity.getProjectOid());
-        if (resultContent.isFailed()) {
-            entity.setVerifyError(resultContent.getMsg());
-            return;
-        }
-        // 支付方式来自项目配置
-        PaymentType paymentType = resultContent.getContent();
-        entity.setPaymentType(paymentType);
-        entity.setPaymentWay(paymentType.getRemark());
-
-        // 刷卡支付 并且钱包支付 paymentType == PaymentType.UserWallet &&
         if (entity.getOrderFromType() == OrderFromType.Swipe) {
             CardInfo cardInfo = cardInfoDao.findByCode(entity.getCardNo());
             // 验证卡片
@@ -358,10 +346,6 @@ public class ExpenseFlowServiceImpl extends SuperService implements ExpenseFlowS
         } else {
             entity.setPayType("消费");
         }
-        if (entity.getPaymentType() == null) {
-            entity.setVerifyError("支付方式不支持");
-            return;
-        }
 
         // 金额判断
         BigDecimal payAmount = entity.getPayAmount();

+ 190 - 6
FullCardServer/src/main/java/com/zhongshu/card/server/core/service/payment/PayCallService.java

@@ -11,14 +11,19 @@ import com.zhongshu.card.client.model.payment.ExpenseRefundParam;
 import com.zhongshu.card.client.type.LogsLevel;
 import com.zhongshu.card.client.type.MessageType;
 import com.zhongshu.card.client.type.RefundState;
+import com.zhongshu.card.client.utils.DateUtils;
 import com.zhongshu.card.client.utils.PayExceptionToShowUtil;
 import com.zhongshu.card.server.core.dao.payment.ExpenseFlowDao;
+import com.zhongshu.card.server.core.dao.payment.PaymentProcessDao;
 import com.zhongshu.card.server.core.domain.payment.ExpenseFlow;
+import com.zhongshu.card.server.core.domain.payment.PaymentProcess;
 import com.zhongshu.card.server.core.domain.schedule.ScheduleTaskConfig;
 import com.zhongshu.card.server.core.service.base.SuperService;
 import com.zhongshu.card.server.core.service.orgManager.ProjectBindOrgServiceImpl;
 import com.zhongshu.card.server.core.service.pay.BalancePayService;
+import com.zhongshu.card.server.core.service.pay.ChinaumsSenselessPayService;
 import com.zhongshu.card.server.core.service.pay.SettleService;
+import com.zhongshu.card.server.core.service.paySetting.PayOrderSettingService;
 import com.zhongshu.card.server.core.service.user.OperationLogsService;
 import com.zhongshu.card.server.core.util.NextNoUtil;
 import lombok.extern.slf4j.Slf4j;
@@ -31,6 +36,8 @@ import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -50,12 +57,21 @@ public class PayCallService extends SuperService {
     @Autowired
     ExpenseFlowDao expenseFlowDao;
 
+    @Autowired
+    private PaymentProcessDao paymentProcessDao;
+
     @Autowired
     private ProjectBindOrgServiceImpl projectBindOrgService;
 
     @Autowired
     private SettleService settleService;
 
+    @Autowired
+    private PayOrderSettingService payOrderSettingService;
+
+    @Autowired
+    private ChinaumsSenselessPayService chinaumsSenselessPayService;
+
     /**
      * 统一调用支付服务
      *
@@ -63,14 +79,66 @@ public class PayCallService extends SuperService {
      * @return
      */
     public ResultContent payOrder(ExpenseFlow entity) {
-        PaymentType paymentType = entity.getPaymentType();
         OrderState orderType = entity.getOrderType();
         if (orderType != OrderState.WAIT_PAYMENT) {
             return ResultContent.buildFail(String.format("订单不能支付:%s", orderType.getRemark()));
         }
-        // 判断支付渠道
-        if (paymentType == PaymentType.UserWallet) {
-            return balancePay(entity);
+        // 得到用户所有的支付渠道,依次尝试扣款
+        List<PaymentType> paymentTypes = payOrderSettingService.getUserPayment(entity.getUserId(), entity.getProjectOid());
+        if (ObjectUtils.isEmpty(paymentTypes)) {
+            String msg = "项目无支付渠道";
+            entity.setPaymentStatus("支付失败");
+            entity.setIsPaySuccess(Boolean.FALSE);
+            entity.setPayRemark(msg);
+            entity.setOrderType(OrderState.PAID_ERROR);
+            entity.setIsPaid(Boolean.TRUE);
+            expenseFlowDao.save(entity);
+            return ResultContent.buildFail(msg);
+        }
+        log.info("开始支付: {} {}", entity.getPaymentNo(), paymentTypes);
+        int maxTime = 4 * 1000;
+        boolean b = false;
+        for (PaymentType paymentType : paymentTypes) {
+            entity = expenseFlowDao.findTopById(entity.getId());
+            entity.setPaymentType(paymentType);
+            entity.setPaymentWay(paymentType.getRemark());
+            if (paymentType == PaymentType.UserWallet) {
+                balancePay(entity);
+            } else if (paymentType == PaymentType.UnionFrictionlessPay) {
+                unionFrictionlessPay(entity);
+            }
+
+            boolean isPaying = isPaying(entity.getPaymentNo(), paymentType);
+            if (isPaying) {
+                AtomicInteger atomicInteger = new AtomicInteger(0);
+                while (atomicInteger.get() <= maxTime) {
+                    isPaying = isPaying(entity.getPaymentNo(), paymentType);
+                    if (!isPaying) {
+                        break;
+                    }
+                    try {
+                        TimeUnit.MILLISECONDS.sleep(300);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                    atomicInteger.addAndGet(300);
+                }
+            }
+            if (entity.getIsPaySuccess() != null && entity.getIsPaySuccess()) {
+                b = true;
+                break;
+            }
+        }
+
+        // 所有的渠道都支付失败
+        if (!b) {
+            String msg = "支付失败";
+            entity.setPaymentStatus("支付失败");
+            entity.setIsPaySuccess(Boolean.FALSE);
+            entity.setPayRemark(msg);
+            entity.setOrderType(OrderState.PAID_ERROR);
+            entity.setIsPaid(Boolean.TRUE);
+            expenseFlowDao.save(entity);
         }
         return ResultContent.buildSuccess();
     }
@@ -86,8 +154,13 @@ public class PayCallService extends SuperService {
         OperationLogsAddParam logsAddParam = initLog(entity.getUserId());
         logsAddParam.setTitle(desc);
         logsAddParam.setDataId(entity.getPaymentNo());
-        entity.setPayStartTime(System.currentTimeMillis());
+        PaymentProcess paymentProcess = new PaymentProcess();
+        paymentProcess.setPaymentNo(entity.getPaymentNo());
+        paymentProcess.setExpenseFlow(entity);
+        paymentProcess.setPaymentType(PaymentType.UserWallet);
+        paymentProcess.setPayStartTime(System.currentTimeMillis());
 
+        entity.setPayStartTime(System.currentTimeMillis());
         com.github.microservice.pay.client.ret.ResultContent<List<TransactionLogModel>> resultContent = balancePayService.balancePay(entity.getProjectOid(), entity.getShopOid(), entity.getUserId(), entity.getPayAmount(), entity.getPaymentNo(), entity.getRemark());
         if (resultContent.getState() == ResultState.Success) {
             // 关联参数
@@ -99,22 +172,35 @@ public class PayCallService extends SuperService {
             entity.setPayRemark(msg);
             entity.setOrderType(OrderState.HAVE_PAID);
 
+            paymentProcess.setIsPaySuccess(Boolean.TRUE);
+            paymentProcess.setPayRemark(msg);
             // 日志
             logsAddParam.setMessageType(MessageType.Info);
             logsAddParam.setLevel(LogsLevel.Low);
             logsAddParam.setContent(msg);
+
+            paymentProcess.setPayStartTime(System.currentTimeMillis());
+            paymentProcess.setPayMillis(paymentProcess.getPayEndTime() - paymentProcess.getPayStartTime());
+            paymentProcess.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+            paymentProcessDao.save(paymentProcess);
         } else {
             String msg = resultContent.getMsg();
             if (StringUtils.isNotEmpty(msg) && msg.contains("Connection reset")) {
                 msg = "调用支付中心失败";
             } else {
                 String cls = resultContent.getException().getCls();
+                msg = PayExceptionToShowUtil.convertException(cls);
                 if (cls.contains("UncategorizedMongoDbException") && entity.getRepeatTime() <= 1) {
                     // 如果是并发引起的问题,则重复一次
                     entity.setRepeatTime(entity.getRepeatTime() + 1);
+                    paymentProcess.setIsPaySuccess(Boolean.FALSE);
+                    paymentProcess.setPayRemark(msg);
+                    paymentProcess.setPayEndTime(System.currentTimeMillis());
+                    paymentProcess.setPayMillis(paymentProcess.getPayEndTime() - paymentProcess.getPayStartTime());
+                    paymentProcess.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+                    paymentProcessDao.save(paymentProcess);
                     return balancePay(entity);
                 }
-                msg = PayExceptionToShowUtil.convertException(cls);
             }
             if (StringUtils.isEmpty(msg)) {
                 msg = "支付中心失败";
@@ -123,19 +209,117 @@ public class PayCallService extends SuperService {
             entity.setIsPaySuccess(Boolean.FALSE);
             entity.setPayRemark(msg);
             entity.setOrderType(OrderState.PAID_ERROR);
+
+            paymentProcess.setIsPaySuccess(Boolean.FALSE);
+            paymentProcess.setPayRemark(msg);
+            // 日志
+            logsAddParam.setMessageType(MessageType.Warn);
+            logsAddParam.setLevel(LogsLevel.Middle);
+            logsAddParam.setContent(msg);
+        }
+        // 保存日志
+        operationLogsService.addLog(logsAddParam);
+
+        entity.setPayEndTime(System.currentTimeMillis());
+        entity.setPayMillis(entity.getPayEndTime() - entity.getPayStartTime());
+        expenseFlowDao.save(entity);
+
+        paymentProcess.setPayEndTime(System.currentTimeMillis());
+        paymentProcess.setPayMillis(paymentProcess.getPayEndTime() - paymentProcess.getPayStartTime());
+        paymentProcess.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        paymentProcessDao.save(paymentProcess);
+        return ResultContent.buildSuccess();
+    }
+
+    /**
+     * 银联 云闪付
+     *
+     * @param entity
+     * @return
+     */
+    public ResultContent unionFrictionlessPay(ExpenseFlow entity) {
+        String desc = "消费";
+        OperationLogsAddParam logsAddParam = initLog(entity.getUserId());
+        logsAddParam.setTitle(desc);
+        logsAddParam.setDataId(entity.getPaymentNo());
+
+        PaymentProcess paymentProcess = new PaymentProcess();
+        paymentProcess.setPaymentNo(entity.getPaymentNo());
+        paymentProcess.setExpenseFlow(entity);
+        paymentProcess.setPaymentType(PaymentType.UnionFrictionlessPay);
+        paymentProcess.setPayStartTime(System.currentTimeMillis());
+        paymentProcess.setIsPaying(Boolean.TRUE); // 支付中
+
+        entity.setPayStartTime(System.currentTimeMillis());
+        com.github.microservice.net.ResultContent resultContent = chinaumsSenselessPayService.senselessPay(entity.getProjectOid(), entity.getShopOid(), entity.getUserId(), entity.getPayAmount(), entity.getPaymentNo(), entity.getRemark());
+        if (resultContent.getState() == com.github.microservice.net.ResultState.Success) {
+            // 关联参数
+            String msg = "支付中";
+            entity.setPaymentStatus(msg);
+            entity.setPayRemark(msg);
+            entity.setOrderType(OrderState.PAY_IN_PROGRESS);
+
+            // 日志
+            logsAddParam.setMessageType(MessageType.Info);
+            logsAddParam.setLevel(LogsLevel.Low);
+            logsAddParam.setContent(msg);
+        } else {
+            String msg = resultContent.getMsg();
+            if (StringUtils.isNotEmpty(msg) && msg.contains("Connection reset")) {
+                msg = "调用支付中心失败";
+            } else {
+                if (resultContent.getException() != null) {
+                    String cls = resultContent.getException().getCls();
+                    msg = PayExceptionToShowUtil.convertException(cls);
+                }
+            }
+            if (StringUtils.isEmpty(msg)) {
+                msg = "支付中心失败";
+            }
+            entity.setPaymentStatus(msg);
+            entity.setIsPaySuccess(Boolean.FALSE);
+            entity.setPayRemark(msg);
+            entity.setOrderType(OrderState.PAID_ERROR);
+
             // 日志
             logsAddParam.setMessageType(MessageType.Warn);
             logsAddParam.setLevel(LogsLevel.Middle);
             logsAddParam.setContent(msg);
+
+            // 支付记录
+            paymentProcess.setIsPaySuccess(Boolean.FALSE);
+            paymentProcess.setPayRemark(msg);
+            paymentProcess.setIsPaying(Boolean.FALSE);
         }
         // 保存日志
         operationLogsService.addLog(logsAddParam);
 
         entity.setPayEndTime(System.currentTimeMillis());
         entity.setPayMillis(entity.getPayEndTime() - entity.getPayStartTime());
+        expenseFlowDao.save(entity);
+
+        paymentProcess.setPayEndTime(System.currentTimeMillis());
+        paymentProcess.setPayMillis(paymentProcess.getPayEndTime() - paymentProcess.getPayStartTime());
+        paymentProcess.setTimeStr(DateUtils.paresTime(System.currentTimeMillis(), DateUtils.FORMAT_LONG));
+        paymentProcessDao.save(paymentProcess);
         return ResultContent.buildSuccess();
     }
 
+    /**
+     * 订单是否在支付中
+     *
+     * @param paymentNo
+     * @param paymentType
+     * @return
+     */
+    public boolean isPaying(String paymentNo, PaymentType paymentType) {
+        PaymentProcess paymentProcess = paymentProcessDao.findTopByPaymentNoAndPaymentTypeOrderByCreateTimeDesc(paymentNo, paymentType);
+        if (ObjectUtils.isNotEmpty(paymentProcess) && paymentProcess.getIsPaying() != null && paymentProcess.getIsPaying()) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * 查询余额
      *