From 229ce39c5e29ab8fca6f4c32c0e2ab6ac8d4171f Mon Sep 17 00:00:00 2001 From: wuchunlei Date: Tue, 19 Dec 2023 17:04:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98=E5=AE=9D?= =?UTF-8?q?=E9=80=80=E6=AC=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/peanut/config/ShiroConfig.java | 1 + .../mq/Consumer/OrderCancelConsumer.java | 2 +- .../modules/pay/alipay/config/AliPayUtil.java | 3 +- .../alipay/controller/AliPayController.java | 4 +- .../modules/pay/alipay/dto/ReFundDTO.java | 5 + .../pay/alipay/service/AliPayService.java | 5 +- .../service/impl/AliPayServiceImpl.java | 42 +++- .../refund/controller/RefundController.java | 30 +++ .../pay/refund/dao/PayRefundOrderDao.java | 9 + .../pay/refund/entity/PayRefundOrder.java | 57 +++++ .../refund/service/PayRefundOrderService.java | 16 ++ .../impl/PayRefundOrderServiceImpl.java | 190 ++++++++++++++ .../pay/weChatPay/config/WechatPayConfig.java | 5 + .../controller/WeChatPayController.java | 160 ++---------- .../pay/weChatPay/service/WxpayService.java | 8 +- .../service/impl/WxpayServiceImpl.java | 234 +++++++++++++++++- src/main/resources/application-dev.yml | 3 +- src/main/resources/application.yml | 2 + 18 files changed, 617 insertions(+), 159 deletions(-) create mode 100644 src/main/java/com/peanut/modules/pay/refund/controller/RefundController.java create mode 100644 src/main/java/com/peanut/modules/pay/refund/dao/PayRefundOrderDao.java create mode 100644 src/main/java/com/peanut/modules/pay/refund/entity/PayRefundOrder.java create mode 100644 src/main/java/com/peanut/modules/pay/refund/service/PayRefundOrderService.java create mode 100644 src/main/java/com/peanut/modules/pay/refund/service/impl/PayRefundOrderServiceImpl.java diff --git a/src/main/java/com/peanut/config/ShiroConfig.java b/src/main/java/com/peanut/config/ShiroConfig.java index 72408404..030239bb 100644 --- a/src/main/java/com/peanut/config/ShiroConfig.java +++ b/src/main/java/com/peanut/config/ShiroConfig.java @@ -58,6 +58,7 @@ public class ShiroConfig { filterMap.put("book/task/list","anon"); // 网站接口 filterMap.put("/pay/aliPay/notify","anon"); // 支付宝回调接口 filterMap.put("/pay/payNotify","anon"); // 微信回调接口 + filterMap.put("/pay/refundNotify","anon"); // 微信退款回调接口 filterMap.put("/weChat/**","anon"); filterMap.put("/book/baseArea/getAllBaseArea","anon");//登录前获取全部区域 // filterMap.put("/book/bookchaptercontent/**","anon"); diff --git a/src/main/java/com/peanut/modules/mq/Consumer/OrderCancelConsumer.java b/src/main/java/com/peanut/modules/mq/Consumer/OrderCancelConsumer.java index 4464ecdb..ac3b3eab 100644 --- a/src/main/java/com/peanut/modules/mq/Consumer/OrderCancelConsumer.java +++ b/src/main/java/com/peanut/modules/mq/Consumer/OrderCancelConsumer.java @@ -47,8 +47,8 @@ public class OrderCancelConsumer { shopProduct.setProductStock(shopProduct.getProductStock()+b.getQuantity()); shopProductDao.updateById(shopProduct); } - } buyOrderService.updateById(buyOrder); + buyOrderService.removeById(buyOrder); } } \ No newline at end of file diff --git a/src/main/java/com/peanut/modules/pay/alipay/config/AliPayUtil.java b/src/main/java/com/peanut/modules/pay/alipay/config/AliPayUtil.java index 45bb0dbd..1f216c4f 100644 --- a/src/main/java/com/peanut/modules/pay/alipay/config/AliPayUtil.java +++ b/src/main/java/com/peanut/modules/pay/alipay/config/AliPayUtil.java @@ -102,7 +102,8 @@ public class AliPayUtil { // 退款原因 model.setRefundReason(reFundDTO.getRefundReason()); } - model.setOutRequestNo(UUID.randomUUID().toString().substring(0, 6)); + //退款请求号。标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 + model.setOutRequestNo(reFundDTO.getOutRequestNo()); request.setBizModel(model); AlipayTradeRefundResponse response = null; diff --git a/src/main/java/com/peanut/modules/pay/alipay/controller/AliPayController.java b/src/main/java/com/peanut/modules/pay/alipay/controller/AliPayController.java index 9bcf8bdc..5ef31b27 100644 --- a/src/main/java/com/peanut/modules/pay/alipay/controller/AliPayController.java +++ b/src/main/java/com/peanut/modules/pay/alipay/controller/AliPayController.java @@ -50,8 +50,8 @@ public class AliPayController { * 支付宝退款 */ @RequestMapping("/refund") - public R refund(@RequestBody ReFundDTO reFundDTO) { - String refund = aliPayService.refund(reFundDTO); + public R refund(@RequestBody Map params) { + String refund = aliPayService.refund(params); return R.ok().put("msg",refund); } diff --git a/src/main/java/com/peanut/modules/pay/alipay/dto/ReFundDTO.java b/src/main/java/com/peanut/modules/pay/alipay/dto/ReFundDTO.java index dbea1027..40c716ed 100644 --- a/src/main/java/com/peanut/modules/pay/alipay/dto/ReFundDTO.java +++ b/src/main/java/com/peanut/modules/pay/alipay/dto/ReFundDTO.java @@ -35,4 +35,9 @@ public class ReFundDTO { */ private String customerId; + /** + * 退款请求号 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 + */ + private String outRequestNo; + } diff --git a/src/main/java/com/peanut/modules/pay/alipay/service/AliPayService.java b/src/main/java/com/peanut/modules/pay/alipay/service/AliPayService.java index 22059550..ed9a4700 100644 --- a/src/main/java/com/peanut/modules/pay/alipay/service/AliPayService.java +++ b/src/main/java/com/peanut/modules/pay/alipay/service/AliPayService.java @@ -5,6 +5,7 @@ package com.peanut.modules.pay.alipay.service; import com.peanut.modules.pay.alipay.dto.AlipayDTO; import com.peanut.modules.pay.alipay.dto.ReFundDTO; import javax.servlet.http.HttpServletRequest; +import java.util.Map; public interface AliPayService { @@ -26,10 +27,8 @@ public interface AliPayService { /** * 支付宝退款 - * @param reFundDTO - * @return */ - String refund(ReFundDTO reFundDTO); + String refund(Map params); } diff --git a/src/main/java/com/peanut/modules/pay/alipay/service/impl/AliPayServiceImpl.java b/src/main/java/com/peanut/modules/pay/alipay/service/impl/AliPayServiceImpl.java index f9827ba1..dcbfe9c0 100644 --- a/src/main/java/com/peanut/modules/pay/alipay/service/impl/AliPayServiceImpl.java +++ b/src/main/java/com/peanut/modules/pay/alipay/service/impl/AliPayServiceImpl.java @@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONObject; import com.alipay.api.internal.util.AlipaySignature; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.peanut.common.utils.CopyUtils; import com.peanut.common.utils.OrderUtils; import com.peanut.modules.book.dao.BuyOrderProductDao; @@ -18,6 +19,8 @@ import com.peanut.modules.pay.alipay.config.AliPayUtil; import com.peanut.modules.pay.alipay.dto.AlipayDTO; import com.peanut.modules.pay.alipay.dto.ReFundDTO; import com.peanut.modules.pay.alipay.service.AliPayService; +import com.peanut.modules.pay.refund.entity.PayRefundOrder; +import com.peanut.modules.pay.refund.service.PayRefundOrderService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -54,6 +57,8 @@ public class AliPayServiceImpl implements AliPayService { private ShopProductBookDao shopProductBookDao; @Autowired private UserEbookBuyDao userEbookBuyDao; + @Autowired + private PayRefundOrderService refundOrderService; @Override public String pay(AlipayDTO payDto) { @@ -248,8 +253,20 @@ public class AliPayServiceImpl implements AliPayService { } @Override - public String refund(ReFundDTO reFundDTO) { + @Transactional + public String refund(Map params) { log.info(">>>>>>>>>>App请求支付宝退款接口"); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper(); + wrapper.eq(PayZfbOrderEntity::getRelevanceoid,params.get("orderSn").toString()); + PayZfbOrderEntity payZfbOrder = payZfbOrderService.getOne(wrapper); + ReFundDTO reFundDTO = new ReFundDTO(); + reFundDTO.setOutTrandeNo(payZfbOrder.getOutTradeNo()); + reFundDTO.setTradeNo(payZfbOrder.getTradeNo()); + reFundDTO.setCustomerId(payZfbOrder.getCustomerid()); + reFundDTO.setRefundReason(params.get("reason").toString()); + reFundDTO.setRefundAmount(new BigDecimal(params.get("refundFee").toString())); +// reFundDTO.setOutRequestNo(params.get("outRequestNo").toString()); + reFundDTO.setOutRequestNo(UUID.randomUUID().toString()); Map map = aliPayUtil.aliPayRefund(reFundDTO); Object obj = map.get("msg"); String resJson = obj.toString(); @@ -260,23 +277,26 @@ public class AliPayServiceImpl implements AliPayService { "Y".equals(((Map)res.get("alipay_trade_refund_response")).get("fund_change"))){ log.info(">>>>>>>>>>>支付宝退款成功!<<<<<<<<<<<<<"); //表操作 - - + MPJLambdaWrapper w = new MPJLambdaWrapper(); + w.leftJoin(PayZfbOrderEntity.class,PayZfbOrderEntity::getRelevanceoid, BuyOrder::getOrderSn); + w.eq("trade_no",((Map)res.get("alipay_trade_refund_response")).get("trade_no")); + BuyOrder order = buyOrderService.getOne(w); + PayRefundOrder refund = new PayRefundOrder(); + refund.setPayType("2"); + refund.setOrderId(order.getOrderId()); + refund.setTradeNo(((Map)res.get("alipay_trade_refund_response")).get("trade_no").toString()); + refund.setOutTradeNo(((Map)res.get("alipay_trade_refund_response")).get("out_trade_no").toString()); + refund.setRefundFee(((Map)res.get("alipay_trade_refund_response")).get("refund_fee").toString()); + refundOrderService.save(refund); + refundOrderService.businessOpt(order); } return resJson; } //TODO 更新订单状态 - - - //TODO 更新用户优惠券状态 - - //TODO 推送一路健康优惠券 + } - - - diff --git a/src/main/java/com/peanut/modules/pay/refund/controller/RefundController.java b/src/main/java/com/peanut/modules/pay/refund/controller/RefundController.java new file mode 100644 index 00000000..8cc3a675 --- /dev/null +++ b/src/main/java/com/peanut/modules/pay/refund/controller/RefundController.java @@ -0,0 +1,30 @@ +package com.peanut.modules.pay.refund.controller; + +import com.peanut.common.utils.R; +import com.peanut.modules.pay.refund.service.PayRefundOrderService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + + +@RestController +@RequestMapping("/refund") +@Slf4j +public class RefundController { + + @Autowired + private PayRefundOrderService refundOrderService; + + /** + * 花生币退款 + */ + @RequestMapping("/refundPeanutCoin") + public R pay(@RequestBody Map map) { + return refundOrderService.refundPeanutCoin(map); + } + +} diff --git a/src/main/java/com/peanut/modules/pay/refund/dao/PayRefundOrderDao.java b/src/main/java/com/peanut/modules/pay/refund/dao/PayRefundOrderDao.java new file mode 100644 index 00000000..8346c0ed --- /dev/null +++ b/src/main/java/com/peanut/modules/pay/refund/dao/PayRefundOrderDao.java @@ -0,0 +1,9 @@ +package com.peanut.modules.pay.refund.dao; + +import com.github.yulichang.base.MPJBaseMapper; +import com.peanut.modules.pay.refund.entity.PayRefundOrder; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PayRefundOrderDao extends MPJBaseMapper { +} diff --git a/src/main/java/com/peanut/modules/pay/refund/entity/PayRefundOrder.java b/src/main/java/com/peanut/modules/pay/refund/entity/PayRefundOrder.java new file mode 100644 index 00000000..01162856 --- /dev/null +++ b/src/main/java/com/peanut/modules/pay/refund/entity/PayRefundOrder.java @@ -0,0 +1,57 @@ +package com.peanut.modules.pay.refund.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 退款信息 + */ +@Data +@TableName("pay_refund_order") +public class PayRefundOrder implements Serializable { + private static final long serialVersionUID = 1L; + + + @TableId + private Integer refundId; + + /** + * 支付方式 1微信,2支付宝,3虚拟币 + */ + private String payType; + + /** + * BuyOrder + */ + private Integer orderId; + + /** + * 微信支付宝订单号 + */ + private String tradeNo; + + /** + * 商户订单号 + */ + private String outTradeNo; + + /** + * 退款总金额 + */ + private String refundFee; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT)//创建注解 + private Date createTime; + + + +} diff --git a/src/main/java/com/peanut/modules/pay/refund/service/PayRefundOrderService.java b/src/main/java/com/peanut/modules/pay/refund/service/PayRefundOrderService.java new file mode 100644 index 00000000..f510db32 --- /dev/null +++ b/src/main/java/com/peanut/modules/pay/refund/service/PayRefundOrderService.java @@ -0,0 +1,16 @@ +package com.peanut.modules.pay.refund.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.peanut.common.utils.R; +import com.peanut.modules.book.entity.BuyOrder; +import com.peanut.modules.pay.refund.entity.PayRefundOrder; +import java.util.Map; + +public interface PayRefundOrderService extends IService { + + R refundPeanutCoin(Map map); + + void businessOpt(BuyOrder order); + + +} diff --git a/src/main/java/com/peanut/modules/pay/refund/service/impl/PayRefundOrderServiceImpl.java b/src/main/java/com/peanut/modules/pay/refund/service/impl/PayRefundOrderServiceImpl.java new file mode 100644 index 00000000..4fd19771 --- /dev/null +++ b/src/main/java/com/peanut/modules/pay/refund/service/impl/PayRefundOrderServiceImpl.java @@ -0,0 +1,190 @@ +package com.peanut.modules.pay.refund.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.peanut.common.utils.R; +import com.peanut.modules.book.entity.*; +import com.peanut.modules.book.service.*; +import com.peanut.modules.pay.refund.dao.PayRefundOrderDao; +import com.peanut.modules.pay.refund.entity.PayRefundOrder; +import com.peanut.modules.pay.refund.service.PayRefundOrderService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class PayRefundOrderServiceImpl extends ServiceImpl implements PayRefundOrderService { + + @Autowired + private BuyOrderService buyOrderService; + + @Autowired + private CouponHistoryService couponHistoryService; + + @Autowired + private BuyOrderProductService buyOrderProductService; + + @Autowired + private ShopProductService shopProductService; + + @Autowired + private UserEbookBuyService userEbookBuyService; + + @Autowired + private MyUserService userService; + + @Autowired + private PayRefundOrderService refundOrderService; + + @Autowired + private TransactionDetailsService transactionDetailsService; + + @Override + @Transactional + public R refundPeanutCoin(Map map) { + log.info(">>>>>>>>>>>花生币退款<<<<<<<<<<<<<"); + MPJLambdaWrapper w = new MPJLambdaWrapper(); + w.eq("order_id",map.get("orderId")); + BuyOrder order = buyOrderService.getOne(w); + //退款详细 + PayRefundOrder refund = new PayRefundOrder(); + refund.setPayType("3"); + refund.setOrderId(order.getOrderId()); + refund.setRefundFee(map.get("refundFee").toString()); + refundOrderService.save(refund); + //权限回退 + refundOrderService.businessOpt(order); + //修改用户花生币 + MyUserEntity byId = userService.getById(order.getUserId()); + int i = byId.getPeanutCoin().intValue() + Integer.valueOf(map.get("refundFee").toString()); + byId.setPeanutCoin(new BigDecimal(i)); + userService.updateById(byId); + //插入花生比交易明细 + TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity(); + transactionDetailsEntity.setUserId(order.getUserId()); + transactionDetailsEntity.setOrderType("退款"); + transactionDetailsEntity.setTel(byId.getTel()); + transactionDetailsEntity.setUserName(byId.getNickname()); + transactionDetailsEntity.setNote(map.get("note").toString()); + transactionDetailsEntity.setChangeAmount(new BigDecimal(map.get("refundFee").toString())); + transactionDetailsEntity.setRemark("退款"); + BigDecimal balance = new BigDecimal(i); + transactionDetailsEntity.setUserBalance(balance); + transactionDetailsService.save(transactionDetailsEntity); + log.info(">>>>>>>>>>>花生币退款成功<<<<<<<<<<<<<"); + return R.ok(); + } + + @Override + public void businessOpt(BuyOrder order) { + //优惠卷回滚 + if (order.getCouponId() != null) { + Integer couponId = order.getCouponId(); + CouponHistoryEntity couponHistory = couponHistoryService.getById(couponId); + couponHistory.setUseStatus(0); + couponHistoryService.updateById(couponHistory); + } + //查询订单所有商品 + QueryWrapper w1 = new QueryWrapper<>(); + w1.eq("order_id", order.getOrderId()); + List buyOrderProductList = buyOrderProductService.list(w1); + for (BuyOrderProduct buyOrderProduct : buyOrderProductList) { + //查询用户之前是否拥有书籍,如没有删除电子书 + QueryWrapper w2 = new QueryWrapper<>(); + w2.eq("product_id",buyOrderProduct.getProductId()); + ShopProduct sp = shopProductService.getOne(w2); + if (sp!=null){ + if (!"".equals(sp.getBookId())) { + String bookids[] = sp.getBookId().split(","); + if (bookids.length > 0){ + for (int i=0;i < bookids.length; i++){ + //查询这本书在哪些商品里 + QueryWrapper w3 = new QueryWrapper<>(); + w3.eq("book_ids",bookids[i]) + .or().like("book_ids",","+bookids[i]+",") + .or().likeLeft("book_ids",","+bookids[i]) + .or().likeRight("book_ids",bookids[i]+","); + List list = shopProductService.list(w3); + //查询此用户之前是否买过这些商品,并且剔除当前订单 + if (list.size() > 0){ + int flag = 0; + for (ShopProduct item : list) { + String arr[] = {item.getProductId().toString()}; + if (isBought(order,arr)){ + flag++; + } + } + //如果用户之前没买过此书,删除电子书权限 + if (flag==0){ + QueryWrapper w5 = new QueryWrapper<>(); + w5.eq("book_id",bookids[i]); + w5.eq("user_id",order.getUserId()); + userEbookBuyService.remove(w5); + } + } + } + } + } + } + //查询用户之前是否拥有权限 + //脉穴的权限0无权,1有权 + MyUserEntity userInfo = userService.getById(order.getUserId()); + if (buyOrderProduct.getProductId()==128||buyOrderProduct.getProductId()==129 ||buyOrderProduct.getProductId()==130 + ||buyOrderProduct.getProductId()==131||buyOrderProduct.getProductId()==136){ + String arr[] = {"128","129","130","131","136"}; + if (!isBought(order,arr)){ + userInfo.setPointPower(0); + userService.updateById(userInfo); + } + } + //时辰取穴权限 0没有1有 + if (buyOrderProduct.getProductId()==133||buyOrderProduct.getProductId()==134 + ||buyOrderProduct.getProductId()==135){ + String arr[] = {"133","134","135"}; + if (!isBought(order,arr)){ + userInfo.setTgdzPower(0); + userService.updateById(userInfo); + } + } + //五运六气的权限0无权1有权 + if (buyOrderProduct.getProductId()==39||buyOrderProduct.getProductId()==62 + ||buyOrderProduct.getProductId()==123||buyOrderProduct.getProductId()==127){ + String arr[] = {"39","62","123","127"}; + if (!isBought(order, arr)){ + userInfo.setWylqPower(0); + userService.updateById(userInfo); + } + } + //库存回滚 + Integer productId = buyOrderProduct.getProductId(); + ShopProduct product = shopProductService.getById(productId); + product.setProductStock(product.getProductStock() + buyOrderProduct.getQuantity()); + shopProductService.updateById(product); + } + //订单设置交易失败状态并删除 + order.setOrderStatus("4"); + buyOrderService.saveOrUpdate(order); + buyOrderService.removeById(order.getOrderId()); + } + + //本订单的用户之前是否买过当前商品 + public boolean isBought(BuyOrder order,String[] productId){ + MPJLambdaWrapper w4 = new MPJLambdaWrapper<>(); + w4.selectAll(BuyOrder.class); + w4.leftJoin(BuyOrderProduct.class,BuyOrderProduct::getOrderId,BuyOrder::getOrderId); + w4.in(BuyOrderProduct::getProductId,productId); + w4.eq(BuyOrder::getUserId,order.getUserId()); + w4.ne(BuyOrder::getOrderId,order.getOrderId()); + w4.in(BuyOrder::getOrderStatus,"1","2","3"); + return buyOrderService.list(w4).size() > 0 ? true : false; + } +} + diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java b/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java index 8522d29f..ab778495 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java @@ -53,6 +53,11 @@ public class WechatPayConfig implements Serializable { */ @Value("${wxpay.payUrl}") private String payUrl; + /** + * pay url + */ + @Value("${wxpay.refundUrl}") + private String refundUrl; /** * 回调地址 */ diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java b/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java index d2b0845b..3cc1d292 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java @@ -1,5 +1,6 @@ package com.peanut.modules.pay.weChatPay.controller; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -10,6 +11,7 @@ import com.peanut.modules.book.entity.*; import com.peanut.modules.book.service.*; import com.peanut.modules.pay.weChatPay.config.WechatPayConfig; import com.peanut.modules.pay.weChatPay.dto.WechatPaymentInfo; +import com.peanut.modules.pay.weChatPay.service.WxpayService; import com.peanut.modules.pay.weChatPay.util.HttpUtils; import com.peanut.modules.pay.weChatPay.util.WechatPayValidator; import com.peanut.modules.pay.weChatPay.util.WxPayUtil; @@ -31,41 +33,26 @@ import java.util.stream.Collectors; @CrossOrigin @RequestMapping("/pay") @Configuration +@Transactional(rollbackFor = Exception.class) public class WeChatPayController { - @Autowired - private MyUserService userService; @Autowired private BuyOrderService buyOrderService; - @Autowired - private BuyOrderProductDao buyOrderProductDao; @Autowired @Lazy private WechatPayConfig wechatPayConfig; - @Autowired - private BookBuyConfigService bookBuyConfigService; - @Autowired private PayWechatOrderService payWechatOrderService; - @Autowired - private PayPaymentOrderService payPaymentOrderService; - - @Autowired - private TransactionDetailsService transactionDetailsService; - - @Autowired - private ShopProductBookService shopProductBookService; - - @Autowired - private UserEbookBuyService userEbookBuyService; - @Autowired private WxPayUtil wxPayUtil; + @Autowired + private WxpayService wxpayService; + /** * 生成预订单 * @@ -74,8 +61,7 @@ public class WeChatPayController { * @throws Exception */ @RequestMapping(value = "/placeAnOrder/shoppingPay") - @Transactional(rollbackFor = Exception.class) - public R newShoppingPay(@RequestBody WechatPaymentInfo paymentInfo) { + public R newShoppingPay(@RequestBody WechatPaymentInfo paymentInfo){ QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_sn", paymentInfo.getOrderSn()); BuyOrder order = buyOrderService.getOne(queryWrapper); @@ -114,122 +100,28 @@ public class WeChatPayController { */ @PostMapping("/payNotify") @Transactional - public R payNotify(HttpServletRequest request, HttpServletResponse response) { - log.info("微信支付回调"); - // 处理通知参数 - Map bodyMap = getNotifyBody(request); - if (bodyMap == null) { - return null; - } - // 解密resource中的通知数据 - String resource = bodyMap.get("resource").toString(); - Map resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(), 1); - String orderNo = resourceMap.get("out_trade_no").toString(); - // 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 - BuyOrder order = this.buyOrderService.getOne(new QueryWrapper().eq("order_sn", orderNo)); - // 1.根据订单id获取订单信息 - if ("order".equals(order.getOrderType())) { - BuyOrder orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper().eq("order_sn", orderNo)); - BigDecimal realMoney = orderEntity.getRealMoney(); - // 查询订单的所有 book_id - List orderBookIdList = shopProductBookService.getOrderBookId(order.getOrderSn()); - // 去重 - Set set = new HashSet<>(orderBookIdList); - orderBookIdList.clear(); - orderBookIdList.addAll(set); - // 查询用户的所有 book_id - List userBookIdList = userEbookBuyService.getUserBookId(order.getUserId()); - // 取差集 - orderBookIdList.removeAll(userBookIdList); - // 为用户添加书籍 - List userEbookBuyEntities = new ArrayList<>(); - for (Integer bookId : orderBookIdList) { - UserEbookBuyEntity entity = new UserEbookBuyEntity(); - entity.setUserId(order.getUserId()); - entity.setBookId(bookId); - userEbookBuyEntities.add(entity); - } - userEbookBuyService.saveBatch(userEbookBuyEntities); - //手摸脚模购买后会开启用户的脉穴的功能 - List collect = buyOrderProductDao.selectList(new LambdaQueryWrapper().eq(BuyOrderProduct::getOrderId, order.getOrderId())).stream().map(BuyOrderProduct::getProductId).collect(Collectors.toList()); - if(collect.contains(128)||collect.contains(129)||collect.contains(130)||collect.contains(131)||collect.contains(136)){ - MyUserEntity userInfo = userService.getById(order.getUserId()); - userInfo.setPointPower(1); - userService.updateById(userInfo); - } - if(collect.contains(133)||collect.contains(134)||collect.contains(135)){ - MyUserEntity userInfo = userService.getById(order.getUserId()); - userInfo.setTgdzPower(1); - userService.updateById(userInfo); - } - if(collect.contains(39)||collect.contains(62)||collect.contains(123)||collect.contains(127)){ - MyUserEntity userInfo = userService.getById(order.getUserId()); - userInfo.setWylqPower(1); - userService.updateById(userInfo); - } + public void payNotify(HttpServletRequest request, HttpServletResponse response){ + wxpayService.payNotify(request); + } + /** + * 微信退款申请 + */ + @RequestMapping("/refund" ) + public R refund(@RequestBody Map map){ + return R.ok(wxpayService.refund(map)); + } - buyOrderService.updateOrderStatus(order.getUserId(), order.getOrderSn(), "0"); - } - if ("point".equals(order.getOrderType())) { - PayWechatOrderEntity buy_order_id = payWechatOrderService.getBaseMapper().selectOne(new QueryWrapper().eq("order_sn", order.getOrderSn())); - Integer buyorder = buy_order_id.getBuyOrderId(); - BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(buyorder); - String realMoney = bookBuyConfigEntity.getRealMoney(); - int money = Integer.parseInt(realMoney); - userService.rechargeHSPoint(order.getUserId(), money); - TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity(); - transactionDetailsEntity.setUserId(order.getUserId()); - transactionDetailsEntity.setChangeAmount(new BigDecimal(money)); - transactionDetailsEntity.setOrderType("充值"); - transactionDetailsEntity.setRelationId(buy_order_id.getId().intValue()); - transactionDetailsEntity.setRemark("充值"); - - MyUserEntity user = userService.getById(order.getUserId()); - BigDecimal peanutCoin = user.getPeanutCoin(); - transactionDetailsEntity.setUserBalance(peanutCoin); - transactionDetailsEntity.setUserName(user.getNickname()); - transactionDetailsEntity.setTel(user.getTel()); - transactionDetailsService.save(transactionDetailsEntity); - // 插入 花生币 充值记录 - PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity(); - payPaymentOrderEntity.setUserId(order.getUserId()); - payPaymentOrderEntity.setOrderId(String.valueOf(buy_order_id.getId())); - payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney())); - payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney())); - payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao()); - payPaymentOrderEntity.setRechargeStatus("success"); - payPaymentOrderEntity.setSuccessTime(new Date()); - payPaymentOrderEntity.setUserName(user.getNickname()); - payPaymentOrderEntity.setTel(user.getTel()); - payPaymentOrderService.save(payPaymentOrderEntity); - buyOrderService.updateOrderStatus(order.getUserId(), order.getOrderSn(), "2"); - } - // 成功应答 - return R.ok(); + /** + * 微信退款回调 + * @return R + */ + @PostMapping("/refundNotify") + public void refundNotify(HttpServletRequest request){ + wxpayService.refundNotify(request); } - private Map getNotifyBody(HttpServletRequest request) { - // 处理通知参数 - String body = HttpUtils.readData(request); - log.info("支付回调参数:{}", body); - // 转换为Map - Map bodyMap = JSONObject.parseObject(body, new TypeReference>() { - }); - // 微信的通知ID(通知的唯一ID) - String notifyId = bodyMap.get("id").toString(); - // 验证签名信息 - try { - WechatPayValidator wechatPayValidator = new WechatPayValidator(wechatPayConfig.getVerifier(), notifyId, body); - if (!wechatPayValidator.validate(request)) { - log.error("通知验签失败"); - return null; - } - log.info("通知验签成功"); - } catch (Exception e) { - log.error(e.getMessage()); - } - return bodyMap; - } + } + diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/service/WxpayService.java b/src/main/java/com/peanut/modules/pay/weChatPay/service/WxpayService.java index 7ef32eb0..4c657b4c 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/service/WxpayService.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/service/WxpayService.java @@ -5,13 +5,19 @@ import com.peanut.modules.book.entity.PayWechatOrderEntity; import com.peanut.modules.pay.weChatPay.dto.WechatPaymentInfo; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Map; @Service public interface WxpayService extends IService { - void prepay(WechatPaymentInfo wechatDto) throws IOException; + void prepay(WechatPaymentInfo wechatDto); + void payNotify(HttpServletRequest request); + + String refund(Map map); + + void refundNotify(HttpServletRequest request); } diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/service/impl/WxpayServiceImpl.java b/src/main/java/com/peanut/modules/pay/weChatPay/service/impl/WxpayServiceImpl.java index 2f61d99d..8893b840 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/service/impl/WxpayServiceImpl.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/service/impl/WxpayServiceImpl.java @@ -2,22 +2,34 @@ package com.peanut.modules.pay.weChatPay.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.peanut.common.exception.RRException; +import com.peanut.modules.book.dao.BuyOrderProductDao; import com.peanut.modules.book.dao.PayWechatOrderDao; -import com.peanut.modules.book.entity.PayWechatOrderEntity; -import com.peanut.modules.book.service.PayWechatOrderService; +import com.peanut.modules.book.entity.*; +import com.peanut.modules.book.service.*; +import com.peanut.modules.pay.refund.entity.PayRefundOrder; +import com.peanut.modules.pay.refund.service.PayRefundOrderService; import com.peanut.modules.pay.weChatPay.config.WechatPayConfig; import com.peanut.modules.pay.weChatPay.dto.WechatPaymentInfo; import com.peanut.modules.pay.weChatPay.service.WxpayService; +import com.peanut.modules.pay.weChatPay.util.HttpUtils; +import com.peanut.modules.pay.weChatPay.util.WechatPayValidator; import com.peanut.modules.pay.weChatPay.util.WxPayUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @Service @@ -32,8 +44,33 @@ public class WxpayServiceImpl extends ServiceImpl paramMap = new HashMap<>(); // app id paramMap.put("appid", wechatPayConfig.getAppId()); @@ -65,4 +102,191 @@ public class WxpayServiceImpl extends ServiceImpl bodyMap = getNotifyBody(request); + // 解密resource中的通知数据 + String resource = bodyMap.get("resource").toString(); + Map resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(), 1); + String orderNo = resourceMap.get("out_trade_no").toString(); + //修改微信订单表 + MPJLambdaWrapper wrapper = new MPJLambdaWrapper(); + wrapper.eq(PayWechatOrderEntity::getOrderSn,orderNo); + PayWechatOrderEntity payWechatOrderEntity = payWechatOrderService.getOne(wrapper); + payWechatOrderEntity.setOrderId(resourceMap.get("transaction_id").toString()); + payWechatOrderService.updateById(payWechatOrderEntity); + // 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 + BuyOrder order = this.buyOrderService.getOne(new QueryWrapper().eq("order_sn", orderNo)); + // 1.根据订单id获取订单信息 + if ("order".equals(order.getOrderType())) { + BuyOrder orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper().eq("order_sn", orderNo)); + BigDecimal realMoney = orderEntity.getRealMoney(); + // 查询订单的所有 book_id + List orderBookIdList = shopProductBookService.getOrderBookId(order.getOrderSn()); + // 去重 + Set set = new HashSet<>(orderBookIdList); + orderBookIdList.clear(); + orderBookIdList.addAll(set); + // 查询用户的所有 book_id + List userBookIdList = userEbookBuyService.getUserBookId(order.getUserId()); + // 取差集 + orderBookIdList.removeAll(userBookIdList); + // 为用户添加书籍 + List userEbookBuyEntities = new ArrayList<>(); + for (Integer bookId : orderBookIdList) { + UserEbookBuyEntity entity = new UserEbookBuyEntity(); + entity.setUserId(order.getUserId()); + entity.setBookId(bookId); + userEbookBuyEntities.add(entity); + } + userEbookBuyService.saveBatch(userEbookBuyEntities); + //手摸脚模购买后会开启用户的脉穴的功能 + List collect = buyOrderProductDao.selectList(new LambdaQueryWrapper().eq(BuyOrderProduct::getOrderId, order.getOrderId())).stream().map(BuyOrderProduct::getProductId).collect(Collectors.toList()); + if(collect.contains(128)||collect.contains(129)||collect.contains(130)||collect.contains(131)||collect.contains(136)){ + MyUserEntity userInfo = userService.getById(order.getUserId()); + userInfo.setPointPower(1); + userService.updateById(userInfo); + } + if(collect.contains(133)||collect.contains(134)||collect.contains(135)){ + MyUserEntity userInfo = userService.getById(order.getUserId()); + userInfo.setTgdzPower(1); + userService.updateById(userInfo); + } + if(collect.contains(39)||collect.contains(62)||collect.contains(123)||collect.contains(127)){ + MyUserEntity userInfo = userService.getById(order.getUserId()); + userInfo.setWylqPower(1); + userService.updateById(userInfo); + } + + + buyOrderService.updateOrderStatus(order.getUserId(), order.getOrderSn(), "0"); + } + if ("point".equals(order.getOrderType())) { + PayWechatOrderEntity buy_order_id = payWechatOrderService.getBaseMapper().selectOne(new QueryWrapper().eq("order_sn", order.getOrderSn())); + Integer buyorder = buy_order_id.getBuyOrderId(); + BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(buyorder); + String realMoney = bookBuyConfigEntity.getRealMoney(); + int money = Integer.parseInt(realMoney); + userService.rechargeHSPoint(order.getUserId(), money); + TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity(); + transactionDetailsEntity.setUserId(order.getUserId()); + transactionDetailsEntity.setChangeAmount(new BigDecimal(money)); + transactionDetailsEntity.setOrderType("充值"); + transactionDetailsEntity.setRelationId(buy_order_id.getId().intValue()); + transactionDetailsEntity.setRemark("充值"); + + MyUserEntity user = userService.getById(order.getUserId()); + BigDecimal peanutCoin = user.getPeanutCoin(); + transactionDetailsEntity.setUserBalance(peanutCoin); + transactionDetailsEntity.setUserName(user.getNickname()); + transactionDetailsEntity.setTel(user.getTel()); + transactionDetailsService.save(transactionDetailsEntity); + // 插入 花生币 充值记录 + PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity(); + payPaymentOrderEntity.setUserId(order.getUserId()); + payPaymentOrderEntity.setOrderId(String.valueOf(buy_order_id.getId())); + payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney())); + payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney())); + payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao()); + payPaymentOrderEntity.setRechargeStatus("success"); + payPaymentOrderEntity.setSuccessTime(new Date()); + payPaymentOrderEntity.setUserName(user.getNickname()); + payPaymentOrderEntity.setTel(user.getTel()); + payPaymentOrderService.save(payPaymentOrderEntity); + buyOrderService.updateOrderStatus(order.getUserId(), order.getOrderSn(), "2"); + } + } + + @Override + public String refund(Map map){ + LambdaQueryWrapper wrapper = new LambdaQueryWrapper(); + wrapper.eq(PayWechatOrderEntity::getOrderSn,map.get("orderSn").toString()); + PayWechatOrderEntity order = payWechatOrderService.getOne(wrapper); + Map paramMap = new HashMap<>(); + //构建订单金额信息 + //退款金额 + BigDecimal refund = new BigDecimal(map.get("refundFee").toString()); + //原订单总金额 + BigDecimal total = order.getTotalAmount(); + // 这里 * 100,微信支付单位为 ‘分’ + BigDecimal hand = new BigDecimal("100"); + Map amountMap = new HashMap<>(); + amountMap.put("refund", refund.multiply(hand)); + amountMap.put("total", total.multiply(hand).intValue()); + amountMap.put("currency", "CNY"); + paramMap.put("amount", amountMap); + //商户退款单号 商户系统内 部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 +// paramMap.put("out_refund_no", map.get("outRefundNo").toString()); + paramMap.put("out_refund_no", UUID.randomUUID().toString()); + //微信支付订单号 + paramMap.put("transaction_id", order.getOrderId()); + //退款原因 + paramMap.put("reason", map.get("reason").toString()); + // 微信退款回调地址 + paramMap.put("notify_url", wechatPayConfig.getRefundNotifyUrl()); + JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap)); + log.info("微信退款申请请求参数:{}", paramMap); + log.info(">>>>>>>>>>App请求微信退款申请接口"); + JSONObject responseJson = wxPayUtil.doPostWexinV3(wechatPayConfig.getRefundUrl(), json.toJSONString()); + log.info(">>>>>>>>>>>微信退款返回的信息是 resJson = {}", responseJson.toJSONString()); + if ("SUCCESS".equals(responseJson.get("status"))){ + //退款申请成功,等待到账 + } + return responseJson.toJSONString(); + } + + @Override + @Transactional + public void refundNotify(HttpServletRequest request){ + log.info("微信退款回调"); + // 处理通知参数 + Map bodyMap = getNotifyBody(request); + // 解密resource中的通知数据 + String resource = bodyMap.get("resource").toString(); + Map resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(), 2); + log.info("微信退款回调结果 msg={}",resourceMap); + if ("SUCCESS".equals(resourceMap.get("refund_status").toString())){ + log.info(">>>>>>>>>>>微信退款成功!<<<<<<<<<<<<<"); + MPJLambdaWrapper w = new MPJLambdaWrapper(); + w.selectAll(BuyOrder.class); + w.leftJoin(PayWechatOrderEntity.class,PayWechatOrderEntity::getOrderSn, BuyOrder::getOrderSn); + w.eq("t1.order_id",resourceMap.get("transaction_id").toString()); + BuyOrder order = buyOrderService.getOne(w); + PayRefundOrder refund = new PayRefundOrder(); + refund.setPayType("1"); + refund.setOrderId(order.getOrderId()); + refund.setTradeNo(resourceMap.get("transaction_id").toString()); + refund.setOutTradeNo(resourceMap.get("out_trade_no").toString()); + refund.setRefundFee(((Map)resourceMap.get("amount")).get("refund").toString()); + refundOrderService.save(refund); + refundOrderService.businessOpt(order); + } + } + + + private Map getNotifyBody(HttpServletRequest request) { + // 处理通知参数 + String body = HttpUtils.readData(request); + log.info("支付回调参数:{}", body); + // 转换为Map + Map bodyMap = JSONObject.parseObject(body, new TypeReference>() { + }); + // 微信的通知ID(通知的唯一ID) + String notifyId = bodyMap.get("id").toString(); + // 验证签名信息 + try { + WechatPayValidator wechatPayValidator = new WechatPayValidator(wechatPayConfig.getVerifier(), notifyId, body); + if (!wechatPayValidator.validate(request)) { + log.error("通知验签失败"); + return null; + } + log.info("通知验签成功"); + } catch (Exception e) { + log.error(e.getMessage()); + } + return bodyMap; + } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 8ff1d83d..343ee157 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -80,7 +80,8 @@ wxpay: mchId: 1612860909 payUrl: https://api.mch.weixin.qq.com/v3/pay/transactions/app notifyUrl: https://testapi.nuttyreading.com/pay/payNotify - refundNotifyUrl: http://pjm6m9.natappfree.cc/pay/refundNotify + refundUrl: https://api.mch.weixin.qq.com/v3/refund/domestic/refunds + refundNotifyUrl: https://testapi.nuttyreading.com/pay/refundNotify keyPemPath: D:/hs/nuttyreading-java/src/main/resources/cent/apiclient_key.pem serialNo: 679AECB2F7AC4183033F713828892BA640E4EEE3 apiV3Key: 4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ad28466c..a9f33274 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,6 +36,8 @@ spring: pool: core-size: 5 max-size: 50 + main: + allow-circular-references: true #mybatis mybatis-plus: