rentaiming
2024-05-21 dd7ad1ab0ead2aa3c9f7fc35cc9b0635309c642a
提交支付
51个文件已添加
8个文件已修改
6326 ■■■■■ 已修改文件
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/pom.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java 954 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/证书使用说明.txt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/证书使用说明.txt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.system.api.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class OrderAuctionBondDTO {
    @ApiModelProperty(value = "拍卖会id")
    private Long auctionSalesroomId;
    @ApiModelProperty(value = "获奖用户ID")
    private List<Long>  userList;
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java
@@ -1,7 +1,14 @@
package com.ruoyi.system.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.PromotionVideo;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import com.ruoyi.system.api.domain.dto.OrderDTO;
import com.ruoyi.system.api.feignClient.OrderClient;
import com.ruoyi.system.api.feignClient.PromotionClient;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.List;
/**
 * @author mitao
@@ -11,6 +18,18 @@
    @Override
    public OrderClient create(Throwable cause) {
        return null;
        return new OrderClient(){
            @Override
            public R<Boolean> saveOrderOne(OrderDTO orderDTO) {
                return R.fail("通过视频集合查询用户失败:" + cause.getMessage());
            }
            @Override
            public R<Boolean> getOrderAuctionBond(OrderAuctionBondDTO orderAuctionBondDTO) {
                return R.fail("通过视频集合查询用户失败:" + cause.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java
@@ -21,4 +21,7 @@
    @PostMapping("/goods-sku/updateGoodsSkuOne")
    R<Boolean> updateGoodsSkuOne(@RequestBody GoodsSku goodsSku);
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java
@@ -3,6 +3,7 @@
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.GoodsSku;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import com.ruoyi.system.api.domain.dto.OrderDTO;
import com.ruoyi.system.api.factory.OrderFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
@@ -15,4 +16,8 @@
    @PostMapping("/order/saveOrderOne")
    R<Boolean> saveOrderOne(@RequestBody OrderDTO orderDTO);
    @PostMapping("/order-auction-bond/getOrderAuctionBond")
    R<Boolean> getOrderAuctionBond(@RequestBody OrderAuctionBondDTO orderAuctionBondDTO);
}
ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java
@@ -21,8 +21,10 @@
import com.ruoyi.common.core.enums.OrderFromEnum;
import com.ruoyi.system.api.domain.GoodsSku;
import com.ruoyi.system.api.domain.MemberAddress;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import com.ruoyi.system.api.feignClient.GoodsSkuClient;
import com.ruoyi.system.api.feignClient.MemberClient;
import com.ruoyi.system.api.feignClient.OrderClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -55,6 +57,9 @@
    @Resource
    private MemberClient emberClient;
    @Resource
    private OrderClient orderClient;
    /**
@@ -184,7 +189,7 @@
                auctionSalesroomVO.setNextAuctionSalesroomGoods(nextAuctionSalesroomGoods);
            }
            LambdaQueryWrapper<AuctionSalesroomGoods> wrapper2=Wrappers.lambdaQuery();
            wrapper2.in(AuctionSalesroomGoods::getSortNum,list);
            wrapper2.notIn(AuctionSalesroomGoods::getSortNum,list);
            wrapper2.eq(AuctionSalesroomGoods::getDelFlag,0);
            wrapper2.eq(AuctionSalesroomGoods::getAuctionSalesroomId,SalesroomId);
            wrapper2.orderByAsc(AuctionSalesroomGoods::getSortNum);
@@ -195,7 +200,7 @@
                R<GoodsSku> goodsSkuOne2 = goodsSkuClient.getGoodsSkuOne(salesroomGoods.getGoodsSkuId());
                GoodsSku goodsSku2=goodsSkuOne2.getData();
                forepartAuctionSalesroomGoodsVO1.setGoodsSkuName(salesroomGoods.getGoodsSkuName());
                forepartAuctionSalesroomGoodsVO1.setCoverPic(goodsSku.getCoverPic());
                forepartAuctionSalesroomGoodsVO1.setCoverPic(goodsSku2.getCoverPic());
                auctionSalesroomGoodsVOS.add(forepartAuctionSalesroomGoodsVO1);
            }
@@ -309,6 +314,7 @@
        AuctionSalesroomGoods auctionSalesroomGoods=iAuctionSalesroomGoodsService.getById(auctionSalesroomGoodsDTO.getGoodsSkuId());
        List<AuctionBidRecord> auctionBidRecordList=new ArrayList<>();
        List<Long> list=new ArrayList<>();
        if (auctionSalesroomGoods.getStatus().getCode()==1) {
            LambdaQueryWrapper<AuctionBidRecord> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(AuctionBidRecord::getGoodsSkuId, auctionSalesroomGoodsDTO.getGoodsSkuId());
@@ -316,21 +322,29 @@
            wrapper.orderByDesc(AuctionBidRecord::getLastBidAmount);
            auctionBidRecordList = iAuctionBidRecordService.list(wrapper);
            //判断
            if (auctionBidRecordList.size() >= auctionSalesroomGoods.getItemQuantity()) {
                for (int i = 0; i <= auctionSalesroomGoods.getItemQuantity(); i++) {
                    AuctionBidRecord auctionBidRecord = auctionBidRecordList.get(i);
                    auctionBidRecord.setStatus(BidStatusEnum.SUCCESSFUL);
                    AddOrder(auctionBidRecord.getGoodsSkuId(),auctionBidRecord.getMemberId(),auctionBidRecord.getLastBidAmount(),auctionSalesroom.getBound());
                    list.add(auctionBidRecord.getMemberId());
                }
            } else {
                for (int i = 0; i <= auctionBidRecordList.size(); i++) {
                    AuctionBidRecord auctionBidRecord = auctionBidRecordList.get(i);
                    auctionBidRecord.setStatus(BidStatusEnum.SUCCESSFUL);
                    AddOrder(auctionBidRecord.getGoodsSkuId(),auctionBidRecord.getMemberId(),auctionBidRecord.getLastBidAmount(),auctionSalesroom.getBound());
                    list.add(auctionBidRecord.getMemberId());
                }
            }
        }
        OrderAuctionBondDTO orderAuctionBondDTO=new OrderAuctionBondDTO();
        orderAuctionBondDTO.setAuctionSalesroomId(auctionSalesroom.getId());
        orderAuctionBondDTO.setUserList(list);
        orderClient.getOrderAuctionBond(orderAuctionBondDTO);
    }
ruoyi-modules/ruoyi-order/pom.xml
@@ -101,6 +101,33 @@
      <version>1.2.47</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.2.4</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.8</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupId>net.sf.json-lib</groupId>
      <artifactId>json-lib</artifactId>
      <version>2.4</version>
      <classifier>jdk15</classifier>
    </dependency>
    <dependency>
      <groupId>cn.afterturn</groupId>
      <artifactId>easypoi-spring-boot-starter</artifactId>
@@ -112,6 +139,12 @@
        </exclusion>
      </exclusions>
    </dependency>
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.47</version>
          <scope>compile</scope>
      </dependency>
  </dependencies>
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java
@@ -1,9 +1,16 @@
package com.ruoyi.order.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.order.domain.pojo.OrderAuctionBond;
import com.ruoyi.order.service.IOrderAuctionBondService;
import com.ruoyi.system.api.domain.GoodsSku;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import io.swagger.annotations.ApiModelProperty;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * <p>
@@ -17,4 +24,20 @@
@RequestMapping("/order-auction-bond")
public class OrderAuctionBondController {
    @Resource
    private IOrderAuctionBondService  iOrderAuctionBondService;
    /**
     * 获取当前商品信息
     *这些还要掉退款,但是还没有支付或者微信
     *
     */
    @PostMapping("/getOrderAuctionBond")
    @ResponseBody
    public R<T> getOrderAuctionBond(@RequestBody OrderAuctionBondDTO orderAuctionBondDTO) {
        iOrderAuctionBondService.getOrderAuctionBond(orderAuctionBondDTO);
        return R.ok();
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java
@@ -2,6 +2,8 @@
import com.ruoyi.order.domain.pojo.OrderAuctionBond;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * <p>
@@ -12,5 +14,6 @@
 * @since 2024-05-16
 */
public interface IOrderAuctionBondService extends IService<OrderAuctionBond> {
   void getOrderAuctionBond( OrderAuctionBondDTO orderAuctionBondDTO);
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java
@@ -1,10 +1,17 @@
package com.ruoyi.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.order.domain.pojo.OrderAuctionBond;
import com.ruoyi.order.mapper.OrderAuctionBondMapper;
import com.ruoyi.order.service.IOrderAuctionBondService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.api.domain.dto.OrderAuctionBondDTO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.awt.*;
import java.util.List;
/**
 * <p>
@@ -17,4 +24,18 @@
@Service
public class OrderAuctionBondServiceImpl extends ServiceImpl<OrderAuctionBondMapper, OrderAuctionBond> implements IOrderAuctionBondService {
    @Resource
    private IOrderAuctionBondService iOrderAuctionBondService;
    @Override
    public void getOrderAuctionBond( OrderAuctionBondDTO orderAuctionBondDTO) {
        LambdaQueryWrapper<OrderAuctionBond> wrapper= Wrappers.lambdaQuery();
        wrapper.notIn(OrderAuctionBond::getMemberId,orderAuctionBondDTO.getUserList());
        wrapper.eq(OrderAuctionBond::getDelFlag,0);
        wrapper.eq(OrderAuctionBond::getAuctionSalesroomId,orderAuctionBondDTO.getAuctionSalesroomId());
        List<OrderAuctionBond> orderAuctionBondList=iOrderAuctionBondService.list(wrapper);
        for (OrderAuctionBond orderAuctionBond:orderAuctionBondList){
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java
New file
@@ -0,0 +1,954 @@
package com.ruoyi.order.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
 * <h3>处理时间的工具类</h3>
 */
public class DateUtil {
    private static TimeZone tz = TimeZone.getTimeZone("GMT+8");
    //private static TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
    /**
     * 得到系统日期
     * @return
     */
    public static Date getDate() {
        TimeZone.setDefault(tz);
        return new Date();
    }
    /**
     * 获取当然凌晨的时间
     * @return Date
     */
    public static Date getZero() {
         Calendar calendar = Calendar.getInstance();
         calendar.setTime(new Date());
         calendar.set(Calendar.HOUR_OF_DAY, 0);
         calendar.set(Calendar.MINUTE, 0);
         calendar.set(Calendar.SECOND, 0);
         return calendar.getTime();
    }
    /**
     * 判断日期是否在from,to之内
     *"yyyy-MM-dd" 格式
     * @param time 指定日期
     * @param from 开始日期
     * @param to   结束日期
     * @return true 在之间  false 不在之间
     */
    public static boolean belongCalendar(Date time, Date from, Date to) {
        Calendar date = Calendar.getInstance();
        date.setTime(time);
        Calendar after = Calendar.getInstance();
        after.setTime(from);
        Calendar before = Calendar.getInstance();
        before.setTime(to);
        if ( (date.after(after) && date.before(before)) || (time.compareTo(from)==0 || time.compareTo(to)==0) ) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 两个时间之差
     * @param startTime
     * @param endTime
     * @param format
     * @return
     * @throws ParseException
     */
    public static String dateDiff(String startTime, String endTime,
            String format) throws ParseException {
        // 按照传入的格式生成一个simpledateformate对象
        SimpleDateFormat sd = new SimpleDateFormat(format);
        long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
        long nh = 1000 * 60 * 60;// 一小时的毫秒数
        long nm = 1000 * 60;// 一分钟的毫秒数
        long ns = 1000;// 一秒钟的毫秒数
        long diff;
        long day = 0;
        long hour = 0;
        long min = 0;
        long sec = 0;
        //long time=0;
        String strTime="";
        // 获得两个时间的毫秒时间差异
        diff = sd.parse(endTime).getTime() - sd.parse(startTime).getTime();
        day = diff / nd;// 计算差多少天
        hour = diff % nd / nh + day * 24;// 计算差多少小时
        min = diff % nd % nh / nm + day * 24 * 60;// 计算差多少分钟
        sec = diff % nd % nh % nm / ns;// 计算差多少秒
        // 输出结果
        /*System.out.println("时间相差:" + day + "天" + (hour - day * 24) + "小时"
                + (min - day * 24 * 60) + "分钟" + sec + "秒。");
        System.out.println("hour=" + hour + ",min=" + min);*/
        if(day==1){
            strTime="昨天";
        }
        else if(day>1){
            //strTime=day+"天前";
            strTime=startTime.substring(0, 10);
        }
        else if(hour>=1 && hour<24){
            strTime=hour+"小时前";
        }
        else{
            if(min==0){strTime=sec+"秒钟前";}else{
                strTime=min+"分钟前";
            }
        }
        // if (str.equalsIgnoreCase("h")) {
        // return hour;
        // } else {
        // return min;
        // }
        // if (str.equalsIgnoreCase("h")) {
        // return hour;
        // } else {
        // return min;
        // }
        return strTime;
    }
    /**
     * 得到系统Calendar日期
     * @return
     */
    public static Calendar getCalendar() {
        TimeZone.setDefault(tz);
        Calendar cal = Calendar.getInstance();
        return cal;
    }
    /**
     * 获取当前时间
     * @return
     */
    public static long getMillisecond() {
        long millisecond = 0;
        TimeZone.setDefault(tz);
        Calendar cal = Calendar.getInstance();
        millisecond = cal.getTimeInMillis();
        return millisecond;
    }
    /**
     * 获取本月1号的时间戳
     * @return
     */
    public static long getMillisecond_MONTH() {
        long millisecond = 0;
        TimeZone.setDefault(tz);
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        millisecond = cal.getTimeInMillis();
        return millisecond;
    }
    /**
     * 获取上个月1号的时间戳
     * @return
     */
    public static long getMillisecond_FRONTMONTH() {
        long millisecond = 0;
        Calendar cal = getCalendar();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.MONTH, Calendar.MONTH-2);
        millisecond = cal.getTimeInMillis();
        return millisecond;
    }
    /**
     * 获取当前毫秒数
     * @return long
     */
    public static long getCurMilli() {
        long millisecond = 0;
        Calendar cal = Calendar.getInstance();
        millisecond = cal.getTimeInMillis();
        return millisecond;
    }
    /**
     * 日期转毫秒
     * @param date
     * @return
     */
    public static long getMillisecond(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if (!"".equals(date)) {
            newDate = sdf.format(date);
        } else {
            newDate = sdf.format(DateUtil.getDate());
        }
        long millisecond = 0;
        try {
            millisecond = sdf.parse(newDate).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 日期转毫秒(加24小时,yyyy-MM-dd HH:mm:ss)
     * @param date
     * @return
     */
    public static long getMillisecond_24h(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if (!"".equals(date)) {
            newDate = sdf.format(date);
        } else {
            newDate = sdf.format(DateUtil.getDate());
        }
        long millisecond = 24*3600*1000;
        try {
            millisecond += sdf.parse(newDate).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 日期转毫秒(加N年)
     * @param date
     * @return
     */
    public static long getMillisecond_year(String date, Integer year){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if ("".equals(date)) {
            newDate = sdf.format(DateUtil.getDate());
        } else {
            newDate = getDateTime(Long.parseLong(date));
        }
        Date dt = null;
        try {
            dt = sdf.parse(newDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        rightNow.add(Calendar.YEAR, year);
        Date dt1 = rightNow.getTime();
        return dt1.getTime();
    }
    /**
     * 日期转毫秒(加N天)
     * @param date 毫秒字符串
     * @return
     */
    public static long getMillisecond_day(String date, Integer day){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if ("".equals(date)) {
            newDate = sdf.format(DateUtil.getDate());
        } else {
            newDate = getDateTime(Long.parseLong(date));
        }
        Date dt = null;
        try {
            dt = sdf.parse(newDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        rightNow.add(Calendar.DATE, day);
        Date dt1 = rightNow.getTime();
        return dt1.getTime();
    }
    /**
     * 日期转毫秒(加N月)
     * @param date
     * @return
     */
    public static long getMillisecond_month(String date, Integer day){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if ("".equals(date)) {
            newDate = sdf.format(DateUtil.getDate());
        } else {
            newDate = getDateTime(Long.parseLong(date));
        }
        Date dt = null;
        try {
            dt = sdf.parse(newDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        rightNow.add(Calendar.MONTH, day);
        Date dt1 = rightNow.getTime();
        return dt1.getTime();
    }
    /**
     * 字符串日期转毫秒
     * @param date
     * @return
     */
    public static long getMillisecond_str(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if ("".equals(date)) {
            date = sdf.format(DateUtil.getDate());
        }
        long millisecond = 0;
        try {
            millisecond = sdf.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 字符串日期转毫秒
     * @param date
     * @return
     */
    public static long getMillisecond_strYmd(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if ("".equals(date)) {
            date = sdf.format(DateUtil.getDate());
        }
        long millisecond = 0;
        try {
            millisecond = sdf.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 字符串日期转Date
     * @param string
     * @return date
     * @throws ParseException
     */
    public static Date getStrToDate(String dateString) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse(dateString);
        return date;
    }
    /**
     * 字符串日期转Date
     * @param date
     * @return
     */
    public static Date getDate_str(String dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setTimeZone(tz);
        if ("".equals(dateStr)) {
            dateStr = sdf.format(DateUtil.getDate());
        }
        Date date = null;
        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 字符串日期转Date yyyy-MM-dd HH:mm
     * @param date
     * @return
     */
    public static Date getDate_str2(String dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        sdf.setTimeZone(tz);
        if ("".equals(dateStr)) {
            dateStr = sdf.format(DateUtil.getDate());
        }
        Date date = null;
        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 字符串日期转Date yyyy-MM-dd HH:mm:ss
     * @param date
     * @return
     */
    public static Date getDate_str3(String dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(tz);
        if ("".equals(dateStr)) {
            dateStr = sdf.format(DateUtil.getDate());
        }
        Date date = null;
        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 字符串日期转Date
     * @param date
     * @return
     */
    public static Date getDate_strYMd(Long dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if (dateStr == null) {
            dateStr = DateUtil.getCurMilli();
        }
        Date date = null;
        try {
            date = sdf.parse(sdf.format(new Date(dateStr)));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 毫秒转Date
     * @param date
     * @return
     */
    public static Date getDate_strYMdHms(Long dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (dateStr == null) {
            dateStr = DateUtil.getCurMilli();
        }
        Date date = null;
        try {
            date = sdf.parse(sdf.format(new Date(dateStr)));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 字符串日期转Date
     * @param date
     * @return
     */
    public static Date getDate_strYMdHm(Long dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        if (dateStr == null) {
            dateStr = DateUtil.getCurMilli();
        }
        Date date = null;
        try {
            date = sdf.parse(sdf.format(new Date(dateStr)));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
    /**
     * 字符串日期转毫秒
     * @param date
     * @return
     */
    public static long getMillisecond_strDmy(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        if ("".equals(date)) {
            date = sdf.format(DateUtil.getDate());
        }
        long millisecond = 0;
        try {
            millisecond = sdf.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 字符串日期转毫秒转毫秒(加24小时)
     * @param date
     * @return
     */
    public static long getMillisecond_str_24h(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if ("".equals(date)) {
            date = sdf.format(DateUtil.getDate());
        }
        long millisecond = 24*3600*1000;
        try {
            millisecond += sdf.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 字符串日期转毫秒转毫秒(加24小时)
     * @param date
     * @return
     */
    public static long getMillisecond_strYmd_24h(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if ("".equals(date)) {
            date = sdf.format(DateUtil.getDate());
        }
        long millisecond = 24*3600*1000;
        try {
            millisecond += sdf.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return millisecond;
    }
    /**
     * 毫秒转日期
     * @param millisecond
     * @return
     */
    public static String getDate(long millisecond) {
        if (millisecond == 0) {
            millisecond = getCurMilli();
        }
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar = getCalendar();
        calendar.setTimeInMillis(millisecond);
        return dateformat.format(calendar.getTime());
    }
    /**
     * 转换为指定格式的时间
     * @return Date
     */
    public static Date getDate(String date, String pattern)  {
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        Date d = null;
        try {
            d = format.parse(date);
        } catch (ParseException ex) {
            return null;
        }
        return d;
    }
    /**
     * 毫秒转日期
     * @param millisecond
     * @return
     */
    public static String getDate_HH(long millisecond) {
        if (millisecond == 0) {
            millisecond = getCurMilli();
        }
        SimpleDateFormat dateformat = new SimpleDateFormat("HH");
        Calendar calendar = getCalendar();
        calendar.setTimeInMillis(millisecond);
        return dateformat.format(calendar.getTime());
    }
    /**
     * 毫秒转日期时间
     * @param millisecond
     * @return
     */
    public static String getDateTime(long millisecond) {
        if (millisecond == 0) {
            millisecond = getCurMilli();
        }
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = getCalendar();
        calendar.setTimeInMillis(millisecond);
        return dateformat.format(calendar.getTime());
    }
    /**
     * 毫秒转年月日
     * @param millisecond
     * @return
     */
    public static String getDateYMD(long millisecond) {
        if (millisecond == 0) {
            millisecond = getCurMilli();
        }
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy年MM月dd日");
        Calendar calendar = getCalendar();
        calendar.setTimeInMillis(millisecond);
        return dateformat.format(calendar.getTime());
    }
    /**
     * 两日期相差毫秒
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getMinusMillisecond(Date startDate, Date endDate) {
        long startMillisecond = getMillisecond(startDate);
        long endMillisecond = getMillisecond(endDate);
        long minusMillisecond = endMillisecond-startMillisecond;
        if (minusMillisecond < 0) {
            minusMillisecond = 0;
        }
        return minusMillisecond;
    }
    /**
     * 两日期相差天数
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getMinusDay(Date startDate, Date endDate) {
        long startMillisecond = getMillisecond(startDate);
        long endMillisecond = getMillisecond(endDate);
        long minusMillisecond = endMillisecond-startMillisecond;
        long day = 0;
        if (minusMillisecond < 0) {
            day = 0;
        } else {
            day = minusMillisecond/(24*3600*1000);
        }
        return day;
    }
    /**
     * 前N天毫秒
     * @param day
     * @return
     */
    public static long getRetreatDay_millisecond(int day) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long nowMillisecond = 0;
        try {
            nowMillisecond = sdf.parse(sdf.format(DateUtil.getDate())).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        nowMillisecond += 24*3600*1000;
        long retreatMillisecond = 24*3600*1000*day;
        return nowMillisecond - retreatMillisecond;
    }
    /**
     * 前N天时间
     * @param day
     * @return
     */
    public static String getRetreatDay_millisecond1(int day) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long nowMillisecond = 0;
        try {
            nowMillisecond = sdf.parse(sdf.format(DateUtil.getDate())).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        nowMillisecond += 24*3600*1000;
        long retreatMillisecond = 24*3600*1000*day;
        long s=nowMillisecond - retreatMillisecond;
        Date date = new Date(s);
        String res = sdf.format(date);
        return res;
    }
    /**
     * 日期转秒
     * @param date
     * @return
     */
    public static long getDecond(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String newDate = "";
        if (!"".equals(date)) {
            newDate = sdf.format(date);
        } else {
            newDate = sdf.format(DateUtil.getDate());
        }
        long second = 0;
        try {
            second = sdf.parse(newDate).getTime()/1000;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return second;
    }
    /**
     * 日期转String
     * @param date
     * @return
     * @throws ParseException
     */
    public static String getDateToString(Date date) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String newDate = sdf.format(date);
        long millisecond = sdf.parse(newDate).getTime();
        Calendar calendar = getCalendar();
        calendar.setTimeInMillis(millisecond);
        return sdf.format(calendar.getTime());
    }
    /**
     * 毫秒转星期XX
     * @param millisecond
     * @return
     */
    public static int getDate_week(Long millisecond) {
        if (millisecond == null) {
            millisecond = getCurMilli();
        }
        Calendar cal = getCalendar();
        cal.setTimeInMillis(millisecond);
        return cal.get(Calendar.DAY_OF_WEEK)-1;
    }
    /**
     * 获取当前系统时间已yyyy-MM-dd HH:mm:ss格式化的字符串
     */
    public static String nowStr(){
        SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
        return dateFormat.format(getDate());
    }
    /**
     *
     * 获取之前几天日期
     * @param pattern yyyy-MM-dd(默认)
     * @param few 之前几天
     */
    public static String beforeFewDayStr(String pattern, Integer few) {
        if(pattern == null || "".equals(pattern)){
            pattern = "yyyy-MM-dd";
        }
        Calendar c = getCalendar();
        c.add(Calendar.DATE,-few);
        return new SimpleDateFormat(pattern).format(c.getTime());
    }
    /**
     * 获取今天日期
     * @param pattern yyyy-MM-dd(默认)
     */
    public static String todayStr(String pattern) {
        if(pattern == null || "".equals(pattern)){
            pattern = "yyyy-MM-dd";
        }
        return new SimpleDateFormat(pattern).format(getDate());
    }
    /**
     * 获取当前系统时间戳字符串
     */
    public static String nowDateLongStr(){
        return getDate().getTime()+"";
    }
    /**
     * 获取当前系统时间
     * @return
     */
    public static Date now(){
        return getDate();
    }
    public static void main(String[] args) throws ParseException {
        //打印测试日期包含
        Date time1  = getDate_str("2017-3-11");
        Date time2 = getDate_str("2017-3-15");
        Date time3 = getDate_str("2017-3-17");
        Date time4 = getDate_str("2017-3-12");
        Date time5 = getDate_str("2017-3-16");
        Date from = getDate_str("2017-3-12");
        Date to= getDate_str("2017-3-16");
        System.out.println(belongCalendar(time1,from,to));
        System.out.println(belongCalendar(time2,from,to));
        System.out.println(belongCalendar(time3,from,to));
        System.out.println(belongCalendar(time4,from,to));
        System.out.println(belongCalendar(time5,from,to));
        System.out.println(nowStr());
    }
    /**
     *  把日期往后增加一天. 正数往后推,负数往前移动
     * @param day
     * @return
     */
    public static String getString(int day) {
        Date date=new Date();//取时间
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.add(calendar.DATE,day);//把日期往后增加一天.整数往后推,负数往前移动
        date=calendar.getTime(); //这个时间就是日期往后推一天的结果
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = formatter.format(date);
        return dateString;
    }
    /**
    * 根据当前日期获得所在周的日期区间(周一和周日日期)
    *
    * @return
    * @author zhaoxuepu
    * @throws ParseException
    */
    public static String getTimeInterval(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
         Calendar cal = Calendar.getInstance();
         cal.setTime(date);
         // 判断要计算的日期是否是周日,如果是则减一天计算周六的,否则会出问题,计算到下一周去了
         int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
         if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
         }
         // System.out.println("要计算日期为:" + sdf.format(cal.getTime())); // 输出要计算日期
         // 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一
         cal.setFirstDayOfWeek(Calendar.MONDAY);
         // 获得当前日期是一个星期的第几天
         int day = cal.get(Calendar.DAY_OF_WEEK);
         // 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
         cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
         String imptimeBegin = sdf.format(cal.getTime());
         // System.out.println("所在周星期一的日期:" + imptimeBegin);
         cal.add(Calendar.DATE, 6);
         String imptimeEnd = sdf.format(cal.getTime());
         // System.out.println("所在周星期日的日期:" + imptimeEnd);
         return imptimeBegin + "," + imptimeEnd;
    }
    /**
    * 根据当前日期获得上周的日期区间(上周周一和周日日期)
    *
    * @return
    * @author zhaoxuepu
    */
    public static String getLastTimeInterval() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
         Calendar calendar1 = Calendar.getInstance();
         Calendar calendar2 = Calendar.getInstance();
         int dayOfWeek = calendar1.get(Calendar.DAY_OF_WEEK) - 1;
         int offset1 = 1 - dayOfWeek;
         int offset2 = 7 - dayOfWeek;
         calendar1.add(Calendar.DATE, offset1 - 7);
         calendar2.add(Calendar.DATE, offset2 - 7);
         // System.out.println(sdf.format(calendar1.getTime()));// last Monday
         String lastBeginDate = sdf.format(calendar1.getTime());
         // System.out.println(sdf.format(calendar2.getTime()));// last Sunday
         String lastEndDate = sdf.format(calendar2.getTime());
         return lastBeginDate + "," + lastEndDate;
    }
    public static String DateYUE(){
    SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
    Calendar c = Calendar.getInstance();
           c.add(Calendar.MONTH, 0);
           c.set(Calendar.DAY_OF_MONTH,1);//设置为1号,当前日期既为本月第一天
           String first = format.format(c.getTime());
           //获取当前月最后一天
           Calendar ca = Calendar.getInstance();
           ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
           String last = format.format(ca.getTime());
           return first+","+last;
    }
  public static String  getBeforeFirstMonthdate(){
    SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
    Calendar calendar=Calendar.getInstance();
    Calendar calendar1=Calendar.getInstance();
    calendar.add(Calendar.MONTH, -1);
    calendar.set(Calendar.DAY_OF_MONTH, 1);
    int month=calendar1.get(Calendar.MONTH);
    calendar1.set(Calendar.MONTH, month-1);
    calendar1.set(Calendar.DAY_OF_MONTH, calendar1.getActualMaximum(Calendar.DAY_OF_MONTH));
    String str=format.format(calendar.getTime());
    String str1=format.format(calendar1.getTime());
    return str+","+str1;
    }
  /**
   * 获取某年第一天和最后一天日期
   * @param year 年份
   * @return Date
   */
  public static String getYearFirst(int year){
      SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
      Calendar calendar = Calendar.getInstance();
      calendar.clear();
      calendar.set(Calendar.YEAR, year);
      Calendar calendar1 = Calendar.getInstance();
      calendar1.clear();
      calendar1.set(Calendar.YEAR, year);
      calendar1.roll(Calendar.DAY_OF_YEAR, -1);
      String str=format.format(calendar.getTime());
      String str1=format.format(calendar1.getTime());
      return str+","+str1;
  }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.order.util;
import java.io.IOException;
import java.util.Properties;
/**
 * 配置信息
 *
 * @version 1.0
 */
public class ParamUtil {
    /**
     * 获取配置信息
     */
    private static Properties properties = new Properties();
    static{
        try {
            //获取properties文件
            properties.load(ParamUtil.class.getClassLoader().getResourceAsStream("conf/param.properties"));
        } catch (IOException e) {e.printStackTrace();}
    }
    /**
     * 获取配置参数值
     *
     * @param key
     * @return
     */
    public static String getValue(String key){
        return (String)properties.get(key);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java
New file
@@ -0,0 +1,345 @@
package com.ruoyi.order.util;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 基本数据处理工具类
 */
public class SinataUtil {
    /**
     * List集合分页<br/>
     * 创建人:Mryang<br/>
     * 时间:2016年7月28日-下午2:58:14 <br/>
     * @param <T>
     * @param pageNo
     * @param pageSize
     * @param list
     * @throws Exception List<UserOrderList> <br/>
     */
    public static <T> List<T> listpage(int pageNo, int pageSize, List<T> list) throws Exception {
        List<T> result = new ArrayList<T>();
        if (list != null && list.size() > 0) {
            int allCount = list.size();
            if(pageNo > 1 && allCount < pageSize) {
                return new ArrayList<>();
            }
            int pageCount = (allCount + pageSize - 1) / pageSize;
            if (pageNo >= pageCount) {
                pageNo = pageCount;
            }
            int start = (pageNo - 1) * pageSize;
            int end = pageNo * pageSize;
            if (end >= allCount) {
                end = allCount;
            }
            for (int i = start; i < end; i++) {
                result.add(list.get(i));
            }
        }
        return (result != null && result.size() > 0) ? result : new ArrayList<T>();
    }
    /**
     * Double类型取整
     * @param num
     * @return
     */
    public static String doubleTrans(double num) {
        return String.valueOf((long) num);
    }
    /**
     * Double类型保留1位小数
     *
     * @param num
     * @return
     */
    public static String doubleRetainOne(double num) {
        DecimalFormat dfs = new DecimalFormat("0.0");
        return dfs.format(num);
    }
    /**
     * Double类型保留2位小数
     *
     * @param num
     * @return
     */
    public static String doubleRetainTwo(double num) {
        DecimalFormat dfs = new DecimalFormat("0.00");
        String.format("%.2f", num);
        return dfs.format(num);
    }
    /**
     * Double类型保留1位小数(四舍五入)
     *
     * @param num
     * @return
     */
    public static String doubleForwardOne(double num) {
        return String.format("%.1f", num);
    }
    /**
     * Double类型保留2位小数(四舍五入)
     *
     * @param num
     * @return
     */
    public static String doubleForwardTwo(double num) {
        return String.format("%.2f", num);
    }
    /**
     * 字符串转换成Ascii
     *
     * @param value
     * @return
     */
    public static String stringToAscii(String value) {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (i != chars.length - 1) {
                sbu.append((int) chars[i]);
            } else {
                sbu.append((int) chars[i]);
            }
        }
        return sbu.toString();
    }
    /**
     * 小数转换为百分比
     *
     * @param decimal
     * @return
     * @author TaoNingBo
     */
    public static String decTurnPercent(double decimal) {
        NumberFormat num = NumberFormat.getPercentInstance();
        num.setMaximumIntegerDigits(3);
        num.setMaximumFractionDigits(2);
        return num.format(decimal);
    }
    /**
     * Ascii转换成字符串
     *
     * @param value
     * @return
     */
    public static String asciiToString(String value) {
        String[] chars = value.split(",");
        StringBuffer sbu = new StringBuffer();
        for (int i = 0; i < chars.length; i++) {
            sbu.append((char) Integer.parseInt(chars[i]));
        }
        return sbu.toString();
    }
    /**
     * 字符串转换unicode
     *
     * @param string
     * @return
     * @author TaoNingBo
     */
    public static String string2Unicode(String string) {
        StringBuffer unicode = new StringBuffer();
        for (int i = 0; i < string.length(); i++) {
            // 取出每一个字符
            char c = string.charAt(i);
            // 转换为unicode
            unicode.append("\\u" + Integer.toHexString(c));
        }
        return unicode.toString();
    }
    /**
     * unicode 转字符串
     *
     * @param unicode
     * @return
     * @author TaoNingBo
     */
    public static String unicode2String(String unicode) {
        StringBuffer string = new StringBuffer();
        String[] hex = unicode.split("\\\\u");
        for (int i = 1; i < hex.length; i++) {
            // 转换出每一个代码点
            int data = Integer.parseInt(hex[i], 16);
            // 追加成string
            string.append((char) data);
        }
        return string.toString();
    }
    /**
     * 字符串编码转换的实现方法
     *
     * @param str
     *            待转换编码的字符串
     * @param newCharset
     *            目标编码
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String changeCharset(String str, String newCharset) throws UnsupportedEncodingException {
        if (str != null) {
            // 用默认字符编码解码字符串。
            byte[] bs = str.getBytes();
            // 用新的字符编码生成字符串
            return new String(bs, newCharset);
        }
        return null;
    }
    /**
     * 注: \n 回车( ) \t 水平制表符( ) \s 空格(\u0008) \r 换行( )
     *
     * @param str
     * @return
     */
    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }
    /**
     * 判断该字符串不能为空
     *
     * @param str
     * @return
     * @author TaoNingBo
     */
    public static boolean isNotEmpty(Object str) {
        return !isEmpty(str);
    }
    public static boolean isNotEmptyUndefined(Object str) {
        return !isEmpty(str) && !str.toString().equals("undefined");
    }
    /**
     * 字符串编码转换的实现方法
     *
     * @param str
     *            待转换编码的字符串
     * @param oldCharset
     *            原编码
     * @param newCharset
     *            目标编码
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String changeCharset(String str, String oldCharset, String newCharset) throws UnsupportedEncodingException {
        if (str != null) {
            // 用旧的字符编码解码字符串。解码可能会出现异常。
            byte[] bs = str.getBytes(oldCharset);
            // 用新的字符编码生成字符串
            return new String(bs, newCharset);
        }
        return null;
    }
    /**
     * 给手机号码加分割符
     *
     * @param phone
     * @return
     * @author TaoNingBo
     */
    public static String splitPhone(String phone) {
        if (isNotEmpty(phone)) {
            String strone = phone.substring(0, 3);
            String strtwo = phone.substring(strone.length(), 7);
            String strthree = phone.substring(strtwo.length() + strone.length(), phone.length());
            return strone + "-" + strtwo + "-" + strthree;
        }
        return "";
    }
    /**
     * 非空判断
     *
     * @param str
     * @return
     * @author TaoNingBo
     */
    public static boolean isEmpty(Object str) {
        return str == null || str.toString().length() == 0 || str.equals("") || str.toString().matches("\\s*");
    }
    /**
     * 把米转换成公里
     *
     * @param km
     * @return
     * @author TaoNingBo
     */
    public static Double kmTransKilo(Integer m) {
        return Math.round(m / 100d) / 10d;
    }
    /**
     * 将List<{@link Object}>转换成List<{@link T}>
     *
     * @param list
     *            将要转换的对象
     * @param clazs
     *            需要转换的泛型对象
     * @return
     * @author TaoNingBo
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> fromToObject(List<?> list, Class<T> clazs) {
        List<T> t = new ArrayList<T>();
        for (Object object : list) {
            t.add((T) object);
        }
        return t;
    }
     /**
     * 生成 uuid, 即用来标识一笔单,也用做 nonce_str
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
    /**
     * 将List<{@link Object}>转换成List<{@link Map<String, Object>}>
     *
     * @param list
     * @return
     * @author TaoNingBo
     */
    @SuppressWarnings("unchecked")
    public static List<Map<String, Object>> fromToObject_M(List<?> list) {
        List<Map<String, Object>> t = new ArrayList<Map<String, Object>>();
        for (Object object : list) {
            t.add((Map<String, Object>) object);
        }
        return t;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.order.util.alipay.config;
import com.ruoyi.order.util.ParamUtil;
/**
 * 类名:AlipayConfig 功能:基础配置类 详细:设置帐户有关信息及返回路径 版本:3.3 日期:2012-08-10
 * 说明:以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *
 * 附:该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 *
 * 提示:如何获取安全校验码和合作身份者ID 1.用您的签约支付宝账号登录支付宝网站(www.alipay.com)
 * 2.点击“商家服务”(https://b.alipay.com/order/myOrder.htm)
 * 3.点击“查询合作者身份(PID)”、“查询安全校验码(Key)”安全校验码查看时,输入支付密码后,页面呈灰色的现象,怎么办? 解决方法:
 * 1、检查浏览器配置,不让浏览器做弹框屏蔽设置 2、更换浏览器或电脑,重新登录查询。
 */
public class  AlipayConfig {
    // ↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    // 商户的私钥
    public static String private_key = ParamUtil.getValue("private_key");
    // 商户收款账号
    public static final String seller_email = ParamUtil.getValue("seller_email");
    // 支付宝的公钥
    public static String ali_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgRS4mO8v95swMPfUAezNK5hM+M/HqX0GT/QEPaICcQU1CNAup7pZuFm0jWyWrA0eOOWCzN5Qky0UEWZjZAd06xvYtxCNCgKh3SoGXWNUVIQ/iTFrZHWK9hn58Krm0vTLSZH1fxhqcYZmiE/ndeJvWRNHZB2UEQhLc5mE/nl3fC3zvMUcY77btfFm/MLRdJRSK83trG1dJ4pXqmROi77rg3dIF6rsdtqB3BCLQ9mks6m/lTx89tcZD6TVooMd4tPUsBnC0bWjqm2d020ufLEi6sf+Lh5UUsT6ueNTk1Pbc/5oOlRjOeV/MQLj4icoWstKn8pWc97FTVAQ9Pmce3COKQIDAQAB";
    // appId
    public static String app_id = ParamUtil.getValue("app_id");
    // 回调地址
    public static String notify_url = ParamUtil.getValue("notify_url");
    // ↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    // 调试用,创建TXT日志文件夹路径
    public static String log_path = "E:\\";
    // 字符编码格式 目前支持 gbk 或 utf-8
    public static String input_charset = "utf-8";
    // 签名方式 不需修改
    public static String sign_type = "RSA2";
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java
New file
@@ -0,0 +1,281 @@
/*
 * Copyright (C) 2010 The MobileSecurePay Project
 * All right reserved.
 * author: shiqun.shi@alipay.com
 */
package com.ruoyi.order.util.alipay.sign;
public final class Base64 {
    static private final int BASELENGTH = 128;
    static private final int LOOKUPLENGTH = 64;
    static private final int TWENTYFOURBITGROUP = 24;
    static private final int EIGHTBIT = 8;
    static private final int SIXTEENBIT = 16;
    static private final int FOURBYTE = 4;
    static private final int SIGN = -128;
    static private final char PAD = '=';
    static private final boolean fDebug = false;
    static final private byte[] base64Alphabet = new byte[BASELENGTH];
    static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
    static {
        for (int i = 0; i < BASELENGTH; ++i) {
            base64Alphabet[i] = -1;
        }
        for (int i = 'Z'; i >= 'A'; i--) {
            base64Alphabet[i] = (byte) (i - 'A');
        }
        for (int i = 'z'; i >= 'a'; i--) {
            base64Alphabet[i] = (byte) (i - 'a' + 26);
        }
        for (int i = '9'; i >= '0'; i--) {
            base64Alphabet[i] = (byte) (i - '0' + 52);
        }
        base64Alphabet['+'] = 62;
        base64Alphabet['/'] = 63;
        for (int i = 0; i <= 25; i++) {
            lookUpBase64Alphabet[i] = (char) ('A' + i);
        }
        for (int i = 26, j = 0; i <= 51; i++, j++) {
            lookUpBase64Alphabet[i] = (char) ('a' + j);
        }
        for (int i = 52, j = 0; i <= 61; i++, j++) {
            lookUpBase64Alphabet[i] = (char) ('0' + j);
        }
        lookUpBase64Alphabet[62] = (char) '+';
        lookUpBase64Alphabet[63] = (char) '/';
    }
    private static boolean isWhiteSpace(char octect) {
        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
    }
    private static boolean isPad(char octect) {
        return (octect == PAD);
    }
    private static boolean isData(char octect) {
        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
    }
    /**
     * Encodes hex octects into Base64
     *
     * @param binaryData
     *            Array containing binaryData
     * @return Encoded Base64 array
     */
    public static String encode(byte[] binaryData) {
        if (binaryData == null) {
            return null;
        }
        int lengthDataBits = binaryData.length * EIGHTBIT;
        if (lengthDataBits == 0) {
            return "";
        }
        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
        char encodedData[] = null;
        encodedData = new char[numberQuartet * 4];
        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
        int encodedIndex = 0;
        int dataIndex = 0;
        if (fDebug) {
            System.out.println("number of triplets = " + numberTriplets);
        }
        for (int i = 0; i < numberTriplets; i++) {
            b1 = binaryData[dataIndex++];
            b2 = binaryData[dataIndex++];
            b3 = binaryData[dataIndex++];
            if (fDebug) {
                System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);
            }
            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
            if (fDebug) {
                System.out.println("val2 = " + val2);
                System.out.println("k4   = " + (k << 4));
                System.out.println("vak  = " + (val2 | (k << 4)));
            }
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
        }
        // form integral number of 6-bit groups
        if (fewerThan24bits == EIGHTBIT) {
            b1 = binaryData[dataIndex];
            k = (byte) (b1 & 0x03);
            if (fDebug) {
                System.out.println("b1=" + b1);
                System.out.println("b1<<2 = " + (b1 >> 2));
            }
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
            encodedData[encodedIndex++] = PAD;
            encodedData[encodedIndex++] = PAD;
        } else if (fewerThan24bits == SIXTEENBIT) {
            b1 = binaryData[dataIndex];
            b2 = binaryData[dataIndex + 1];
            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
            encodedData[encodedIndex++] = PAD;
        }
        return new String(encodedData);
    }
    /**
     * Decodes Base64 data into octects
     *
     * @param encoded
     *            string containing Base64 data
     * @return Array containind decoded data.
     */
    public static byte[] decode(String encoded) {
        if (encoded == null) {
            return null;
        }
        char[] base64Data = encoded.toCharArray();
        // remove white spaces
        int len = removeWhiteSpace(base64Data);
        if (len % FOURBYTE != 0) {
            return null;// should be divisible by four
        }
        int numberQuadruple = (len / FOURBYTE);
        if (numberQuadruple == 0) {
            return new byte[0];
        }
        byte decodedData[] = null;
        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
        int i = 0;
        int encodedIndex = 0;
        int dataIndex = 0;
        decodedData = new byte[(numberQuadruple) * 3];
        for (; i < numberQuadruple - 1; i++) {
            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
                    || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {
                return null;
            } // if found "no data" just return null
            b1 = base64Alphabet[d1];
            b2 = base64Alphabet[d2];
            b3 = base64Alphabet[d3];
            b4 = base64Alphabet[d4];
            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
        }
        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
            return null;// if found "no data" just return null
        }
        b1 = base64Alphabet[d1];
        b2 = base64Alphabet[d2];
        d3 = base64Data[dataIndex++];
        d4 = base64Data[dataIndex++];
        if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
            if (isPad(d3) && isPad(d4)) {
                if ((b2 & 0xf) != 0)// last 4 bits should be zero
                {
                    return null;
                }
                byte[] tmp = new byte[i * 3 + 1];
                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
                return tmp;
            } else if (!isPad(d3) && isPad(d4)) {
                b3 = base64Alphabet[d3];
                if ((b3 & 0x3) != 0)// last 2 bits should be zero
                {
                    return null;
                }
                byte[] tmp = new byte[i * 3 + 2];
                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
                return tmp;
            } else {
                return null;
            }
        } else { // No PAD e.g 3cQl
            b3 = base64Alphabet[d3];
            b4 = base64Alphabet[d4];
            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
        }
        return decodedData;
    }
    /**
     * remove WhiteSpace from MIME containing encoded Base64 data.
     *
     * @param data
     *            the byte array of base64 data (with WS)
     * @return the new length
     */
    private static int removeWhiteSpace(char[] data) {
        if (data == null) {
            return 0;
        }
        // count characters that's not whitespace
        int newSize = 0;
        int len = data.length;
        for (int i = 0; i < len; i++) {
            if (!isWhiteSpace(data[i])) {
                data[newSize++] = data[i];
            }
        }
        return newSize;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java
New file
@@ -0,0 +1,134 @@
package com.ruoyi.order.util.alipay.sign;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RSA {
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
    /**
     * RSA签名
     *
     * @param content
     *            待签名数据
     * @param privateKey
     *            商户私钥
     * @param input_charset
     *            编码格式
     * @return 签名值
     */
    public static String sign(String content, String privateKey, String input_charset) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(input_charset));
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * RSA验签名检查
     *
     * @param content
     *            待签名数据
     * @param sign
     *            签名值
     * @param ali_public_key
     *            支付宝公钥
     * @param input_charset
     *            编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, String ali_public_key, String input_charset) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(ali_public_key);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(input_charset));
            boolean bverify = signature.verify(Base64.decode(sign));
            return bverify;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    /**
     * 解密
     *
     * @param content
     *            密文
     * @param private_key
     *            商户私钥
     * @param input_charset
     *            编码格式
     * @return 解密后的字符串
     */
    public static String decrypt(String content, String private_key, String input_charset) throws Exception {
        PrivateKey prikey = getPrivateKey(private_key);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, prikey);
        InputStream ins = new ByteArrayInputStream(Base64.decode(content));
        ByteArrayOutputStream writer = new ByteArrayOutputStream();
        // rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密
        byte[] buf = new byte[128];
        int bufl;
        while ((bufl = ins.read(buf)) != -1) {
            byte[] block = null;
            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];
                for (int i = 0; i < bufl; i++) {
                    block[i] = buf[i];
                }
            }
            writer.write(cipher.doFinal(block));
        }
        return new String(writer.toByteArray(), input_charset);
    }
    /**
     * 得到私钥
     *
     * @param key
     *            密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = Base64.decode(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.order.util.alipay.sign;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
public class SignUtils {
    private static final String ALGORITHM = "RSA";
    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
    private static final String DEFAULT_CHARSET = "UTF-8";
    public static String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(DEFAULT_CHARSET));
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java
New file
@@ -0,0 +1,98 @@
package com.ruoyi.order.util.alipay.util;
import com.ruoyi.order.util.alipay.config.AlipayConfig;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
/* *
 *类名:AlipayFunction
 *功能:支付宝接口公用函数类
 *详细:该类是请求、通知返回两个文件所调用的公用函数核心处理文件,不需要修改
 *版本:3.3
 *日期:2012-08-14
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */
public class AlipayCore {
    /**
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }
    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }
    /**
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * @param sWord 要写入日志里的文本内容
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(AlipayConfig.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
            writer.write(sWord);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.order.util.alipay.util;
/* *
 *类名:AlipayNotify
 *功能:支付宝通知处理类
 *详细:处理支付宝各接口通知返回
 *版本:3.3
 *日期:2012-08-17
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考
 *************************注意*************************
 *调试通知返回时,可查看或改写log日志的写入TXT里的数据,来检查通知返回是否正常
 */
public class AlipayNotify {
  public static void main(String[] args) {
    System.out.println("11111111111111111111");
    String dd="2017-09-02 17:11";
    System.out.println(dd.length());
}
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java
New file
@@ -0,0 +1,101 @@
package com.ruoyi.order.util.alipay.util;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.config.AlipayConfig;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
/**
 * 支付宝签名工具类<
 */
@SuppressWarnings("unused")
public class PayDemoActivity {
     /**        支付宝参数配置项    */
        public static String serverUrl = "https://openapi.alipay.com/gateway.do";
        public static String format = "json";
        public static String charset = "UTF-8";
        public static String signType = "RSA2";
        public static String APP_ID = AlipayConfig.app_id;
        public static String APP_PRIVATE_KEY = AlipayConfig.private_key;
        public static String ALIPAY_PUBLIC_KEY = AlipayConfig.ali_public_key;
        public static String NOTIFY_URL = AlipayConfig.notify_url;
        //实例化客户端
        private static AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, APP_ID, APP_PRIVATE_KEY, format, charset, ALIPAY_PUBLIC_KEY, signType);
    /**
     * @throws AlipayApiException
     * @throws UnsupportedEncodingException
     * @throws UnsupportedEncodingException
     * @throws AlipayApiException
     *
     * 方法功能说明:支付宝2.0 app支付
     * 创建:2017年2月10日 by tzj
     * 修改:日期 by 修改者
     * 修改内容:
     * @参数: @param subject
     * @参数: @param body
     * @参数: @param price
     * @参数: @param out_trade_no
     * @参数: @param notify_url
     * @参数: @return
     * @return Map<String,Object>
     * @throws
     */
    public static Map<String, Object> appPay(String subject, String body, Double price, String out_trade_no) throws UnsupportedEncodingException, AlipayApiException{
        String outtradeno = out_trade_no;
        String total_amount = price+"";
        /*********** 测试数据(仅供测试) ************/
//            subject = "subject"+DateUtil.getTodayDate("yyMMssHHmmss");
//            total_amount = "0.01";
        /*********** 测试数据(仅供测试)end ************/
            Map<String, Object> map = new HashMap<>();
        try {
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody(body);
            model.setSubject(subject);
            model.setOutTradeNo(outtradeno);
            model.setTimeoutExpress("30m");
            model.setTotalAmount(total_amount);
            model.setProductCode("QUICK_MSECURITY_PAY");
            request.setBizModel(model);
            request.setNotifyUrl(NOTIFY_URL);
            // 这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            String string = response.getBody();
            int one = string.lastIndexOf("&");
            String s=string.substring(0,(one));
            map.put("orderInfo", s);
            System.out.println(java.net.URLDecoder.decode(s, "UTF-8"));
            System.out.println(java.net.URLDecoder.decode(response.getBody(), "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
         return map;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java
New file
@@ -0,0 +1,71 @@
package com.ruoyi.order.util.alipay.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
/* *
 *类名:UtilDate
 *功能:自定义订单类
 *详细:工具类,可以用作获取系统日期、订单编号等
 *版本:3.3
 *日期:2012-08-17
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */
public class UtilDate {
    /** 年月日时分秒(无下划线) yyyyMMddHHmmss */
    public static final String dtLong                  = "yyyyMMddHHmmss";
    /** 完整时间 yyyy-MM-dd HH:mm:ss */
    public static final String simple                  = "yyyy-MM-dd HH:mm:ss";
    /** 年月日(无下划线) yyyyMMdd */
    public static final String dtShort                 = "yyyyMMdd";
    /**
     * 返回系统当前时间(精确到毫秒),作为一个唯一的订单编号
     * @return
     *      以yyyyMMddHHmmss为格式的当前系统时间
     */
    public  static String getOrderNum(){
        Date date=new Date();
        DateFormat df=new SimpleDateFormat(dtLong);
        return df.format(date);
    }
    /**
     * 获取系统当前日期(精确到毫秒),格式:yyyy-MM-dd HH:mm:ss
     * @return
     */
    public  static String getDateFormatter(){
        Date date=new Date();
        DateFormat df=new SimpleDateFormat(simple);
        return df.format(date);
    }
    /**
     * 获取系统当期年月日(精确到天),格式:yyyyMMdd
     * @return
     */
    public static String getDate(){
        Date date=new Date();
        DateFormat df=new SimpleDateFormat(dtShort);
        return df.format(date);
    }
    /**
     * 产生随机的三位数
     * @return
     */
    public static String getThree(){
        Random rad=new Random();
        return rad.nextInt(1000)+"";
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java
New file
@@ -0,0 +1,304 @@
package com.tencent;
import java.util.Map;
import com.tencent.common.Configure;
import com.tencent.common.WXPayConfig;
import com.tencent.common.WXPayConstants;
import com.tencent.common.WXPayConstants.SignType;
import com.tencent.common.WXPayUtil;
import com.tencent.protocol.RefundReqData;
import com.tencent.protocol.UnifiedorderReqData;
import com.tencent.protocol.WXPayRequest;
import com.tencent.service.RefundService;
import com.tencent.service.UnifiedorderService;
/**
 * SDK总入口
 */
/**
 * SDK总入口
 */
public class WXPay {
      private WXPayConfig config;
        private SignType signType;
        private boolean autoReport;
        private boolean useSandbox;
        private String notifyUrl;
        private WXPayRequest wxPayRequest;
        public WXPay(final WXPayConfig config) throws Exception {
            this(config, null, true, false);
        }
        public WXPay(final WXPayConfig config, final boolean autoReport) throws Exception {
            this(config, null, autoReport, false);
        }
        public WXPay(final WXPayConfig config, final boolean autoReport, final boolean useSandbox) throws Exception{
            this(config, null, autoReport, useSandbox);
        }
        public WXPay(final WXPayConfig config, final String notifyUrl) throws Exception {
            this(config, notifyUrl, true, false);
        }
        public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport) throws Exception {
            this(config, notifyUrl, autoReport, false);
        }
        public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
            this.config = config;
            this.notifyUrl = notifyUrl;
            this.autoReport = autoReport;
            this.useSandbox = useSandbox;
            if (useSandbox) {
                this.signType = SignType.MD5; // 沙箱环境
            }
            else {
                this.signType = SignType.HMACSHA256;
            }
            this.wxPayRequest = new WXPayRequest(config);
        }
    /**
     * 初始化SDK依赖的几个关键配置
     * @param key 签名算法需要用到的秘钥
     * @param appID 公众账号ID
     * @param mchID 商户ID
     * @param sdbMchID 子商户ID,受理模式必填
     * @param certLocalPath HTTP证书在服务器中的路径,用来加载证书用
     * @param certPassword HTTP证书的密码,默认等于MCHID
     */
    public static void initSDKConfiguration(String key,String appID,String mchID,String sdbMchID,String certLocalPath,String certPassword){
        System.out.println("________@@@@@______initSDKConfiguration");
        Configure.setKey(key);
        Configure.setAppID(appID);
        Configure.setMchID(mchID);
        Configure.setSubMchID(sdbMchID);
        Configure.setCertLocalPath(certLocalPath);
        Configure.setCertPassword(certPassword);
    }
    /**
     * 请求统一下单服务
     */
    public static String requestUnifiedorderService(Integer apptype,UnifiedorderReqData unifiedorderReqData) throws Exception{
        return new UnifiedorderService(apptype).request(unifiedorderReqData);
    }
    /**
     * 商家向用户付款(提现)
     */
   /* public static String requestPayToTheUserService(Integer apptype, PayToTheUserReqData payToTheUserReqData) throws Exception{
        return new PayToTheUserService(apptype).request(payToTheUserReqData);
    }*/
    /**
     * 请求退款服务
     */
    public static String requestRefundService(Integer apptype, RefundReqData refundReqData) throws Exception{
        return new RefundService(apptype).request(refundReqData);
    }
    /**
     * 提交刷卡支付,针对软POS,尽可能做成功
     * 内置重试机制,最多60s
     * @param reqData
     * @return
     * @throws Exception
     */
    public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception {
        return this.microPayWithPos(reqData, 6*1000);
    }
    /**
     * 作用:提交刷卡支付<br>
     * 场景:刷卡支付
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
    public Map<String, String> microPay(Map<String, String> reqData) throws Exception {
        return this.microPay(reqData, 6*1000, 8*1000);
    }
    /**
     * 作用:提交刷卡支付<br>
     * 场景:刷卡支付
     * @param reqData 向wxpay post的请求数据
     * @param connectTimeoutMs 连接超时时间,单位是毫秒
     * @param readTimeoutMs 读超时时间,单位是毫秒
     * @return API返回数据
     * @throws Exception
     */
    public Map<String, String> microPay(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
        String url;
       /* if (this.useSandbox) {//沙箱环境
            url = WXPayConstants.SANDBOX_MICROPAY_URL_SUFFIX;
        }*/
        url = WXPayConstants.MICROPAY_URL_SUFFIX;
        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
        return this.processResponseXml(respXml);
    }
    /**
     * 不需要证书的请求
     * @param urlSuffix String
     * @param reqData 向wxpay post的请求数据
     * @param connectTimeoutMs 超时时间,单位是毫秒
     * @param readTimeoutMs 超时时间,单位是毫秒
     * @return API返回数据
     * @throws Exception
     */
    public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
                                     int connectTimeoutMs, int readTimeoutMs) throws Exception {
        String msgUUID = reqData.get("nonce_str");
        String reqBody = WXPayUtil.mapToXml(reqData);
        String resp = new WXPayRequest(config).requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);
        return resp;
    }
    /**
     * 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
     * @param xmlStr API返回的XML格式数据
     * @return Map类型数据
     * @throws Exception
     */
    public Map<String, String> processResponseXml(String xmlStr) throws Exception {
        /*String RETURN_CODE = "return_code";
        String return_code;*/
        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
        return respData;
    /*    if (respData.containsKey(RETURN_CODE)) {
            return_code = respData.get(RETURN_CODE);
        }
        else {
            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
        }
        if (return_code.equals(WXPayConstants.FAIL)) {
            return respData;
        }
        else if (return_code.equals(WXPayConstants.SUCCESS)) {
           if (this.isResponseSignatureValid(respData)) {
               return respData;
           }
           else {
               throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
           }
        }
        else {
            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
        }*/
    }
    /**
     * 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
     *
     * @param reqData 向wxpay post的请求数据
     * @return 签名是否有效
     * @throws Exception
     */
    public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
        // 返回数据的签名方式和请求中给定的签名方式是一致的
        return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
    }
    /**
     * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
     * 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口
     *
     * @param reqData
     * @return
     * @throws Exception
     */
    public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
        reqData.put("appid", Configure.getAppid());
        reqData.put("mch_id", Configure.getMchid());
        reqData.put("nonce_str", WXPayUtil.generateUUID());
        reqData.put("sign_type", WXPayConstants.MD5);
        //reqData.put("sign_type", WXPayConstants.HMACSHA256);
        reqData.put("sign", WXPayUtil.generateSignature(reqData, Configure.getKey(), SignType.MD5));
        return reqData;
    }
    /**
     * 提交刷卡支付,针对软POS,尽可能做成功
     * 内置重试机制,最多60s
     * @param reqData
     * @param connectTimeoutMs
     * @return
     * @throws Exception
     */
    public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception {
        int remainingTimeMs = 10*1000;
        long startTimestampMs = 0;
        Map<String, String> lastResult = null;
        Exception lastException = null;
        while (true) {
            startTimestampMs = WXPayUtil.getCurrentTimestampMs();
            int readTimeoutMs = remainingTimeMs - connectTimeoutMs;
            if (readTimeoutMs > 1000) {
                try {
                    lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs);
                    String returnCode = lastResult.get("return_code");//return_code
                    if (returnCode.equals("SUCCESS")) {break;
                 /*       String resultCode = lastResult.get("result_code");
                        String errCode = lastResult.get("err_code");
                        if (resultCode.equals("SUCCESS")) {
                            break;
                        }
                        else {
                            // 看错误码,若支付结果未知,则重试提交刷卡支付
                            if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) {
                                remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs);
                                if (remainingTimeMs <= 100) {
                                    break;
                                }
                                else {
                                    WXPayUtil.getLogger().info("microPayWithPos: try micropay again");
                                    if (remainingTimeMs > 5*1000) {
                                        Thread.sleep(5*1000);
                                    }
                                    else {
                                        Thread.sleep(1*1000);
                                    }
                                    continue;
                                }
                            }
                            else {
                                break;
                            }
                        }*/
                    }
                    else {
                        break;
                    }
                }
                catch (Exception ex) {
                    lastResult = null;
                    lastException = ex;
                }
            }
            else {
                break;
            }
        }
        if (lastResult == null) {
            throw lastException;
        }
        else {
            return lastResult;
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java
New file
@@ -0,0 +1,202 @@
package com.ruoyi.order.util.tencent.common;
import com.ruoyi.order.util.ParamUtil;
/**
 * User: TZJ
 * Date: 2017/08/29
 * Time: 14:40
 * 这里放置微信支付的各种配置数据
 */
public class Configure {
//这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
    // 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
    // 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
    private static String key = ParamUtil.getValue("key");
    //用户端开发者平台的appid
    private static String appID = ParamUtil.getValue("appID");
    //公众号的appid
    private static String GappID = ParamUtil.getValue("GappID");
    //小程序的appid
    private static String XappID = ParamUtil.getValue("XappID");
    //公众号的appSecret
    private static String appSecret = ParamUtil.getValue("appSecret");
    //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
    private static String mchID = ParamUtil.getValue("mchID");
    private static String GmchID = ParamUtil.getValue("GmchID");
    private static String XmchID = ParamUtil.getValue("XmchID");
    //微信回调地址
    public static String wx_notify_url = ParamUtil.getValue("wx_notify_url");
    //受理模式下给子商户分配的子商户号
    private static String subMchID = "";
    //HTTPS证书的本地路径
    private static String certLocalPath;
    static{//从服务器相对路径中获取
        certLocalPath = Configure.class.getClassLoader().getResource("").getPath() + "com/tencent/common/cert/apiclient_cert.p12";
    }
    //HTTPS证书密码,默认密码等于商户号MCHID
        private static String certPassword = mchID;
    //HTTPS证书的本地路径
    private static String certLocalPath_2;
    static{//从服务器相对路径中获取
        certLocalPath_2 = Configure.class.getClassLoader().getResource("").getPath() + "com/tencent/common/cert_2/apiclient_cert.p12";
    }
    //HTTPS证书密码,默认密码等于商户号MCHID
    private static String certPassword_2 = GmchID;
    //是否使用异步线程的方式来上报API测速,默认为异步模式
    private static boolean useThreadToDoReport = true;
    //机器IP
    private static String ip = "";
    //以下是几个API的路径:
    //1)被扫支付API
    public static String PAY_API = "https://api.mch.weixin.qq.com/pay/micropay";
    //2)被扫支付查询API
    public static String PAY_QUERY_API = "https://api.mch.weixin.qq.com/pay/orderquery";
    //3)退款API
    public static String REFUND_API = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    //4)退款查询API
    public static String REFUND_QUERY_API = "https://api.mch.weixin.qq.com/pay/refundquery";
    //5)撤销API
    public static String REVERSE_API = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
    //6)下载对账单API
    public static String DOWNLOAD_BILL_API = "https://api.mch.weixin.qq.com/pay/downloadbill";
    //7) 统计上报API
    public static String REPORT_API = "https://api.mch.weixin.qq.com/payitil/report";
    //商家支付用户(提现)API
    public static String PayToTheUser_API = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    //统一下单API(预支付生成接口)
    public static String UNIFIEDORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    public static boolean isUseThreadToDoReport() {
        return useThreadToDoReport;
    }
    public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
        Configure.useThreadToDoReport = useThreadToDoReport;
    }
    public static String HttpsRequestClassName = "com.tencent.common.HttpsRequest";
    public static String HttpsRequestClassName_2 = "com.tencent.common.HttpsRequest_2";
    public static void setKey(String key) {
        Configure.key = key;
    }
    public static void setAppID(String appID) {
        Configure.appID = appID;
    }
    public static void setMchID(String mchID) {
        Configure.mchID = mchID;
    }
    public static void setSubMchID(String subMchID) {
        Configure.subMchID = subMchID;
    }
    public static void setCertLocalPath(String certLocalPath) {
        Configure.certLocalPath = certLocalPath;
    }
    public static void setCertPassword(String certPassword) {
        Configure.certPassword = certPassword;
    }
    public static void setIp(String ip) {
        Configure.ip = ip;
    }
    public static String getKey(){
        return key;
    }
    public static String getAppid(){
        return appID;
    }
    public static String getGappid(){
        return GappID;
    }
    public static String getXappid(){
        return XappID;
    }
    public static String getMchid(){
        return mchID;
    }
    public static String getGmchid(){
        return GmchID;
    }
    public static String getXmchid(){
        return XmchID;
    }
    public static String getCertPassword_2() {
        return certPassword_2;
    }
    public static void setCertPassword_2(String certPassword_2) {
        Configure.certPassword_2 = certPassword_2;
    }
    public static String getSubMchid(){
        return subMchID;
    }
    public static String getCertLocalPath(){
        return certLocalPath;
    }
    public static String getCertLocalPath_2() {
        return certLocalPath_2;
    }
    public static void setCertLocalPath_2(String certLocalPath_2) {
        Configure.certLocalPath_2 = certLocalPath_2;
    }
    public static String getCertPassword(){
        return certPassword;
    }
    public static String getIP(){
        return ip;
    }
    public static String getAppSecret() {
        return appSecret;
    }
    public static void setAppSecret(String appSecret) {
        Configure.appSecret = appSecret;
    }
    public static void setHttpsRequestClassName(String name){
        HttpsRequestClassName = name;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java
New file
@@ -0,0 +1,187 @@
package com.ruoyi.order.util.tencent.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.tencent.service.IServiceRequest;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 14:36
 */
@SuppressWarnings("deprecation")
public class HttpsRequest implements IServiceRequest{
    public interface ResultListener {
        public void onConnectionPoolTimeoutError();
    }
    //表示请求器是否已经做了初始化工作
    private boolean hasInit = false;
    //连接超时时间,默认10秒
    private int socketTimeout = 10000;
    //传输超时时间,默认30秒
    private int connectTimeout = 30000;
    //请求器的配置
    private RequestConfig requestConfig;
    //HTTP请求器
    private CloseableHttpClient httpClient;
    public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
        init();
    }
    private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));//加载本地的证书进行https加密传输
        try {
            keyStore.load(instream, Configure.getCertPassword().toCharArray());//设置证书密码
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } finally {
            instream.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        //根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        hasInit = true;
    }
    /**
     * 通过Https往API post xml数据
     *
     * @param url    API地址
     * @param xmlObj 要提交的XML数据对象
     * @return API回包的实际数据
     * @throws IOException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public String sendPost(String url, Object xmlObj) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
        if (!hasInit) {
            init();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        //解决XStream对出现双下划线的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        //将要提交给API的数据对象转换成XML格式数据Post给API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
//      Util.log("API,POST过去的数据是:\n"+postDataXML);
        //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        //设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            httpPost.abort();
        }
        return result;
    }
    /**
     * 设置连接超时时间
     *
     * @param socketTimeout 连接时长,默认10秒
     */
    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
        resetRequestConfig();
    }
    /**
     * 设置传输超时时间
     *
     * @param connectTimeout 传输时长,默认30秒
     */
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
        resetRequestConfig();
    }
    private void resetRequestConfig(){
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
    }
    /**
     * 允许商户自己做更高级更复杂的请求器配置
     *
     * @param requestConfig 设置HttpsRequest的请求器配置
     */
    public void setRequestConfig(RequestConfig requestConfig) {
        this.requestConfig = requestConfig;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java
New file
@@ -0,0 +1,188 @@
package com.ruoyi.order.util.tencent.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.tencent.service.IServiceRequest;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 14:36
 */
@SuppressWarnings("deprecation")
public class HttpsRequest_2 implements IServiceRequest{
    public interface ResultListener {
        public void onConnectionPoolTimeoutError();
    }
    //表示请求器是否已经做了初始化工作
    private boolean hasInit = false;
    //连接超时时间,默认10秒
    private int socketTimeout = 10000;
    //传输超时时间,默认30秒
    private int connectTimeout = 30000;
    //请求器的配置
    private RequestConfig requestConfig;
    //HTTP请求器
    private CloseableHttpClient httpClient;
    public HttpsRequest_2() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
        init();
    }
    private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath_2()));//加载本地的证书进行https加密传输
        try {
            keyStore.load(instream, Configure.getCertPassword_2().toCharArray());//设置证书密码
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } finally {
            instream.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, Configure.getCertPassword_2().toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        //根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        hasInit = true;
    }
    /**
     * 通过Https往API post xml数据
     *
     * @param url    API地址
     * @param xmlObj 要提交的XML数据对象
     * @return API回包的实际数据
     * @throws IOException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public String sendPost(String url, Object xmlObj) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
        if (!hasInit) {
            init();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        //解决XStream对出现双下划线的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        //将要提交给API的数据对象转换成XML格式数据Post给API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
//      Util.log("API,POST过去的数据是:\n"+postDataXML);
        //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        //设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            httpPost.abort();
        }
        return result;
    }
    /**
     * 设置连接超时时间
     *
     * @param socketTimeout 连接时长,默认10秒
     */
    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
        resetRequestConfig();
    }
    /**
     * 设置传输超时时间
     *
     * @param connectTimeout 传输时长,默认30秒
     */
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
        resetRequestConfig();
    }
    private void resetRequestConfig(){
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
    }
    /**
     * 允许商户自己做更高级更复杂的请求器配置
     *
     * @param requestConfig 设置HttpsRequest的请求器配置
     */
    public void setRequestConfig(RequestConfig requestConfig) {
        this.requestConfig = requestConfig;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.order.util.tencent.common;
/**
 * 域名管理,实现主备域名自动切换
 */
public abstract interface IWXPayDomain {
    /**
     * 上报域名网络状况
     * @param domain 域名。 比如:api.mch.weixin.qq.com
     * @param elapsedTimeMillis 耗时
     * @param ex 网络请求中出现的异常。
     *           null表示没有异常
     *           ConnectTimeoutException,表示建立网络连接异常
     *           UnknownHostException, 表示dns解析异常
     */
    abstract void report(final String domain, long elapsedTimeMillis, final Exception ex);
    /**
     * 获取域名
     * @param config 配置
     * @return 域名
     */
    abstract DomainInfo getDomain(final WXPayConfig config);
    static class DomainInfo{
        public String domain;       //域名
        public boolean primaryDomain;     //该域名是否为主域名。例如:api.mch.weixin.qq.com为主域名
        public DomainInfo(String domain, boolean primaryDomain) {
            this.domain = domain;
            this.primaryDomain = primaryDomain;
        }
        @Override
        public String toString() {
            return "DomainInfo{" +
                    "domain='" + domain + '\'' +
                    ", primaryDomain=" + primaryDomain +
                    '}';
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java
New file
@@ -0,0 +1,157 @@
package com.ruoyi.order.util.tencent.common;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/***
 * @author V型知识库  www.vxzsk.com
 *
 */
public class JsapiTicketUtil {
    /***
     * 模拟get请求
     * @param url
     * @param charset
     * @param timeout
     * @return
     */
     public static String sendGet(String url, String charset, int timeout)
      {
        String result = "";
        try
        {
          URL u = new URL(url);
          try
          {
            URLConnection conn = u.openConnection();
            conn.connect();
            conn.setConnectTimeout(timeout);
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
            String line="";
            while ((line = in.readLine()) != null)
            {
              result = result + line;
            }
            in.close();
          } catch (IOException e) {
            return result;
          }
        }
        catch (MalformedURLException e)
        {
          return result;
        }
        return result;
      }
     /***
      * 获取acess_token
      * 来源www.vxzsk.com
      * @return
      */
     public static String getAccessToken(){
            String appid=Configure.getGappid();//应用IDwxa15aa9429f9646fd
            String appSecret="3f14c55e683735aefd19271959e27187";//(应用密钥)3f14c55e683735aefd19271959e27187
            String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+appSecret+"";
            String backData=sendGet(url, "utf-8", 10000);
            String accessToken = (String) JSONObject.fromObject(backData).get("access_token");
            return accessToken;
     }
    /***
      * 获取jsapiTicket
      * 来源 www.vxzsk.com
      * @return
      */
    public static String getJSApiTicket(){
        //获取token
        String acess_token= JsapiTicketUtil.getAccessToken();
        String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+acess_token+"&type=jsapi";
        String backData=sendGet(urlStr, "utf-8", 10000);
        String ticket = (String) JSONObject.fromObject(backData).get("ticket");
        return  ticket;
    }
    /**
     * 获取用户openid(公众号)
     * @return
     */
    public static String getOpenId(String code){
        String urlStr = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+Configure.getGappid()+"&secret=3f14c55e683735aefd19271959e27187&code="+code+"&grant_type=authorization_code";
        String backData=sendGet(urlStr, "utf-8", 10000);
        String ticket = (String) JSONObject.fromObject(backData).get("openid");
        return  ticket;
    }
    /**
     * 获取用户openid(小程序)
     * @return
     */
    public static String getOpenId2(String code){
        String urlStr = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+Configure.getXappid()+"&secret=9a38480b9c220688d4fed071d490d732&code="+code+"&grant_type=authorization_code";
        String backData=sendGet(urlStr, "utf-8", 10000);
        String ticket = (String) JSONObject.fromObject(backData).get("openid");
        return  ticket;
    }
    /**
     * 获取请求用户信息的access_token
     *
     * @param code
     * @return
     */
//    public static Map<String, String> getUserInfoAccessToken(String code) {
//        JsonObject object = null;
//        Map<String, String> data = new HashMap();
//        try {
//            String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
//                                       "wx614b91446fc49a0e","ca2288ec80d4e05976425a6508e9b7", code);
//            DefaultHttpClient httpClient = new DefaultHttpClient();
//            HttpGet httpGet = new HttpGet(url);
//            HttpResponse httpResponse = httpClient.execute(httpGet);
//            HttpEntity httpEntity = httpResponse.getEntity();
//            String tokens = EntityUtils.toString(httpEntity, "utf-8");
//            Gson token_gson = new Gson();
//            object = token_gson.fromJson(tokens, JsonObject.class);
//            data.put("openid", object.get("openid").toString().replaceAll("\"", ""));
//            data.put("access_token", object.get("access_token").toString().replaceAll("\"", ""));
//        } catch (Exception ex) {
//        }
//        return data;
//    }
    public static String getCode(){
        JsonObject object = null;
        Map<String, String> data = new HashMap();
        try {
            String codes = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect",
                    "wx614b91446fc49a0e", "http://www.txciot.com/resources/html/first.html", "snsapi_base", "xxxx_state");
            DefaultHttpClient httpClient = new DefaultHttpClient();
           return sendGet(codes, "utf-8", 10000);
        } catch (Exception ex) {
            return "";
        }
    }
    public static void main(String[] args) {
        System.out.println(URLEncoder.encode("http://www.txciot.com/resources/html/first.html"));
        //System.out.println(getUserInfoAccessToken(getCode()));
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.order.util.tencent.common;
import org.slf4j.Logger;
/**
 * User: rizenguo
 * Date: 2014/11/12
 * Time: 14:32
 */
public class Log {
    public static final String LOG_TYPE_TRACE = "logTypeTrace";
    public static final String LOG_TYPE_DEBUG = "logTypeDebug";
    public static final String LOG_TYPE_INFO = "logTypeInfo";
    public static final String LOG_TYPE_WARN = "logTypeWarn";
    public static final String LOG_TYPE_ERROR = "logTypeError";
    //打印日志
    private Logger logger;
    public Log(Logger log){
        logger = log;
    }
    public void t(String s){
        logger.trace(s);
    }
    public void d(String s){
        logger.debug(s);
    }
    public void i(String s){
        logger.info(s);
    }
    public void w(String s){
        logger.warn(s);
    }
    public void e(String s){
        logger.error(s);
    }
    public void log(String type,String s){
        if(type.equals(Log.LOG_TYPE_TRACE)){
            t(s);
        }else if(type.equals(Log.LOG_TYPE_DEBUG)){
            d(s);
        }else if(type.equals(Log.LOG_TYPE_INFO)){
            i(s);
        }else if(type.equals(Log.LOG_TYPE_WARN)){
            w(s);
        }else if(type.equals(Log.LOG_TYPE_ERROR)){
            e(s);
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.order.util.tencent.common;
import java.security.MessageDigest;
/**
 * User: rizenguo
 * Date: 2014/10/23
 * Time: 15:43
 */
public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};
    /**
     * 转换字节数组为16进制字串
     * @param b 字节数组
     * @return 16进制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }
    /**
     * 转换byte到16进制
     * @param b 要转换的byte
     * @return 16进制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }
    /**
     * MD5编码
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes("UTF-8")));//微信支付有汉字时出现签名错误的解决办法
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.order.util.tencent.common;
import java.util.Random;
/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 14:18
 */
public class RandomStringGenerator {
    /**
     * 获取一定长度的随机字符串
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java
New file
@@ -0,0 +1,122 @@
package com.ruoyi.order.util.tencent.common;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
 * User: tzj
 * Date: 2017/06/17
 * Time: 15:23
 */
public class Signature {
    /**
     * 签名算法
     * @param o 要参与签名的数据对象
     * @return 签名
     * @throws IllegalAccessException
     */
    public static String getSign(Object o) throws IllegalAccessException {
        ArrayList<String> list = new ArrayList<String>();
        Class<? extends Object> cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            if (f.get(o) != null && f.get(o) != "") {
                list.add(f.getName() + "=" + f.get(o) + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();
        Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        Util.log("Sign Result:" + result);
        return result;
    }
    public static String getSign(Integer apptype,Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
            result += "key=" + Configure.getKey();
//        Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
//        Util.log("Sign Result:" + result);
        return result;
    }
    /**
     * 从API返回的XML数据里面重新计算一次签名
     * @param responseString API返回的XML数据
     * @return 新鲜出炉的签名
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static String getSignFromResponseString(Integer apptype, String responseString) throws IOException, SAXException, ParserConfigurationException {
        Map<String,Object> map = XMLParser.getMapFromXML(responseString);
        //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign","");
        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        return Signature.getSign(apptype, map);
    }
    /**
     * 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
     * @param responseString API返回的XML数据字符串
     * @return API签名是否合法
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static boolean checkIsSignValidFromResponseString(Integer apptype, String responseString) throws ParserConfigurationException, IOException, SAXException {
        Map<String,Object> map = XMLParser.getMapFromXML(responseString);
        Util.log(map.toString());
        String signFromAPIResponse = map.get("sign").toString();
        if(signFromAPIResponse=="" || signFromAPIResponse == null){
            Util.log("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
            return false;
        }
        Util.log("服务器回包里面的签名是:" + signFromAPIResponse);
        //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign","");
        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        String signForAPIResponse = Signature.getSign(apptype, map);
        if(!signForAPIResponse.equals(signFromAPIResponse)){
            //签名验不过,表示这个API返回的数据有可能已经被篡改了
            Util.log("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
            return false;
        }
        Util.log("恭喜,API返回的数据签名验证通过!!!");
        return true;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java
New file
@@ -0,0 +1,126 @@
package com.ruoyi.order.util.tencent.common;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.XStream;
/**
 * User: tzj
 * Date: 2017/08/23
 * Time: 14:59
 */
public class Util {
    Logger logger= LoggerFactory.getLogger(getClass());
    /**
     * 通过反射的方式遍历对象的属性和属性值,方便调试
     *
     * @param o 要遍历的对象
     * @throws Exception
     */
    public static void reflect(Object o) throws Exception {
        Class<? extends Object> cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            f.setAccessible(true);
            Util.log(f.getName() + " -> " + f.get(o));
        }
    }
    public static byte[] readInput(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        out.close();
        in.close();
        return out.toByteArray();
    }
    public static String inputStreamToString(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i;
        while ((i = is.read()) != -1) {
            baos.write(i);
        }
        return baos.toString();
    }
    public static InputStream getStringStream(String sInputString) {
        ByteArrayInputStream tInputStringStream = null;
        if (sInputString != null && !sInputString.trim().equals("")) {
            try {
                tInputStringStream = new ByteArrayInputStream(sInputString.getBytes("UTF-8"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return tInputStringStream;
    }
    public static Object getObjectFromXML(String xml, Class<?> tClass) {
        //将从API返回的XML数据映射到Java对象
        XStream xStreamForResponseData = new XStream();
        xStreamForResponseData.alias("xml", tClass);
        xStreamForResponseData.ignoreUnknownElements();//暂时忽略掉一些新增的字段
        return xStreamForResponseData.fromXML(xml);
    }
    public static String getStringFromMap(Map<String, Object> map, String key, String defaultValue) {
        if (key == "" || key == null) {
            return defaultValue;
        }
        String result = (String) map.get(key);
        if (result == null) {
            return defaultValue;
        } else {
            return result;
        }
    }
    public static int getIntFromMap(Map<String, Object> map, String key) {
        if (key == "" || key == null) {
            return 0;
        }
        if (map.get(key) == null) {
            return 0;
        }
        return Integer.parseInt((String) map.get(key));
    }
    /**
     * 打log接口
     * @param log 要打印的log字符串
     * @return 返回log
     */
    public static String log(Object log){
        System.out.println(log.toString());
        return log.toString();
    }
    /**
     * 读取本地的xml数据,一般用来自测用
     * @param localPath 本地xml文件路径
     * @return 读到的xml字符串
     */
    public static String getLocalXMLString(String localPath) throws IOException {
        return Util.inputStreamToString(Util.class.getResourceAsStream(localPath));
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java
New file
@@ -0,0 +1,103 @@
package com.ruoyi.order.util.tencent.common;
import java.io.InputStream;
public abstract class WXPayConfig {
    /**
     * 获取 App ID
     *
     * @return App ID
     */
    public abstract String getAppID();
    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    public abstract String getMchID();
    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    public abstract String getKey();
    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    public abstract InputStream getCertStream();
    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpConnectTimeoutMs() {
        return 6*1000;
    }
    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpReadTimeoutMs() {
        return 8*1000;
    }
    /**
     * 获取WXPayDomain, 用于多域名容灾自动切换
     * @return
     */
    public abstract IWXPayDomain getWXPayDomain();
    /**
     * 是否自动上报。
     * 若要关闭自动上报,子类中实现该函数返回 false 即可。
     *
     * @return
     */
    public boolean shouldAutoReport() {
        return true;
    }
    /**
     * 进行健康上报的线程的数量
     *
     * @return
     */
    public int getReportWorkerNum() {
        return 6;
    }
    /**
     * 健康上报缓存消息的最大数量。会有线程去独立上报
     * 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
     *
     * @return
     */
    public int getReportQueueMaxSize() {
        return 10000;
    }
    /**
     * 批量上报,一次最多上报多个数据
     *
     * @return
     */
    public int getReportBatchSize() {
        return 10;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.order.util.tencent.common;
/**
 * 常量
 */
public class WXPayConstants {
    public enum SignType {
        MD5, HMACSHA256
    }
    public static final String DOMAIN_API = "api.mch.weixin.qq.com";
    public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
    public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
    public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
    public static final String FAIL     = "FAIL";
    public static final String SUCCESS  = "SUCCESS";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";
    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";
    public static final String MICROPAY_URL_SUFFIX     = "/pay/micropay";
    public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
    public static final String ORDERQUERY_URL_SUFFIX   = "/pay/orderquery";
    public static final String REVERSE_URL_SUFFIX      = "/secapi/pay/reverse";
    public static final String CLOSEORDER_URL_SUFFIX   = "/pay/closeorder";
    public static final String REFUND_URL_SUFFIX       = "/secapi/pay/refund";
    public static final String REFUNDQUERY_URL_SUFFIX  = "/pay/refundquery";
    public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
    public static final String REPORT_URL_SUFFIX       = "/payitil/report";
    public static final String SHORTURL_URL_SUFFIX     = "/tools/shorturl";
    public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
    // sandbox
    public static final String SANDBOX_MICROPAY_URL_SUFFIX     = "/sandboxnew/pay/micropay";
    public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
    public static final String SANDBOX_ORDERQUERY_URL_SUFFIX   = "/sandboxnew/pay/orderquery";
    public static final String SANDBOX_REVERSE_URL_SUFFIX      = "/sandboxnew/secapi/pay/reverse";
    public static final String SANDBOX_CLOSEORDER_URL_SUFFIX   = "/sandboxnew/pay/closeorder";
    public static final String SANDBOX_REFUND_URL_SUFFIX       = "/sandboxnew/secapi/pay/refund";
    public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX  = "/sandboxnew/pay/refundquery";
    public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
    public static final String SANDBOX_REPORT_URL_SUFFIX       = "/sandboxnew/payitil/report";
    public static final String SANDBOX_SHORTURL_URL_SUFFIX     = "/sandboxnew/tools/shorturl";
    public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java
New file
@@ -0,0 +1,301 @@
package com.ruoyi.order.util.tencent.common;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.*;
import java.security.MessageDigest;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.order.util.tencent.common.WXPayConstants.SignType;
public class WXPayUtil {
    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }
    }
    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }
    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, SignType.MD5);
    }
    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名类型
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
        String sign = generateSignature(data, key, signType);
        data.put(WXPayConstants.FIELD_SIGN, sign);
        return mapToXml(data);
    }
    /**
     * 判断签名是否正确
     *
     * @param xmlStr XML格式数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = xmlToMap(xmlStr);
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }
    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, SignType.MD5);
    }
    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }
    /**
     * 生成签名
     *
     * @param data 待签名数据
     * @param key API密钥
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, SignType.MD5);
    }
    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }
    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    /**
     * 生成 HMACSHA256
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    /**
     * 日志
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }
    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }
    /**
     * 获取当前时间戳,单位毫秒
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }
    /**
     * 生成 uuid, 即用来标识一笔单,也用做 nonce_str
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java
New file
@@ -0,0 +1,43 @@
package com.ruoyi.order.util.tencent.common;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XMLParser {
    public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {
        //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is =  Util.getStringStream(xmlString);
        Document document = builder.parse(is);
        //获取到document里面的全部结点
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, Object> map = new HashMap<String, Object>();
        int i=0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if(node instanceof Element){
                map.put(node.getNodeName(),node.getTextContent());
            }
            i++;
        }
        return map;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12
Binary files differ
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem
New file
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEbzCCA9igAwIBAgIEAapJajANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
Q04xEjAQBgNVBAgTCUd1YW5nZG9uZzERMA8GA1UEBxMIU2hlbnpoZW4xEDAOBgNV
BAoTB1RlbmNlbnQxDDAKBgNVBAsTA1dYRzETMBEGA1UEAxMKTW1wYXltY2hDQTEf
MB0GCSqGSIb3DQEJARYQbW1wYXltY2hAdGVuY2VudDAeFw0xODAyMDIxMTA3NDRa
Fw0yODAxMzExMTA3NDRaMIGeMQswCQYDVQQGEwJDTjESMBAGA1UECBMJR3Vhbmdk
b25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UEChMHVGVuY2VudDEOMAwGA1UE
CxMFTU1QYXkxMzAxBgNVBAMUKuaIkOmDvemprOWPr+aXtuWwmue9kee7nOenkeaK
gOaciemZkOWFrOWPuDERMA8GA1UEBBMIOTM1NTUzMzUwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDF+apUVzPxdAF2FA8r2kg6aPeLynMRsUE17eW+wPZS
q8mVMrwACzYePbL5003/snEsEU936qnEiu8t5J6w+74dc3mQMr88FW29jD9MQOYR
yOeWz+LVKFXqA7sm9Aj+7hFrcjxdjl2FvKoITTSXNbtM71FrhsGXsnW/1Zjl3sSy
cu2qBq40QNqpefzz2gJ+vbtROy+ZynK7QaSwOtiFZuIbPgYv6W18wmigss6JuVkM
MZUquK4yDIPhwJ4Dk4lLXak1qqNfplfVKQtOo+MHdfIHnG9Z9q+R4iEvAGF4oxaR
zeKFk+zir8E8xEngmnH0ESue7fn9qh6q0mO7xTaYaG0HAgMBAAGjggFGMIIBQjAJ
BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh0iQ0VTLUNBIEdlbmVyYXRlIENlcnRp
ZmljYXRlIjAdBgNVHQ4EFgQUYkOGN8XaKFwFtBCoEBKwGCnBbtYwgb8GA1UdIwSB
tzCBtIAUPgUm9iJitBVbiM1kfrDUYqflhnShgZCkgY0wgYoxCzAJBgNVBAYTAkNO
MRIwEAYDVQQIEwlHdWFuZ2RvbmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQK
EwdUZW5jZW50MQwwCgYDVQQLEwNXWEcxEzARBgNVBAMTCk1tcGF5bWNoQ0ExHzAd
BgkqhkiG9w0BCQEWEG1tcGF5bWNoQHRlbmNlbnSCCQC7VJcrvADoVzAOBgNVHQ8B
Af8EBAMCBsAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQAD
gYEARXIWwdW50A3JM8BIC94argtpvZXqTj5hWeheeJSDBxNVFv/WzdahqZx7Yokn
DP/Ac6dekFfvvXR6ujwevEUTFbpSac1h7k7u/URmwH/R2URTevPTrMeg5D+gLeYP
nEJ+GhyRRAFOoGbN1SoK4f1j6pExpl2GaQ0PVNv7NRVW1tI=
-----END CERTIFICATE-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem
New file
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDF+apUVzPxdAF2
FA8r2kg6aPeLynMRsUE17eW+wPZSq8mVMrwACzYePbL5003/snEsEU936qnEiu8t
5J6w+74dc3mQMr88FW29jD9MQOYRyOeWz+LVKFXqA7sm9Aj+7hFrcjxdjl2FvKoI
TTSXNbtM71FrhsGXsnW/1Zjl3sSycu2qBq40QNqpefzz2gJ+vbtROy+ZynK7QaSw
OtiFZuIbPgYv6W18wmigss6JuVkMMZUquK4yDIPhwJ4Dk4lLXak1qqNfplfVKQtO
o+MHdfIHnG9Z9q+R4iEvAGF4oxaRzeKFk+zir8E8xEngmnH0ESue7fn9qh6q0mO7
xTaYaG0HAgMBAAECggEBAL1yjrYqxIAX3JYb1/DE7z1F2S0iTD7v+lEbGDEUAiNW
VI67gAAiumTx8eTzeb5oCbh70CoWmuKDeSHXlqwSGvmf2QJOLYXXk65BGQllSURX
lpZQ1OXZtW08qMk72afqNS02oeF9LK2fvq43A053koaqtKVKzNLXaaprQecm599C
P+BdahdKk6SE39cOB9fMfX1s5BZRJUhIKNBmcEBsgo9xtKTvgvbgBkSvFBd2ycMo
5KRUbquytqIfwDJTETn7FZqh86+BgLOsWEQZ+1ByEJkFNdOGfjz0HVxNMY60j42D
u9fsIMFup+a/oP190atMSt+GE6ZjgQit/LsF7BrPaiECgYEA7uUDZIpQ1j07MZP+
CFMGIE8aTlhvbWho67HCCLDAAv2ZdOSusV66iMzEfDkfOcj9fp8OlfbK2ocUCkS5
5hQAuAk4O8KTmbGgf71gyFZzddZkilc1UAgHVMj9wVp2PfLmpb6iFhazd4enwTPs
TKxogxfXWRQKQqkCnunOSS7m7V8CgYEA1CaXqMWm2v52FHN8dl8AnH6YjwOQpNXI
qNOyA8Af03dEQ5w/sNlusiF73/FIgErb0be4rgIvJOW0ziBU9RKn/vB5wl4IpJjM
JuL3hh7RlAbfSWaqaKCZKIDolS2vtvwKp9mZgmxWgvKx3Q/L8I1MXPIZTTRpGwIe
HyBxYBTSeVkCgYEAuRHbzgDFXSN0fxUKYMKI5XDCjV0/fX+8gJNmITtklSyHvKII
n8omMiKIOUA7a5XEpFpuf0kAn8izgrz5Uz+9ytUBpnI3zIJqN2ur1af7rJdZrj6f
Q4/ktg87XpZJMfbCLjb/9KLDh98k26zw6/lIwf4vS4gV5sxuBlRVBib6qXsCgYA9
2f6PWwi5gkSk08KutCX2ekY6M9zg4d89sXKN+16MBZKmb5B2G/5U+sQl1y+/D5Ql
AX5bZRGfnUTob661QLov7qZhD49PhajHkZZ+yM9ra9F/keoO1PuyPlsQ2yAGplbo
9tz1lGZoiR9oBun7eQaRdpf/1U7ra+OiSZAkZg2zCQKBgQCl+bszQ202CkLchQ1R
Mu2svFFi+BeVvtA8yp4Il3bznxkgBZJ1/4gB+u84f9ybfpKHok7+f+iHVGJSNiay
Uw5ouE6oNEJODsvWDDliCjrckGUBDmJ8XOHEkm3MPOmEov8UJ6twev/QWhEfEUZk
fclCYjgt3YJ3XW9hXgORWhJj9g==
-----END PRIVATE KEY-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem
New file
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/证书使用说明.txt
New file
@@ -0,0 +1,20 @@
欢迎使用微信支付!
微信支付API共四份(证书pkcs12格式、证书pem格式、证书密钥pem格式、CA证书),为接口中强制要求时需携带的证书文件。
证书属于敏感信息,请妥善保管不要泄露和被他人复制。
不同开发语言下的证书格式不同,以下为说明指引:
    证书pkcs12格式(apiclient_cert.p12)
        包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
        部分安全性要求较高的API需要使用该证书来确认您的调用身份
        windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户ID(如:10010000)
    证书pem格式(apiclient_cert.pem)
        从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
    证书密钥pem格式(apiclient_key.pem)
        从apiclient_cert.p12中导出密钥部分的文件,为pem格式
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
    CA证书(rootca.pem)
        微信支付api服务器上也部署了证明微信支付身份的服务器证书,您在使用api进行调用时也需要验证所调用服务器及域名的真实性
        该文件为签署微信支付证书的权威机构的根证书,可以用来验证微信支付服务器证书的真实性
        某些环境和工具已经内置了若干权威机构的根证书,无需引用该证书也可以正常进行验证,这里提供给您在未内置所必须根证书的环境中载入使用
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12
Binary files differ
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem
New file
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEbDCCA9WgAwIBAgIEAV66BDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
Q04xEjAQBgNVBAgTCUd1YW5nZG9uZzERMA8GA1UEBxMIU2hlbnpoZW4xEDAOBgNV
BAoTB1RlbmNlbnQxDDAKBgNVBAsTA1dYRzETMBEGA1UEAxMKTW1wYXltY2hDQTEf
MB0GCSqGSIb3DQEJARYQbW1wYXltY2hAdGVuY2VudDAeFw0xNzA2MjkwNjQwMzha
Fw0yNzA2MjcwNjQwMzhaMIGbMQswCQYDVQQGEwJDTjESMBAGA1UECBMJR3Vhbmdk
b25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UEChMHVGVuY2VudDEOMAwGA1UE
CxMFTU1QYXkxMDAuBgNVBAMUJ+WTiOWwlOa7qOWQjeeahOe9kee7nOenkeaKgOac
iemZkOWFrOWPuDERMA8GA1UEBBMIMzE4OTQ3MDYwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDJS/abCCpwvnppCU0TxW84PF2cqpUF3Ip9KmQytNCPRZPK
q299efclcWOYozB7zSY4OsZ9UyAod1+HsFEjyJBTWyJfakNewiF6AIsydZtFZ65t
8ReQ66VTebB1imEq/OrQMXphBwJ1TSH5Jk2pW7JA9U5LqZ3apgDa9PrFHY4dhnkf
IxQI6Xn0Y7fUJsGszP+5ETbtlLW5PZQiE2H8T3xCwH3mnmg/rppTZaBVIE/7SskW
iEVPd8g/fec8xDAoAulv1FsBdrPGXyWiFkpcTdrQKHaMa0J7AtMfSnMb1Z/pAIQp
DxCjCJRM0gfvZNOj2nyqbZD3JJAP8LFnTrasdb/NAgMBAAGjggFGMIIBQjAJBgNV
HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh0iQ0VTLUNBIEdlbmVyYXRlIENlcnRpZmlj
YXRlIjAdBgNVHQ4EFgQUBMf3KHk8wfRKtAdcnz+VCFwDdeYwgb8GA1UdIwSBtzCB
tIAUPgUm9iJitBVbiM1kfrDUYqflhnShgZCkgY0wgYoxCzAJBgNVBAYTAkNOMRIw
EAYDVQQIEwlHdWFuZ2RvbmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQKEwdU
ZW5jZW50MQwwCgYDVQQLEwNXWEcxEzARBgNVBAMTCk1tcGF5bWNoQ0ExHzAdBgkq
hkiG9w0BCQEWEG1tcGF5bWNoQHRlbmNlbnSCCQC7VJcrvADoVzAOBgNVHQ8BAf8E
BAMCBsAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEA
EFcsWO+7E4v7NNdprZYbYASr4e4Uw+EO+RLbkuFUQhHTGMW/biFlYounarNncTFs
SylXIkCylTRxSZmLNhr8D9/TJ1c9Vrbp8SqlgS5DlhLU3PGjmY7Yl+Zth06v6kv+
0GqwKCljlN+DQNq2wxL7z3S/XDZGTb8tAhLtOKyPkpg=
-----END CERTIFICATE-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem
New file
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDJS/abCCpwvnpp
CU0TxW84PF2cqpUF3Ip9KmQytNCPRZPKq299efclcWOYozB7zSY4OsZ9UyAod1+H
sFEjyJBTWyJfakNewiF6AIsydZtFZ65t8ReQ66VTebB1imEq/OrQMXphBwJ1TSH5
Jk2pW7JA9U5LqZ3apgDa9PrFHY4dhnkfIxQI6Xn0Y7fUJsGszP+5ETbtlLW5PZQi
E2H8T3xCwH3mnmg/rppTZaBVIE/7SskWiEVPd8g/fec8xDAoAulv1FsBdrPGXyWi
FkpcTdrQKHaMa0J7AtMfSnMb1Z/pAIQpDxCjCJRM0gfvZNOj2nyqbZD3JJAP8LFn
Trasdb/NAgMBAAECggEAPP5pgNxej78HtF//HOiMJMwVyWzeruH+jKKVrc+gCmmY
uKZtp+WW/592AEAiZsDovc0Ba+QOMHTLW4APdfpF0RwiNCsssr+8CQ21H9sG62bZ
MbMF4wGkHMEYuOTvQGlWdTMo09Gd6xEuJCJ5EcIZJxmtWbvRevkl9TKZ8bRc5ZDZ
2s/8cgzps9lbnkbTZ9TAF7n7XOlPy4YqIBKW33RCHUtQCfCDERLaBRAG6SbNUsBQ
SUYq6PsroFtqgwXxbygVsXfNBBfXZ8nfyqupvkOto+2NJjDr6cm5cT+bMyStcNdN
UAssNUbw7DjcQ+0esS0Z9K7Tah5kmLzzGcDnyNoLgQKBgQDtLeEzRWU9QIksvzUb
tIAwz/yLTVAQ74/ke6VP4pAWkLdgcVWxY+HOfXyNTWC3MZ2BSyj3oTaahgCQAVb3
dXmRDWjRWggUcTLNWgZCcjPw4oshdzVPz1t5WR9/9c+UDqSFSrBGVJ5cVLNpDxSk
1KYX7gNdu+h8RTKpKxyOleJaEQKBgQDZRShPjZYvcLNv79y3cT8RHX8qi2akd7by
X6VwxoFeZ5TeK51fdZjUSyU5/8Jmq9m+K3W7edYrzAOiLYYSCNAVQEoxqGacPla2
OlpiSPbtafoIy0ovsJvV6DAZwwtF0cowvdNnXvPsYlKlAKCXnRxf+Ap9/XYArBD6
dTpcj1Ht/QKBgQDHhCK/GI9TeWhrZeVtCugTJ8MQA9doh72JvKGRk8sV0GMSFAS8
FoOrSlBuJTtIn8oZK3qo5MqkLjlm6lmYDRBYfuk/wvatC5RqvmIpnxwmdMwJONh1
7L7MU7Z10/fsWmz3W57xatlUXkQWCoOZLPETjDr0rM+jSiqVuBhrsZYZoQKBgGN8
mFW9Fm0IpRF3etCAyYkTwZwAkvJyYtIe28mFC83njjIhNIF2fpGPCy/AngqXXDAJ
BHLg+iG53FwRjleJs4ZXluGgI8Qdg2UT2TkyAed6ZGRMed7WdrT/Ca9yMSI6SuQA
t+GYiAbQitlFLUPfTBPhpyRDwBnUsD2PGjcx5SkdAn8KkFSMExG4y4eZD+UxLXsj
/QfQj0eUyzu5XyvPXLD9XV1YL3pX7+DxvU98ogFH4QBh2KtDbe2RldyiH8toSP0F
kXoj4/CtUp8EDBpeLgbdxCYvKW+Lz9oyPNouhkV7m0KRrHV/3CPOeHqkh0dNdW6f
RKW9WsNAtAS2ncev7Mjj
-----END PRIVATE KEY-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem
New file
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/证书使用说明.txt
New file
@@ -0,0 +1,20 @@
欢迎使用微信支付!
微信支付API共四份(证书pkcs12格式、证书pem格式、证书密钥pem格式、CA证书),为接口中强制要求时需携带的证书文件。
证书属于敏感信息,请妥善保管不要泄露和被他人复制。
不同开发语言下的证书格式不同,以下为说明指引:
    证书pkcs12格式(apiclient_cert.p12)
        包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
        部分安全性要求较高的API需要使用该证书来确认您的调用身份
        windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户ID(如:10010000)
    证书pem格式(apiclient_cert.pem)
        从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
    证书密钥pem格式(apiclient_key.pem)
        从apiclient_cert.p12中导出密钥部分的文件,为pem格式
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
    CA证书(rootca.pem)
        微信支付api服务器上也部署了证明微信支付身份的服务器证书,您在使用api进行调用时也需要验证所调用服务器及域名的真实性
        该文件为签署微信支付证书的权威机构的根证书,可以用来验证微信支付服务器证书的真实性
        某些环境和工具已经内置了若干权威机构的根证书,无需引用该证书也可以正常进行验证,这里提供给您在未内置所必须根证书的环境中载入使用
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java
New file
@@ -0,0 +1,125 @@
package com.ruoyi.order.util.tencent.protocol;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import com.ruoyi.order.util.DateUtil;
import com.ruoyi.order.util.tencent.common.RandomStringGenerator;
import com.ruoyi.order.util.tencent.common.Signature;
/**
 * 调起支付-API需要提交的数据
 * @author pan
 */
public class AppPayReqData {
    //每个字段具体的意思请查看API文档
    private String appid;//公众账号ID
    private String partnerid;//商户号
    private String prepayid;//预支付编号(微信返回的支付交易会话ID)
    private String _package = "Sign=WXPay";//扩展字段(暂填写固定值Sign=WXPay)
    private String noncestr = RandomStringGenerator.getRandomStringByLength(32);//随机字符串 (32位)
    private String timestamp = DateUtil.nowDateLongStr().substring(0, 10);//时间戳
    private String sign;//签名
    /**
     * 调用微信支付
     * @param appid 微信分配的APPID
     * @param partnerid 微信支付分配的商户号ID
     * @param prepayid 预支付编号(微信返回的支付交易会话ID)
     */
    public AppPayReqData(Integer apptype, String appid, String partnerid, String prepayid, String noncestr){
        //微信分配的APPID
        this.appid = appid;
        //微信支付接口与预支付接口随机字符串,保持一致!
        this.noncestr = noncestr;
        //微信支付分配的商户号ID
        this.partnerid = partnerid;
        //预支付编号(微信返回的支付交易会话ID)
        this.prepayid = prepayid;
        // 根据API给的签名规则进行签名
        String sign = Signature.getSign(apptype, toMap());
        setSign(sign);//微信公众号(最后参与签名的参数有appId, timeStamp, nonceStr, package, signType)
    }
    public Map<String,Object> toMap(){
        Map<String,Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object obj;
            try {
                obj = field.get(this);
                if(obj!=null){
                    if ("_package".equals(field.getName())) {
                        map.put("package", obj);
                    }else{
                         map.put(field.getName(), obj);
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
    public String getAppid() {
        return appid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public String getPartnerid() {
        return partnerid;
    }
    public void setPartnerid(String partnerid) {
        this.partnerid = partnerid;
    }
    public String getPrepayid() {
        return prepayid;
    }
    public void setPrepayid(String prepayid) {
        this.prepayid = prepayid;
    }
    public String get_package() {
        return _package;
    }
    public void set_package(String _package) {
        this._package = _package;
    }
    public String getNoncestr() {
        return noncestr;
    }
    public void setNoncestr(String noncestr) {
        this.noncestr = noncestr;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java
New file
@@ -0,0 +1,176 @@
/*package com.tencent.protocol;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import com.tencent.common.Configure;
import com.tencent.common.RandomStringGenerator;
import com.tencent.common.Signature;
*//**
 * 商户想用户付款(提现)
 *//*
public class PayToTheUserReqData {
    //每个字段具体的意思请查看API文档 https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
    private String mch_appid;//公众账号appid        是    wx8888888888888888    String    微信分配的公众账号ID(企业号corpid即为此appId)
    private String mchid;//商户号        是    1900000109    String(32)    微信支付分配的商户号
    private String nonce_str;// 随机字符串        是    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    String(32)    随机字符串,不长于32位
    private String sign;// 签名        是    C380BEC2BFD727A4B6845133519F3AD6    String(32)    签名,详见签名算法
    private String partner_trade_no;// 商户订单号        是    10000098201411111234567890    String    商户订单号,需保持唯一性
    private String openid;// 用户openid    是    oxTWIuGaIt6gTKsQRLau2M0yL16E    String    商户appid下,某用户的openid
    private String check_name;// 校验用户姓名选项    是    OPTION_CHECK    String    NO_CHECK:不校验真实姓名     FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)    OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
    private String re_user_name;// 收款用户姓名        可选    马花花    String    收款用户真实姓名。     如果check_name设置为FORCE_CHECK或OPTION_CHECK,则必填用户真实姓名
    private Integer amount;// 金额        是    10099    int    企业付款金额,单位为分
    private String desc;// 企业付款描述信息        是    理赔    String    企业付款操作说明信息。必填。
    private String spbill_create_ip = "127.0.0.1";// Ip地址        是    192.168.0.1    String(32)    调用接口的机器Ip地址
    *//**
     * 企业向用户付款
     * @param partner_trade_no 商户订单号,需保持唯一性
     * @param openid 商户appid下,某用户的openid
     * @param check_name 校验用户姓名选项:NO_CHECK:不校验真实姓名  FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
     * @param re_user_name 收款用户姓名
     * @param amount 企业付款金额,单位为分
     * @param desc 企业付款描述信息
     * @param spbill_create_ip 调用接口的机器Ip地址
     *//*
    public PayToTheUserReqData(String partner_trade_no, String openid, String re_user_name, Integer amount, String desc){
        //微信分配的公众号ID(开通公众号之后可以获取到)
        setMch_appid(Configure.getAppid());
        //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
        setMchid(Configure.getMchid());
        setPartner_trade_no(partner_trade_no);
        setOpenid(openid);
        setCheck_name("OPTION_CHECK");//默认:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
        //      setRe_user_name(re_user_name);
        setAmount(amount);
        setDesc(desc);
        //随机字符串,不长于32 位
        setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        //根据API给的签名规则进行签名
        String sign;
        try {
            sign = Signature.getSign(toMap());
            setSign(sign);//把签名数据设置到Sign这个属性中
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    public String getMch_appid() {
        return mch_appid;
    }
    public void setMch_appid(String mch_appid) {
        this.mch_appid = mch_appid;
    }
    public String getMchid() {
        return mchid;
    }
    public void setMchid(String mchid) {
        this.mchid = mchid;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getPartner_trade_no() {
        return partner_trade_no;
    }
    public void setPartner_trade_no(String partner_trade_no) {
        this.partner_trade_no = partner_trade_no;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
    public String getCheck_name() {
        return check_name;
    }
    public void setCheck_name(String check_name) {
        this.check_name = check_name;
    }
    public String getRe_user_name() {
        return re_user_name;
    }
    public void setRe_user_name(String re_user_name) {
        this.re_user_name = re_user_name;
    }
    public Integer getAmount() {
        return amount;
    }
    public void setAmount(Integer amount) {
        this.amount = amount;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public Map<String,Object> toMap(){
        Map<String,Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object obj;
            try {
                obj = field.get(this);
                if(obj!=null){
                    map.put(field.getName(), obj);
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
}
*/
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java
New file
@@ -0,0 +1,210 @@
package com.tencent.protocol;
import com.ruoyi.order.util.tencent.common.Configure;
import com.ruoyi.order.util.tencent.common.RandomStringGenerator;
import com.ruoyi.order.util.tencent.common.Signature;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * 商户退款-API需要提交的数据
 * @author pan
 */
public class RefundReqData {
    //每个字段具体的意思请查看API文档
    private String appid = "";
    private String mch_id = "";
    private String device_info = "";
    private String nonce_str = "";
    private String sign = "";
    private String transaction_id = "";
    private String out_trade_no = "";
    private String out_refund_no = "";
    private int total_fee = 0;
    private int refund_fee = 0;
    private String refund_fee_type = "CNY";
    private String op_user_id = "";
    private String pay_type = "";//2微信app支付,3公众号支付,4小程序支付
    /**
     * 请求退款服务
     * @param transactionID 是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单API支付成功时返回的数据里面获取到。建议优先使用
     * @param outTradeNo 商户系统内部的订单号,transaction_id 、out_trade_no 二选一,如果同时存在优先级:transaction_id>out_trade_no
     * @param deviceInfo 微信支付分配的终端设备号,与下单一致
     * @param outRefundNo 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
     * @param totalFee 订单总金额,单位为分
     * @param refundFee 退款总金额,单位为分,可以做部分退款
     * @param opUserID 操作员帐号, 默认为商户号
     * @param refundFeeType 货币类型,符合ISO 4217标准的三位字母代码,默认为CNY(人民币)
     */
    public RefundReqData(String transactionID, String outTradeNo, String outRefundNo, int totalFee, int refundFee,String payType) {
        // 微信分配的公众号ID(开通公众号之后可以获取到)
        if (payType.equals("2")) {//微信app
            System.out.println("微信app退款!!!!!!!!!!!");
            setAppid(Configure.getAppid());
            setMch_id(Configure.getMchid());
            setOp_user_id(getMch_id());
        }else if(payType.equals("3")) {//微信公众号
            System.out.println("微信公众号退款!!!!!!!!!!!");
            setAppid(Configure.getGappid());
            setMch_id(Configure.getGmchid());
            setOp_user_id(Configure.getGmchid());
        }else if(payType.equals("4")) {//微信小程序
            System.out.println("微信小程序退款!!!!!!!!!!!");
            setAppid(Configure.getXappid());
            setMch_id(Configure.getXmchid());
            setOp_user_id(Configure.getXmchid());
        }
        //transaction_id是微信系统为每一笔支付交易分配的订单号,通过这个订单号可以标识这笔交易,它由支付订单API支付成功时返回的数据里面获取到。
        setTransaction_id(transactionID);
        //商户系统自己生成的唯一的订单号
        setOut_trade_no(outTradeNo);
        setOut_refund_no(outRefundNo);
        setTotal_fee(totalFee);
        setRefund_fee(refundFee);
        //随机字符串,不长于32 位
        setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        //根据API给的签名规则进行签名
        String sign = Signature.getSign(1,toMap());
        setSign(sign);//把签名数据设置到Sign这个属性中
    }
    public String getAppid() {
        return appid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public String getMch_id() {
        return mch_id;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public String getDevice_info() {
        return device_info;
    }
    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getTransaction_id() {
        return transaction_id;
    }
    public void setTransaction_id(String transaction_id) {
        this.transaction_id = transaction_id;
    }
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public String getOut_refund_no() {
        return out_refund_no;
    }
    public void setOut_refund_no(String out_refund_no) {
        this.out_refund_no = out_refund_no;
    }
    public int getTotal_fee() {
        return total_fee;
    }
    public void setTotal_fee(int total_fee) {
        this.total_fee = total_fee;
    }
    public int getRefund_fee() {
        return refund_fee;
    }
    public void setRefund_fee(int refund_fee) {
        this.refund_fee = refund_fee;
    }
    public String getOp_user_id() {
        return op_user_id;
    }
    public void setOp_user_id(String op_user_id) {
        this.op_user_id = op_user_id;
    }
    public String getRefund_fee_type() {
        return refund_fee_type;
    }
    public void setRefund_fee_type(String refund_fee_type) {
        this.refund_fee_type = refund_fee_type;
    }
    public String getPay_type() {
        return pay_type;
    }
    public void setPay_type(String pay_type) {
        this.pay_type = pay_type;
    }
    public Map<String,Object> toMap(){
        Map<String,Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object obj;
            try {
                obj = field.get(this);
                if(obj!=null){
                    map.put(field.getName(), obj);
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java
New file
@@ -0,0 +1,285 @@
package com.ruoyi.order.util.tencent.protocol;
import com.ruoyi.order.util.SinataUtil;
import com.ruoyi.order.util.tencent.common.Configure;
import com.ruoyi.order.util.tencent.common.RandomStringGenerator;
import com.ruoyi.order.util.tencent.common.Signature;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * 请求统一下单-API需要提交的数据
 * @author pan
 */
public class UnifiedorderReqData {
    //每个字段具体的意思请查看API文档
    private String appid;//公众账号ID (内置)
    private String mch_id;//商户号  (内置)
    private String nonce_str;//随机字符串 (内置)
    private String sign;//签名
    //接口调用需要的参数
    private String body;//商品描述
    private String out_trade_no;//商户订单号
    private Integer total_fee;//总金额
    private String spbill_create_ip;//终端IP
    private String notify_url;//通知地址
    private String trade_type;//交易类型
    private String openid;//用户标识
    /**
     * 预支付请求需要的参数(获取微信统一下单-必填参数)
     * 公众账号ID     appid (内置)
     * 商户号      mch_id (内置)
     * 随机字符串      nonce_str (内置)
     * 签名     sign (内置)
     * @param body                商品描述
     * @param out_trade_no        商户订单号
     * @param total_fee            总金额 (单位:分)
     * @param notify_url        通知地址
     */
    public UnifiedorderReqData(Integer apptype, String out_trade_no, String body, Double total_fee, String notify_url){
        //乘客appID与商户号
        this.appid = Configure.getAppid();
        //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
        this.mch_id = Configure.getMchid();
        //随机字符串,不长于32 位
        //        setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
        //        setBody(body);// 商品描述
        this.body = body;
        //        setOut_trade_no(out_trade_no);// 商户订单号
        this.out_trade_no = out_trade_no;
        //        setTotal_fee();// 总金额
        String money= SinataUtil.doubleRetainTwo(total_fee*100d);
        this.total_fee = Integer.parseInt(money.substring(0,money.length()-3));
        //        setNotify_url(notify_url);// 通知地址
        this.notify_url = notify_url;
        //        setTrade_type("APP");// 交易类型,默认:APP
        this.trade_type = "APP";
        // 根据API给的签名规则进行签名
        this.sign =  Signature.getSign(apptype, toMap());
    }
    /**
     * 预支付请求需要的参数(获取微信统一下单-必填参数)(公众号)
     * 公众账号ID     appid (内置)
     * 商户号      mch_id (内置)
     * 随机字符串      nonce_str (内置)
     * 签名     sign (内置)
     * @param body                商品描述
     * @param out_trade_no        商户订单号
     * @param total_fee            总金额 (单位:分)
     * @param notify_url        通知地址
     * @throws IllegalAccessException
     */
    public UnifiedorderReqData(String out_trade_no, String body, Double total_fee, String notify_url,String trade_type,String openid) throws IllegalAccessException{
        //appID与商户号
        this.appid = Configure.getGappid();
        //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
        this.mch_id = Configure.getGmchid();
        //随机字符串,不长于32 位
        //        setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
        //        setBody(body);// 商品描述
        this.body = body;
        //        setOut_trade_no(out_trade_no);// 商户订单号
        this.out_trade_no = out_trade_no;
        //        setTotal_fee();// 总金额
        String money=SinataUtil.doubleRetainTwo(total_fee*100d);
        this.total_fee = Integer.parseInt(money.substring(0,money.length()-3));
        //this.total_fee = (int)(total_fee * 100);
        //        setNotify_url(notify_url);// 通知地址
        this.notify_url = notify_url;
        /*trade_type 交易类型JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
        MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口*/
        this.trade_type = trade_type;
        this.openid = openid;
        // 根据API给的签名规则进行签名
        this.sign =  Signature.getSign(1,toMap());
    }
    /**
     * 预支付请求需要的参数(获取微信统一下单-必填参数)(小程序)
     * 公众账号ID     appid (内置)
     * 商户号      mch_id (内置)
     * 随机字符串      nonce_str (内置)
     * 签名     sign (内置)
     * @param body                商品描述
     * @param out_trade_no        商户订单号
     * @param total_fee            总金额 (单位:分)
     * @param notify_url        通知地址
     * @throws IllegalAccessException
     */
    public UnifiedorderReqData(String out_trade_no, Double total_fee, String notify_url,String trade_type,String openid) throws IllegalAccessException{
        //appID与商户号
        this.appid = Configure.getXappid();
        //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
        this.mch_id = Configure.getXmchid();
        //随机字符串,不长于32 位
        //        setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
        //        setBody(body);// 商品描述
        this.body = "小程序支付";
        //        setOut_trade_no(out_trade_no);// 商户订单号
        this.out_trade_no = out_trade_no;
        //        setTotal_fee();// 总金额
        String money=SinataUtil.doubleRetainTwo(total_fee*100d);
        this.total_fee = Integer.parseInt(money.substring(0,money.length()-3));
        //this.total_fee = (int)(total_fee * 100);
        //        setNotify_url(notify_url);// 通知地址
        this.notify_url = notify_url;
        /*trade_type 交易类型JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
        MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口*/
        this.trade_type = trade_type;
        this.openid = openid;
        // 根据API给的签名规则进行签名
        this.sign =  Signature.getSign(1,toMap());
    }
    public Map<String,Object> toMap(){
        Map<String,Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object obj;
            try {
                obj = field.get(this);
                if(obj!=null){
                    map.put(field.getName(), obj);
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
    public String getAppid() {
        return appid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public String getMch_id() {
        return mch_id;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public Integer getTotal_fee() {
        return total_fee;
    }
    public void setTotal_fee(Integer total_fee) {
        this.total_fee = total_fee;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public String getNotify_url() {
        return notify_url;
    }
    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }
    public String getTrade_type() {
        return trade_type;
    }
    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java
New file
@@ -0,0 +1,268 @@
package com.ruoyi.order.util.tencent.protocol;
import com.ruoyi.order.util.tencent.common.WXPayConfig;
import com.ruoyi.order.util.tencent.common.WXPayUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
/**
 * 交易保障
 */
public class WXPayReport {
    public static class ReportInfo {
        /**
         * 布尔变量使用int。0为false, 1为true。
         */
        // 基本信息
        private String version = "v0";
        private String sdk = "wxpay java sdk v1.0";
        private String uuid;  // 交易的标识
        private long timestamp;   // 上报时的时间戳,单位秒
        private long elapsedTimeMillis; // 耗时,单位 毫秒
        // 针对主域名
        private String firstDomain;  // 第1次请求的域名
        private boolean primaryDomain; //是否主域名
        private int firstConnectTimeoutMillis;  // 第1次请求设置的连接超时时间,单位 毫秒
        private int firstReadTimeoutMillis;  // 第1次请求设置的读写超时时间,单位 毫秒
        private int firstHasDnsError;  // 第1次请求是否出现dns问题
        private int firstHasConnectTimeout; // 第1次请求是否出现连接超时
        private int firstHasReadTimeout; // 第1次请求是否出现连接超时
        public ReportInfo(String uuid, long timestamp, long elapsedTimeMillis, String firstDomain, boolean primaryDomain, int firstConnectTimeoutMillis, int firstReadTimeoutMillis, boolean firstHasDnsError, boolean firstHasConnectTimeout, boolean firstHasReadTimeout) {
            this.uuid = uuid;
            this.timestamp = timestamp;
            this.elapsedTimeMillis = elapsedTimeMillis;
            this.firstDomain = firstDomain;
            this.primaryDomain = primaryDomain;
            this.firstConnectTimeoutMillis = firstConnectTimeoutMillis;
            this.firstReadTimeoutMillis = firstReadTimeoutMillis;
            this.firstHasDnsError = firstHasDnsError?1:0;
            this.firstHasConnectTimeout = firstHasConnectTimeout?1:0;
            this.firstHasReadTimeout = firstHasReadTimeout?1:0;
         }
        @Override
        public String toString() {
            return "ReportInfo{" +
                    "version='" + version + '\'' +
                    ", sdk='" + sdk + '\'' +
                    ", uuid='" + uuid + '\'' +
                    ", timestamp=" + timestamp +
                    ", elapsedTimeMillis=" + elapsedTimeMillis +
                    ", firstDomain='" + firstDomain + '\'' +
                    ", primaryDomain=" + primaryDomain +
                    ", firstConnectTimeoutMillis=" + firstConnectTimeoutMillis +
                    ", firstReadTimeoutMillis=" + firstReadTimeoutMillis +
                    ", firstHasDnsError=" + firstHasDnsError +
                    ", firstHasConnectTimeout=" + firstHasConnectTimeout +
                    ", firstHasReadTimeout=" + firstHasReadTimeout +
                    '}';
        }
        /**
         * 转换成 csv 格式
         *
         * @return
         */
        public String toLineString(String key) {
            String separator = ",";
            Object[] objects = new Object[] {
                version, sdk, uuid, timestamp, elapsedTimeMillis,
                    firstDomain, primaryDomain, firstConnectTimeoutMillis, firstReadTimeoutMillis,
                    firstHasDnsError, firstHasConnectTimeout, firstHasReadTimeout
            };
            StringBuffer sb = new StringBuffer();
            for(Object obj: objects) {
                sb.append(obj).append(separator);
            }
            try {
                String sign = WXPayUtil.HMACSHA256(sb.toString(), key);
                sb.append(sign);
                return sb.toString();
            }
            catch (Exception ex) {
                return null;
            }
        }
    }
    private static final String REPORT_URL = "http://report.mch.weixin.qq.com/wxpay/report/default";
    // private static final String REPORT_URL = "http://127.0.0.1:5000/test";
    private static final int DEFAULT_CONNECT_TIMEOUT_MS = 6*1000;
    private static final int DEFAULT_READ_TIMEOUT_MS = 8*1000;
    private LinkedBlockingQueue<String> reportMsgQueue = null;
    private WXPayConfig config;
    private ExecutorService executorService;
    private volatile static WXPayReport INSTANCE;
    private WXPayReport(final WXPayConfig config) {
        this.config = config;
        reportMsgQueue = new LinkedBlockingQueue<String>(config.getReportQueueMaxSize());
        // 添加处理线程
        executorService = Executors.newFixedThreadPool(config.getReportWorkerNum(), new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread t = Executors.defaultThreadFactory().newThread(r);
                t.setDaemon(true);
                return t;
            }
        });
        if (config.shouldAutoReport()) {
            WXPayUtil.getLogger().info("report worker num: {}", config.getReportWorkerNum());
            for (int i = 0; i < config.getReportWorkerNum(); ++i) {
                executorService.execute(new Runnable() {
                    public void run() {
                        while (true) {
                            // 先用 take 获取数据
                            try {
                                StringBuffer sb = new StringBuffer();
                                String firstMsg = reportMsgQueue.take();
                                WXPayUtil.getLogger().info("get first report msg: {}", firstMsg);
                                String msg = null;
                                sb.append(firstMsg); //会阻塞至有消息
                                int remainNum = config.getReportBatchSize() - 1;
                                for (int j=0; j<remainNum; ++j) {
                                    WXPayUtil.getLogger().info("try get remain report msg");
                                    // msg = reportMsgQueue.poll();  // 不阻塞了
                                    msg = reportMsgQueue.take();
                                    WXPayUtil.getLogger().info("get remain report msg: {}", msg);
                                    if (msg == null) {
                                        break;
                                    }
                                    else {
                                        sb.append("\n");
                                        sb.append(msg);
                                    }
                                }
                                // 上报
                                WXPayReport.httpRequest(sb.toString(), DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
                            }
                            catch (Exception ex) {
                                WXPayUtil.getLogger().warn("report fail. reason: {}", ex.getMessage());
                            }
                        }
                    }
                });
            }
        }
    }
    /**
     * 单例,双重校验,请在 JDK 1.5及更高版本中使用
     *
     * @param config
     * @return
     */
    public static WXPayReport getInstance(WXPayConfig config) {
        if (INSTANCE == null) {
            synchronized (WXPayReport.class) {
                if (INSTANCE == null) {
                    INSTANCE = new WXPayReport(config);
                }
            }
        }
        return INSTANCE;
    }
    public void report(String uuid, long elapsedTimeMillis,
                       String firstDomain, boolean primaryDomain, int firstConnectTimeoutMillis, int firstReadTimeoutMillis,
                       boolean firstHasDnsError, boolean firstHasConnectTimeout, boolean firstHasReadTimeout) {
        long currentTimestamp = WXPayUtil.getCurrentTimestamp();
        ReportInfo reportInfo = new ReportInfo(uuid, currentTimestamp, elapsedTimeMillis,
                firstDomain, primaryDomain, firstConnectTimeoutMillis, firstReadTimeoutMillis,
                firstHasDnsError, firstHasConnectTimeout, firstHasReadTimeout);
        String data = reportInfo.toLineString(config.getKey());
        WXPayUtil.getLogger().info("report {}", data);
        if (data != null) {
            reportMsgQueue.offer(data);
        }
    }
    @Deprecated
    private void reportSync(final String data) throws Exception {
        httpRequest(data, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
    }
    @Deprecated
    private void reportAsync(final String data) throws Exception {
        new Thread(new Runnable() {
            public void run() {
                try {
                    httpRequest(data, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_READ_TIMEOUT_MS);
                }
                catch (Exception ex) {
                    WXPayUtil.getLogger().warn("report fail. reason: {}", ex.getMessage());
                }
            }
        }).start();
    }
    /**
     * http 请求
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     * @throws Exception
     */
    private static String httpRequest(String data, int connectTimeoutMs, int readTimeoutMs) throws Exception{
        BasicHttpClientConnectionManager connManager;
        connManager = new BasicHttpClientConnectionManager(
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", SSLConnectionSocketFactory.getSocketFactory())
                        .build(),
                null,
                null,
                null
        );
        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();
        HttpPost httpPost = new HttpPost(REPORT_URL);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
        httpPost.setConfig(requestConfig);
        StringEntity postEntity = new StringEntity(data, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 ");  // TODO: 很重要,用来检测 sdk 的使用情况,要不要加上商户信息?
        httpPost.setEntity(postEntity);
        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        return EntityUtils.toString(httpEntity, "UTF-8");
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java
New file
@@ -0,0 +1,331 @@
package com.ruoyi.order.util.tencent.protocol;
import com.ruoyi.order.util.tencent.common.IWXPayDomain;
import com.ruoyi.order.util.tencent.common.WXPayConfig;
import com.ruoyi.order.util.tencent.common.WXPayUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.SecureRandom;
public class WXPayRequest {
    private WXPayConfig config;
    public WXPayRequest(WXPayConfig config) throws Exception{
        this.config = config;
    }
    /**
     * 请求,只请求一次,不做重试
     * @param domain
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @param useCert 是否使用证书,针对退款、撤销等操作
     * @return
     * @throws Exception
     */
    private String requestOnce(final String domain, String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws Exception {
        BasicHttpClientConnectionManager connManager;
        if (useCert) {
            // 证书
            char[] password = config.getMchID().toCharArray();
            InputStream certStream = config.getCertStream();
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(certStream, password);
            // 实例化密钥库 & 初始化密钥工厂
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password);
            // 创建 SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                    sslContext);
            connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslConnectionSocketFactory)
                            .build(),
                    null,
                    null,
                    null
            );
        }
        else {
            connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", SSLConnectionSocketFactory.getSocketFactory())
                            .build(),
                    null,
                    null,
                    null
            );
        }
        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();
        String url = "https://" + domain + urlSuffix;
        HttpPost httpPost = new HttpPost(url);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
        httpPost.setConfig(requestConfig);
        StringEntity postEntity = new StringEntity(data, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 " + config.getMchID());  // TODO: 很重要,用来检测 sdk 的使用情况,要不要加上商户信息?
        httpPost.setEntity(postEntity);
        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        return EntityUtils.toString(httpEntity, "UTF-8");
    }
    private String request(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert, boolean autoReport) throws Exception {
        Exception exception = null;
        long elapsedTimeMillis = 0;
        long startTimestampMs = WXPayUtil.getCurrentTimestampMs();
        boolean firstHasDnsErr = false;
        boolean firstHasConnectTimeout = false;
        boolean firstHasReadTimeout = false;
        IWXPayDomain.DomainInfo domainInfo = config.getWXPayDomain().getDomain(config);
        if(domainInfo == null){
            throw new Exception("WXPayConfig.getWXPayDomain().getDomain() is empty or null");
        }
        try {
            String result = requestOnce(domainInfo.domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, null);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
            return result;
        }
        catch (UnknownHostException ex) {  // dns 解析错误,或域名不存在
            exception = ex;
            firstHasDnsErr = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("UnknownHostException for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout
            );
        }
        catch (ConnectTimeoutException ex) {
            exception = ex;
            firstHasConnectTimeout = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("connect timeout happened for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout
            );
        }
        catch (SocketTimeoutException ex) {
            exception = ex;
            firstHasReadTimeout = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("timeout happened for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
        }
        catch (Exception ex) {
            exception = ex;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
        }
        config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, exception);
        throw exception;
    }
    /**
     * 可重试的,非双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @return
     */
    public String requestWithoutCert(String urlSuffix, String uuid, String data, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), false, autoReport);
        //return requestWithoutCert(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), autoReport);
    }
    /**
     * 可重试的,非双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     */
    public String requestWithoutCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs,  boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, false, autoReport);
        /*
        String result;
        Exception exception;
        boolean shouldRetry = false;
        boolean useCert = false;
        try {
            result = requestOnce(domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
            return result;
        }
        catch (UnknownHostException ex) {  // dns 解析错误,或域名不存在
            exception = ex;
            WXPayUtil.getLogger().warn("UnknownHostException for domain {}, try to use {}", domain, this.primaryDomain);
            shouldRetry = true;
        }
        catch (ConnectTimeoutException ex) {
            exception = ex;
            WXPayUtil.getLogger().warn("connect timeout happened for domain {}, try to use {}", domain, this.primaryDomain);
            shouldRetry = true;
        }
        catch (SocketTimeoutException ex) {
            exception = ex;
            shouldRetry = false;
        }
        catch (Exception ex) {
            exception = ex;
            shouldRetry = false;
        }
        if (shouldRetry) {
            result = requestOnce(this.primaryDomain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
            return result;
        }
        else {
            throw exception;
        }
        */
    }
    /**
     * 可重试的,双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @return
     */
    public String requestWithCert(String urlSuffix, String uuid, String data, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), true, autoReport);
        //return requestWithCert(urlSuffix, uuid, data, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), autoReport);
    }
    /**
     * 可重试的,双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     */
    public String requestWithCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, true, autoReport);
        /*
        String result;
        Exception exception;
        boolean shouldRetry = false;
        boolean useCert = true;
        try {
            result = requestOnce(domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
            return result;
        }
        catch (ConnectTimeoutException ex) {
            exception = ex;
            WXPayUtil.getLogger().warn(String.format("connect timeout happened for domain {}, try to use {}", domain, this.primaryDomain));
            shouldRetry = true;
        }
        catch (SocketTimeoutException ex) {
            exception = ex;
            shouldRetry = false;
        }
        catch (Exception ex) {
            exception = ex;
            shouldRetry = false;
        }
        if (shouldRetry && this.primaryDomain != null) {
            result = requestOnce(this.primaryDomain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert, autoReport);
            return result;
        }
        else {
            throw exception;
        }
        */
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java
New file
@@ -0,0 +1,49 @@
package com.ruoyi.order.util.tencent.service;
import com.ruoyi.order.util.tencent.common.Configure;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
 * User: rizenguo
 * Date: 2014/12/10
 * Time: 15:44
 * 服务的基类
 */
public class BaseService{
    //API的地址
    private String apiURL;
    //发请求的HTTPS请求器
    private IServiceRequest serviceRequest;
    public BaseService(Integer apptype,String api) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        apiURL = api;
           if (apptype.equals(2)) {//微信小程序和公众号的
                Class<?> c = Class.forName(Configure.HttpsRequestClassName_2);
                serviceRequest = (IServiceRequest) c.newInstance();
            } else {//微信app的
                Class<?> c = Class.forName(Configure.HttpsRequestClassName);
                serviceRequest = (IServiceRequest) c.newInstance();
            }
    }
    protected String sendPost(Object xmlObj) throws UnrecoverableKeyException, IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        return serviceRequest.sendPost(apiURL, xmlObj);
    }
    /**
     * 供商户想自定义自己的HTTP请求器用
     * @param request 实现了IserviceRequest接口的HttpsRequest
     */
    public void setServiceRequest(IServiceRequest request){
        serviceRequest = request;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.order.util.tencent.service;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
 * User: rizenguo
 * Date: 2014/12/10
 * Time: 15:16
 * 这里定义服务层需要请求器标准接口
 */
public interface IServiceRequest {
    //Service依赖的底层https请求器必须实现这么一个接口
    public String sendPost(String api_url, Object xmlObj) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java
New file
@@ -0,0 +1,30 @@
/*package com.ruoyi.order.util.tencent.service;
import com.tencent.common.Configure;
import com.tencent.protocol.PayToTheUserReqData;
public class PayToTheUserService extends BaseService{
    public PayToTheUserService(Integer apptype) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        super(apptype,Configure.PayToTheUser_API);
    }
    *//**
     * 请求商户向用户支付
     * @param refundReqData 这个数据对象里面包含了API要求提交的各种数据字段
     * @return API返回的XML数据
     * @throws Exception
     *//*
    public String request(PayToTheUserReqData payToTheUserReqData) throws Exception {
        //--------------------------------------------------------------------
        //发送HTTPS的Post请求到API地址
        //--------------------------------------------------------------------
        String responseString = sendPost(payToTheUserReqData);
        return responseString;
    }
}
*/
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.order.util.tencent.service;
import com.ruoyi.order.util.tencent.common.Configure;
import com.tencent.protocol.RefundReqData;
/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 16:04
 */
public class RefundService extends BaseService{
    public RefundService(Integer apptype) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        super(apptype, Configure.REFUND_API);
    }
    /**
     * 请求退款服务
     * @param refundReqData 这个数据对象里面包含了API要求提交的各种数据字段
     * @return API返回的XML数据
     * @throws Exception
     */
    public String request(RefundReqData refundReqData) throws Exception {
        //--------------------------------------------------------------------
        //发送HTTPS的Post请求到API地址
        //--------------------------------------------------------------------
        String responseString = sendPost(refundReqData);
        return responseString;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.order.util.tencent.service;
import com.ruoyi.order.util.tencent.common.Configure;
import com.ruoyi.order.util.tencent.protocol.UnifiedorderReqData;
/**
 * 统一下单服务
 * @author pen
 */
public class UnifiedorderService extends BaseService{
    public UnifiedorderService(Integer apptype) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        super(apptype, Configure.UNIFIEDORDER_API);
    }
    /**
     * 统一下单服务
     * @param UnifiedorderReqData 这个数据对象里面包含了API要求提交的各种数据字段
     */
    public String request(UnifiedorderReqData unifiedorderReqData) throws Exception {
        //--------------------------------------------------------------------
        //发送HTTPS的Post请求到API地址
        //--------------------------------------------------------------------
        String responseString = sendPost(unifiedorderReqData);
        return responseString;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java
New file
@@ -0,0 +1,89 @@
package com.ruoyi.order.util.tencent.service;
import com.ruoyi.order.util.tencent.common.IWXPayDomain;
import com.ruoyi.order.util.tencent.common.WXPayConfig;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class WXPayConfigImpl extends WXPayConfig {
    private byte[] certData;
    private static WXPayConfigImpl INSTANCE;
    private WXPayConfigImpl() throws Exception{
        String certPath = WXPayConfigImpl.class.getClassLoader().getResource("").getPath() + "com/tencent/common/cert/apiclient_cert.p12";
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }
    public static WXPayConfigImpl getInstance() throws Exception{
        if (INSTANCE == null) {
            synchronized (WXPayConfigImpl.class) {
                if (INSTANCE == null) {
                    INSTANCE = new WXPayConfigImpl();
                }
            }
        }
        return INSTANCE;
    }
    public String getMchID() {
        return "11473623";
    }
    public String getKey() {
        return "2ab9071b06b9f739b950ddb41db2690d";
    }
    public InputStream getCertStream() {
        ByteArrayInputStream certBis;
        certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }
    public int getHttpConnectTimeoutMs() {
        return 2000;
    }
    public int getHttpReadTimeoutMs() {
        return 10000;
    }
    public IWXPayDomain getWXPayDomain() {
        return WXPayDomainSimpleImpl.instance();
    }
    public String getPrimaryDomain() {
        return "api.mch.weixin.qq.com";
    }
    public String getAlternateDomain() {
        return "api2.mch.weixin.qq.com";
    }
    @Override
    public int getReportWorkerNum() {
        return 1;
    }
    @Override
    public int getReportBatchSize() {
        return 2;
    }
    @Override
    public String getAppID() {
        // TODO Auto-generated method stub
        return null;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java
New file
@@ -0,0 +1,101 @@
package com.ruoyi.order.util.tencent.service;
import com.ruoyi.order.util.tencent.common.IWXPayDomain;
import com.ruoyi.order.util.tencent.common.WXPayConfig;
import com.ruoyi.order.util.tencent.common.WXPayConstants;
import org.apache.http.conn.ConnectTimeoutException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by blaketang on 2017/6/16.
 */
public class WXPayDomainSimpleImpl implements IWXPayDomain {
    private WXPayDomainSimpleImpl(){}
    private static class WxpayDomainHolder{
        private static IWXPayDomain holder = new WXPayDomainSimpleImpl();
    }
    public static IWXPayDomain instance(){
        return WxpayDomainHolder.holder;
    }
    public synchronized void report(final String domain, long elapsedTimeMillis, final Exception ex) {
        DomainStatics info = domainData.get(domain);
        if(info == null){
            info = new DomainStatics(domain);
            domainData.put(domain, info);
        }
        if(ex == null){ //success
            if(info.succCount >= 2){    //continue succ, clear error count
                info.connectTimeoutCount = info.dnsErrorCount = info.otherErrorCount = 0;
            }else{
                ++info.succCount;
            }
        }else if(ex instanceof ConnectTimeoutException){
            info.succCount = info.dnsErrorCount = 0;
            ++info.connectTimeoutCount;
        }else if(ex instanceof UnknownHostException){
            info.succCount = 0;
            ++info.dnsErrorCount;
        }else{
            info.succCount = 0;
            ++info.otherErrorCount;
        }
    }
    public synchronized DomainInfo getDomain(final WXPayConfig config) {
        DomainStatics primaryDomain = domainData.get(WXPayConstants.DOMAIN_API);
        if(primaryDomain == null ||
                primaryDomain.isGood()) {
            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
        }
        long now = System.currentTimeMillis();
        if(switchToAlternateDomainTime == 0){   //first switch
            switchToAlternateDomainTime = now;
            return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
        }else if(now - switchToAlternateDomainTime < MIN_SWITCH_PRIMARY_MSEC){
            DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
            if(alternateDomain == null ||
                alternateDomain.isGood() ||
                alternateDomain.badCount() < primaryDomain.badCount()){
                return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
            }else{
                return new DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        }else{  //force switch back
            switchToAlternateDomainTime = 0;
            primaryDomain.resetCount();
            DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
            if(alternateDomain != null)
                alternateDomain.resetCount();
            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
        }
    }
    static class DomainStatics {
        final String domain;
        int succCount = 0;
        int connectTimeoutCount = 0;
        int dnsErrorCount =0;
        int otherErrorCount = 0;
        DomainStatics(String domain) {
            this.domain = domain;
        }
        void resetCount(){
            succCount = connectTimeoutCount = dnsErrorCount = otherErrorCount = 0;
        }
        boolean isGood(){ return connectTimeoutCount <= 2 && dnsErrorCount <= 2; }
        int badCount(){
            return connectTimeoutCount + dnsErrorCount * 5 + otherErrorCount / 4;
        }
    }
    private final int MIN_SWITCH_PRIMARY_MSEC = 3 * 60 * 1000;  //3 minutes
    private long switchToAlternateDomainTime = 0;
    private Map<String, DomainStatics> domainData = new HashMap<String, DomainStatics>();
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java
New file
@@ -0,0 +1,148 @@
/*package com.ruoyi.order.util.tencent.service;
import java.util.HashMap;
import java.util.Map;
import com.tencent.WXPay;
import com.tencent.common.WXPayUtil;
public class WXPayPerformance {
    private WXPay wxpay;
    private WXPayConfigImpl config;
    public WXPayPerformance() throws Exception {
        config = WXPayConfigImpl.getInstance();
        wxpay = new WXPay(config);
    }
    *//**
     * 商户 扫码抢 扫码支付
     * @param out_trade_no
     * @param body
     * @param total_fee
     * @param auth_code
     *//*
    public Map<String, String> doMicropayWithPos(String out_trade_no,String body,String total_fee,String auth_code) {
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", out_trade_no);
        data.put("body", body);
        data.put("total_fee", total_fee);
        data.put("auth_code", auth_code);
        Map<String, String> r=new HashMap<>();
        try {
             r = wxpay.microPayWithPos(data);
            System.out.println(r);
        } catch (Exception e) {
            e.printStackTrace();
            return r;
        }
        return r;
    }
    *//**
     * 统一 下单2.0
     *//*
    public Map<String, String> doUnifiedOrder(String out_trade_no,String total_fee,String notify_url) {
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", "超级教材");
        data.put("out_trade_no", out_trade_no);
        data.put("device_info", "");
        data.put("fee_type", "CNY");
        data.put("total_fee", total_fee);
        //data.put("spbill_create_ip", "123.12.12.123");
        data.put("notify_url", notify_url);
        data.put("trade_type", "APP");
        //data.put("product_id", "12");
        // data.put("time_expire", "20170112104120");
        try {
            Map<String, String> r = wxpay.unifiedOrder(data);
            System.out.println(r);
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    //    public void doReport() {
    //        HashMap<String, String> data = new HashMap<String, String>();
    //        data.put("interface_url", "20160822");
    //        data.put("bill_type", "ALL");
    //    }
    *//**
     * 小测试
     *//*
    public void test001() {
        String xmlStr="<xml><return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "<return_msg><![CDATA[OK]]></return_msg>\n" +
                "<appid><![CDATA[wx273fe72f2db863ed]]></appid>\n" +
                "<mch_id><![CDATA[1228845802]]></mch_id>\n" +
                "<nonce_str><![CDATA[lCXjx3wNx45HfTV2]]></nonce_str>\n" +
                "<sign><![CDATA[68D7573E006F0661FD2A77BA59124E87]]></sign>\n" +
                "<result_code><![CDATA[SUCCESS]]></result_code>\n" +
                "<openid><![CDATA[oZyc_uPx_oed7b4q1yKmj_3M2fTU]]></openid>\n" +
                "<is_subscribe><![CDATA[N]]></is_subscribe>\n" +
                "<trade_type><![CDATA[NATIVE]]></trade_type>\n" +
                "<bank_type><![CDATA[CFT]]></bank_type>\n" +
                "<total_fee>1</total_fee>\n" +
                "<fee_type><![CDATA[CNY]]></fee_type>\n" +
                "<transaction_id><![CDATA[4008852001201608221983528929]]></transaction_id>\n" +
                "<out_trade_no><![CDATA[20160822162018]]></out_trade_no>\n" +
                "<attach><![CDATA[]]></attach>\n" +
                "<time_end><![CDATA[20160822202556]]></time_end>\n" +
                "<trade_state><![CDATA[SUCCESS]]></trade_state>\n" +
                "<cash_fee>1</cash_fee>\n" +
                "</xml>";
        try {
            System.out.println(xmlStr);
            System.out.println("+++++++++++++++++");
            System.out.println(WXPayUtil.isSignatureValid(xmlStr, config.getKey()));
            Map<String, String> hm = WXPayUtil.xmlToMap(xmlStr);
            System.out.println("+++++++++++++++++");
            System.out.println(hm);
            System.out.println(hm.get("attach").length());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void testHelloWorld() throws Exception {
        for (int i=0; i<1000; ++i) {
            System.out.println("hi");
            Thread.sleep(1000);
        }
    }
    public static void main(String[] args) throws Exception {
       //测试商户扫码支付
        WXPayPerformance dodo = new WXPayPerformance();
        Map<String, String> r =dodo.doMicropayWithPos("11151521","测试","1","130226992988325162");
        if(r.isEmpty()){
            System.out.println("--------------->");
        }else{
            if(r.containsKey("err_code_des")){
                System.out.println("支付失败:"+r.get("err_code_des"));
            }else{
                if(r.get("return_msg").equals("OK")){
                    System.out.println("支付成功:"+r.get("err_code_des"));
                }
            }
        }
         //测试app支付
        WXPayPerformance dodo1 = new WXPayPerformance();
        dodo1.doUnifiedOrder("12121", "1", "http://192.168.1.90:8080/textbook/app/pay/wxpay/notify");
    }
}
*/