| package com.mzl.flower.service.payment; | 
|   | 
| import com.alibaba.fastjson.JSON; | 
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 
| import com.mzl.flower.config.PyamentV3Configurer; | 
| import com.mzl.flower.config.exception.ValidationException; | 
| import com.mzl.flower.config.security.SecurityUtils; | 
| import com.mzl.flower.constant.Constants; | 
| import com.mzl.flower.dto.request.payment.TransferDetailReqDTO; | 
| import com.mzl.flower.dto.request.payment.TransferReqDTO; | 
| import com.mzl.flower.dto.request.payment.UserPaymentDTO; | 
| import com.mzl.flower.entity.flower.Flower; | 
| import com.mzl.flower.entity.payment.*; | 
| import com.mzl.flower.entity.system.UserWechat; | 
| import com.mzl.flower.mapper.flower.FlowerMapper; | 
| import com.mzl.flower.mapper.payment.*; | 
| import com.mzl.flower.mapper.system.UserWechatMapper; | 
| import com.mzl.flower.service.BaseService; | 
| import com.mzl.flower.service.coupon.CouponRecordService; | 
| import com.mzl.flower.service.flower.FlowerService; | 
| import com.mzl.flower.service.point.PointGoodsService; | 
| import com.mzl.flower.utils.UUIDGenerator; | 
| import com.wechat.pay.java.core.notification.NotificationParser; | 
| import com.wechat.pay.java.core.notification.RequestParam; | 
| import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension; | 
| import com.wechat.pay.java.service.payments.jsapi.model.*; | 
| import com.wechat.pay.java.service.payments.jsapi.model.Amount; | 
| import com.wechat.pay.java.service.payments.model.Transaction; | 
| import com.wechat.pay.java.service.payments.model.TransactionAmount; | 
| import com.wechat.pay.java.service.refund.RefundService; | 
| import com.wechat.pay.java.service.refund.model.*; | 
| import com.wechat.pay.java.service.transferbatch.TransferBatchService; | 
| import com.wechat.pay.java.service.transferbatch.model.*; | 
| import io.micrometer.core.instrument.util.StringUtils; | 
| import lombok.extern.slf4j.Slf4j; | 
| import org.springframework.beans.BeanUtils; | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.http.HttpStatus; | 
| import org.springframework.http.ResponseEntity; | 
| import org.springframework.stereotype.Service; | 
| import org.springframework.transaction.annotation.Transactional; | 
|   | 
| import javax.servlet.ServletInputStream; | 
| import javax.servlet.http.HttpServletRequest; | 
| import java.io.BufferedReader; | 
| import java.io.InputStreamReader; | 
| import java.math.BigDecimal; | 
| import java.math.RoundingMode; | 
| import java.time.LocalDateTime; | 
| import java.time.ZoneId; | 
| import java.time.ZonedDateTime; | 
| import java.time.format.DateTimeFormatter; | 
| import java.util.ArrayList; | 
| import java.util.HashMap; | 
| import java.util.List; | 
| import java.util.Map; | 
|   | 
| import com.wechat.pay.java.core.http.Constant; | 
|   | 
| @Service | 
| @Transactional | 
| @Slf4j | 
| public class UserPaymentV3Service extends BaseService { | 
|   | 
|     @Autowired | 
|     private UserPaymentMapper userPaymentMapper; | 
|   | 
|     @Autowired | 
|     private RedisLockService lockService; | 
|   | 
|     @Autowired | 
|     private UserWechatMapper wechatMapper; | 
|   | 
|     @Autowired | 
|     private OrderMapper orderMapper; | 
|   | 
|     @Autowired | 
|     private OrderItemMapper orderItemMapper; | 
|   | 
|     @Autowired | 
|     private FlowerMapper flowerMapper; | 
|   | 
|     @Autowired | 
|     private FlowerService flowerService; | 
|   | 
|     @Autowired | 
|     private DeliveryOrderService deliveryOrderService; | 
|   | 
|     @Autowired | 
|     private JsapiServiceExtension jsapiService; | 
|   | 
|     @Autowired | 
|     private NotificationParser notificationParser; | 
|   | 
|     @Autowired | 
|     private RefundService refundService; | 
|   | 
|     @Autowired | 
|     private TransferBatchService transferBatchService; | 
|   | 
|     @Autowired | 
|     private TransferMapper transferMapper; | 
|   | 
|     @Autowired | 
|     private TransferDetailMapper transferDetailMapper; | 
|   | 
|     @Autowired | 
|     private OrderRefundMapper orderRefundMapper; | 
|   | 
|     @Autowired | 
|     private OrderPointGoodsMapper orderPointGoodsMapper; | 
|   | 
|     @Autowired | 
|     private PointGoodsService pointGoodsService; | 
|   | 
|     @Autowired | 
|     private CouponRecordService couponRecordService; | 
|   | 
|     @Autowired | 
|     private OrderService orderService; | 
|   | 
|     /** | 
|      * 微信预支付 | 
|      * | 
|      * @param order | 
|      * @return | 
|      */ | 
|     public Map wxPrepay(Order order){ | 
|         String openId = order.getPayOpenid(); | 
|   | 
|         String userId = SecurityUtils.getUserId(); | 
| //        UserWechat wechat = wechatMapper.selectOne(new QueryWrapper<UserWechat>().eq("user_id", userId)); | 
| //        if (wechat != null) { | 
| //            openId = wechat.getOpenId(); | 
| //        } | 
|   | 
|         if(StringUtils.isEmpty(openId)){ | 
|             throw new ValidationException("openId不存在"); | 
|         } | 
|   | 
|         UserPayment up = prepareUserPayment(userId, order); | 
|         log.info("UserPayment: " + toJSONString(up)); | 
|   | 
|         PrepayRequest request = new PrepayRequest(); | 
|         Amount amount = new Amount(); | 
|         amount.setTotal(prepareAmount(up.getPaymentAmount())); | 
|         request.setAmount(amount); | 
|         request.setAppid(PyamentV3Configurer.customer_app_id); | 
|         request.setMchid(PyamentV3Configurer.merchantId); | 
|         request.setDescription(order.getOrderNo()); | 
|         request.setNotifyUrl(PyamentV3Configurer.notify_url_pay); | 
|         request.setOutTradeNo(up.getOrderId()); | 
|         request.setTimeExpire(getTimeExpire()); | 
|         Payer payer = new Payer(); | 
|         payer.setOpenid(openId); | 
|         request.setPayer(payer); | 
|   | 
|         // 调用接口 | 
|         PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(request); | 
|   | 
|         up.setPrepayResponse(toJSONString(response)); | 
|   | 
|         userPaymentMapper.insert(up); | 
|   | 
|         Map<String, Object> map = new HashMap<>(); | 
|         map.put("appId", response.getAppId()); | 
|         map.put("timeStamp", response.getTimeStamp()); | 
|         map.put("nonceStr", response.getNonceStr()); | 
|         map.put("package", response.getPackageVal()); | 
|         map.put("signType", response.getSignType()); | 
|         map.put("paySign", response.getPaySign()); | 
|   | 
|         return map; | 
|     } | 
|   | 
|     private static String getTimeExpire(){//设置微信订单5分钟过期 | 
|         // 获取当前日期和时间 | 
|         ZonedDateTime zonedDateTime = ZonedDateTime.now(); | 
|   | 
|         // 创建一个DateTimeFormatter | 
|         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); | 
|   | 
|         // 使用formatter格式化ZonedDateTime | 
|         String formattedDateTime = zonedDateTime.plusMinutes(5).format(formatter); | 
|   | 
|         return formattedDateTime; // 格式化日期 | 
|     } | 
|   | 
|     private Integer prepareAmount(BigDecimal amount){ | 
|          return amount.multiply(new BigDecimal(100)).intValue(); | 
|     } | 
|   | 
|     private UserPayment prepareUserPayment(String userId, Order order){ | 
|         UserPayment up = new UserPayment(); | 
|         up.setId(UUIDGenerator.getUUID()); | 
|         up.setUserId(userId); | 
|         up.setOrderId(order.getId()); | 
|         up.setPaymentAmount(order.getTotalAmount()); | 
|         up.setPaymentTime(LocalDateTime.now()); | 
|         up.create(userId); | 
|   | 
|         return up; | 
|     } | 
|   | 
|     public ResponseEntity<?> handlePayCallback(HttpServletRequest request) { | 
|         try { | 
|             ServletInputStream inputStream = request.getInputStream(); | 
|             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); | 
|             // 读取请求体 | 
|             StringBuilder sb = new StringBuilder(); | 
|             String line; | 
|             while ((line = bufferedReader.readLine()) != null) { | 
|                 sb.append(line); | 
|             } | 
|             String notify = sb.toString(); | 
|             log.info("微信支付通知: {}", notify); | 
|             RequestParam requestParam = buildByHeaderRequestParam(request, notify); | 
|             log.info("微信支付通知解析请求: {}", JSON.toJSONString(requestParam)); | 
|             // 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败,SDK 会抛出 ValidationException。 | 
|             log.info("支付通知回调:验签、解密并转换成 Transaction对象:-------"); | 
|             Transaction transaction = null; | 
|             try { | 
|                 /** | 
|                  * 常用的通知回调调对象类型有: | 
|                  * 支付 Transaction | 
|                  * 退款 RefundNotification | 
|                  * 若 SDK 暂不支持的类型,请使用 Map.class,嵌套的 Json 对象将被转换成 LinkedTreeMap | 
|                  */ | 
|                 transaction = notificationParser.parse(requestParam, Transaction.class); | 
|             }catch (ValidationException e) { | 
|                 // 签名验证失败,返回 401 UNAUTHORIZED 状态码 | 
|                 log.error("sign verification failed", e); | 
|                 return new ResponseEntity<>("", HttpStatus.UNAUTHORIZED); | 
|             } | 
|             log.info("Transaction:===>>>>支付CallBack状态" + transaction.getTradeState()); | 
|   | 
|             String originalXml = toJSONString(transaction);//回调请求内容 | 
|             log.info("transaction: " + originalXml); | 
|             String outTradeNo = transaction.getOutTradeNo();//对外贸易编号 | 
|             String transactionId = transaction.getTransactionId();//事务处理id | 
|             String orderId = outTradeNo; | 
|             TransactionAmount amount = transaction.getAmount(); | 
|             Integer paymentAmountCallback = amount.getTotal();//实际支付金额 单位:分 | 
|             log.info("======paymentAmountCallback: " + paymentAmountCallback); | 
|   | 
|             Transaction.TradeStateEnum tradeState = transaction.getTradeState();//支付状态 | 
|             String status = Constants.PAYMENT_STATUS.FAILED.name(); | 
|             //交易状态 | 
|             if (Transaction.TradeStateEnum.SUCCESS.equals(tradeState)){ | 
|                 status = Constants.PAYMENT_STATUS.SUCCESS.name(); | 
|             } | 
|   | 
|             UserPaymentDTO dto = new UserPaymentDTO(); | 
|             dto.setOrderId(orderId); | 
|             dto.setTransactionId(transactionId); | 
|             dto.setOutTradeNo(outTradeNo); | 
|             dto.setOriginalXml(originalXml); | 
|             dto.setPaymentAmountCallback(paymentAmountCallback + ""); | 
|             dto.setStatus(status); | 
|   | 
|             saveCallbackInfo(dto, Constants.ORDER_STATUS_BACKEND.PAYMENT.name()); | 
|   | 
|         } catch (Exception e) { | 
|             log.error("解析付款通知出错:{}", e.getMessage(), e); | 
|             return new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR); | 
|         } | 
|   | 
|         return new ResponseEntity<>("", HttpStatus.OK); | 
|     } | 
|   | 
|     private Map prepareReturn(String code, String message){ | 
|         Map m = new HashMap(); | 
|         m.put("code", code); | 
|         m.put("message", message); | 
|   | 
|         return m; | 
|     } | 
|   | 
|     public RequestParam buildByHeaderRequestParam(HttpServletRequest request, String notify) { | 
|         log.info("-------------------WxPay---------GetHeader--------------------BEGIN"); | 
|         // HTTP 头 Wechatpay-Timestamp。签名中的时间戳。 | 
|         String timestamp = request.getHeader(Constant.WECHAT_PAY_TIMESTAMP); | 
|         // HTTP 头 Wechatpay-Nonce。签名中的随机数。 | 
|         String nonce = request.getHeader(Constant.WECHAT_PAY_NONCE); | 
|         // HTTP 头 Wechatpay-Signature-Type。签名类型。 | 
|         String signType = request.getHeader("Wechatpay-Signature-Type"); | 
|         //HTTP 头 Wechatpay-Serial。微信支付平台证书的序列号,验签必须使用序列号对应的微信支付平台证书。 | 
|         String serialNo = request.getHeader(Constant.WECHAT_PAY_SERIAL); | 
|         //  HTTP 头 Wechatpay-Signature。应答的微信支付签名。 | 
|         String signature = request.getHeader(Constant.WECHAT_PAY_SIGNATURE); | 
|         log.info("应答的微信支付签名: {}", signature); | 
|         if(StringUtils.isNotBlank(signature) && signature.contains("WECHATPAY/SIGNTEST/")){ | 
|             //应对签名探测流量 | 
|             //为了确保商户系统的安全,微信支付会在极少数应答或通知回调中生成错误签名,以探测商户系统是否正确地验证了签名。 | 
|             log.error("微信对签名探测流量验证"); | 
|         } | 
|         log.info("-------------------WxPay---------GetHeader--------------------ENDING"); | 
|         // 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048 | 
|         RequestParam requestParam = new RequestParam.Builder() | 
|                 .serialNumber(serialNo) | 
|                 .nonce(nonce) | 
|                 .signature(signature) | 
|                 .timestamp(timestamp) | 
|                 .signType(signType) | 
|                 .body(notify) | 
|                 .build(); | 
|         return requestParam; | 
|     } | 
|   | 
|     public void saveCallbackInfo(UserPaymentDTO dto, String orderStatus){ | 
|         String orderId = dto.getOrderId(); | 
|         boolean lock = lockService.getObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId); | 
|         if(!lock){ | 
|             return; | 
|         } | 
|   | 
|         try { | 
|             UserPayment up = userPaymentMapper.selectOne( | 
|                     new QueryWrapper<UserPayment>().eq("order_id", orderId)); | 
|             BeanUtils.copyProperties(dto, up); | 
|   | 
|             up.setPaymentTimeCallback(LocalDateTime.now()); | 
|             userPaymentMapper.updateById(up); | 
|   | 
|             Order order = orderMapper.selectById(orderId); | 
|             if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) { | 
|                 order.setPaymentTrId(dto.getTransactionId()); | 
|                 if (up.getPaymentAmountCallback() != null) { | 
|                     order.setPaymentAmount(new BigDecimal(up.getPaymentAmountCallback()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));//转换成单位元 | 
|                 } else { | 
|                     order.setPaymentAmount(order.getTotalAmount()); | 
|                 } | 
|                 order.setPaymentTime(up.getPaymentTimeCallback()); | 
|                 order.setStatus(Constants.ORDER_STATUS.SEND.name()); | 
|             } else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){ | 
|                 order.setStatus(Constants.ORDER_STATUS.CANCEL.name()); | 
|             } | 
|             order.setStatusBackend(orderStatus); | 
|             orderMapper.updateById(order); | 
|   | 
|             if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) { | 
|                 postPayment(order); | 
|             } else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){ | 
|                 releasePrepayLock(order); | 
|             } | 
|         } catch (Exception e) { | 
|             log.error(e.getMessage(), e); | 
|         } finally { | 
|             lockService.releaseObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId); | 
|         } | 
|     } | 
|   | 
|     private void postPayment(Order order){ | 
|         log.info("回调后处理订单信息:" + toJSONString(order)); | 
|         //创建配送单 | 
|         deliveryOrderService.createDeliveryOrder(order); | 
|         flowerService.updateFlowerSales(order); | 
|     } | 
|   | 
|     public Transaction queryOrder(String outTradeNo){ | 
|         QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest(); | 
|         request.setOutTradeNo(outTradeNo); | 
|         request.setMchid(PyamentV3Configurer.merchantId); | 
|         // 调用接口 | 
|         return jsapiService.queryOrderByOutTradeNo(request); | 
|     } | 
|   | 
|     public boolean checkOrderStatus(String outTradeNo){ | 
|         return checkOrderStatus(outTradeNo, false); | 
|     } | 
|   | 
|     public boolean checkOrderStatus(String outTradeNo, boolean isPayAgain){ | 
|         UserPayment up = userPaymentMapper.selectOne( | 
|                 new QueryWrapper<UserPayment>().eq("order_id", outTradeNo)); | 
|         String s = up.getStatus(); | 
|         if(StringUtils.isNotEmpty(s)){ | 
|             return true; | 
|         } | 
|   | 
|         Transaction transaction = queryOrder(outTradeNo); | 
|         /* | 
|         交易成功判断条件: return_code、result_code和trade_state都为SUCCESS | 
|         SUCCESS--支付成功 | 
|         REFUND--转入退款 | 
|         NOTPAY--未支付 | 
|         CLOSED--已关闭 | 
|         REVOKED--已撤销(刷卡支付) | 
|         USERPAYING--用户支付中 | 
|         PAYERROR--支付失败(其他原因,如银行返回失败) | 
|         ACCEPT--已接收,等待扣款 | 
|         */ | 
|         String originalXml = toJSONString(transaction);//回调请求内容 | 
|         log.info("message: " + originalXml); | 
|         Transaction.TradeStateEnum tradeState = transaction.getTradeState(); | 
|         String transactionId = transaction.getTransactionId();//事务处理id | 
|         String orderId = outTradeNo; | 
|   | 
|         if(Transaction.TradeStateEnum.SUCCESS.equals(tradeState) | 
|                 || Transaction.TradeStateEnum.CLOSED.equals(tradeState)) { | 
|             UserPaymentDTO dto = new UserPaymentDTO(); | 
|             dto.setOrderId(orderId); | 
|             dto.setTransactionId(transactionId); | 
|             dto.setOutTradeNo(outTradeNo); | 
|             dto.setOriginalXml(originalXml); | 
|             TransactionAmount amount = transaction.getAmount(); | 
|             if(amount != null) { | 
|                 Integer paymentAmountCallback = amount.getTotal();//实际支付金额 单位:分 | 
|                 log.info("======paymentAmountCallback: " + paymentAmountCallback); | 
|                 dto.setPaymentAmountCallback(paymentAmountCallback + ""); | 
|             } | 
|             dto.setStatus(tradeState.name()); | 
|   | 
|             String orderStatus = Transaction.TradeStateEnum.CLOSED.equals(tradeState) | 
|                     ? Constants.ORDER_STATUS_BACKEND.CANCEL.name() | 
|                     : Constants.ORDER_STATUS_BACKEND.PAYMENT.name(); | 
|   | 
|             saveCallbackInfo(dto, orderStatus); | 
|   | 
|             return true; | 
|         } | 
|   | 
|         if(isPayAgain){ | 
|             return !Transaction.TradeStateEnum.NOTPAY.equals(tradeState); | 
|         } | 
|   | 
|         return false; | 
|     } | 
|   | 
|     public void cancelOrder(String orderId){ | 
|         UserPayment up = userPaymentMapper.selectOne( | 
|                 new QueryWrapper<UserPayment>().eq("order_id", orderId)); | 
|         if(StringUtils.isNotEmpty(up.getStatus())){ | 
|             throw new ValidationException("订单不可取消"); | 
|         } | 
|   | 
|         CloseOrderRequest request = new CloseOrderRequest(); | 
|         request.setOutTradeNo(orderId); | 
|         request.setMchid(PyamentV3Configurer.merchantId); | 
|         // 调用接口 | 
|         jsapiService.closeOrder(request); | 
|   | 
|         Order order = orderMapper.selectById(orderId); | 
|         order.setStatus(Constants.ORDER_STATUS.CANCEL.name()); | 
|         order.setStatusBackend(Constants.ORDER_STATUS_BACKEND.CANCEL.name()); | 
|         order.setCancelTime(LocalDateTime.now()); | 
|         order.update(SecurityUtils.getUserId()); | 
|         orderMapper.updateById(order); | 
|   | 
|         up.setStatus(Constants.PAYMENT_STATUS.CLOSED.name()); | 
|         up.update(SecurityUtils.getUserId()); | 
|         userPaymentMapper.updateById(up); | 
|   | 
|         releasePrepayLock(order); | 
|     } | 
|   | 
|     private void releasePrepayLock(Order order){ | 
|         log.info("恢复库存 积分商品兑换券 优惠券: " + order); | 
|         revertFlowerStock(order.getId()); | 
|   | 
|         //恢复积分商品兑换券 | 
|         revertPointGoodsRecord(order.getId()); | 
|   | 
|         //恢复优惠券 | 
|         String memberCouponId = order.getMemberCouponId(); | 
|         if(StringUtils.isNotEmpty(memberCouponId)) { | 
|             couponRecordService.cancelCouponUsage(order.getId()); | 
|         } | 
|     } | 
|   | 
|     private void revertPointGoodsRecord(String orderId){ | 
|         List<OrderPointGoods> ls = orderPointGoodsMapper.selectList(new QueryWrapper<OrderPointGoods>() | 
|                 .eq("order_id", orderId)); | 
|         if(ls != null && ls.size() > 0){ | 
|             for(OrderPointGoods pg : ls){ | 
|                 pointGoodsService.revertExchangeGoods(pg.getGoodsRecordId()); | 
|             } | 
|         } | 
|     } | 
|   | 
|     public Map payAgain(String orderId){ | 
|         // 获取订单里面的商品是否有限购的,如果有则判断是否已经超过限购数量 | 
|         List<OrderItem> orderItemList = orderItemMapper.selectList(new QueryWrapper<OrderItem>() | 
|                 .eq("order_id", orderId)); | 
|   | 
|         orderItemList.forEach(orderItem -> { | 
|             // 限购数量 鲜花数量校验 | 
|             Integer completeNumToday=orderService.getFlowerCompleteNumToday(orderItem.getCreateBy(),orderItem.getFlowerId()); | 
|             Integer tmp=completeNumToday+orderItem.getNum(); | 
|             Flower flower=flowerMapper.selectById(orderItem.getFlowerId()); | 
|             if(null!=flower.getLimited() && tmp.compareTo(flower.getLimited())>0){ | 
|                 throw new ValidationException("商品:'"+flower.getName()+"' 昨天17:00到今天17:00 超过限售数量:"+flower.getLimited()+"!"); | 
|             } | 
|   | 
|         }); | 
|   | 
|   | 
|         UserPayment up = userPaymentMapper.selectOne( | 
|                 new QueryWrapper<UserPayment>().eq("order_id", orderId)); | 
|         if(StringUtils.isNotEmpty(up.getStatus())){ | 
|             throw new ValidationException("订单不可再支付"); | 
|         } | 
|   | 
|         PrepayWithRequestPaymentResponse response = parseObject(up.getPrepayResponse() | 
|                 , PrepayWithRequestPaymentResponse.class); | 
|   | 
|         Map<String, Object> map = new HashMap<>(); | 
|         map.put("appId", response.getAppId()); | 
|         map.put("timeStamp", response.getTimeStamp()); | 
|         map.put("nonceStr", response.getNonceStr()); | 
|         map.put("package", response.getPackageVal()); | 
|         map.put("signType", response.getSignType()); | 
|         map.put("paySign", response.getPaySign()); | 
|   | 
|         return map; | 
|     } | 
|   | 
|     public synchronized void revertFlowerStock(String orderId){ | 
|         List<OrderItem> ls = orderItemMapper.selectList(new QueryWrapper<OrderItem>() | 
|                 .eq("order_id", orderId)); | 
|         for(OrderItem c : ls){ | 
|             flowerMapper.addFlowerStock(c.getFlowerId(), c.getNum()); | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * 退款 | 
|      * | 
|      * @param orderId | 
|      */ | 
|     public void refundOrder(String orderId){ | 
|         Order o = orderMapper.selectById(orderId); | 
|         /*String status = o.getStatusBackend(); | 
|         if(!Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(status)){ | 
|             throw new ValidationException("未支付订单不可申请退款"); | 
|         }*/ | 
|   | 
|         CreateRequest request = new CreateRequest(); | 
|         request.setOutTradeNo(orderId); | 
|         request.setOutRefundNo(UUIDGenerator.getUUID()); | 
|         request.setTransactionId(o.getPaymentTrId()); | 
|         request.setNotifyUrl(PyamentV3Configurer.notify_url_refund); | 
|   | 
|         AmountReq amount = new AmountReq(); | 
|         int oa = prepareAmount(o.getPaymentAmount()); | 
|         amount.setTotal((long)oa); | 
|         amount.setRefund((long)oa); | 
|         amount.setCurrency("CNY"); | 
|         request.setAmount(amount); | 
|         // 调用接口 | 
|         refundService.create(request); | 
|   | 
|         o.setRefundAmount(o.getPaymentAmount()); | 
|         o.setRefundNo(request.getOutRefundNo()); | 
|         o.setRefundTime(LocalDateTime.now()); | 
|   | 
|         o.setStatus(Constants.ORDER_STATUS.REFUND.name()); | 
|         o.setStatusBackend(Constants.ORDER_STATUS_BACKEND.REFUND.name()); | 
|         o.update("sys"); | 
|   | 
|         orderMapper.updateById(o); | 
|   | 
|         deliveryOrderService.refundDelete(orderId); | 
|   | 
|         releasePrepayLock(o); | 
|     } | 
|   | 
|     public String refundOrderSub(Order o, BigDecimal refundAmount){ | 
|         if(o.getPaymentTime() == null || o.getPaymentAmount() == null){ | 
|             throw new ValidationException("未支付订单不可退款"); | 
|         } | 
|   | 
|         if(o.getRefundTime() != null){ | 
|             throw new ValidationException("已全额退款订单不可退款"); | 
|         } | 
|   | 
|         if (refundAmount == null || refundAmount.doubleValue() == 0) { | 
|             throw new ValidationException("退款金额不能为空"); | 
|         } | 
|   | 
|         List<OrderRefund> reLs = orderRefundMapper.selectList(new QueryWrapper<OrderRefund>() | 
|                 .eq("order_id", o.getId())); | 
|         BigDecimal rra = new BigDecimal(0); | 
|         if(reLs != null && reLs.size() > 0){ | 
|             for(OrderRefund r : reLs){ | 
|                 rra = rra.add(r.getRefundAmount()); | 
|             } | 
|         } | 
|   | 
|         long total = (long)prepareAmount(o.getPaymentAmount()); | 
|         long refund = (long)prepareAmount(refundAmount); | 
|   | 
|         long rraa = (long)prepareAmount(rra); | 
|         if(rraa + refund > total){ | 
|             throw new ValidationException("退款金额不能大于订单金额"); | 
|         } | 
|   | 
|         OrderRefund re = new OrderRefund(); | 
|         re.setId(UUIDGenerator.getUUID()); | 
|         re.setOrderId(o.getId()); | 
|         re.setOrderAmount(o.getPaymentAmount()); | 
|         re.setRefundAmount(refundAmount); | 
|   | 
|         CreateRequest request = new CreateRequest(); | 
|         request.setOutTradeNo(o.getId()); | 
|         request.setOutRefundNo(re.getId()); | 
|         request.setTransactionId(o.getPaymentTrId()); | 
|         request.setNotifyUrl(PyamentV3Configurer.notify_url_refund); | 
|   | 
|         AmountReq amount = new AmountReq(); | 
|         amount.setTotal(total); | 
|         amount.setRefund(refund); | 
|         amount.setCurrency("CNY"); | 
|         request.setAmount(amount); | 
|   | 
|         // 调用接口 | 
|         Refund refund1 = refundService.create(request); | 
|   | 
|         re.setRequest(toJSONString(refund1)); | 
|         re.create(SecurityUtils.getUserId()); | 
|   | 
|         orderRefundMapper.insert(re); | 
|   | 
|         return re.getId(); | 
|     } | 
|   | 
|     /** | 
|      * 查询退款 | 
|      * | 
|      * @param orderId | 
|      * @return | 
|      */ | 
|     public Refund refundQuery(String orderId){ | 
|         Order o = orderMapper.selectById(orderId); | 
|         if(StringUtils.isEmpty(o.getRefundNo())){ | 
|             throw new ValidationException("该订单没有退款"); | 
|         } | 
|   | 
|         QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest(); | 
|         request.setOutRefundNo(orderId); | 
|         // 调用接口 | 
|         return refundService.queryByOutRefundNo(request); | 
|     } | 
|   | 
|     public ResponseEntity<?> handleRefundCallback(HttpServletRequest request) { | 
|         try { | 
|             ServletInputStream inputStream = request.getInputStream(); | 
|             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); | 
|             // 读取请求体 | 
|             StringBuilder sb = new StringBuilder(); | 
|             String line; | 
|             while ((line = bufferedReader.readLine()) != null) { | 
|                 sb.append(line); | 
|             } | 
|             String notify = sb.toString(); | 
|             log.info("微信退款通知: {}", notify); | 
|             RequestParam requestParam = buildByHeaderRequestParam(request, notify); | 
|   | 
|             // 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败,SDK 会抛出 ValidationException。 | 
|             log.info("退款通知回调:验签、解密并转换成 RefundNotification对象:-------"); | 
|             /** | 
|              * 常用的通知回调调对象类型有: | 
|              * 支付 Transaction | 
|              * 退款 RefundNotification | 
|              * 若 SDK 暂不支持的类型,请使用 Map.class,嵌套的 Json 对象将被转换成 LinkedTreeMap | 
|              */ | 
|             RefundNotification refundNotification; | 
|             try { | 
|                 refundNotification = notificationParser.parse(requestParam, RefundNotification.class); | 
|             } catch (ValidationException e) { | 
|                 // 签名验证失败,返回 401 UNAUTHORIZED 状态码 | 
|                 log.error("sign verification failed", e); | 
|                 return new ResponseEntity<>("", HttpStatus.UNAUTHORIZED); | 
|             } | 
|             log.info("Transaction:===>>>>退款CallBack状态" + refundNotification.getRefundStatus()); | 
|             String outRefundNo = refundNotification.getOutRefundNo(); | 
|             OrderRefund re = orderRefundMapper.selectById(outRefundNo); | 
|             if(re != null) { | 
|                 re.setStatus(refundNotification.getRefundStatus().name()); | 
|                 re.setNotification(toJSONString(refundNotification)); | 
|                 re.setNotifyTime(LocalDateTime.now()); | 
|                 orderRefundMapper.updateById(re); | 
|             } | 
|         } catch (Exception e) { | 
|             log.error("解析退款通知出错:{}", e.getMessage(), e); | 
|             return new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR); | 
|         } | 
|   | 
|         return new ResponseEntity<>("", HttpStatus.OK); | 
|     } | 
|   | 
|     /** | 
|      * 商家转账 | 
|      * | 
|      * @return | 
|      */ | 
|     public String doBatchTransfer(TransferReqDTO dto, String userId) { | 
|         Transfer transfer = new Transfer(); | 
|         transfer.setId(StringUtils.isNotEmpty(dto.getId()) ? dto.getId() : UUIDGenerator.getUUID()); | 
|         transfer.setName(dto.getName()); | 
|         transfer.setRemarks(dto.getRemarks()); | 
|   | 
|         InitiateBatchTransferRequest request = new InitiateBatchTransferRequest(); | 
|         request.setAppid(dto.getAppId()); | 
|         request.setOutBatchNo(transfer.getId());//【商家批次单号】 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一 | 
|         request.setBatchName(transfer.getName());//【批次名称】 该笔批量转账的名称 | 
|         request.setBatchRemark(transfer.getRemarks());//【批次备注】 转账说明,UTF8编码,最多允许32个字符 | 
|   | 
|         List<TransferDetailReqDTO> details = dto.getDetails(); | 
|         transfer.setTotalNum(details.size()); | 
|   | 
|         Long totalAmount = 0L; | 
|         for(TransferDetailReqDTO d : details) { | 
|             TransferDetail td = new TransferDetail(); | 
|             td.setId(StringUtils.isNotEmpty(d.getId()) ? d.getId() : UUIDGenerator.getUUID()); | 
|             td.setTransferId(transfer.getId()); | 
|             td.setAmount(d.getAmount()); | 
|             td.setRemarks(d.getRemarks()); | 
|             td.setUserName(d.getUserName()); | 
|             td.setOpenId(d.getOpenId()); | 
|             td.create(userId); | 
|             transferDetailMapper.insert(td); | 
|   | 
|             totalAmount += td.getAmount(); | 
|   | 
|             TransferDetailInput di = new TransferDetailInput(); | 
|             di.setOutDetailNo(td.getId());//【商家明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成 | 
|             di.setTransferAmount(td.getAmount());//【转账金额】 转账金额单位为“分” | 
|             di.setTransferRemark(td.getRemarks());//【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符 | 
|             di.setOpenid(td.getOpenId()); | 
|             di.setUserName(td.getUserName());//【收款用户姓名】 收款方真实姓名。明细转账金额<0.3元时,不允许填写收款用户姓名。明细转账金额 >= 2,000元,该笔明细必须填写收款用户姓名 | 
|             request.getTransferDetailList().add(di); | 
|         } | 
|         transfer.setTotalAmount(totalAmount); | 
|   | 
|         request.setTotalAmount(transfer.getTotalAmount());//【转账总金额】 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 | 
|         request.setTotalNum(transfer.getTotalNum());//【转账总笔数】 一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作 | 
|   | 
|         //ACCEPTED:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认 | 
|         //PROCESSING:转账中。已开始处理批次内的转账明细单 | 
|         //FINISHED:已完成。批次内的所有转账明细单都已处理完成 | 
|         //CLOSED:已关闭。可查询具体的批次关闭原因确认 | 
|         InitiateBatchTransferResponse r = transferBatchService.initiateBatchTransfer(request); | 
|         transfer.setBatchId(r.getBatchId()); | 
|         transfer.setStatus(r.getBatchStatus()); | 
|         transfer.create(userId); | 
|         transferMapper.insert(transfer); | 
|   | 
|         return transfer.getId(); | 
|     } | 
|   | 
|     public void checkTransferStatus(Transfer transfer) {//定时任务验证转账状态 | 
|         GetTransferBatchByOutNoRequest request = new GetTransferBatchByOutNoRequest(); | 
|         request.setOutBatchNo(transfer.getId()); | 
|         request.setNeedQueryDetail(true); | 
|         request.setDetailStatus("ALL"); | 
|         TransferBatchEntity tb = transferBatchService.getTransferBatchByOutNo(request); | 
|         transfer.setCheckLog(toJSONString(tb)); | 
|         transfer.setCheckTime(LocalDateTime.now()); | 
|         TransferBatchGet b = tb.getTransferBatch(); | 
|         transfer.setStatus(b.getBatchStatus()); | 
|         transfer.update("sys"); | 
|         transferMapper.updateById(transfer); | 
|   | 
|         List<TransferDetailCompact> dLs = tb.getTransferDetailList(); | 
|         if(dLs != null && dLs.size() > 0) { | 
|             for (TransferDetailCompact d : dLs) { | 
|                 TransferDetail td = transferDetailMapper.selectById(d.getOutDetailNo()); | 
|                 td.setStatus(d.getDetailStatus()); | 
|                 td.update("sys"); | 
|                 transferDetailMapper.updateById(td); | 
|             } | 
|         } | 
|     } | 
|   | 
|     public List<Transfer> getUnCompletedTransfer(){//定时任务验证转账状态 | 
|         List<String> statusList = new ArrayList<>(); | 
|         statusList.add(Constants.TRANSFER_STATUS.ACCEPTED.name()); | 
|         statusList.add(Constants.TRANSFER_STATUS.PROCESSING.name()); | 
|         return transferMapper.selectList(new QueryWrapper<Transfer>() | 
|                 .in("status", statusList) | 
|         ); | 
|     } | 
| } |