Browse Source

支付相关

master
dimengzhe 1 year ago
commit
c54d4008c8
  1. 27
      doc/Order.sql
  2. 130
      pom.xml
  3. 23
      src/main/java/com/yxt/pay/PayApplication.java
  4. 29
      src/main/java/com/yxt/pay/api/order/OrderDto.java
  5. 19
      src/main/java/com/yxt/pay/api/order/OrderQuery.java
  6. 17
      src/main/java/com/yxt/pay/api/order/OrderVo.java
  7. 65
      src/main/java/com/yxt/pay/api/order/PayOrder.java
  8. 39
      src/main/java/com/yxt/pay/api/order/PayTypeEnum.java
  9. 36
      src/main/java/com/yxt/pay/api/wxpay/WxPayVo.java
  10. 15
      src/main/java/com/yxt/pay/biz/order/OrderMapper.java
  11. 9
      src/main/java/com/yxt/pay/biz/order/OrderMapper.xml
  12. 52
      src/main/java/com/yxt/pay/biz/order/OrderRest.java
  13. 248
      src/main/java/com/yxt/pay/biz/order/OrderService.java
  14. 144
      src/main/java/com/yxt/pay/biz/wxpay/WxPayRest.java
  15. 13
      src/main/java/com/yxt/pay/biz/wxpay/WxPayService.java
  16. 126
      src/main/java/com/yxt/pay/utils/ApiBaseAction.java
  17. 80
      src/main/java/com/yxt/pay/utils/CharUtil.java
  18. 220
      src/main/java/com/yxt/pay/utils/DateUtils.java
  19. 7
      src/main/java/com/yxt/pay/utils/DealMapValueHelper.java
  20. 306
      src/main/java/com/yxt/pay/utils/MapUtils.java
  21. 20
      src/main/java/com/yxt/pay/utils/UrlComponent.java
  22. 254
      src/main/java/com/yxt/pay/utils/XmlHelper.java
  23. 201
      src/main/java/com/yxt/pay/utils/XmlUtil.java
  24. 29
      src/main/java/com/yxt/pay/utils/applet/MD5.java
  25. 39
      src/main/java/com/yxt/pay/utils/applet/WechatConfig.java
  26. 214
      src/main/java/com/yxt/pay/utils/applet/WechatRefundApiResult.java
  27. 296
      src/main/java/com/yxt/pay/utils/applet/WechatUtil.java
  28. 30
      src/main/resources/application-devv.yml
  29. 0
      src/main/resources/application-pro.yml
  30. 0
      src/main/resources/application-test.yml
  31. 62
      src/main/resources/application.yml
  32. 13
      src/main/resources/banner.txt
  33. 54
      src/main/resources/logback-spring.xml

27
doc/Order.sql

@ -0,0 +1,27 @@
DROP TABLE IF EXISTS `pay_order`;
CREATE TABLE `pay_order`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`sid` varchar(64) NOT NULL COMMENT 'sid',
`lockVersion` int(11) NOT NULL DEFAULT '0' COMMENT '记录版本,锁',
`createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
`modifyTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录最后修改时间',
`isEnable` int(11) NOT NULL DEFAULT '1' COMMENT '记录是否可用,1:可用,0:不可用',
`state` int(11) DEFAULT '1' COMMENT '状态',
`isDelete` int(11) DEFAULT NULL COMMENT '记录是否被删除,0:未删除,1:已经删除',
`remarks` varchar(255) DEFAULT NULL COMMENT '备注信息',
`createBySid` varchar(64) DEFAULT NULL COMMENT '创建者',
`updateBySid` varchar(64) DEFAULT NULL COMMENT '更新者',
`outTradeNo` varchar(500) DEFAULT NULL COMMENT '订单编号',
`source` int(64) DEFAULT NULL COMMENT '来源:0、云菜窖',
`name` varchar(500) DEFAULT NULL COMMENT '需传入应用市场上的APP名字-实际商品名称,如天天爱消除-游戏充值',
`totalTee` varchar(500) DEFAULT NULL COMMENT '金额',
`openId` varchar(500) DEFAULT NULL COMMENT 'openId',
`payTypeValue` varchar(500) DEFAULT NULL COMMENT '支付方式Value',
`payType` varchar(500) DEFAULT NULL COMMENT '支付方式key',
`timeRemarks` int(64) DEFAULT NULL COMMENT '过期时间设置(以分钟为准,例如5分钟,则为5)',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 18
DEFAULT CHARSET = utf8 COMMENT ='订单表';

130
pom.xml

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.yxt</groupId>
<artifactId>yxt-parent</artifactId>
<version>0.0.1</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yxt-pay</artifactId>
<groupId>com.yxt.pay</groupId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>com.yxt</groupId>
<artifactId>yxt-common-base</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.0.4</version>
</dependency>
<!-- huTool 工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.0.4</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.0.4</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.6</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*Mapper.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>

23
src/main/java/com/yxt/pay/PayApplication.java

@ -0,0 +1,23 @@
package com.yxt.pay;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author dimengzhe
*/
@SpringBootApplication(scanBasePackages = {
"com.yxt.common.base.config",
"com.yxt.pay"
})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {})
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class, args);
}
}

29
src/main/java/com/yxt/pay/api/order/OrderDto.java

@ -0,0 +1,29 @@
package com.yxt.pay.api.order;
import com.yxt.common.core.dto.Dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/6
**/
@Data
public class OrderDto implements Dto {
@ApiModelProperty("来源:0、云菜窖")
private int source;
@ApiModelProperty("金额")
private String totalTee;
@ApiModelProperty("微信唯一标识openid")
private String openId;
@ApiModelProperty("用户sid")
private String userSid;
@ApiModelProperty("商品名称")
private String name;
@ApiModelProperty("过期时间:以分钟为单位")
private int timeRemarks;
}

19
src/main/java/com/yxt/pay/api/order/OrderQuery.java

@ -0,0 +1,19 @@
package com.yxt.pay.api.order;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
@Data
public class OrderQuery {
private String mainSid;
@ApiModelProperty("支付类型:微信wxpay")
private String type;
}

17
src/main/java/com/yxt/pay/api/order/OrderVo.java

@ -0,0 +1,17 @@
package com.yxt.pay.api.order;
import lombok.Data;
import java.util.Map;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
@Data
public class OrderVo {
private Map<Object,Object> orderInfo;
}

65
src/main/java/com/yxt/pay/api/order/PayOrder.java

@ -0,0 +1,65 @@
package com.yxt.pay.api.order;
import com.baomidou.mybatisplus.annotation.TableField;
import com.yxt.common.base.utils.StringRandom;
import com.yxt.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/6
**/
@Data
public class PayOrder extends BaseEntity {
@ApiModelProperty("订单编号")
private String outTradeNo;
@ApiModelProperty("来源:0云菜窖")
private int source;
@ApiModelProperty("金额")
private String totalTee;
@ApiModelProperty("支付方式:wxPay微信")
private String payType;
private String payTypeValue;
private String name;
private String openId;
@ApiModelProperty("过期时间描述:以分钟为单位,例如:5")
private int timeRemarks;
/*@TableField(exist = false)
private String appId;
@TableField(exist = false)
private String mchId;
@TableField(exist = false)
private String secret;
@TableField(exist = false)
private String remark;*/
public String getTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
return format.format(new Date());
}
public PayOrder(int source) {
String Randomstr = StringRandom.getRandomString(15);
if (source == 0) {//云菜窖
outTradeNo = "YCJ" + getTime() + Randomstr;
/* appId = "wx4724e3a3c27f36b5";
mchId = "1664882765";
secret = "yxtcxjshbyxgs1234567898765432101";*/
} else if (source == 1) {
} else if (source == 2) {
}
this.source = source;
}
}

39
src/main/java/com/yxt/pay/api/order/PayTypeEnum.java

@ -0,0 +1,39 @@
package com.yxt.pay.api.order;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
public enum PayTypeEnum {
WECHAT("wxPay","微信支付"),
ZHIFUBAO("zfbPay","支付宝支付"),
;
private String code;
private String remarks;
public String getCode() {
return code;
}
public String getRemarks() {
return remarks;
}
PayTypeEnum(String code,String remarks){
this.code = code;
this.remarks = remarks;
}
}

36
src/main/java/com/yxt/pay/api/wxpay/WxPayVo.java

@ -0,0 +1,36 @@
package com.yxt.pay.api.wxpay;
import com.baomidou.mybatisplus.annotation.TableField;
import com.yxt.common.base.utils.StringRandom;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
@Data
public class WxPayVo {
@ApiModelProperty("来源:0云菜窖")
private int source;
private String appId;
private String mchId;
private String secret;
public WxPayVo(int source) {
if (source == 0) {//云菜窖
appId = "wx4724e3a3c27f36b5";
mchId = "1664882765";
secret = "yxtcxjshbyxgs1234567898765432101";
} else if (source == 1) {
} else if (source == 2) {
}
this.source = source;
}
}

15
src/main/java/com/yxt/pay/biz/order/OrderMapper.java

@ -0,0 +1,15 @@
package com.yxt.pay.biz.order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yxt.pay.api.order.PayOrder;
import org.apache.ibatis.annotations.Mapper;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/6
**/
@Mapper
public interface OrderMapper extends BaseMapper<PayOrder> {
PayOrder selectBySn(String out_trade_no);
}

9
src/main/java/com/yxt/pay/biz/order/OrderMapper.xml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yxt.pay.biz.order.OrderMapper">
<select id="selectBySn" resultType="com.yxt.pay.api.order.PayOrder">
select *
from pay_order
where outTradeNo = #{out_trade_no}
</select>
</mapper>

52
src/main/java/com/yxt/pay/biz/order/OrderRest.java

@ -0,0 +1,52 @@
package com.yxt.pay.biz.order;
import com.yxt.common.core.result.ResultBean;
import com.yxt.pay.api.order.OrderDto;
import com.yxt.pay.api.order.OrderQuery;
import com.yxt.pay.api.order.OrderVo;
import com.yxt.pay.utils.ApiBaseAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/6
**/
@RestController
@RequestMapping("/order")
public class OrderRest extends ApiBaseAction {
//9370ec84-2723-40ee-bb6f-680fce6a25a2
//05ba58d6-f1f0-4f68-9bcc-62ceeaf4c088
//o81zC60V3ymrfjgK-BifvcyWfJBo
@Autowired
private OrderService orderService;
@PostMapping("createOrder")
ResultBean<String> createOrder(@RequestBody OrderDto dto) {
return orderService.createOrder(dto);
}
@PostMapping("/pay")
ResultBean pay(@RequestBody OrderQuery query) {
return orderService.pay(query, getClientIp());
}
@PostMapping("wxPayNotify")
ResultBean wxPayNotify(HttpServletRequest request) throws IOException {
return orderService.wxPayNotify(request.getInputStream());
}
@PostMapping("selectOrder")
ResultBean selectOrder(@RequestBody OrderQuery query) {
return orderService.selectOrder(query);
}
}

248
src/main/java/com/yxt/pay/biz/order/OrderService.java

@ -0,0 +1,248 @@
package com.yxt.pay.biz.order;
import com.yxt.common.base.service.MybatisBaseService;
import com.yxt.common.core.result.ResultBean;
import com.yxt.pay.api.order.*;
import com.yxt.pay.api.wxpay.WxPayVo;
import com.yxt.pay.biz.wxpay.WxPayService;
import com.yxt.pay.utils.*;
import com.yxt.pay.utils.applet.WechatRefundApiResult;
import com.yxt.pay.utils.applet.WechatUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.axis.i18n.RB;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.*;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/6
**/
@Service
@Slf4j
public class OrderService extends MybatisBaseService<OrderMapper, PayOrder> {
@Autowired
private UrlComponent urlComponent;
String tradeType = "JSAPI";
//微信统一下单
String uniformorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public ResultBean<String> createOrder(OrderDto dto) {
ResultBean<String> rb = ResultBean.fireFail();
PayOrder order = new PayOrder(dto.getSource());
order.setTotalTee(dto.getTotalTee());
order.setTimeRemarks(dto.getTimeRemarks());
order.setName(dto.getName());
order.setCreateBySid(dto.getUserSid());
order.setOpenId(dto.getOpenId());
baseMapper.insert(order);
/* OrderVo orderVo = new OrderVo();
Map<Object, Object> resultObj = new TreeMap();
if (dto.getPayType() == 0) {//支付宝
return rb.setMsg("暂不支持支付宝支付");
} else if (dto.getPayType() == 1) {
try {
Map<Object, Object> parame = new TreeMap<Object, Object>();
parame.put("appid", order.getAppId());
// 商家账号。
parame.put("mch_id", order.getMchId());
String randomStr = CharUtil.getRandomNum(18).toUpperCase();
// 随机字符串
parame.put("nonce_str", randomStr);
// 商户订单编号
parame.put("out_trade_no", order.getOutTradeNo());
// 商品描述
parame.put("body", order.getRemark());
//支付金额
parame.put("total_fee", new BigDecimal(dto.getTotalTee()).multiply(new BigDecimal(100)).intValue());
// 回调地址
parame.put("notify_url", "http://192.168.0.111:7777/wxPay/payNotify");
// 交易类型APP
parame.put("trade_type", tradeType);
parame.put("spbill_create_ip", ip);
parame.put("openid", dto.getOpenId());
String sign = WechatUtil.arraySign(parame, order.getSecret());
// 数字签证
parame.put("sign", sign);
String xml = MapUtils.convertMap2Xml(parame);
log.info("xml:" + xml);
Map<String, Object> resultUn = XmlUtil.xmlStrToMap(WechatUtil.requestOnce(uniformorder, xml));
// 响应报文
String return_code = MapUtils.getString("return_code", resultUn);
String return_msg = MapUtils.getString("return_msg", resultUn);
if (return_code.equalsIgnoreCase("FAIL")) {
return rb.setMsg("支付失败," + return_msg);
} else if (return_code.equalsIgnoreCase("SUCCESS")) {
String prepay_id = MapUtils.getString("prepay_id", resultUn);
// 先生成paySign 参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
resultObj.put("appId", order.getAppId());
resultObj.put("timeStamp", DateUtils.timeToStr(System.currentTimeMillis() / 1000, DateUtils.DATE_TIME_PATTERN));
resultObj.put("nonceStr", nonceStr);
// resultObj.put("package", "prepay_id=" + prepay_id);
resultObj.put("package", "Sign=WXPay");
resultObj.put("partnerid", order.getMchId());
resultObj.put("signType", "MD5");
resultObj.put("prepayid", prepay_id);
String paySign = WechatUtil.arraySign(resultObj, order.getSecret());
// resultObj.put("paySign", paySign);
resultObj.put("sign", paySign);
return rb.success().setData(orderVo);
}
} catch (Exception e) {
e.printStackTrace();
return rb.setMsg("支付失败,error=" + e.getMessage());
}
}*/
return rb.success().setData(order.getSid());
}
public ResultBean pay(OrderQuery query, String ip) {
ResultBean rb = ResultBean.fireFail();
PayOrder payOrder = fetchBySid(query.getMainSid());
if (payOrder == null) {
return rb.setMsg("此订单不存在");
}
//验证订单是否已过期
Date createTime = payOrder.getCreateTime();
Calendar calendar = Calendar.getInstance();
calendar.setTime(createTime);
calendar.add(Calendar.MINUTE, payOrder.getTimeRemarks());
//过期时间
long newTimeInMillis = calendar.getTimeInMillis();
// 获取当前时间的毫秒表示
long currentTimeInMillis = System.currentTimeMillis();
if (newTimeInMillis < currentTimeInMillis) {
return rb.setMsg("订单已过期");
}
//订单是否已支付过
if (payOrder.getState() == 2) {
return rb.setMsg("订单不是未支付,不能重复支付");
}
Map<Object, Object> resultObj = new TreeMap();
if (query.getType().equals(PayTypeEnum.WECHAT.getCode())) {//微信
WxPayVo wxPayVo = new WxPayVo(payOrder.getSource());
String nonceStr = CharUtil.getRandomString(32);
try {
Map<Object, Object> parame = new TreeMap<Object, Object>();
parame.put("appid", wxPayVo.getAppId());
// 商家账号。
parame.put("mch_id", wxPayVo.getMchId());
String randomStr = CharUtil.getRandomNum(18).toUpperCase();
// 随机字符串
parame.put("nonce_str", randomStr);
// 商户订单编号
parame.put("out_trade_no", payOrder.getOutTradeNo());
// 商品描述
parame.put("body", payOrder.getName());
//支付金额
parame.put("total_fee", new BigDecimal(payOrder.getTotalTee()).multiply(new BigDecimal(100)).intValue());
// 回调地址
parame.put("notify_url", urlComponent.getDoMainUrl() + "/wxPay/payNotify");
// 交易类型APP
parame.put("trade_type", tradeType);
parame.put("spbill_create_ip", ip);
parame.put("openid", payOrder.getOpenId());
String sign = WechatUtil.arraySign(parame, wxPayVo.getSecret());
// 数字签证
parame.put("sign", sign);
String xml = MapUtils.convertMap2Xml(parame);
log.info("xml:" + xml);
Map<String, Object> resultUn = XmlUtil.xmlStrToMap(WechatUtil.requestOnce(uniformorder, xml));
// 响应报文
String return_code = MapUtils.getString("return_code", resultUn);
String return_msg = MapUtils.getString("return_msg", resultUn);
if (return_code.equalsIgnoreCase("FAIL")) {
return rb.setMsg("支付失败," + return_msg);
} else if (return_code.equalsIgnoreCase("SUCCESS")) {
String prepay_id = MapUtils.getString("prepay_id", resultUn);
// 先生成paySign 参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
resultObj.put("appId", wxPayVo.getAppId());
resultObj.put("timeStamp", DateUtils.timeToStr(System.currentTimeMillis() / 1000, DateUtils.DATE_TIME_PATTERN));
resultObj.put("nonceStr", nonceStr);
// resultObj.put("package", "prepay_id=" + prepay_id);
resultObj.put("package", "Sign=WXPay");
resultObj.put("partnerid", wxPayVo.getMchId());
resultObj.put("signType", "MD5");
resultObj.put("prepayid", prepay_id);
String paySign = WechatUtil.arraySign(resultObj, wxPayVo.getSecret());
// resultObj.put("paySign", paySign);
resultObj.put("sign", paySign);
payOrder.setPayType(PayTypeEnum.WECHAT.getCode());
baseMapper.updateById(payOrder);
return rb.success().setData(resultObj);
}
} catch (Exception e) {
e.printStackTrace();
return rb.setMsg("支付失败,error=" + e.getMessage());
}
} else {
}
return rb;
}
public ResultBean wxPayNotify(ServletInputStream inputStream) {
ResultBean rb = ResultBean.fireFail();
try {
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inputStream.close(); //xml数据
String reponseXml = new String(outSteam.toByteArray(), "utf-8");
WechatRefundApiResult result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
String result_code = result.getResult_code();
//订单编号
String out_trade_no = result.getOut_trade_no();
log.error("订单" + out_trade_no + "支付成功");
// 业务处理
//根据订单编号查询
PayOrder payOrder = baseMapper.selectBySn(out_trade_no);
payOrder.setState(2);
payOrder.setModifyTime(new Date());
payOrder.setPayTypeValue("微信");
if (result_code.equalsIgnoreCase("FAIL")) {
log.error("订单" + out_trade_no + "支付失败");
return rb.setMsg("订单" + out_trade_no + "支付失败");
} else if (result_code.equalsIgnoreCase("SUCCESS")) {
baseMapper.updateById(payOrder);
return rb.success();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rb;
}
public ResultBean selectOrder(OrderQuery query) {
ResultBean rb = ResultBean.fireFail();
Map<String, Object> map = new HashMap<>();
PayOrder payOrder = fetchBySid(query.getMainSid());
if (payOrder == null) {
return rb.setMsg("订单不存在");
}
map.put("createTime", DateUtils.format(payOrder.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
map.put("payTime", DateUtils.format(payOrder.getModifyTime(), "yyyy-MM-dd HH:mm:ss"));
map.put("outTradeNo", payOrder.getOutTradeNo());
map.put("mainSid", payOrder.getSid());
map.put("payType", payOrder.getPayTypeValue());
return rb.success().setData(map);
}
}

144
src/main/java/com/yxt/pay/biz/wxpay/WxPayRest.java

@ -0,0 +1,144 @@
/*
package com.yxt.pay.biz.wxpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.client.utils.ValidatorUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yxt.common.base.utils.StringUtils;
import com.yxt.common.core.result.ResultBean;
import com.yxt.pay.api.wxpay.*;
import com.yxt.pay.config.WxPayApi;
import com.yxt.pay.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
*/
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/3
**//*
@RestController
@RequestMapping("/wxPay")
public class WxPayRest extends AbstractWxPayApiController {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
WxPayBean wxPayBean;
private String notifyUrl;
private String refundNotifyUrl;
private static final String USER_PAYING = "USERPAYING";
@Override
public WxPayApiConfig getApiConfig() {
WxPayApiConfig apiConfig;
try {
apiConfig = WxPayApiConfigKit.getApiConfig(wxPayBean.getAppId());
} catch (Exception e) {
apiConfig = WxPayApiConfig.builder()
.appId(wxPayBean.getAppId())
.mchId(wxPayBean.getMchId())
.partnerKey(wxPayBean.getPartnerKey())
.certPath(wxPayBean.getCertPath())
.domain(wxPayBean.getDomain())
.build();
}
notifyUrl = apiConfig.getDomain().concat("/wxPay/payNotify");
refundNotifyUrl = apiConfig.getDomain().concat("/wxPay/refundNotify");
return apiConfig;
}
@PostMapping(value = "/miniAppPay")
ResultBean miniAppPay(HttpServletRequest request, PayOrderQuery payOrderQuery) {
ResultBean rb = ResultBean.fireFail();
try {
String ip = IpKit.getRealIp(request);
if (StringUtils.isBlank(ip)) {
ip = "127.0.0.1";
}
WxPayBean wxPayApiConfig = new WxPayBean();
wxPayApiConfig.setAppId(payOrderQuery.getAppId());
wxPayApiConfig.setMchId(payOrderQuery.getMchId());
wxPayApiConfig.setDomain(payOrderQuery.getNotifyUrl());
wxPayApiConfig.setPartnerKey(payOrderQuery.getPaySignKey());
Map<String, String> params = UnifiedOrderModel
.builder()
.appid(wxPayApiConfig.getAppId())
.mch_id(wxPayApiConfig.getMchId())
.nonce_str(WxPayKit.generateStr())
.body("小程序支付")
.attach("Node.js 版:https://gitee.com/javen205/TNW")
.out_trade_no(payOrderQuery.getOrderSn())
.total_fee(new BigDecimal(payOrderQuery.getAmount()).multiply(new BigDecimal("100")) + "")
.spbill_create_ip(ip)
.notify_url(notifyUrl)
.trade_type(TradeType.JSAPI.getTradeType())
.openid(payOrderQuery.getOpenId())
.build()
.createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
//统一下单
String xmlResult = WxPayApi.pushOrder(false, params);
log.info(xmlResult);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
String returnCode = result.get("return_code");
String returnMsg = result.get("return_msg");
if (!WxPayKit.codeIsOk(returnCode)) {
return rb.setMsg(returnMsg);
}
String resultCode = result.get("result_code");
if (!WxPayKit.codeIsOk(resultCode)) {
return rb.setMsg(returnMsg);
}
// 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回(统一下单成功)
String prepayId = result.get("prepay_id");
Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
String jsonStr = JSON.toJSONString(packageParams);
log.info("小程序支付的参数:" + jsonStr);
return rb.success().setData(packageParams);
} catch (Exception e) {
e.printStackTrace();
return rb.setMsg(e.getMessage());
}
}
@PostMapping(value = "/payNotify")
@ResponseBody
public String payNotify(HttpServletRequest request) {
String xmlMsg = HttpKit.readData(request);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
log.info("微信支付通知=" + params);
String returnCode = params.get("return_code");
//订单编号
String out_trade_no = params.get("out_trade_no");
// 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
// 注意此处签名方式需与统一下单的签名类型一致
if (WxPayKit.verifyNotify(params, this.getApiConfig().getPartnerKey(), SignType.HMACSHA256)) {
if (WxPayKit.codeIsOk(returnCode)) {
// 发送通知等
Map<String, String> xml = new HashMap<String, String>(2);
xml.put("return_code", "SUCCESS");
xml.put("return_msg", "OK");
return WxPayKit.toXml(xml);
} else {
log.error("订单" + out_trade_no + "支付失败");
return null;
}
}
return null;
}
}
*/

13
src/main/java/com/yxt/pay/biz/wxpay/WxPayService.java

@ -0,0 +1,13 @@
package com.yxt.pay.biz.wxpay;
import org.springframework.stereotype.Service;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
@Service
public class WxPayService {
}

126
src/main/java/com/yxt/pay/utils/ApiBaseAction.java

@ -0,0 +1,126 @@
package com.yxt.pay.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author mallplus
* @ClassName: ApiBaseAction
* @Description: 基础控制类
* @date 2016年9月2日
*/
@Slf4j
public class ApiBaseAction {
/**
* 得到request对象
*/
@Autowired
protected HttpServletRequest request;
/**
* 得到response对象
*/
@Resource
protected HttpServletResponse response;
/**
* 获取请求的用户Id
*
* @return 客户端Ip
*/
/*public String getUserId() {
String token = request.getHeader(AuthorizationInterceptor.LOGIN_TOKEN_KEY);
//查询token信息
TokenEntity tokenEntity = tokenService.queryByToken(token);
if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
return null;
}
return tokenEntity.getUserId();
}*/
/**
* @param requestCode
* @param msg
* @param data
* @return Map<String, Object>
* @throws
* @Description:构建统一格式返回对象
* @date 2016年9月2日
* @author zhuliyun
*/
public static Map<String, Object> toResponsObject(int requestCode, String msg, Object data) {
Map<String, Object> obj = new HashMap<String, Object>();
obj.put("code", requestCode);
obj.put("msg", msg);
if (data != null)
obj.put("data", data);
return obj;
}
public static Map<String, Object> toResponsSuccessForSelect(Object data) {
Map<String, Object> result = new HashMap<>(2);
result.put("list", data);
return toResponsObject(200, "执行成功", result);
}
/**
* 参数绑定异常
*/
@ExceptionHandler({BindException.class, MissingServletRequestParameterException.class, TypeMismatchException.class})
@ResponseBody
public Map<String, Object> bindException(Exception e) {
if (e instanceof BindException) {
return toResponsObject(1, "参数绑定异常", e.getMessage());
}
return toResponsObject(1, "处理异常", e.getMessage());
}
/**
* initBinder 初始化绑定 <br>
* 这里处理了3种类型<br>
* 1字符串自动 trim 去掉前后空格 <br>
* 2java.util.Date 转换为 "yyyy-MM-dd HH:mm:ss" 格式<br>
* 3java.sql.Date 转换为 "yyyy-MM-dd" 格式<br>
* 4java.util.Timestamps 时间转换
*
* @param binder WebDataBinder 要注册的binder
* @param request 前端请求
*/
@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
// 字符串自动Trim
binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}
/**
* 获取请求方IP
*
* @return 客户端Ip
*/
public String getClientIp() {
String xff = request.getHeader("x-forwarded-for");
if (xff == null) {
xff = "127.0.0.1";
}
return xff;
}
}

80
src/main/java/com/yxt/pay/utils/CharUtil.java

@ -0,0 +1,80 @@
package com.yxt.pay.utils;
import java.util.Random;
public class CharUtil {
/**
* 获取随机字符串
*
* @param num
* @return
*/
public static String getRandomString(Integer num) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < num; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取随机字符串
*
* @param num
* @return
*/
public static String getRandomNum(Integer num) {
String base = "0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < num; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 右补位左对齐
*
* @param oriStr 原字符串
* @param len 目标字符串长度
* @param fillChar 补位字符
* @return 目标字符串
*/
public static String padRight(String oriStr, int len, char fillChar) {
String str = "";
int strlen = oriStr.length();
if (strlen < len) {
for (int i = 0; i < len - strlen; i++) {
str = str + fillChar;
}
}
str = str + oriStr;
return str;
}
/**
* 左补位右对齐
*
* @param oriStr 原字符串
* @param len 目标字符串长度
* @param fillChar 补位字符
* @return 目标字符串
*/
public static String padLeft(String oriStr, int len, char fillChar) {
int strlen = oriStr.length();
String str = "";
if (strlen < len) {
for (int i = 0; i < len - strlen; i++) {
str = str + fillChar;
}
}
str = oriStr + str;
return str;
}
}

220
src/main/java/com/yxt/pay/utils/DateUtils.java

@ -0,0 +1,220 @@
package com.yxt.pay.utils;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 日期处理
*
* @author zscat
* @email 951449465@qq.com
* @date 2016年12月21日 下午12:53:33
*/
public class DateUtils {
/**
* 时间格式(yyyy-MM-dd)
*/
public final static String DATE_PATTERN = "yyyy-MM-dd";
/**
* 时间格式(yyyy-MM-dd HH:mm:ss)
*/
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 无分隔符日期格式 "yyyyMMddHHmmssSSS"
*/
public static String DATE_TIME_PATTERN_YYYY_MM_DD_HH_MM_SS_SSS = "yyyyMMddHHmmssSSS";
// 日期转换格式数组
public static String[][] regularExp = new String[][]{
// 默认格式
{"\\d{4}-((([0][1,3-9]|[1][0-2]|[1-9])-([0-2]\\d|[3][0,1]|[1-9]))|((02|2)-(([1-9])|[0-2]\\d)))\\s+([0,1]\\d|[2][0-3]|\\d):([0-5]\\d|\\d):([0-5]\\d|\\d)",
DATE_TIME_PATTERN},
// 仅日期格式 年月日
{"\\d{4}-((([0][1,3-9]|[1][0-2]|[1-9])-([0-2]\\d|[3][0,1]|[1-9]))|((02|2)-(([1-9])|[0-2]\\d)))",
DATE_PATTERN},
// 带毫秒格式
{"\\d{4}((([0][1,3-9]|[1][0-2]|[1-9])([0-2]\\d|[3][0,1]|[1-9]))|((02|2)(([1-9])|[0-2]\\d)))([0,1]\\d|[2][0-3])([0-5]\\d|\\d)([0-5]\\d|\\d)\\d{1,3}",
DATE_TIME_PATTERN_YYYY_MM_DD_HH_MM_SS_SSS}
};
// 日志
private static org.slf4j.Logger logger = LoggerFactory.getLogger(DateUtils.class);
public static String format(Date date) {
return format(date, DATE_PATTERN);
}
public static String format(Date date, String pattern) {
if (date != null) {
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
return null;
}
public static String timeToStr(Long time, String pattern) {
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
if (time.toString().length() < 13) {
time = time * 1000L;
}
Date date = new Date(time);
String value = dateFormat.format(date);
return value;
}
public static long strToTime(String timeStr) {
Date time = strToDate(timeStr);
return time.getTime() / 1000;
}
/**
* 转换为时间类型格式
*
* @param strDate 日期
* @return
*/
public static Date strToDate(String strDate) {
try {
String strType = getDateFormat(strDate);
SimpleDateFormat sf = new SimpleDateFormat(strType);
return new Date((sf.parse(strDate).getTime()));
} catch (Exception e) {
return null;
}
}
/**
* 根据传入的日期格式字符串获取日期的格式
*
* @return
*/
public static String getDateFormat(String date_str) {
String style = null;
if (StringUtils.isEmpty(date_str)) {
return null;
}
boolean b = false;
for (int i = 0; i < regularExp.length; i++) {
b = date_str.matches(regularExp[i][0]);
if (b) {
style = regularExp[i][1];
}
}
if (StringUtils.isEmpty(style)) {
logger.info("date_str:" + date_str);
logger.info("日期格式获取出错,未识别的日期格式");
}
return style;
}
/**
* 将字符串类型的转换成Date类型
*
* @param dateStr 字符串类型的日期 yyyy-MM-dd
* @return Date类型的日期
* @throws ParseException
*/
public static Date convertStringToDate(String dateStr) {
// 返回的日期
Date resultDate = null;
try {
// 日期格式转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
resultDate = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return resultDate;
}
/**
* 将字符串类型的转换成Date类型
*
* @param dateStr 字符串类型的日期 yyyy-MM-dd
* @return Date类型的日期
* @throws ParseException
*/
public static Date convertStringToDate(String dateStr, String formate) {
// 返回的日期
Date resultDate = null;
try {
// 日期格式转换
SimpleDateFormat sdf = new SimpleDateFormat(formate);
resultDate = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return resultDate;
}
/**
* 添加小时
*
* @param date
* @param hour
* @return
*/
public static String addHours(Date date, int hour) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.HOUR, hour);// 24小时制
date = cal.getTime();
cal = null;
return format.format(date);
}
/**
* 添加分钟
*
* @param date
* @param min
* @return
*/
public static String addMins(Date date, int min) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MINUTE, min);// 24小时制
date = cal.getTime();
cal = null;
return format.format(date);
}
public static void main(String[] args) throws ParseException {
System.out.println(DateUtils.addMins(new Date(), 10));
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前时间
Date date = new Date();
String nowtime = df.format(date);
// System.out.println(nowtime);
Date d2 = df.parse("2019-09-10 10:07:00"); //86400000 1728
Date d1 = df.parse(nowtime);
long diff = d1.getTime() - d2.getTime();// 这样得到的差值是微秒级别
System.out.println(d1.getTime());
System.out.println(diff);
System.out.println(1000 * 60 * 60 * 24);
long days = diff / (1000 * 60 * 60 * 24);//天
long hours = (diff - days * (1000 * 60 * 60 * 24))
/ (1000 * 60 * 60); //小时
long mins = (diff - days * (1000 * 60 * 60 * 24) - hours * (1000 * 60 * 60)) / (1000 * 60); //小时
long sc = (diff - days * (1000 * 60 * 60 * 24) - hours
* (1000 * 60 * 60) - mins * (1000 * 60)) / (1000); // 秒
System.out.println(days);
System.out.println(hours);
System.out.println(mins);
System.out.println(sc);
}
}

7
src/main/java/com/yxt/pay/utils/DealMapValueHelper.java

@ -0,0 +1,7 @@
package com.yxt.pay.utils;
import java.util.Map;
public interface DealMapValueHelper {
void dealValue(String key, Map<String, Object> map);
}

306
src/main/java/com/yxt/pay/utils/MapUtils.java

@ -0,0 +1,306 @@
package com.yxt.pay.utils;
import com.yxt.common.base.utils.StringUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.util.Assert;
import java.beans.PropertyDescriptor;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 获取map中值的工具类,自动进行类型转换
*
* @author DT_panda
*/
public class MapUtils {
public static String getString(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
return value.toString();
}
public static Integer getInteger(String key, Map<String, Object> map) {
if (map == null || key == null) {
throw new IllegalArgumentException();
}
if (!map.containsKey(key)) {
return null;
}
Object value = map.get(key);
if (value == null) {
return null;
}
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof String)
return Integer.valueOf((String) value);
//Date 不支持变成为date类型
if (value instanceof Date)
throw new ClassCastException();
if (value instanceof Number)
return ((Number) value).intValue();
throw new ClassCastException();
}
public static Long getLong(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
if (value instanceof Long)
return (Long) value;
if (value instanceof Number)
return ((Number) value).longValue();
if (value instanceof String)
return Long.valueOf((String) value);
if (value instanceof Date) {
return (((Date) value).getTime());
}
if (value instanceof java.sql.Time) {
return ((java.sql.Time) value).getTime();
}
if (value instanceof Timestamp) {
return ((Timestamp) value).getTime();
}
throw new ClassCastException();
}
public static Double getDouble(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
if (value instanceof Double)
return (Double) value;
if (value instanceof Number)
return ((Number) value).doubleValue();
if (value instanceof String)
return Double.valueOf((String) value);
throw new ClassCastException();
}
public static BigDecimal getBigDecimal(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
if (value instanceof BigDecimal)
return (BigDecimal) value;
if (value instanceof Integer)
return new BigDecimal((Integer) value);
if (value instanceof Short)
return new BigDecimal((Short) value);
if (value instanceof Byte)
return new BigDecimal((Byte) value);
if (value instanceof Long)
return new BigDecimal((Long) value);
if (value instanceof Float)
return new BigDecimal((Float) value);
if (value instanceof Double)
return new BigDecimal((Double) value);
if (value instanceof Date) {
return new BigDecimal(((Date) value).getTime());
}
if (value instanceof java.sql.Time) {
return new BigDecimal(((java.sql.Time) value).getTime());
}
if (value instanceof Timestamp) {
return new BigDecimal(((Timestamp) value).getTime());
}
if (value instanceof String) {
if (!StringUtils.isBlank((String) value))
return new BigDecimal((String) value);
else
return null;
}
throw new ClassCastException();
}
/**
* 将bean转化为map
*
* @param bean
* @return
*/
public static Map<String, Object> getMap(Object bean) {
return beanToMap(bean);
}
/**
* 将map中key为likeKey的value前后加上字符'%'用于like查询
*
* @param map
* @param likeKey
*/
public static void toLikeValue(Map<String, Object> map, String... likeKey) {
if (ArrayUtils.isEmpty(likeKey))
return;
for (String key : likeKey) {
if (map.containsKey(key))
map.put(key, "%" + map.get(key) + "%");
}
}
/**
* 获取日期
*
* @param key
* @param map
* @return
*/
public static Date getDate(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
else {
if (value instanceof Date) {
return (Date) value;
} else if (value instanceof Timestamp) {
return new Date(((Timestamp) value).getTime());
}
}
return null;
}
/**
* 获取日期
*
* @param key
* @param map
* @return
*/
public static java.util.Date getTimestamp(String key, Map<String, Object> map) {
if (map == null || key == null)
throw new IllegalArgumentException();
if (!map.containsKey(key))
return null;
Object value = map.get(key);
if (value == null)
return null;
else {
if (value instanceof Date) {
return (Date) value;
} else if (value instanceof Timestamp) {
Timestamp ts = (Timestamp) value;
return ts;
}
}
return null;
}
/**
* 如果value不为空 则放到map中
*
* @param map
* @param key
* @param value
*/
public static void putIfValueNotNull(Map<String, Object> map, String key, Object value) {
Assert.notNull(map);
Assert.hasText(key);
if (value != null)
map.put(key, value);
}
/**
* 如果value不为空 则放到map中
*
* @param map
* @param key
* @param value
*/
public static void putIfValueNotEmpty(Map<String, Object> map, String key, String value) {
Assert.notNull(map);
Assert.hasText(key);
if (!StringUtils.isBlank(value))
map.put(key, value);
}
/**
* 将map中指定的key的value值进行处理
*
* @param key
* @param map
* @param helper
*/
public static void convertMapValuePattern(String key, Map<String, Object> map, DealMapValueHelper helper) {
Assert.hasText(key);
Assert.notNull(map);
Assert.notNull(helper);
helper.dealValue(key, map);
}
/**
* 将javabean实体类转为map类型然后返回一个map类型的值
*
* @return
*/
public static Map<String, Object> beanToMap(Object beanObj) {
Map<String, Object> params = new HashMap<String, Object>(0);
try {
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(beanObj);
for (int i = 0; i < descriptors.length; i++) {
String name = descriptors[i].getName();
if (!"class".equals(name)) {
params.put(name, propertyUtilsBean.getNestedProperty(beanObj, name));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return params;
}
public static String convertMap2Xml(Map<Object, Object> paraMap) {
StringBuffer xmlStr = new StringBuffer();
if (paraMap != null) {
xmlStr.append("<xml>");
Set<Object> keySet = paraMap.keySet();
Iterator<Object> keyIte = keySet.iterator();
while (keyIte.hasNext()) {
String key = (String) keyIte.next();
String val = String.valueOf(paraMap.get(key));
xmlStr.append("<");
xmlStr.append(key);
xmlStr.append(">");
xmlStr.append(val);
xmlStr.append("</");
xmlStr.append(key);
xmlStr.append(">");
}
xmlStr.append("</xml>");
}
return xmlStr.toString();
}
}

20
src/main/java/com/yxt/pay/utils/UrlComponent.java

@ -0,0 +1,20 @@
package com.yxt.pay.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @description:
* @author: dimengzhe
* @date: 2024/1/7
**/
@Component
public class UrlComponent {
@Value("${domain.url:http://127.0.0.1:7777}")
private String doMainUrl;
public String getDoMainUrl() {
return doMainUrl;
}
}

254
src/main/java/com/yxt/pay/utils/XmlHelper.java

@ -0,0 +1,254 @@
package com.yxt.pay.utils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
/**
* xpath解析xml
* <p>
* <pre>
* 文档地址
* http://www.w3school.com.cn/xpath/index.asp
* </pre>
*
* @author L.cm
*/
public class XmlHelper {
private final XPath path;
private final Document doc;
private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all
// XML entity attacks are prevented
// Xerces 2 only -
// http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// If you can't completely disable DTDs, then at least do the following:
// Xerces 1 -
// http://xerces.apache.org/xerces-j/features.html#external-general-entities
// Xerces 2 -
// http://xerces.apache.org/xerces2-j/features.html#external-general-entities
// JDK7+ - http://xml.org/sax/features/external-general-entities
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
// Xerces 1 -
// http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
// Xerces 2 -
// http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
// JDK7+ - http://xml.org/sax/features/external-parameter-entities
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Disable external DTDs as well
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and
// Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(inputSource);
path = getXpathFactory().newXPath();
}
private static XmlHelper create(InputSource inputSource) {
try {
return new XmlHelper(inputSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static XmlHelper of(InputStream is) {
InputSource inputSource = new InputSource(is);
return create(inputSource);
}
public static XmlHelper of(File file) {
InputSource inputSource = new InputSource(file.toURI().toASCIIString());
return create(inputSource);
}
public static XmlHelper of(String xmlStr) {
StringReader sr = new StringReader(xmlStr.trim());
InputSource inputSource = new InputSource(sr);
XmlHelper xmlHelper = create(inputSource);
sr.close();
return xmlHelper;
}
private static DocumentBuilderFactory getDocumentBuilderFactory() {
return XmlHelperHolder.documentBuilderFactory;
}
private static XPathFactory getXpathFactory() {
return XmlHelperHolder.xPathFactory;
}
private Object evalXpath(String expression, Object item, QName returnType) {
item = null == item ? doc : item;
try {
return path.evaluate(expression, item, returnType);
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
}
}
/**
* 获取String
*
* @param expression 路径
* @return String
*/
public String getString(String expression) {
return (String) evalXpath(expression, null, XPathConstants.STRING);
}
/**
* 获取Boolean
*
* @param expression 路径
* @return String
*/
public Boolean getBoolean(String expression) {
return (Boolean) evalXpath(expression, null, XPathConstants.BOOLEAN);
}
/**
* 获取Number
*
* @param expression 路径
* @return {Number}
*/
public Number getNumber(String expression) {
return (Number) evalXpath(expression, null, XPathConstants.NUMBER);
}
/**
* 获取某个节点
*
* @param expression 路径
* @return {Node}
*/
public Node getNode(String expression) {
return (Node) evalXpath(expression, null, XPathConstants.NODE);
}
/**
* 获取子节点
*
* @param expression 路径
* @return NodeList
*/
public NodeList getNodeList(String expression) {
return (NodeList) evalXpath(expression, null, XPathConstants.NODESET);
}
/**
* 获取String
*
* @param node 节点
* @param expression 相对于node的路径
* @return String
*/
public String getString(Object node, String expression) {
return (String) evalXpath(expression, node, XPathConstants.STRING);
}
/**
* 获取
*
* @param node 节点
* @param expression 相对于node的路径
* @return String
*/
public Boolean getBoolean(Object node, String expression) {
return (Boolean) evalXpath(expression, node, XPathConstants.BOOLEAN);
}
/**
* 获取
*
* @param node 节点
* @param expression 相对于node的路径
* @return {Number}
*/
public Number getNumber(Object node, String expression) {
return (Number) evalXpath(expression, node, XPathConstants.NUMBER);
}
/**
* 获取某个节点
*
* @param node 节点
* @param expression 路径
* @return {Node}
*/
public Node getNode(Object node, String expression) {
return (Node) evalXpath(expression, node, XPathConstants.NODE);
}
/**
* 获取子节点
*
* @param node 节点
* @param expression 相对于node的路径
* @return NodeList
*/
public NodeList getNodeList(Object node, String expression) {
return (NodeList) evalXpath(expression, node, XPathConstants.NODESET);
}
/**
* 针对没有嵌套节点的简单处理
*
* @return map集合
*/
public Map<String, String> toMap() {
Element root = doc.getDocumentElement();
// 将节点封装成map形式
NodeList list = root.getChildNodes();
Map<String, String> params = new HashMap<String, String>(list.getLength());
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
params.put(node.getNodeName(), node.getTextContent());
}
// 含有空白符会生成一个#text参数
params.remove("#text");
return params;
}
/**
* 内部类单例
*/
private static class XmlHelperHolder {
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static XPathFactory xPathFactory = XPathFactory.newInstance();
}
}

201
src/main/java/com/yxt/pay/utils/XmlUtil.java

@ -0,0 +1,201 @@
package com.yxt.pay.utils;
import com.yxt.common.base.utils.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* xml相关的工具类
*
* @author yang.y
*/
@SuppressWarnings("unchecked")
public class XmlUtil {
/**
* xml字符串转换成bean对象
*
* @param xmlStr xml字符串
* @param clazz 待转换的class
* @return 转换后的对象
*/
public static Object xmlStrToBean(String xmlStr, Class clazz) {
Object obj = null;
try {
// 将xml格式的数据转换成Map对象
Map<String, Object> map = xmlStrToMap(xmlStr);
// 将map对象的数据转换成Bean对象
obj = mapToBean(map, clazz);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 将xml格式的字符串转换成Map对象
*
* @param xmlStr xml格式的字符串
* @return Map对象
* @throws Exception 异常
*/
public static Map<String, Object> xmlStrToMap(String xmlStr) throws Exception {
if (StringUtils.isBlank(xmlStr)) {
return null;
}
Map<String, Object> map = new HashMap<String, Object>();
// 将xml格式的字符串转换成Document对象
Document doc = DocumentHelper.parseText(xmlStr);
// 获取根节点
Element root = doc.getRootElement();
// 获取根节点下的所有元素
List children = root.elements();
// 循环所有子元素
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
Element child = (Element) children.get(i);
map.put(child.getName(), child.getTextTrim());
}
}
return map;
}
/**
* 将xml格式字符串转换成Bean对象
* 多级子节点递归遍历
*
* @param xmlStr
* @param clazz
* @return
* @throws Exception
*/
public static Object xmlStrToJavaBean(String xmlStr, Class clazz) {
if (StringUtils.isBlank(xmlStr)) {
return null;
}
Object obj = null;
Map<String, Object> map = new HashMap<String, Object>();
// 将xml格式的字符串转换成Document对象
Document doc;
try {
doc = DocumentHelper.parseText(xmlStr);
// 获取根节点
Element root = doc.getRootElement();
map = elementToMap(root, map);
// 将map对象的数据转换成Bean对象
obj = mapToBean(map, clazz);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 递归遍历xml子节点转换Map
*
* @param element
* @param map
* @return
*/
public static Map<String, Object> elementToMap(Element element, Map<String, Object> map) {
if (element == null || map == null)
return null;
List children = element.elements();
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
Element child = (Element) children.get(i);
if (child.elements() != null && child.elements().size() > 0)
elementToMap(child, map);
else
map.put(child.getName(), child.getTextTrim());
}
}
return map;
}
/**
* 将Map对象通过反射机制转换成Bean对象
*
* @param map 存放数据的map对象
* @param clazz 待转换的class
* @return 转换后的Bean对象
* @throws Exception 异常
*/
public static Object mapToBean(Map<String, Object> map, Class clazz) throws Exception {
Object obj = clazz.newInstance();
if (map != null && map.size() > 0) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
String setMethodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
Field field = getClassField(clazz, propertyName);
if (field != null) {
Class fieldTypeClass = field.getType();
value = convertValType(value, fieldTypeClass);
clazz.getMethod(setMethodName, field.getType()).invoke(obj, value);
}
}
}
return obj;
}
/**
* 将Object类型的值转换成bean对象属性里对应的类型值
*
* @param value Object对象值
* @param fieldTypeClass 属性的类型
* @return 转换后的值
*/
private static Object convertValType(Object value, Class fieldTypeClass) {
Object retVal = null;
if (Long.class.getName().equals(fieldTypeClass.getName())
|| long.class.getName().equals(fieldTypeClass.getName())) {
retVal = Long.parseLong(value.toString());
} else if (Integer.class.getName().equals(fieldTypeClass.getName())
|| int.class.getName().equals(fieldTypeClass.getName())) {
retVal = Integer.parseInt(value.toString());
} else if (Float.class.getName().equals(fieldTypeClass.getName())
|| float.class.getName().equals(fieldTypeClass.getName())) {
retVal = Float.parseFloat(value.toString());
} else if (Double.class.getName().equals(fieldTypeClass.getName())
|| double.class.getName().equals(fieldTypeClass.getName())) {
retVal = Double.parseDouble(value.toString());
} else {
retVal = value;
}
return retVal;
}
/**
* 获取指定字段名称查找在class中的对应的Field对象(包括查找父类)
*
* @param clazz 指定的class
* @param fieldName 字段名称
* @return Field对象
*/
private static Field getClassField(Class clazz, String fieldName) {
if (Object.class.getName().equals(clazz.getName())) {
return null;
}
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
if (field.getName().equals(fieldName)) {
return field;
}
}
Class superClass = clazz.getSuperclass();
if (superClass != null) {// 简单的递归一下
return getClassField(superClass, fieldName);
}
return null;
}
}

29
src/main/java/com/yxt/pay/utils/applet/MD5.java

@ -0,0 +1,29 @@
package com.yxt.pay.utils.applet;
import java.security.MessageDigest;
public class MD5 {
private MD5() {
}
/* * 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String getMessageDigest(String data) {
StringBuilder sb = new StringBuilder();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
} catch (Exception e) {
return null;
}
return sb.toString().toUpperCase();
}
}

39
src/main/java/com/yxt/pay/utils/applet/WechatConfig.java

@ -0,0 +1,39 @@
package com.yxt.pay.utils.applet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.security.KeyStore;
@SuppressWarnings("deprecation")
public class WechatConfig {
private static SSLConnectionSocketFactory sslcsf;
public static SSLConnectionSocketFactory getSslcsf() {
if (null == sslcsf) {
setSsslcsf();
}
return sslcsf;
}
private static void setSsslcsf() {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
Thread.currentThread().getContextClassLoader();
InputStream instream = new WechatRefundApiResult().getClass().getResourceAsStream("certName");
try {
keyStore.load(instream, "mchId".toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "mchId".toCharArray()).build();
sslcsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
} catch (Exception e) {
e.printStackTrace();
}
}
}

214
src/main/java/com/yxt/pay/utils/applet/WechatRefundApiResult.java

@ -0,0 +1,214 @@
package com.yxt.pay.utils.applet;
public class WechatRefundApiResult {
private String return_code;
private String return_msg;
private String result_code;
private String err_code;
private String err_code_des;
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 String refund_id;
private String refund_channel;
private String refund_fee;
private String settlement_refund_fee;
private String total_fee;
private String settlement_total_fee;
private String fee_type;
private String cash_fee;
private String cash_refund_fee;
private String refund_status;
public String getRefund_status() {
return refund_status;
}
public void setRefund_status(String refund_status) {
this.refund_status = refund_status;
}
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
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 String getRefund_id() {
return refund_id;
}
public void setRefund_id(String refund_id) {
this.refund_id = refund_id;
}
public String getRefund_channel() {
return refund_channel;
}
public void setRefund_channel(String refund_channel) {
this.refund_channel = refund_channel;
}
public String getRefund_fee() {
return refund_fee;
}
public void setRefund_fee(String refund_fee) {
this.refund_fee = refund_fee;
}
public String getSettlement_refund_fee() {
return settlement_refund_fee;
}
public void setSettlement_refund_fee(String settlement_refund_fee) {
this.settlement_refund_fee = settlement_refund_fee;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
}
public String getSettlement_total_fee() {
return settlement_total_fee;
}
public void setSettlement_total_fee(String settlement_total_fee) {
this.settlement_total_fee = settlement_total_fee;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getCash_fee() {
return cash_fee;
}
public void setCash_fee(String cash_fee) {
this.cash_fee = cash_fee;
}
public String getCash_refund_fee() {
return cash_refund_fee;
}
public void setCash_refund_fee(String cash_refund_fee) {
this.cash_refund_fee = cash_refund_fee;
}
}

296
src/main/java/com/yxt/pay/utils/applet/WechatUtil.java

@ -0,0 +1,296 @@
package com.yxt.pay.utils.applet;
import com.yxt.pay.utils.CharUtil;
import com.yxt.pay.utils.MapUtils;
import com.yxt.pay.utils.XmlUtil;
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.CloseableHttpResponse;
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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* <p>Title: 微信退款工具类</p>
* <p>Description: 微信退款工具类通过充值客户端的不同初始化不同的工具类得到相应微信退款相关的appid和muchid</p>
*
* @author xubo
* @date 2017年6月6日 下午5:05:03
*/
public class WechatUtil {
/**
* 充值客户端类型--微信公众号
*/
public static Integer CLIENTTYPE_WX = 2;
/**
* 充值客户端类型--app
*/
public static Integer CLIENTTYPE_APP = 1;
private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
/**
* 方法描述微信退款逻辑
* 创建时间2017年4月12日 上午11:04:25
* 作者 xubo
*
* @param
* @return
*/
/* public static WechatRefundApiResult wxRefund(String out_trade_no, Double orderMoney, Double refundMoney) {
//初始化请求微信服务器的配置信息包括appid密钥等
//转换金钱格式
BigDecimal bdOrderMoney = new BigDecimal(orderMoney, MathContext.DECIMAL32);
BigDecimal bdRefundMoney = new BigDecimal(refundMoney, MathContext.DECIMAL32);
//构建请求参数
Map<Object, Object> params = buildRequsetMapParam(out_trade_no, bdOrderMoney, bdRefundMoney);
String mapToXml = MapUtils.convertMap2Xml(params);
//请求微信
String reponseXml = sendSSLPostToWx(mapToXml, WechatConfig.getSslcsf());
WechatRefundApiResult result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
return result;
}*/
/**
* 方法描述得到请求微信退款请求的参数
* 创建时间2017年6月8日 上午11:27:02
* 作者 xubo
*
* @param
* @return
*/
/* private static Map<Object, Object> buildRequsetMapParam(String out_trade_no, BigDecimal bdOrderMoney, BigDecimal bdRefundMoney) {
Map<Object, Object> params = new HashMap<Object, Object>();
//微信分配的公众账号ID(企业号corpid即为此appId)
params.put("appid", ResourceUtil.getConfigByName("wx.appId"));
//微信支付分配的商户号
params.put("mch_id", ResourceUtil.getConfigByName("wx.mchId"));
//随机字符串,不长于32位。推荐随机数生成算法
params.put("nonce_str", CharUtil.getRandomString(16));
//商户侧传给微信的订单号
params.put("out_trade_no", out_trade_no);
//商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
params.put("out_refund_no", getBundleId());
//订单总金额,单位为分,只能为整数
params.put("total_fee", bdOrderMoney.multiply(new BigDecimal(100)).intValue());
//退款总金额,订单总金额,单位为分,只能为整数
params.put("refund_fee", bdRefundMoney.multiply(new BigDecimal(100)).intValue());
//操作员帐号, 默认为商户号
params.put("op_user_id", ResourceUtil.getConfigByName("wx.mchId"));
//签名前必须要参数全部写在前面
params.put("sign", arraySign(params, ResourceUtil.getConfigByName("wx.paySignKey")));
return params;
}*/
/**
* ResourceUtil.getConfigByName("wx.refundUrl")
* 请求微信https
**/
public static String sendSSLPostToWx(String mapToXml, SSLConnectionSocketFactory sslcsf, String refundUrl) {
logger.info("*******退款(WX Request:" + mapToXml);
HttpPost httPost = new HttpPost(refundUrl);
httPost.addHeader("Connection", "keep-alive");
httPost.addHeader("Accept", "*/*");
httPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httPost.addHeader("Host", "api.mch.weixin.qq.com");
httPost.addHeader("X-Requested-With", "XMLHttpRequest");
httPost.addHeader("Cache-Control", "max-age=0");
httPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httPost.setEntity(new StringEntity(mapToXml, "UTF-8"));
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslcsf).build();
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httPost);
HttpEntity entity = response.getEntity();
String xmlStr = EntityUtils.toString(entity, "UTF-8");
logger.info("*******退款(WX Response:" + xmlStr);
return xmlStr;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
/**
* 支付交易ID
*/
public static String getBundleId() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String tradeno = dateFormat.format(new Date());
String str = "000000" + (int) (Math.random() * 1000000);
tradeno = tradeno + str.substring(str.length() - 6);
return tradeno;
}
/**
* 方法描述根据签名加密请求参数
* 创建时间2017年6月8日 上午11:28:52
* 作者 xubo
*
* @param
* @return
*/
public static String arraySign(Map<Object, Object> params, String paySignKey) {
boolean encode = false;
Set<Object> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
StringBuffer temp = new StringBuffer();
boolean first = true;
for (Object key : keys) {
if (first) {
first = false;
} else {
temp.append("&");
}
temp.append(key).append("=");
Object value = params.get(key);
String valueString = "";
if (null != value) {
valueString = value.toString();
}
if (encode) {
try {
temp.append(URLEncoder.encode(valueString, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
temp.append(valueString);
}
}
temp.append("&key=");
temp.append(paySignKey);
System.out.println(temp.toString());
String packageSign = MD5.getMessageDigest(temp.toString());
return packageSign;
}
/**
* 请求只请求一次不做重试
*
* @param url
* @param data
* @return
* @throws Exception
*/
public static String requestOnce(final String url, String data) 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(url);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(5000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(10000).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 " + "mchId");
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
String reusltObj = EntityUtils.toString(httpEntity, "UTF-8");
logger.info("请求结果:" + reusltObj);
return reusltObj;
}
/**
* 方法描述微信查询退款逻辑
* 创建时间2017年4月12日 上午11:04:25
* 作者 xubo
*
* @param
* @return
*/
public Map<String, Object> wxRefundquery(String out_trade_no, String out_refund_no) {
Map<Object, Object> params = new HashMap<Object, Object>();
//微信分配的公众账号ID(企业号corpid即为此appId)
params.put("appid", "xx");
//微信支付分配的商户号
params.put("mch_id", "xx");
//随机字符串,不长于32位。推荐随机数生成算法
params.put("nonce_str", CharUtil.getRandomString(16));
//商户侧传给微信的订单号
params.put("out_trade_no", out_trade_no);
//签名前必须要参数全部写在前面
//签名
params.put("sign", arraySign(params, "wx.paySignKey"));
String mapToXml = MapUtils.convertMap2Xml(params);
HttpPost httPost = new HttpPost("refundqueryUrl");
httPost.addHeader("Connection", "keep-alive");
httPost.addHeader("Accept", "*/*");
httPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httPost.addHeader("Host", "api.mch.weixin.qq.com");
httPost.addHeader("X-Requested-With", "XMLHttpRequest");
httPost.addHeader("Cache-Control", "max-age=0");
httPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httPost.setEntity(new StringEntity(mapToXml, "UTF-8"));
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(WechatConfig.getSslcsf()).build();
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httPost);
HttpEntity entity = response.getEntity();
String xmlStr = EntityUtils.toString(entity, "UTF-8");
System.out.println(xmlStr);
Map<String, Object> result = XmlUtil.xmlStrToMap(xmlStr);//.xmlStrToBean(xmlStr, WechatRefundApiResult.class);
return result;
//将信息保存到数据库
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}

30
src/main/resources/application-devv.yml

@ -0,0 +1,30 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/yxt_pay?serverTimezone=GMT%2B8&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true
username: root
password: root
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
redis:
database: 3 # Redis数据库索引(默认为0)
host: 127.0.0.1
jedis:
pool:
max-active: -1 #连接池最大连接数(使用负值表示没有限制)
max-idle: 8 #连接池中的最大空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
min-idle: 0 # 连接池中的最小空闲连接
password: 123456
port: 6379
timeout: 0 # 连接超时时间(毫秒)
image:
upload:
path: D:\\pay\\upload\\
url:
prefix: http://192.168.1.120:8111/upload/
domain:
urlPrex: http://192.168.0.111:7777

0
src/main/resources/application-pro.yml

0
src/main/resources/application-test.yml

62
src/main/resources/application.yml

@ -0,0 +1,62 @@
spring:
application:
name: yxt_pay
profiles:
active: devv
messages:
# 国际化资源文件路径
basename: i18n/messages
servlet:
#上传文件
multipart:
max-file-size: 50MB
max-request-size: 100MB
devtools:
restart:
# 热部署开关
enabled: true
mvc:
async:
request-timeout: 20000
server:
port: 7777
max-http-header-size: 102400
tomcat:
max-http-form-post-size: -1
#mybatis
mybatis-plus:
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapper-locations: classpath*:**Mapper.xml
global-config:
refresh: true
db-config:
#定义生成ID的类型
id-type: Auto
db-type: mysql
configuration:
map-underscore-to-camel-case: false
cache-enabled: true
call-setters-on-nulls: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#hystrix的超时时间
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 60000
#ribbon的超时时间
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000

13
src/main/resources/banner.txt

@ -0,0 +1,13 @@
,----.. ____
/ / \ ,' , `.
| : : ,---. ,-+-,.' _ | ,---. ,---,
. | ;. / ' ,'\ ,-+-. ; , || ' ,'\ ,-+-. / |
. ; /--` / / | ,--.'|' | || ,---. / / | ,--.'|' |
; | ; . ; ,. :| | ,', | |,/ \ . ; ,. :| | ,"' |
| : | ' | |: :| | / | |--'/ / | ' | |: :| | / | |
. | '___' | .; :| : | | , . ' / | ' | .; :| | | | |
' ; : .'| : || : | |/ ' ; /| | : || | | |/
' | '/ :\ \ / | | |`-' ' | / | \ \ / | | |--'
| : / `----' | ;/ | : | `----' | |/
\ \ .' '---' \ \ / '---'
`---` `----'

54
src/main/resources/logback-spring.xml

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.base" value="logs/yxt_pay"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 :
|%blue(%thread) 线程 如 :DiscoveryClient-CacheRefreshExecutor-0-->
<!--<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
<pattern>%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%green(%logger:%line) |%blue(%msg%n)
</pattern>
<!--<charset>UTF-8</charset> -->
</encoder>
</appender>
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="FILEOUT"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.base}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.base}.%d{yyyyMMdd}.%i.log.zip
</fileNamePattern>
<!-- 当文件大小超过10MB时触发滚动 -->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<!--<pattern>%date [%thread] %-5level %logger{35} - %msg%n</pattern>-->
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}
-%msg%n
</Pattern>
<!--<charset>UTF-8</charset> -->
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILEOUT"/>
</root>
</configuration>
Loading…
Cancel
Save