微信支付后端相关代码

This commit is contained in:
lixiang 2024-10-26 18:02:49 +08:00
parent 25a66b8690
commit 2a4b5697af
28 changed files with 1112 additions and 44 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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)
{

View File

@ -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));

View File

@ -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)
{

View File

@ -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));
}
/**
* 新增订单
*/

View File

@ -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)
{

View File

@ -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
}
}

View 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-----

View File

@ -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

View File

@ -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>

View 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;
}
}

View 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
}
}

View File

@ -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
}
}

View File

@ -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实例
}
}

View 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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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>

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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()
}

View File

@ -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
},

View File

@ -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()

View File

@ -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;">

View File

@ -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)
},

View File

@ -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]: ''