diff --git a/ruoyi-admin/src/test/java/com/fjp/lc/test/service/ServiceTest.java b/ruoyi-admin/src/test/java/com/fjp/lc/test/service/ServiceTest.java index e0ad6c7..5313849 100644 --- a/ruoyi-admin/src/test/java/com/fjp/lc/test/service/ServiceTest.java +++ b/ruoyi-admin/src/test/java/com/fjp/lc/test/service/ServiceTest.java @@ -5,6 +5,8 @@ import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; +import com.cyl.h5.pojo.dto.PayNotifyMessageDTO; +import com.cyl.h5.service.H5OrderService; import com.cyl.manager.ums.service.MemberCartService; import com.cyl.wechat.WechatAuthService; import com.cyl.wechat.WechatPayService; @@ -13,15 +15,19 @@ import com.ruoyi.RuoYiApplication; import com.ruoyi.common.config.properties.SmsProperties; import com.ruoyi.common.core.sms.AliyunSmsTemplate; import com.ruoyi.common.core.sms.SmsTemplate; +import com.ruoyi.common.enums.TradeStatusEnum; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.parameters.P; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -91,4 +97,19 @@ public class ServiceTest { System.out.println(res); } + + @Autowired + private H5OrderService h5OrderService; + + @Test + public void test6(){ + PayNotifyMessageDTO messageDTO = new PayNotifyMessageDTO(); + messageDTO.setPayTime(new Date()); + messageDTO.setOutTradeNo(5364038883215361L); + messageDTO.setMemberId(22L); + messageDTO.setTradeStatus(TradeStatusEnum.TRADE_SUCCESS.getStatus()); + messageDTO.setTradeNo(""); + ResponseEntity stringResponseEntity = h5OrderService.payCallBack(messageDTO); + System.out.println(stringResponseEntity.getBody()); + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 88f05e5..e5e1a09 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -230,4 +230,21 @@ public class Constants public static final Integer INVALID = 5; public static final Integer REFUND = -2; } + + /** + * 交易类型(1为支付 2为提现 3为退款) + */ + public static class PaymentOpType { + public static final Integer PAY = 1; + public static final Integer WITHDRAWAL = 2; + public static final Integer REFUND = 3; + } + + /** + * 状态(0:未完成交易 1:完成关键交易) + */ + public static class PaymentStatus { + public static final Integer INCOMPLETE = 0; + public static final Integer COMPLETE = 1; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OrderStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OrderStatus.java new file mode 100644 index 0000000..9e3a88c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OrderStatus.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum OrderStatus +{ + ALL_DATA(-1,"全部订单"), + UN_PAY(0, "待付款"), + NOT_DELIVERED(1, "待发货"), + DELIVERED(2, "待收货"), + COMPLETE(3, "已完成"), + CLOSED(4, "已关闭"), + INVALID(5, "无效订单"), + REFUUND(-2, "售后订单"); + + private final Integer type; + private final String msg; + + private OrderStatus(Integer type, String msg) { + this.type = type; + this.msg = msg; + } + + public Integer getType() { + return this.type; + } + + public String getMsg() { + return this.msg; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/TradeStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/TradeStatusEnum.java new file mode 100644 index 0000000..5e057d1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/TradeStatusEnum.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.enums; + +/** + * 支付状态 + */ +public enum TradeStatusEnum { + + // 交易成功 + TRADE_SUCCESS("TRADE_SUCCESS", 10), + SUCCESS("SUCCESS", 10), + // 用户待支付 + WAIT_BUYER_PAY("WAIT_BUYER_PAY", 20), + NOTPAY("NOTPAY", 20), + // 交易关闭 + TRADE_CLOSED("TRADE_CLOSED", 30), + CLOSED("CLOSED", 30), + // 交易结束 + TRADE_FINISHED("TRADE_FINISHED", 40), + // 未知状态码 + UNKNOWN("UNKNOWN", -1); + + public static int dealTradeStatus(String code) { + for (TradeStatusEnum tradeStatus : values()) { + if (tradeStatus.getCode().equals(code)) { + return tradeStatus.getStatus(); + } + } + return UNKNOWN.getStatus(); + } + + TradeStatusEnum(String code, Integer status) { + this.code = code; + this.status = status; + } + + private String code; + + private Integer status; + + public String getCode() { + return code; + } + + public TradeStatusEnum setCode(String code) { + this.code = code; + return this; + } + + public Integer getStatus() { + return status; + } + + public TradeStatusEnum setStatus(Integer status) { + this.status = status; + return this; + } + +} diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/pojo/dto/PayNotifyMessageDTO.java b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/dto/PayNotifyMessageDTO.java new file mode 100644 index 0000000..d0a589a --- /dev/null +++ b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/dto/PayNotifyMessageDTO.java @@ -0,0 +1,33 @@ +package com.cyl.h5.pojo.dto; + +import lombok.Data; + +import java.util.Date; + +@Data +public class PayNotifyMessageDTO { + + /**主订单号**/ + private Long outTradeNo; + + /**第三方订单号**/ + private String tradeNo; + + /** + * 交易状态,枚举值: + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(仅付款码支付会返回) + * USERPAYING:用户支付中(仅付款码支付会返回) + * PAYERROR:支付失败(仅付款码支付会返回) + */ + private Integer tradeStatus; + + /**支付时间**/ + private Date payTime; + + /**用户id**/ + private Long memberId; +} diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java index f9839ca..7b6bc94 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java @@ -2,12 +2,14 @@ package com.cyl.h5.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyl.h5.pojo.dto.OrderCreateDTO; import com.cyl.h5.pojo.dto.OrderProductListDTO; +import com.cyl.h5.pojo.dto.PayNotifyMessageDTO; import com.cyl.h5.pojo.request.CancelOrderRequest; import com.cyl.h5.pojo.request.OrderPayRequest; import com.cyl.h5.pojo.response.OrderPayResponse; @@ -19,9 +21,11 @@ import com.cyl.h5.pojo.vo.form.OrderSubmitForm; import com.cyl.manager.oms.domain.Order; import com.cyl.manager.oms.domain.OrderItem; import com.cyl.manager.oms.domain.OrderOperateHistory; +import com.cyl.manager.oms.domain.WechatPaymentHistory; import com.cyl.manager.oms.mapper.OrderItemMapper; import com.cyl.manager.oms.mapper.OrderMapper; import com.cyl.manager.oms.mapper.OrderOperateHistoryMapper; +import com.cyl.manager.oms.mapper.WechatPaymentHistoryMapper; import com.cyl.manager.oms.service.OrderItemService; import com.cyl.manager.oms.service.OrderOperateHistoryService; import com.cyl.manager.pms.domain.Product; @@ -40,13 +44,19 @@ import com.cyl.wechat.WechatPayService; import com.cyl.wechat.WechatPayUtil; import com.github.pagehelper.PageHelper; import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisService; +import com.ruoyi.common.enums.OrderStatus; +import com.ruoyi.common.enums.TradeStatusEnum; import com.ruoyi.common.utils.IDGenerator; import com.ruoyi.framework.config.LocalDataUtil; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -64,6 +74,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @Service +@Slf4j public class H5OrderService { @Autowired @@ -99,6 +110,12 @@ public class H5OrderService { @Autowired private MemberWechatMapper memberWechatMapper; + @Autowired + private WechatPaymentHistoryMapper wechatPaymentHistoryMapper; + + @Autowired + private RedisService redisService; + @Transactional public Long submit(OrderSubmitForm form) { Member member = (Member) LocalDataUtil.getVar(Constants.MEMBER_INFO); @@ -446,6 +463,31 @@ public class H5OrderService { orderItemQw.eq("order_id", orderList.get(0).getId()); List orderItemList = orderItemMapper.selectList(orderItemQw); String orderDesc = orderItemList.get(0).getProductName().substring(0, Math.min(40, orderItemList.get(0).getProductName().length())); + //保存微信支付历史 + LocalDateTime optDate = LocalDateTime.now(); + QueryWrapper wxPaymentQw = new QueryWrapper<>(); + wxPaymentQw.eq("order_id", orderList.get(0).getId()); + wxPaymentQw.eq("op_type", Constants.PaymentOpType.PAY); + WechatPaymentHistory wechatPaymentHistory = wechatPaymentHistoryMapper.selectOne(wxPaymentQw); + if (wechatPaymentHistory == null){ + wechatPaymentHistory = new WechatPaymentHistory(); + wechatPaymentHistory.setOrderId(orderList.get(0).getPayId()); + wechatPaymentHistory.setMemberId(req.getMemberId()); + wechatPaymentHistory.setOpenid(memberWechat.getOpenid()); + wechatPaymentHistory.setTitle(orderItemList.get(0).getProductName()); + wechatPaymentHistory.setMoney(orderList.get(0).getPayAmount()); + wechatPaymentHistory.setOpType(Constants.PaymentOpType.PAY); + wechatPaymentHistory.setPaymentStatus(0); + wechatPaymentHistory.setCreateBy(req.getMemberId()); + wechatPaymentHistory.setCreateTime(optDate); + wechatPaymentHistory.setUpdateBy(req.getMemberId()); + wechatPaymentHistory.setUpdateTime(optDate); + wechatPaymentHistoryMapper.insert(wechatPaymentHistory); + }else { + wechatPaymentHistory.setMoney(orderList.get(0).getPayAmount()); + wechatPaymentHistoryMapper.updateById(wechatPaymentHistory); + } + //调用wx的jsapi拿prepayId,返回签名等信息 String prepayId = wechatPayService.jsapiPay( String.valueOf(req.getPayId()), orderDesc, @@ -477,4 +519,65 @@ public class H5OrderService { return response; } + /** + * 支付回调方法 + * @param messageDTO + * @return + */ + @Transactional + public ResponseEntity payCallBack(PayNotifyMessageDTO messageDTO){ + log.info("【订单支付回调】" + JSONObject.toJSON(messageDTO)); + String redisKey = "h5_oms_order_pay_notify_" + messageDTO.getOutTradeNo(); + String redisValue = messageDTO.getOutTradeNo() + "_" + System.currentTimeMillis(); + LocalDateTime optDate = LocalDateTime.now(); + try{ + redisService.lock(redisKey, redisValue, 60); + //先判断回信回调的是否未success + if (!TradeStatusEnum.SUCCESS.getStatus().equals(messageDTO.getTradeStatus())){ + log.error("【订单支付回调】订单状态不是支付成功状态" + messageDTO.getTradeStatus()); + throw new RuntimeException(); + } + QueryWrapper paymentWrapper = new QueryWrapper<>(); + paymentWrapper.eq("order_id", messageDTO.getOutTradeNo()); + paymentWrapper.eq("op_type", Constants.PaymentOpType.PAY); + WechatPaymentHistory paymentHistory = wechatPaymentHistoryMapper.selectOne(paymentWrapper); + if (paymentHistory.getPaymentStatus() != Constants.PaymentStatus.INCOMPLETE) { + log.info("【订单支付回调】支付订单不是未支付状态,不再处理" + "orderId" + paymentHistory.getOrderId() + "status" + paymentHistory.getPaymentStatus()); + throw new RuntimeException(); + } + QueryWrapper orderQw = new QueryWrapper<>(); + orderQw.eq("pay_id", messageDTO.getOutTradeNo()); + orderQw.eq("status", OrderStatus.UN_PAY.getType()); + List orderList = orderMapper.selectList(orderQw); + orderList.forEach(order -> { + order.setPaymentTime(messageDTO.getPayTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); + order.setStatus(OrderStatus.NOT_DELIVERED.getType()); + orderMapper.updateById(order); + + OrderOperateHistory optHistory = new OrderOperateHistory(); + optHistory.setOrderId(order.getId()); + optHistory.setOperateMan("系统"); + optHistory.setOrderStatus(OrderStatus.COMPLETE.getType()); + optHistory.setCreateTime(optDate); + optHistory.setCreateBy(order.getMemberId()); + optHistory.setUpdateBy(order.getMemberId()); + optHistory.setUpdateTime(optDate); + orderOperateHistoryMapper.insert(optHistory); + }); + UpdateWrapper paymentHistoryUpdateWrapper = new UpdateWrapper<>(); + paymentHistoryUpdateWrapper.eq("order_id", messageDTO.getOutTradeNo()).set("payment_id", messageDTO.getTradeNo()) + .set("payment_status", Constants.PaymentStatus.COMPLETE).set("update_time", optDate); + wechatPaymentHistoryMapper.update(null, paymentHistoryUpdateWrapper); + }catch (Exception e){ + log.error("订单支付回调异常"); + throw new RuntimeException("订单支付回调异常"); + }finally { + try{ + redisService.unLock(redisKey, redisValue); + }catch (Exception e){ + log.error("", e); + } + } + return ResponseEntity.ok("订单支付回调成功"); + } }