微信支付后端相关代码
This commit is contained in:
parent
25a66b8690
commit
2a4b5697af
7
pom.xml
7
pom.xml
@ -31,6 +31,7 @@
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<wechatpay-java.version>0.2.12</wechatpay-java.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
@ -181,8 +182,14 @@
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-java</artifactId>
|
||||
<version>${wechatpay-java.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
|
@ -14,6 +14,12 @@
|
||||
<description>
|
||||
web服务入口
|
||||
</description>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@ -65,6 +71,15 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-java</artifactId>
|
||||
<version>0.2.12</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -94,6 +109,14 @@
|
||||
<warName>${project.artifactId}</warName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>9</source>
|
||||
<target>9</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
@ -2,6 +2,8 @@ package com.ruoyi.system.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -37,7 +39,8 @@ public class BsDesignTypeController extends BaseController
|
||||
/**
|
||||
* 查询设计类型列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:designType:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:designType:list')")
|
||||
@Anonymous
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BsDesignType bsDesignType)
|
||||
{
|
||||
@ -62,7 +65,7 @@ public class BsDesignTypeController extends BaseController
|
||||
/**
|
||||
* 获取设计类型详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:designType:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:designType:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
@ -105,7 +108,7 @@ public class BsDesignTypeController extends BaseController
|
||||
/**
|
||||
* 根据编号查询设计类型名称
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:designType:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:designType:query')")
|
||||
@GetMapping("nameByType/{type}")
|
||||
public AjaxResult nameByType(@PathVariable("type") int type)
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
@ -33,7 +34,7 @@ public class BsGoodsController extends BaseController {
|
||||
/**
|
||||
* 查询商品列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:goods:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:goods:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BsGoods bsGoods) {
|
||||
startPage();
|
||||
@ -57,7 +58,7 @@ public class BsGoodsController extends BaseController {
|
||||
/**
|
||||
* 获取商品详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:goods:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:goods:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return success(bsGoodsService.selectBsGoodsById(id));
|
||||
|
@ -23,7 +23,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 注意事项Controller
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-10-25
|
||||
*/
|
||||
@ -37,7 +37,7 @@ public class BsNoteController extends BaseController
|
||||
/**
|
||||
* 查询注意事项列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:note:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:note:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BsNote bsNote)
|
||||
{
|
||||
@ -62,7 +62,7 @@ public class BsNoteController extends BaseController
|
||||
/**
|
||||
* 获取注意事项详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:note:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:note:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ public class BsOrderController extends BaseController
|
||||
/**
|
||||
* 查询订单列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:order:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:order:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BsOrder bsOrder)
|
||||
{
|
||||
@ -62,7 +62,7 @@ public class BsOrderController extends BaseController
|
||||
/**
|
||||
* 获取订单详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:order:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:order:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
@ -72,15 +72,13 @@ public class BsOrderController extends BaseController
|
||||
/**
|
||||
* 根据订单号获取订单详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:order:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:order:query')")
|
||||
@GetMapping(value = "/orderNo/{num}")
|
||||
public AjaxResult getInfoByOrderNo(@PathVariable("num") String num)
|
||||
{
|
||||
return success(bsOrderService.selectBsOrderByOrderNo(num));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 新增订单
|
||||
*/
|
||||
|
@ -2,6 +2,8 @@ package com.ruoyi.system.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -23,7 +25,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 技术类型Controller
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-10-22
|
||||
*/
|
||||
@ -37,7 +39,7 @@ public class BsTechnicalTypeController extends BaseController
|
||||
/**
|
||||
* 查询技术类型列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:type:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:type:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BsTechnicalType bsTechnicalType)
|
||||
{
|
||||
@ -62,7 +64,7 @@ public class BsTechnicalTypeController extends BaseController
|
||||
/**
|
||||
* 获取技术类型详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:type:query')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:type:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
|
@ -0,0 +1,198 @@
|
||||
package com.ruoyi.wxPay;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
import com.github.wxpay.sdk.WXPayUtil;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.utils.DateUtil;
|
||||
import com.ruoyi.common.wx.PayService;
|
||||
import com.ruoyi.common.wx.StreamUtils;
|
||||
import com.ruoyi.common.wx.WxPayDTO;
|
||||
import com.ruoyi.common.wx.WxPayProperties;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Api(tags = "微信支付相关接口") // 用于API文档生成,标记该控制器的功能分类
|
||||
@RestController // 标记该类为一个Spring MVC控制器,返回对象会自动转换为JSON格式
|
||||
@Slf4j // 使用Lombok的注解,提供日志功能
|
||||
@Validated // 启用参数校验
|
||||
@RequiredArgsConstructor // 自动生成构造函数,注入必要的依赖
|
||||
@RequestMapping("/pay") // 定义请求路径的前缀为/pay
|
||||
public class WechatPayController {
|
||||
|
||||
@Resource // 注入PayService,用于处理支付相关的业务逻辑
|
||||
private PayService payService;
|
||||
|
||||
@Resource // 注入WxPayProperties,用于获取微信支付的配置信息
|
||||
private WxPayProperties wxPayProperties;
|
||||
|
||||
@Anonymous
|
||||
|
||||
@ApiOperation(value = "微信native下单,返回支付二维码") // 描述该API的功能
|
||||
@GetMapping("/nativePay") // 定义GET请求的路径为/pay/nativePay
|
||||
public Object nativePay(@RequestParam("orderNumber") String orderNumber) {
|
||||
// todo 业务操作-根据订单编号查询订单信息
|
||||
// 将订单信息中的数据存到WxPayDTO
|
||||
WxPayDTO payDTO = new WxPayDTO(); // 创建WxPayDTO对象用于存储支付信息
|
||||
payDTO.setBody("商品描述"); // 设置商品描述
|
||||
|
||||
// 订单总金额,单位为分
|
||||
payDTO.setTotalFee(1); // 设置订单总金额(1分)
|
||||
|
||||
// 支付回调地址
|
||||
payDTO.setNotifyUrl(wxPayProperties.getNotifyUrl()); // 设置支付成功后的回调地址
|
||||
|
||||
// 商品订单编号
|
||||
payDTO.setOutTradeNo(orderNumber); // 设置商户订单编号
|
||||
|
||||
// 获取当前时间
|
||||
Date date = new Date(); // 创建Date对象表示当前时间
|
||||
String timeStart = DateUtil.formatDateToString(date, "yyyyMMddHHmmss"); // 格式化开始时间
|
||||
|
||||
// 结束时间设置在30分钟后
|
||||
String timeExpire = DateUtil.formatDateToString(DateUtil.addMinutesToDate(date, 30), "yyyyMMddHHmmss"); // 设置结束时间
|
||||
|
||||
// 交易起始时间
|
||||
payDTO.setTimeStart(timeStart); // 设置交易开始时间
|
||||
|
||||
// 交易结束时间
|
||||
payDTO.setTimeExpire(timeExpire); // 设置交易结束时间
|
||||
|
||||
Object url = payService.transactions(payDTO, WxPayConstants.TradeType.NATIVE); // 调用支付服务进行下单
|
||||
|
||||
return url; // 返回二维码或支付链接
|
||||
}
|
||||
|
||||
@ApiOperation(value = "微信JSAPI下单,返回JS支付相关配置") // 描述该API的功能
|
||||
@GetMapping("/jsapiPay") // 定义GET请求的路径为/pay/jsapiPay
|
||||
public Object jsapiPay(@RequestParam("orderNumber") String orderNumber) {
|
||||
// todo 业务操作-根据订单编号查询订单信息
|
||||
// 将订单信息中的数据存到WxPayDTO
|
||||
WxPayDTO payDTO = new WxPayDTO(); // 创建WxPayDTO对象用于存储支付信息
|
||||
payDTO.setBody("支付测试"); // 设置商品描述
|
||||
|
||||
// 订单总金额,单位为分
|
||||
payDTO.setTotalFee(1); // 设置订单总金额(1分)
|
||||
|
||||
// 支付回调地址
|
||||
payDTO.setNotifyUrl(wxPayProperties.getNotifyUrl()); // 设置支付成功后的回调地址
|
||||
payDTO.setOutTradeNo("商户订单号"); // 设置商户订单编号
|
||||
|
||||
// 获取当前时间
|
||||
Date date = new Date(); // 创建Date对象表示当前时间
|
||||
String timeStart = DateUtil.formatDateToString(date, "yyyyMMddHHmmss"); // 格式化开始时间
|
||||
|
||||
// 结束时间设置在30分钟后
|
||||
String timeExpire = DateUtil.formatDateToString(DateUtil.addMinutesToDate(date, 30), "yyyyMMddHHmmss"); // 设置结束时间
|
||||
|
||||
// 交易起始时间
|
||||
payDTO.setTimeStart(timeStart); // 设置交易开始时间
|
||||
|
||||
// 交易结束时间
|
||||
payDTO.setTimeExpire(timeExpire); // 设置交易结束时间
|
||||
|
||||
// todo jsapi下单需要用户的openid
|
||||
payDTO.setOpenId("openid"); // 设置用户的OpenID(需从用户信息中获取)
|
||||
|
||||
Object url = payService.transactions(payDTO, WxPayConstants.TradeType.JSAPI); // 调用支付服务进行下单
|
||||
|
||||
return url; // 返回JS支付相关配置
|
||||
}
|
||||
|
||||
@ApiOperation(value = "微信H5下单,返回跳转链接") // 描述该API的功能
|
||||
@GetMapping("/h5Pay") // 定义GET请求的路径为/pay/h5Pay
|
||||
public Object h5Pay(@RequestParam("orderNumber") String orderNumber, HttpServletRequest request) {
|
||||
// todo 业务操作-根据订单编号查询订单信息
|
||||
// 将订单信息中的数据存到WxPayDTO
|
||||
WxPayDTO payDTO = new WxPayDTO(); // 创建WxPayDTO对象用于存储支付信息
|
||||
payDTO.setBody("商品描述"); // 设置商品描述
|
||||
|
||||
// 订单总金额,单位为分
|
||||
payDTO.setTotalFee(1); // 设置订单总金额(1分)
|
||||
|
||||
// 支付回调地址
|
||||
payDTO.setNotifyUrl(wxPayProperties.getNotifyUrl()); // 设置支付成功后的回调地址
|
||||
payDTO.setOutTradeNo("商户订单号"); // 设置商户订单编号
|
||||
|
||||
// 获取当前时间
|
||||
Date date = new Date(); // 创建Date对象表示当前时间
|
||||
String timeStart = DateUtil.formatDateToString(date, "yyyyMMddHHmmss"); // 格式化开始时间
|
||||
|
||||
// 结束时间设置在30分钟后
|
||||
String timeExpire = DateUtil.formatDateToString(DateUtil.addMinutesToDate(date, 30), "yyyyMMddHHmmss"); // 设置结束时间
|
||||
|
||||
// 交易起始时间
|
||||
payDTO.setTimeStart(timeStart); // 设置交易开始时间
|
||||
|
||||
// 交易结束时间
|
||||
payDTO.setTimeExpire(timeExpire); // 设置交易结束时间
|
||||
|
||||
// todo H5下单需要用户的客户端IP
|
||||
String remoteAddr = request.getRemoteAddr(); // 获取用户的IP地址
|
||||
payDTO.setPayerClientIp(remoteAddr); // 设置支付者的IP地址
|
||||
|
||||
Object url = payService.transactions(payDTO, WxPayConstants.TradeType.JSAPI); // 调用支付服务进行下单
|
||||
|
||||
return url; // 返回H5支付链接
|
||||
}
|
||||
|
||||
@ApiOperation(value = "微信支付回调") // 描述该API的功能
|
||||
@PostMapping("/wxCallback") // 定义POST请求的路径为/pay/wxCallback
|
||||
public Object wxCallback(HttpServletRequest request, HttpServletResponse response) {
|
||||
ServletInputStream inputStream = null; // 定义输入流用于接收请求体
|
||||
try {
|
||||
inputStream = request.getInputStream(); // 获取请求体的输入流
|
||||
String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8"); // 将输入流转换为字符串
|
||||
log.info(notifyXml); // 打印接收到的通知内容
|
||||
|
||||
// 解析返回结果
|
||||
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml); // 将XML转换为Map
|
||||
String jsonString = JSONObject.toJSONString(notifyMap); // 将Map转换为JSON字符串
|
||||
log.info(jsonString); // 打印解析后的结果
|
||||
|
||||
// 判断支付是否成功
|
||||
if ("SUCCESS".equals(notifyMap.get("result_code"))) { // 如果支付结果为成功
|
||||
// todo 修改订单状态
|
||||
|
||||
// 支付成功:给微信发送我已接收通知的响应 创建响应对象
|
||||
Map<String, String> returnMap = new HashMap<>(); // 创建返回的Map
|
||||
returnMap.put("return_code", "SUCCESS"); // 设置返回码为SUCCESS
|
||||
returnMap.put("return_msg", "OK"); // 设置返回消息为OK
|
||||
|
||||
String returnXml = WXPayUtil.mapToXml(returnMap); // 将Map转换为XML格式
|
||||
response.setContentType("text/xml"); // 设置响应的内容类型为XML
|
||||
log.info("支付成功"); // 打印支付成功日志
|
||||
return returnXml; // 返回成功的XML响应
|
||||
} else {
|
||||
// 保存回调信息,方便排除问题
|
||||
}
|
||||
// 创建响应对象:微信接收到校验失败的结果后,会反复的调用当前回调函数
|
||||
Map<String, String> returnMap = new HashMap<>(); // 创建返回的Map
|
||||
returnMap.put("return_code", "FAIL"); // 设置返回码为FAIL
|
||||
returnMap.put("return_msg", ""); // 设置返回消息为空
|
||||
|
||||
String returnXml = WXPayUtil.mapToXml(returnMap); // 将Map转换为XML格式
|
||||
response.setContentType("text/xml"); // 设置响应的内容类型为XML
|
||||
log.info("校验失败"); // 打印校验失败日志
|
||||
return returnXml; // 返回失败的XML响应
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(); // 打印异常栈信息
|
||||
}
|
||||
|
||||
return null; // 如果发生异常,返回null
|
||||
}
|
||||
}
|
||||
|
28
ruoyi-admin/src/main/resources/apiclient_key.pem
Normal file
28
ruoyi-admin/src/main/resources/apiclient_key.pem
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDF0mLbDwjEycxc
|
||||
4rosptSPNXLwJI9c5X2Ztfy3ffVZnlG0Vm8leXSaKQfLw9Fsu1v8qomnbd/3xfpf
|
||||
wOJCG8sd4nmoJBgFV2mM7GHz9f0eiQ9vo9fpySo6CBm3NJTcUPFr54sJgHzRmIJ4
|
||||
ouNif66cs5R2lSP09QeUeNSm0h62OGNb0+0crGy1qqYb5OSOCIrN4Py/MMZZGWgc
|
||||
9FtXGFWVPviWcOTOd4m64P0kbpU3ST3iJwsYaupwC4/t7JxB1fKrJMvgsCWojPps
|
||||
ecpFj6YufIt9jiIQeZ+3uHK9IXuWOBhR+aAaPelhc5UBqB+DIdmGFxhIDjwBnixq
|
||||
7Dnmy9GbAgMBAAECggEAH2d60hPc7ICstMI5lAUYEXQGvyDOsYytF83QRMD2Rjff
|
||||
+KUQF/7kB+Ujm7GGeaB0xfO8zpuexpLjYHgacyw+XowpjAvO1GpAE2MjWbtfZ42S
|
||||
qcJ08GRDE9tdWbw7NY6UBPFJHUUMi6mObOBPAMAMVEwd8YVTo/uSLHvSJnmM7phV
|
||||
CW02o3QLYGebTmyimXUqXF7hnaUZOmYVBRf+j/psCjbnfsd9TVQcIc0ILfABS9g8
|
||||
6z7PQDPRceafiTV5qJrtvMuH8/a1KqCtWOlmML3jhE9aDNN2vf5iRgJYSTx/Wprf
|
||||
FC7FlBwYi4nDGVFk3B7yRwuMWwGUnHF3/BQo8U74QQKBgQD/Mb3s48EglH9MerB4
|
||||
vJGq7c67S+skb6Wu9hdQ63Qhdk/kXYHkUupJnSyaW6spsHqXd4TbomdBNyCs11t+
|
||||
HQ4n/+ScM3K4khY+uQftZxF3J1mIvLv08cJa0WuDepbAxKjrM1WJh5Kp9ihc3p2s
|
||||
v8PB2pUeiyAFfz7dCgRm7e8CyQKBgQDGckYHr6aHLDfCNEBOJCViWOewEMgSUvqt
|
||||
KhYiiPKMZCbX2OWmTnBgWhuumIf9sIBJONZ8QXqdXoOBwFKlhgIynHS54EGhapIY
|
||||
ebNvXopIkF4Bw7yInj7KvwOmojDBiJKUY7BZCVOicT2rnElix/+L9S6gHA7/V5rx
|
||||
vVJkTsnfQwKBgQDVTsKuTAGWNgnh53uysAwij/yJWgAGyLv47wK2RNkhTz+gZvi3
|
||||
3Qaw2Yv6yjzb8APIr8KBw9IDFQ1e6/QyCh9XF/IDDo4J8TJe41LZAZn7uwx/2yJQ
|
||||
r/QA7aOslr+ECd4YGySYfJX/Mx6x4fJx/yil1QtoKGpvrdjh8gmT77Yk4QKBgQDD
|
||||
qeSWq3/8g4KuvyowYb9iitpWZRV/y8VSe867WmAcQJtz823rXie7ON1WdxqO7jpu
|
||||
99WzSjSFea0cf+59OfZsxIrqwsyzRQqri0N6qbKa/Y1THBWGdtDewxvsbrq399re
|
||||
6LP19hY6coEl9cD93sh+zM6eG0xGQ7CIbe0Q7gZpVQKBgQCv+sd/JVm4MhTTQBmi
|
||||
sCLyL53U82481aJIRLsBbqFzLCNxlBge6uSFb2w8ZcQEPswR3ONVW5TpRA6Prk5q
|
||||
07tqGeibyxHL2A1XFgSa62oaKR3lZHn1/wlZyURFYT3sK0tzgvMCIvvYO8ny3Dn/
|
||||
FBKpJ0zO+E0KnZdcwY+Uf0///A==
|
||||
-----END PRIVATE KEY-----
|
@ -16,7 +16,7 @@ ruoyi:
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
port: 40506
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
@ -130,8 +130,16 @@ xss:
|
||||
|
||||
wx:
|
||||
pay:
|
||||
appId: #微信公众号或者小程序等的appid
|
||||
secret:
|
||||
mchId: #微信支付商户号
|
||||
mchKey: #微信支付商户密钥
|
||||
notifyUrl: http://8.130.135.74:8053/api/order/student/pay/wxCallback #支付回调地址
|
||||
appId: wxb1f71e5e0c5f9ee7 #微信公众号或者小程序等的appid
|
||||
secret: 2e9864a6b224feb6fba4ab73b70212cd
|
||||
mchId: 1695785302 #微信支付商户号
|
||||
mchKey: Su7vFDrgVScnUqABUrIWLYyrNapWeH1a #微信支付商户密钥
|
||||
notifyUrl: https://9qsxf7949501.vicp.fun/pay/wxCallback #支付回调地址
|
||||
|
||||
wechat:
|
||||
pay:
|
||||
merchantId: 1695785302
|
||||
privateKeyPath: D:\Code\bishe\RuoYi-Vue\ruoyi-admin\src\main\resources\apiclient_key.pem
|
||||
merchantSerialNumber: 3E96EC705218574D63ECE2EA792B83B224FE9319
|
||||
apiV3Key: Su7vFDrgVScnUqABUrIWLYyrNapWeH1a
|
||||
appId: wxb1f71e5e0c5f9ee7
|
||||
|
@ -52,13 +52,13 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- JSON工具类 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 阿里JSON解析器 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
@ -119,6 +119,39 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 微信支付 -->
|
||||
<dependency>
|
||||
<groupId>com.github.wxpay</groupId>
|
||||
<artifactId>wxpay-sdk</artifactId>
|
||||
<version>0.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-miniapp</artifactId>
|
||||
<version>4.4.2.B</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-pay</artifactId>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-mp</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
111
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtil.java
Normal file
111
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtil.java
Normal file
@ -0,0 +1,111 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateUtil {
|
||||
|
||||
public static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
public static final DateTimeFormatter dateFormatterChinese = DateTimeFormatter.ofPattern("yyyy年M月d日");
|
||||
public static final DateTimeFormatter dateFormatterMonthDay = DateTimeFormatter.ofPattern("M-d日");
|
||||
public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
public static final DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
public static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
|
||||
|
||||
/**
|
||||
* 将给定的 Date 对象往后推指定分钟数。
|
||||
*
|
||||
* @param date 要推后的 Date 对象
|
||||
* @param minutes 要推后的分钟数
|
||||
* @return 推后后的 Date 对象
|
||||
*/
|
||||
public static Date addMinutesToDate(Date date, int minutes) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.MINUTE, minutes);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Date 对象格式化为字符串,指定日期时间格式。
|
||||
*
|
||||
* @param date 要格式化的 Date 对象
|
||||
* @param format 日期时间格式
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public static String formatDateToString(Date date, String format) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
|
||||
public static Date asDate(LocalDate localDate) {
|
||||
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
public static Date asDate(LocalDateTime localDateTime) {
|
||||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
public static LocalDate asLocalDate(Date date) {
|
||||
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
public static LocalDateTime asLocalDateTime(Date date) {
|
||||
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间之间的分钟数
|
||||
*
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static Long timeToTimeMin(Date date, Date date2) {
|
||||
return timeToTimeMin(asLocalDateTime(date), asLocalDateTime(date2));
|
||||
}
|
||||
|
||||
public static Long timeToTimeMin(LocalDateTime localDateTime, LocalDateTime localDateTime2) {
|
||||
Duration duration = Duration.between(localDateTime, localDateTime2);
|
||||
return duration.toMinutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期差
|
||||
*
|
||||
* @param localDate
|
||||
* @param localDate2
|
||||
* @return
|
||||
*/
|
||||
public static Long DateDifference(LocalDate localDate, LocalDate localDate2) {
|
||||
return localDate.toEpochDay() - localDate2.toEpochDay();
|
||||
}
|
||||
|
||||
// 计算两个LocalDateTime秒数差
|
||||
public static Long LocalDateTimeDifference(LocalDateTime localDateTime, LocalDateTime localDateTime2) {
|
||||
Duration duration = Duration.between(localDateTime, localDateTime2);
|
||||
return duration.getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取星期
|
||||
*
|
||||
* @param parse
|
||||
* @return
|
||||
*/
|
||||
public static String getWeek(LocalDate parse) {
|
||||
int value = parse.getDayOfWeek().getValue();
|
||||
if (value == 1) return "一";
|
||||
if (value == 2) return "二";
|
||||
if (value == 3) return "三";
|
||||
if (value == 4) return "四";
|
||||
if (value == 5) return "五";
|
||||
if (value == 6) return "六";
|
||||
if (value == 7) return "日";
|
||||
return null;
|
||||
}
|
||||
}
|
226
ruoyi-common/src/main/java/com/ruoyi/common/wx/PayService.java
Normal file
226
ruoyi-common/src/main/java/com/ruoyi/common/wx/PayService.java
Normal file
@ -0,0 +1,226 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.util.SignUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author: ghl
|
||||
* @date: Created in 2023/6/27 0027
|
||||
* @description: 支付相关通用服务
|
||||
*/
|
||||
@Service // 表示这是一个服务类,Spring会自动识别并管理
|
||||
public class PayService {
|
||||
|
||||
@Autowired // 自动注入WxPayService,处理与微信支付相关的请求
|
||||
private WxPayService wxPayService;
|
||||
|
||||
/**
|
||||
* 根据传入的tradeType来进行不同类型的支付
|
||||
* @param dto 商品参数
|
||||
* @param tradeType WxPayConstants.TradeType.MWEB为H5支付/WxPayConstants.TradeType.NATIVE为扫码支付/WxPayConstants.TradeType.JSAPI为JSAPI支付
|
||||
* @return 返回支付结果
|
||||
*/
|
||||
public Object transactions(WxPayDTO dto, String tradeType) {
|
||||
try {
|
||||
// 检查tradeType是否不为空
|
||||
if (tradeType != null && !tradeType.isEmpty()) {
|
||||
// 判断支付类型并调用对应的方法
|
||||
if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
|
||||
// H5支付
|
||||
return noMiniappPay(dto, tradeType);
|
||||
} else if (WxPayConstants.TradeType.NATIVE.equals(tradeType)) {
|
||||
// NATIVE支付
|
||||
return noMiniappPay(dto, tradeType);
|
||||
} else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
|
||||
// JSAPI支付
|
||||
return miniappPay(dto);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 处理异常并打印堆栈跟踪
|
||||
e.printStackTrace();
|
||||
return null; // 返回null表示出错
|
||||
}
|
||||
return null; // 返回null以处理未匹配的情况
|
||||
}
|
||||
|
||||
// 处理非小程序支付的逻辑
|
||||
private String noMiniappPay(WxPayDTO dto, String tradeType) throws WxPayException {
|
||||
// 创建请求对象
|
||||
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
|
||||
request.setOutTradeNo(dto.getOutTradeNo()); // 设置订单号
|
||||
request.setTotalFee(dto.getTotalFee()); // 设置订单金额
|
||||
request.setBody(dto.getBody()); // 设置商品描述
|
||||
HttpServletRequest httpServletRequest = getHttpServletRequest(); // 获取当前HTTP请求
|
||||
request.setSpbillCreateIp(httpServletRequest.getRemoteAddr()); // 设置发起支付的IP
|
||||
request.setNotifyUrl(dto.getNotifyUrl()); // 设置支付回调地址
|
||||
request.setProductId(dto.getOutTradeNo()); // 设置商品ID
|
||||
request.setTradeType(tradeType); // 设置交易类型
|
||||
// 调用统一下单接口,发起支付请求
|
||||
WxPayNativeOrderResult result = wxPayService.createOrder(request);
|
||||
// 返回的二维码或H5支付跳转链接
|
||||
String codeUrl = result.getCodeUrl();
|
||||
// 返回二维码链接给前端
|
||||
return codeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理JSAPI支付逻辑
|
||||
* @param dto 包含订单信息
|
||||
* @return 返回JSAPI支付参数
|
||||
*/
|
||||
@SneakyThrows // 隐藏抛出的异常
|
||||
public Map<String, String> miniappPay(WxPayDTO dto) {
|
||||
// 设置请求参数
|
||||
WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
|
||||
.body(dto.getBody()) // 商品描述
|
||||
.totalFee(dto.getTotalFee()) // 订单金额
|
||||
.spbillCreateIp(this.getIpAddress(this.getHttpServletRequest())) // 发起支付的IP地址
|
||||
.notifyUrl(dto.getNotifyUrl()) // 回调地址
|
||||
.tradeType(WxPayConstants.TradeType.JSAPI) // 交易类型为JSAPI
|
||||
.openid(dto.getOpenId()) // 用户的OpenID
|
||||
.outTradeNo(dto.getOutTradeNo()) // 订单号
|
||||
.timeStart(dto.getTimeStart()) // 交易开始时间
|
||||
.timeExpire(dto.getTimeExpire()) // 交易结束时间
|
||||
.build(); // 构建请求对象
|
||||
request.setSignType(WxPayConstants.SignType.MD5); // 设置签名类型
|
||||
|
||||
// 发起微信统一下单
|
||||
WxPayUnifiedOrderResult result = this.wxPayService.unifiedOrder(request);
|
||||
// 检查prepayId是否为空
|
||||
if (StringUtils.isBlank(result.getPrepayId())) {
|
||||
return null; // 返回null表示下单失败
|
||||
}
|
||||
// 返回前端调用JSAPI支付所需的参数
|
||||
return getWxJsapiPayParam(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信退款处理
|
||||
* @param body 退款请求参数
|
||||
*/
|
||||
public void wxRefund(WxRefundDTO body) {
|
||||
// 创建退款请求对象
|
||||
WxPayRefundRequest refundRequest = new WxPayRefundRequest();
|
||||
refundRequest.setOutTradeNo(body.getOutTradeNo()); // 设置订单号
|
||||
refundRequest.setOutRefundNo(body.getOutRefundNo()); // 设置退款单号
|
||||
refundRequest.setTotalFee(body.getTotalFee()); // 设置订单总金额
|
||||
refundRequest.setRefundFee(body.getRefundFee()); // 设置退款金额
|
||||
refundRequest.setRefundDesc("商品退款"); // 设置退款说明
|
||||
refundRequest.setNotifyUrl(body.getNotifyUrl()); // 设置退款回调地址
|
||||
|
||||
try {
|
||||
// 调用微信退款接口
|
||||
WxPayRefundResult result = wxPayService.refund(refundRequest);
|
||||
System.out.println("微信退款成功,返回参数{}" + JSONObject.toJSONString(result)); // 打印退款成功信息
|
||||
} catch (WxPayException e) {
|
||||
// 处理退款异常
|
||||
System.out.println("微信退款异常,返回参数{}" + JSONObject.toJSONString(e)); // 打印异常信息
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求对象
|
||||
* @return 当前HTTP请求对象
|
||||
*/
|
||||
private HttpServletRequest getHttpServletRequest() {
|
||||
// 从上下文中获取当前请求的Servlet请求属性
|
||||
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
// 获取请求头信息
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String key = headerNames.nextElement(); // 获取请求头名称
|
||||
System.out.println("请求头:" + key + "值:" + request.getHeader(key)); // 打印请求头信息
|
||||
}
|
||||
return request; // 返回当前请求对象
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求主机的IP地址
|
||||
* @param request 当前HTTP请求对象
|
||||
* @return 客户端IP地址
|
||||
*/
|
||||
private String getIpAddress(HttpServletRequest request) {
|
||||
// 尝试获取多个请求头中的真实IP地址
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("REMOTE-HOST");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr(); // 如果未找到,使用远程地址
|
||||
}
|
||||
|
||||
// 处理可能存在的多个IP地址
|
||||
if (ip.length() > 15) {
|
||||
String[] ips = ip.split(","); // 分割多个IP
|
||||
for (String s : ips) {
|
||||
if (!("unknown".equalsIgnoreCase(s))) {
|
||||
ip = s; // 取第一个非unknown的IP
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ip; // 返回真实IP地址
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到JS支付相关配置
|
||||
* @param weiXinPayOrder 微信支付订单结果
|
||||
* @return 返回JS支付所需的参数
|
||||
*/
|
||||
private Map<String, String> getWxJsapiPayParam(WxPayUnifiedOrderResult weiXinPayOrder) {
|
||||
WxPayWebDTO wxPayParam = new WxPayWebDTO();
|
||||
String package_ = "prepay_id=" + weiXinPayOrder.getPrepayId(); // 构建包参数
|
||||
wxPayParam.setAppId(weiXinPayOrder.getAppid()); // 设置小程序ID
|
||||
wxPayParam.setTimeStamp(System.currentTimeMillis() / 1000 + ""); // 设置时间戳
|
||||
wxPayParam.setNonceStr(UUID.randomUUID().toString().replace("-", "")); // 设置随机字符串
|
||||
wxPayParam.setPackage1(package_); // 设置包参数
|
||||
wxPayParam.setSignType(WxPayConstants.SignType.MD5); // 设置签名类型为MD5
|
||||
|
||||
// 构建签名参数
|
||||
Map<String, String> signParam = new LinkedHashMap<>();
|
||||
signParam.put("appId", weiXinPayOrder.getAppid());
|
||||
signParam.put("nonceStr", wxPayParam.getNonceStr());
|
||||
signParam.put("package", package_);
|
||||
signParam.put("signType", WxPayConstants.SignType.MD5);
|
||||
signParam.put("timeStamp", wxPayParam.getTimeStamp());
|
||||
|
||||
// 生成签名
|
||||
String paySign = SignUtils.createSign(signParam, WxPayConstants.SignType.MD5, this.wxPayService.getConfig().getMchKey(), new String[0]);
|
||||
signParam.put("paySign", paySign); // 将签名加入参数
|
||||
return signParam; // 返回包含支付参数和签名的Map
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class StreamUtils {
|
||||
// 设置缓冲区大小为1024字节
|
||||
private static int _buffer_size = 1024;
|
||||
|
||||
/**
|
||||
* 将InputStream流转换成String字符串
|
||||
* @param inStream 输入的InputStream流
|
||||
* @param encoding 字符编码格式,例如"UTF-8"
|
||||
* @return 转换后的String字符串
|
||||
*/
|
||||
public static String inputStream2String(InputStream inStream, String encoding) {
|
||||
String result = null; // 用于存储最终结果的字符串
|
||||
ByteArrayOutputStream outStream = null; // 用于将字节流写入内存中
|
||||
try {
|
||||
// 检查输入流是否为空
|
||||
if (inStream != null) {
|
||||
outStream = new ByteArrayOutputStream(); // 初始化字节数组输出流
|
||||
byte[] tempBytes = new byte[_buffer_size]; // 创建一个字节数组作为缓冲区
|
||||
int count = -1; // 初始化读取字节数的计数器
|
||||
// 循环读取InputStream中的数据
|
||||
while ((count = inStream.read(tempBytes, 0, _buffer_size)) != -1) {
|
||||
// 将读取到的字节写入到ByteArrayOutputStream
|
||||
outStream.write(tempBytes, 0, count);
|
||||
}
|
||||
tempBytes = null; // 释放缓冲区的引用
|
||||
outStream.flush(); // 刷新输出流,确保所有数据都被写入
|
||||
// 将ByteArrayOutputStream中的字节转换为字符串,使用指定的编码格式
|
||||
result = new String(outStream.toByteArray(), encoding);
|
||||
outStream.close(); // 关闭输出流
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 发生异常时,设置结果为null
|
||||
result = null;
|
||||
} finally {
|
||||
try {
|
||||
// 在finally块中关闭输入流
|
||||
if (inStream != null) {
|
||||
inStream.close(); // 关闭InputStream
|
||||
inStream = null; // 释放输入流引用
|
||||
}
|
||||
// 关闭输出流
|
||||
if (outStream != null) {
|
||||
outStream.close(); // 关闭ByteArrayOutputStream
|
||||
outStream = null; // 释放输出流引用
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(); // 打印异常堆栈跟踪
|
||||
}
|
||||
}
|
||||
return result; // 返回转换后的字符串,若发生异常则为null
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Configuration // 标记该类为配置类,Spring会自动加载
|
||||
@ConditionalOnClass(WxPayService.class) // 当WxPayService类存在时,该配置类才会生效
|
||||
@EnableConfigurationProperties(WxPayProperties.class) // 启用WxPayProperties类的配置属性
|
||||
@AllArgsConstructor // 自动生成一个包含所有字段的构造函数
|
||||
public class WxPayConfiguration {
|
||||
|
||||
private WxPayProperties properties; // 注入WxPayProperties属性,包含微信支付的配置信息
|
||||
|
||||
/**
|
||||
* 创建WxPayService的Bean
|
||||
* @return WxPayService实例
|
||||
*/
|
||||
@Bean // 将该方法返回的对象注册为Spring Bean
|
||||
@ConditionalOnMissingBean // 如果容器中不存在WxPayService类型的Bean,则创建新的实例
|
||||
public WxPayService wxService() {
|
||||
WxPayConfig payConfig = new WxPayConfig(); // 创建WxPayConfig对象用于存储微信支付的配置
|
||||
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); // 设置应用ID,去除空格并处理null
|
||||
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); // 设置商户ID,去除空格并处理null
|
||||
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); // 设置商户密钥,去除空格并处理null
|
||||
|
||||
WxPayService wxPayService = new WxPayServiceImpl(); // 创建WxPayService的实现类实例
|
||||
wxPayService.setConfig(payConfig); // 将配置设置到WxPayService实例中
|
||||
return wxPayService; // 返回WxPayService实例
|
||||
}
|
||||
}
|
95
ruoyi-common/src/main/java/com/ruoyi/common/wx/WxPayDTO.java
Normal file
95
ruoyi-common/src/main/java/com/ruoyi/common/wx/WxPayDTO.java
Normal file
@ -0,0 +1,95 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author: ghl
|
||||
* @date: Created in 2023/6/27 0027
|
||||
* @description: 微信统一下单请求参数
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class WxPayDTO {
|
||||
/**
|
||||
* openId
|
||||
*/
|
||||
private String openId;
|
||||
|
||||
|
||||
/**
|
||||
* 字段名:商品描述.
|
||||
* 变量名:body
|
||||
* 是否必填:是
|
||||
* 类型:String(128)
|
||||
* 示例值: 腾讯充值中心-QQ会员充值
|
||||
* 描述:商品简单描述,该字段须严格按照规范传递,具体请见参数规定
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 字段名:总金额.
|
||||
* 变量名:total_fee
|
||||
* 是否必填:是
|
||||
* 类型:Int
|
||||
* 示例值: 888
|
||||
* 描述:订单总金额,单位为分,详见支付金额
|
||||
*/
|
||||
private Integer totalFee;
|
||||
|
||||
/**
|
||||
* 字段名:通知地址.
|
||||
* 变量名:notify_url
|
||||
* 是否必填:是
|
||||
* 类型:String(256)
|
||||
* 示例值:http://www.weixin.qq.com/wxpay/pay.php
|
||||
* 描述:接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
|
||||
*/
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 字段名:商户订单号.
|
||||
* 变量名:out_trade_no
|
||||
* 是否必填:是
|
||||
* 类型:String(32)
|
||||
* 示例值:20150806125346
|
||||
* 描述:商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
|
||||
*/
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* 字段名:交易起始时间.
|
||||
* 变量名:time_start
|
||||
* 是否必填:否
|
||||
* 类型:String(14)
|
||||
* 示例值:20091225091010
|
||||
* 描述:订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
|
||||
*/
|
||||
private String timeStart;
|
||||
|
||||
/**
|
||||
* 字段名:交易结束时间.
|
||||
* 变量名:time_expire
|
||||
* 是否必填:否
|
||||
* 类型:String(14)
|
||||
* 示例值:20091227091010
|
||||
* 描述:订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
|
||||
* 注意:最短失效时间间隔必须大于5分钟
|
||||
*/
|
||||
private String timeExpire;
|
||||
|
||||
/**
|
||||
* 字段名:用户的客户端IP
|
||||
* 变量名:payer_client_ip
|
||||
* 是否必填:是
|
||||
* 类型:String(14)
|
||||
* 示例值:14.23.150.211
|
||||
* 描述:用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
|
||||
* 注意:最短失效时间间隔必须大于5分钟
|
||||
*/
|
||||
private String payerClientIp;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
|
||||
/**
|
||||
* wxpay pay properties.
|
||||
*
|
||||
* @author Binary Wang
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "wx.pay")
|
||||
public class WxPayProperties {
|
||||
/**
|
||||
* 设置微信公众号或者小程序等的appid
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 微信支付商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 微信支付商户密钥
|
||||
*/
|
||||
private String mchKey;
|
||||
|
||||
|
||||
private String notifyUrl;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author: ghl
|
||||
* @date: Created in 2023/6/27 0027
|
||||
* @description:
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@ApiModel
|
||||
public class WxPayWebDTO {
|
||||
/**
|
||||
* 公众号id
|
||||
*/
|
||||
@ApiModelProperty("公众号id")
|
||||
private String appId;
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
@ApiModelProperty("时间戳")
|
||||
private String timeStamp;
|
||||
/**
|
||||
* 随机字符串
|
||||
*/
|
||||
@ApiModelProperty("随机字符串")
|
||||
private String nonceStr;
|
||||
/**
|
||||
* 订单详情扩展字符串
|
||||
*/
|
||||
@ApiModelProperty("订单详情扩展字符串")
|
||||
private String package1;
|
||||
/**
|
||||
* 签名方式
|
||||
*/
|
||||
@ApiModelProperty("签名方式")
|
||||
private String signType;
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
@ApiModelProperty("签名")
|
||||
private String paySign;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.ruoyi.common.wx;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author: ghl
|
||||
* @date: Created in 2023/7/1 0001
|
||||
* @description:
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class WxRefundDTO {
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* 商户退款单号
|
||||
*/
|
||||
private String outRefundNo;
|
||||
|
||||
/**
|
||||
* 订单金额 单位:分
|
||||
*/
|
||||
private Integer totalFee;
|
||||
|
||||
/**
|
||||
* 退款金额 单位:分
|
||||
*/
|
||||
private Integer refundFee;
|
||||
|
||||
/**
|
||||
* 退款结果通知url
|
||||
*/
|
||||
private String notifyUrl;
|
||||
}
|
@ -59,6 +59,14 @@
|
||||
<artifactId>ruoyi-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
@ -23,7 +23,7 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
|
||||
|
||||
/**
|
||||
* spring security配置
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
@ -35,7 +35,7 @@ public class SecurityConfig
|
||||
*/
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
|
||||
/**
|
||||
* 认证失败处理类
|
||||
*/
|
||||
@ -53,7 +53,7 @@ public class SecurityConfig
|
||||
*/
|
||||
@Autowired
|
||||
private JwtAuthenticationTokenFilter authenticationTokenFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 跨域过滤器
|
||||
*/
|
||||
@ -111,7 +111,7 @@ public class SecurityConfig
|
||||
.authorizeHttpRequests((requests) -> {
|
||||
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
|
||||
requests.antMatchers("/login", "/register", "/captchaImage","/system/goods/**","/system/designType/**","/system/type/**","/system/order/**","/system/note/**").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
|
@ -0,0 +1,99 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||
import com.wechat.pay.java.service.payments.h5.H5Service;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
||||
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
* @desc: 微信config
|
||||
* @author: shy
|
||||
* @date: 2024/4/9 10:06
|
||||
*/
|
||||
@Configuration
|
||||
@Getter
|
||||
public class WeChatConfig {
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
@Value("${wechat.pay.merchantId}")
|
||||
public String merchantId;
|
||||
/**
|
||||
* 商户API私钥路径
|
||||
*/
|
||||
@Value("${wechat.pay.privateKeyPath}")
|
||||
public String privateKeyPath;
|
||||
/**
|
||||
* 商户证书序列号
|
||||
*/
|
||||
@Value("${wechat.pay.merchantSerialNumber}")
|
||||
public String merchantSerialNumber;
|
||||
/**
|
||||
* 商户APIV3密钥
|
||||
*/
|
||||
@Value("${wechat.pay.apiV3Key}")
|
||||
public String apiV3Key;
|
||||
/**
|
||||
* AppId
|
||||
*/
|
||||
@Value("${wechat.pay.appId}")
|
||||
public String appId;
|
||||
|
||||
private Config config;
|
||||
|
||||
@PostConstruct
|
||||
public void initConfig() {
|
||||
// 使用自动更新平台证书的RSA配置
|
||||
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
|
||||
config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(merchantId)
|
||||
.privateKeyFromPath(privateKeyPath)
|
||||
.merchantSerialNumber(merchantSerialNumber)
|
||||
.apiV3Key(apiV3Key)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean()
|
||||
public H5Service h5Service() {
|
||||
return new H5Service.Builder()
|
||||
.config(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean()
|
||||
public JsapiService jsapiService() {
|
||||
return new JsapiService.Builder()
|
||||
.config(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean()
|
||||
public NativePayService nativePayService() {
|
||||
return new NativePayService.Builder()
|
||||
.config(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public NotificationParser notificationParser() {
|
||||
return new NotificationParser((NotificationConfig) config);
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import { isRelogin } from '@/utils/request'
|
||||
|
||||
NProgress.configure({ showSpinner: false })
|
||||
|
||||
const whiteList = ['/login', '/register','/bishe']
|
||||
const whiteList = ['/login', '/register','/cus','/cusDetails','/cusDetPay','/userOrder','/note']
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
@ -32,11 +32,11 @@ router.beforeEach((to, from, next) => {
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
||||
})
|
||||
}).catch(err => {
|
||||
store.dispatch('LogOut').then(() => {
|
||||
Message.error(err)
|
||||
next({ path: '/' })
|
||||
})
|
||||
store.dispatch('LogOut').then(() => {
|
||||
Message.error(err)
|
||||
next({ path: '/' })
|
||||
})
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
@ -72,12 +72,12 @@ export const constantRoutes = [
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/cusDetails/:id',
|
||||
path: '/cusDetails',
|
||||
component: () => import('@/views/system/userFront/details'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/cusDetPay/:id',
|
||||
path: '/cusDetPay',
|
||||
component: () => import('@/views/system/userFront/payBefore'),
|
||||
hidden: true
|
||||
},
|
||||
|
@ -54,7 +54,8 @@
|
||||
<h3 style="text-align: center;color: rgb(82, 182, 106)">¥{{ goods.price }}</h3>
|
||||
<!-- @click="downloadFile(goods.resource)"-->
|
||||
<div class="pay-but">
|
||||
<router-link :to="`/cusDetPay/${goods.id}`">
|
||||
<!-- <router-link :to="`/cusDetPay/${goods.id}`">-->
|
||||
<router-link :to="{ path: '/cusDetPay', query: { id: goods.id } }">
|
||||
<el-button class="but-pay" ><i class="el-icon-download"></i> 支付下载</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
@ -130,7 +131,8 @@ export default {
|
||||
mounted() {
|
||||
},
|
||||
created() {
|
||||
this.goodsId = this.$route.params.id;
|
||||
// this.goodsId = this.$route.params.id;
|
||||
this.goodsId = this.$route.query.id;
|
||||
this.getGood(this.goodsId);
|
||||
this.getTnList()
|
||||
this.getDnList()
|
||||
|
@ -74,7 +74,8 @@
|
||||
<el-row :gutter="20" class="card-row">
|
||||
<template v-if="goodsList.length > 0">
|
||||
<el-col :span="6" v-for="(item, index) in goodsList" :key="index" style="margin-bottom: 30px">
|
||||
<router-link :to="`/cusDetails/${item.id}`">
|
||||
<!-- <router-link :to="{ path: `/cusDetails/${item.id}` }">-->
|
||||
<router-link :to="{ path: '/cusDetails', query: { id: item.id } }">
|
||||
<el-card :body-style="{ padding: '5px' }">
|
||||
<el-image :src="`http://127.0.0.1:8080${item.cover}`" class="image" />
|
||||
<div style="padding: 8px;">
|
||||
|
@ -63,7 +63,8 @@ export default {
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.goodsId = this.$route.params.id;
|
||||
// this.goodsId = this.$route.params.id;
|
||||
this.goodsId = this.$route.query.id;
|
||||
this.getGood(this.goodsId);
|
||||
console.log('goodsId',this.goodsId)
|
||||
},
|
||||
|
@ -7,9 +7,9 @@ function resolve(dir) {
|
||||
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
|
||||
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
|
||||
const name = process.env.VUE_APP_TITLE || '毕设管理' // 网页标题
|
||||
|
||||
const port = process.env.port || process.env.npm_config_port || 80 // 端口
|
||||
const port = process.env.port || process.env.npm_config_port || 99 // 端口
|
||||
|
||||
// vue.config.js 配置说明
|
||||
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
|
||||
@ -36,7 +36,7 @@ module.exports = {
|
||||
proxy: {
|
||||
// detail: https://cli.vuejs.org/config/#devserver-proxy
|
||||
[process.env.VUE_APP_BASE_API]: {
|
||||
target: `http://localhost:8080`,
|
||||
target: `http://localhost:40506`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||
|
Loading…
Reference in New Issue
Block a user