From dd7ad1ab0ead2aa3c9f7fc35cc9b0635309c642a Mon Sep 17 00:00:00 2001 From: rentaiming <806181062@qq.com> Date: 星期二, 21 五月 2024 18:34:55 +0800 Subject: [PATCH] 提交支付 --- ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/证书使用说明.txt | 20 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java | 125 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java | 331 +++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java | 176 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java | 36 ruoyi-modules/ruoyi-order/pom.xml | 33 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java | 202 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java | 71 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem | 19 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java | 101 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java | 3 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java | 301 ++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java | 101 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java | 52 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java | 98 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java | 27 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java | 28 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem | 26 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java | 954 +++++++++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java | 188 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java | 27 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java | 281 ++ ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java | 21 ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java | 5 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem | 28 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java | 157 + ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java | 15 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java | 304 ++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem | 19 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java | 30 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java | 103 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem | 28 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java | 20 ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java | 3 ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java | 20 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java | 21 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java | 345 +++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem | 26 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java | 210 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java | 89 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java | 126 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java | 42 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12 | 0 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java | 34 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/证书使用说明.txt | 20 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java | 34 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java | 42 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java | 43 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java | 49 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java | 59 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12 | 0 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java | 59 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java | 187 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java | 285 ++ ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java | 134 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java | 122 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java | 30 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java | 148 + ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java | 268 ++ 59 files changed, 6,320 insertions(+), 6 deletions(-) diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java new file mode 100644 index 0000000..c81596e --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/dto/OrderAuctionBondDTO.java @@ -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; +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java index 87b45a2..8721568 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/OrderFallbackFactory.java +++ b/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()); + } + }; } } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java index 42e8ac9..d0011ef 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/GoodsSkuClient.java +++ b/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); + + + } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java index 5ca18dc..f273b2d 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/OrderClient.java +++ b/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); + } diff --git a/ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java b/ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java index e8da7fd..b25cf07 100644 --- a/ruoyi-modules/ruoyi-auction/src/main/java/com/ruoyi/auction/service/impl/AuctionSalesroomGoodsServiceImpl.java +++ b/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); + } diff --git a/ruoyi-modules/ruoyi-order/pom.xml b/ruoyi-modules/ruoyi-order/pom.xml index 21c4fd8..c6527bc 100644 --- a/ruoyi-modules/ruoyi-order/pom.xml +++ b/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> diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java index 01f67b6..76aa20c 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderAuctionBondController.java +++ b/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(); + + } + } diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java index 6e7e02e..e8aaf5d 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/IOrderAuctionBondService.java +++ b/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); } diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java index 8a662f8..7f5ccd6 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/OrderAuctionBondServiceImpl.java +++ b/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){ + + } + } } diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java new file mode 100644 index 0000000..5d3fff8 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/DateUtil.java @@ -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; + } + + + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java new file mode 100644 index 0000000..3ff58b2 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/ParamUtil.java @@ -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); + } + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java new file mode 100644 index 0000000..fb7e844 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/SinataUtil.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java new file mode 100644 index 0000000..c4eb2b5 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/config/AlipayConfig.java @@ -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"; + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java new file mode 100644 index 0000000..d8c8c80 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/Base64.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java new file mode 100644 index 0000000..9375733 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/RSA.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java new file mode 100644 index 0000000..c2a6633 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/sign/SignUtils.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java new file mode 100644 index 0000000..53a7dc9 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayCore.java @@ -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(); + } + } + } + } + + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java new file mode 100644 index 0000000..57ef6d6 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/AlipayNotify.java @@ -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()); +} + + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java new file mode 100644 index 0000000..6952611 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/PayDemoActivity.java @@ -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; + } + + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java new file mode 100644 index 0000000..df083e0 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/alipay/util/UtilDate.java @@ -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)+""; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java new file mode 100644 index 0000000..7ec78f2 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/WXPay.java @@ -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; + } + } + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java new file mode 100644 index 0000000..a1c12dd --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Configure.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java new file mode 100644 index 0000000..e546561 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java new file mode 100644 index 0000000..606fb11 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/HttpsRequest_2.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java new file mode 100644 index 0000000..6c91eb0 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/IWXPayDomain.java @@ -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 + + '}'; + } + } + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java new file mode 100644 index 0000000..6f86f99 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/JsapiTicketUtil.java @@ -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())); + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java new file mode 100644 index 0000000..7df776e --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Log.java @@ -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); + } + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java new file mode 100644 index 0000000..00cbe6d --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/MD5.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java new file mode 100644 index 0000000..3f6596f --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/RandomStringGenerator.java @@ -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(); + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java new file mode 100644 index 0000000..89f3439 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Signature.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java new file mode 100644 index 0000000..368aa0b --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/Util.java @@ -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)); + } + +} + diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java new file mode 100644 index 0000000..7ea0fb3 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConfig.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java new file mode 100644 index 0000000..cfe76e6 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayConstants.java @@ -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"; + +} + diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java new file mode 100644 index 0000000..a4551ca --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/WXPayUtil.java @@ -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); + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java new file mode 100644 index 0000000..c6e4a1f --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/XMLParser.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12 b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12 new file mode 100644 index 0000000..07690fc --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.p12 Binary files differ diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem new file mode 100644 index 0000000..5435148 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_cert.pem @@ -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----- diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem new file mode 100644 index 0000000..680a521 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/apiclient_key.pem @@ -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----- diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem new file mode 100644 index 0000000..7a36225 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/rootca.pem @@ -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----- \ No newline at end of file diff --git "a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" "b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" new file mode 100644 index 0000000..e8a73dd --- /dev/null +++ "b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" @@ -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进行调用时也需要验证所调用服务器及域名的真实性 + 该文件为签署微信支付证书的权威机构的根证书,可以用来验证微信支付服务器证书的真实性 + 某些环境和工具已经内置了若干权威机构的根证书,无需引用该证书也可以正常进行验证,这里提供给您在未内置所必须根证书的环境中载入使用 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12 b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12 new file mode 100644 index 0000000..9364e13 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.p12 Binary files differ diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem new file mode 100644 index 0000000..e3ed127 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_cert.pem @@ -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----- diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem new file mode 100644 index 0000000..78dbd07 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/apiclient_key.pem @@ -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----- diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem new file mode 100644 index 0000000..7a36225 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/rootca.pem @@ -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----- \ No newline at end of file diff --git "a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" "b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" new file mode 100644 index 0000000..e8a73dd --- /dev/null +++ "b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/common/cert_2/\350\257\201\344\271\246\344\275\277\347\224\250\350\257\264\346\230\216.txt" @@ -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进行调用时也需要验证所调用服务器及域名的真实性 + 该文件为签署微信支付证书的权威机构的根证书,可以用来验证微信支付服务器证书的真实性 + 某些环境和工具已经内置了若干权威机构的根证书,无需引用该证书也可以正常进行验证,这里提供给您在未内置所必须根证书的环境中载入使用 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java new file mode 100644 index 0000000..92842f3 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/AppPayReqData.java @@ -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; + } + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java new file mode 100644 index 0000000..75f16ca --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/PayToTheUserReqData.java @@ -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; + } + +} +*/ \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java new file mode 100644 index 0000000..a917abc --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/RefundReqData.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java new file mode 100644 index 0000000..ee959fd --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/UnifiedorderReqData.java @@ -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; + } + +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java new file mode 100644 index 0000000..79c99c0 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayReport.java @@ -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"); + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java new file mode 100644 index 0000000..8e50521 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/protocol/WXPayRequest.java @@ -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; + } + */ + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java new file mode 100644 index 0000000..af9da18 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/BaseService.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java new file mode 100644 index 0000000..9b21366 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/IServiceRequest.java @@ -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; + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java new file mode 100644 index 0000000..6560e34 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/PayToTheUserService.java @@ -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; + } + +} +*/ \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java new file mode 100644 index 0000000..782be4b --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/RefundService.java @@ -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; + } + +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java new file mode 100644 index 0000000..2bd8371 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/UnifiedorderService.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java new file mode 100644 index 0000000..8254fcb --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayConfigImpl.java @@ -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; + } +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java new file mode 100644 index 0000000..4440c94 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayDomainSimpleImpl.java @@ -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>(); +} diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java new file mode 100644 index 0000000..63ff623 --- /dev/null +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/tencent/service/WXPayPerformance.java @@ -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"); + + } +} + + +*/ \ No newline at end of file -- Gitblit v1.7.1