接入微信支付

This commit is contained in:
2025-06-30 20:49:17 +08:00
15 changed files with 731 additions and 9 deletions

View File

@ -221,6 +221,11 @@
</dependency>
<!--rabbitmq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

View File

@ -72,11 +72,15 @@ public class PermissionCheck {
String userRole = userInfo.getUserRole();
UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(userRole);
// 接口权限只能是 USERADMINBOSS用户权限是 ADMINBOSSUSERBAN
// 接口权限只能是 USERADMINBOSS用户权限是 ADMINBOSSUSERBANMANAGERSUPERVISORSTAFF
// 校验角色
ThrowUtils.throwIf(UserRoleEnum.USER.equals(userRoleEnum) && !UserRoleEnum.USER.equals(interfaceRoleEnum), ErrorCode.NO_AUTH_ERROR);
ThrowUtils.throwIf(UserRoleEnum.BAN.equals(userRoleEnum), ErrorCode.NO_AUTH_ERROR, "用户已被封禁");
ThrowUtils.throwIf(UserRoleEnum.ADMIN.equals(userRoleEnum) && UserRoleEnum.BOSS.equals(interfaceRoleEnum), ErrorCode.NO_AUTH_ERROR);
ThrowUtils.throwIf(UserRoleEnum.BOSS.equals(userRoleEnum) && UserRoleEnum.USER.equals(interfaceRoleEnum), ErrorCode.NO_AUTH_ERROR);
ThrowUtils.throwIf(UserRoleEnum.ADMIN.equals(userRoleEnum) && !UserRoleEnum.ADMIN.equals(interfaceRoleEnum), ErrorCode.NO_AUTH_ERROR);
ThrowUtils.throwIf(UserRoleEnum.BAN.equals(userRoleEnum), ErrorCode.PARAMS_ERROR, "用户已被封禁");
ThrowUtils.throwIf((UserRoleEnum.USER.equals(userRoleEnum)
|| UserRoleEnum.STAFF.equals(userRoleEnum)
|| UserRoleEnum.SUPERVISOR.equals(userRoleEnum)
|| UserRoleEnum.MANAGER.equals(userRoleEnum)) && !UserRoleEnum.USER.equals(interfaceRoleEnum), ErrorCode.NO_AUTH_ERROR);
return joinPoint.proceed();
}

View File

@ -0,0 +1,25 @@
package com.greenorange.promotion.config;
import org.springframework.amqp.support.converter.DefaultClassMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public MessageConverter jsonToMapMessageConverter() {
DefaultClassMapper defaultClassMapper = new DefaultClassMapper();
defaultClassMapper.setTrustedPackages("com.greenorange.promotion.utils.MultiDelayMessage"); // trusted packages
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
jackson2JsonMessageConverter.setClassMapper(defaultClassMapper);
return jackson2JsonMessageConverter;
}
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}

View File

@ -0,0 +1,44 @@
package com.greenorange.promotion.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "wx.mini")
public class WxOpenConfig {
private String appId;
private String appSecret;
private WxMaService wxMaService;
/**
* 单例模式
*/
public WxMaService getWxMaService() {
if (wxMaService != null) {
return wxMaService;
}
synchronized (this) {
if (wxMaService != null) {
return wxMaService;
}
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(appId);
config.setSecret(appSecret);
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(config);
wxMaService = service;
return wxMaService;
}
}
}

View File

@ -0,0 +1,78 @@
package com.greenorange.promotion.config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Data
@Slf4j
@Configuration
@Component("WxPayConfig")
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {
private String appId;
private String apiV3Key;
private String notifyUrl;
private String merchantId;
private String privateKeyPath;
private String merchantSerialNumber;
// RSA配置
private RSAAutoCertificateConfig RSAConfig;
// JSAPI支付
private JsapiServiceExtension jsapiServiceExtension;
// 退款
private RefundService refundService;
/**
* 初始化配置
*/
@Bean
public boolean initWxPayConfig() throws IOException {
this.RSAConfig = buildRSAAutoCertificateConfig();
this.jsapiServiceExtension = buildJsapiServiceExtension(RSAConfig);
this.refundService = buildRefundService(RSAConfig);
return true;
}
// 构建并使用自动更新平台证书的RSA配置一个商户号只能初始化一个配置否则会因为重复的下载任务报错
private RSAAutoCertificateConfig buildRSAAutoCertificateConfig() throws IOException {
// 将 resource 目录下的文件转为 InputStream然后利用 IOUtil.toString(inputStream) 转化为密钥
String privateKey = IOUtil.toString(new ClassPathResource(privateKeyPath).getInputStream());
return new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
}
// 构建JSAPI支付
private JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {
return new JsapiServiceExtension.Builder().config(config).build();
}
// 构建退款
private RefundService buildRefundService(RSAAutoCertificateConfig config) {
return new RefundService.Builder().config(config).build();
}
}

View File

@ -56,9 +56,6 @@ public class CourseController {
@Resource
private CommonService commonService;
@Resource
private WechatGetQrcodeService wechatGetQrcodeService;
/**
* 小程序端用户查看热门课程列表

View File

@ -0,0 +1,150 @@
package com.greenorange.promotion.controller.wechat;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.greenorange.promotion.annotation.RequiresPermission;
import com.greenorange.promotion.common.BaseResponse;
import com.greenorange.promotion.common.ErrorCode;
import com.greenorange.promotion.common.ResultUtils;
import com.greenorange.promotion.config.WxOpenConfig;
import com.greenorange.promotion.constant.OrderStatusConstant;
import com.greenorange.promotion.constant.UserConstant;
import com.greenorange.promotion.exception.BusinessException;
import com.greenorange.promotion.exception.ThrowUtils;
import com.greenorange.promotion.model.dto.CommonRequest;
import com.greenorange.promotion.model.dto.wxPay.WechatPayRequest;
import com.greenorange.promotion.model.entity.CourseOrder;
import com.greenorange.promotion.model.entity.UserInfo;
import com.greenorange.promotion.service.course.CourseOrderService;
import com.greenorange.promotion.service.userInfo.UserInfoService;
import com.greenorange.promotion.service.wechat.WechatPayService;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
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 java.io.IOException;
import java.math.BigDecimal;
@Slf4j
@RestController
@Tag(name = "微信支付")
@RequestMapping("/wxPay")
public class WechatPayController {
@Resource
private WechatPayService weChatService;
@Resource
private UserInfoService userInfoService;
@Resource
private CourseOrderService courseOrderService;
@Resource
private WxOpenConfig wxOpenConfig;
/**
* JSAPI 下单(商品类)
*/
@PostMapping("/payment/create")
@Operation(summary = "JSAPI 下单(商品类)", description = "参数订单id, 权限:所有人, 方法名createPayment")
@RequiresPermission(mustRole = UserConstant.DEFAULT_ROLE)
public BaseResponse<PrepayWithRequestPaymentResponse> createPayment(@Valid @RequestBody WechatPayRequest wechatPayRequest, HttpServletRequest request) {
String code = wechatPayRequest.getCode();
WxMaJscode2SessionResult sessionInfo;
String miniOpenId;
try {
WxMaService wxMaService = wxOpenConfig.getWxMaService();
sessionInfo = wxMaService.jsCode2SessionInfo(code);
miniOpenId = sessionInfo.getOpenid();
if (StringUtils.isAnyBlank(miniOpenId)) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
} catch (WxErrorException e) {
log.error("userLoginByWxOpen error", e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "登录失败,系统错误");
}
Long userId = (Long) request.getAttribute("userId");
UserInfo userInfo = userInfoService.getById(userId);
Long orderId = wechatPayRequest.getOrderId();
CourseOrder courseOrder = courseOrderService.getById(orderId);
ThrowUtils.throwIf(courseOrder == null, ErrorCode.NOT_FOUND_ERROR, "订单不存在");
ThrowUtils.throwIf(!courseOrder.getOrderStatus().equals(OrderStatusConstant.PENDING), ErrorCode.OPERATION_ERROR, "订单状态错误");
if (!userInfo.getId().equals(courseOrder.getUserId())) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "你不是该订单用户!");
}
PrepayWithRequestPaymentResponse response = weChatService.createPayment(String.valueOf(orderId), miniOpenId, courseOrder.getTotalAmount());
return ResultUtils.success(response);
}
/**
* JSAPI 下单回调(商品类)
*/
@Hidden
@PostMapping("/payment/callback")
@Operation(summary = "JSAPI 下单回调(商品类)", description = "参数订单id, 权限:所有人, 方法名callbackPayment")
public synchronized BaseResponse<Boolean> callbackPayment(HttpServletRequest request) throws IOException {
// 获取下单信息
Transaction transaction = weChatService.getTransactionInfo(request);
System.out.println("下单信息:" + transaction);
// 支付回调
boolean result = weChatService.paymentCallback(transaction);
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "微信支付回调失败");
return ResultUtils.success(true);
}
/**
* Web管理员部分退款
* @param commonRequest 订单id
*/
@PostMapping("/refund/part/create")
@Operation(summary = "Web管理员部分退款", description = "参数订单id, 权限web端管理员, 方法名createPartRefund")
@RequiresPermission(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Refund> createPartRefund(@Valid @RequestBody CommonRequest commonRequest) {
Long courseOrderId = commonRequest.getId();
CourseOrder courseOrder = courseOrderService.getById(courseOrderId);
ThrowUtils.throwIf(courseOrder == null, ErrorCode.OPERATION_ERROR, "订单不存在");
Refund refund = weChatService.refundPartPayment(String.valueOf(courseOrderId), courseOrder.getTotalAmount());
return ResultUtils.success(refund);
}
/**
* 部分退款回调
*/
@Hidden
@PostMapping("/refund/part/callback")
@Operation(summary = "部分退款回调", description = "参数订单id, 权限web端管理员, 方法名callbackRefundPart")
public BaseResponse<Boolean> callbackRefundPart(HttpServletRequest request) {
// 获取退款信息
RefundNotification refundNotification = weChatService.getRefundInfo(request);
// 退款回调
boolean result = weChatService.refundPartCallback(refundNotification);
ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "退款回调失败");
return ResultUtils.success(true);
}
}

View File

@ -26,7 +26,6 @@ import java.util.Map;
public class WechatPayoutsController {
/**
* 微信小程序积分提现到银行卡
*/

View File

@ -0,0 +1,33 @@
package com.greenorange.promotion.model.dto.wxPay;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
@Schema(description = "微信支付请求体", requiredProperties = {"orderId", "code"})
public class WechatPayRequest implements Serializable {
/**
* 订单id
*/
@NotNull(message = "id不能为null")
@Min(value = 1L, message = "id不能小于1")
@Schema(description = "订单id", example = "1")
private Long orderId;
/**
* 用户登录凭证
*/
@NotBlank(message = "用户登录凭证不能为空")
@Schema(description = "用户登录凭证", example = "23nm5jfds22a2324rr32rr")
private String code;
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -37,7 +37,6 @@ public class UserInfoVO implements Serializable {
@Schema(description = "手机号", example = "15888610253")
private String phoneNumber;
/**
* 账号
*/

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.greenorange.promotion.common.ErrorCode;
import com.greenorange.promotion.config.WxOpenConfig;
import com.greenorange.promotion.constant.CommonConstant;
import com.greenorange.promotion.constant.SystemConstant;
import com.greenorange.promotion.constant.UserConstant;
@ -95,6 +96,8 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
/**
* 获取查询条件
*/

View File

@ -0,0 +1,53 @@
package com.greenorange.promotion.service.wechat;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
/**
* @author 陈新知
*/
public interface WechatPayService {
/**
* 微信支付
*/
PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount);
/**
* 支付回调
*/
boolean paymentCallback(Transaction transaction) throws IOException;
/**
* 部分退款申请
*/
Refund refundPartPayment(String orderId, BigDecimal refundAmount);
/**
* 部分退款回调
*/
boolean refundPartCallback(RefundNotification refundNotification);
/**
* 获取支付回调信息
*/
Transaction getTransactionInfo(HttpServletRequest request);
/**
* 获取退款回调信息
*/
RefundNotification getRefundInfo(HttpServletRequest request);
}

View File

@ -0,0 +1,252 @@
package com.greenorange.promotion.service.wechat.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.greenorange.promotion.common.ErrorCode;
import com.greenorange.promotion.config.WxPayConfig;
import com.greenorange.promotion.constant.OrderStatusConstant;
import com.greenorange.promotion.exception.ThrowUtils;
import com.greenorange.promotion.model.entity.Course;
import com.greenorange.promotion.model.entity.CourseOrder;
import com.greenorange.promotion.service.course.CourseOrderService;
import com.greenorange.promotion.service.course.CourseService;
import com.greenorange.promotion.service.wechat.WechatPayService;
import com.greenorange.promotion.utils.RefundUtils;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
/**
* @author 陈新知
*/
@Service
public class WechatPayServiceImpl implements WechatPayService {
@Resource
private WxPayConfig wxPayConfig;
@Resource
private CourseOrderService courseOrderService;
@Resource
private CourseService courseService;
/**
* 请求参数
*/
public static RequestParam requestParam = null;
/**
* 微信支付
*/
@Override
public PrepayWithRequestPaymentResponse createPayment(String orderId, String miniOpenId, BigDecimal amount) {
// request.setXxx(val)设置所需参数具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
// 金额
Amount WxAmount = new Amount();
WxAmount.setTotal(amount.movePointRight(2).intValue());
WxAmount.setCurrency("CNY");
request.setAmount(WxAmount);
// 公众号id
request.setAppid(wxPayConfig.getAppId());
// 商户号
request.setMchid(wxPayConfig.getMerchantId());
// 支付者信息
Payer payer = new Payer();
payer.setOpenid(miniOpenId);
request.setPayer(payer);
// 获取订单号
CourseOrder courseOrder = courseOrderService.getById(orderId);
String orderNumber = courseOrder.getOrderNumber();
// 描述
request.setDescription("订单号:" + orderNumber);
// 微信回调地址
request.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/wxPay/payment/callback");
// 商户订单号
request.setOutTradeNo(orderNumber);
//返回数据,前端调起支付
return wxPayConfig.getJsapiServiceExtension().prepayWithRequestPayment(request);
}
/**
* 支付回调
*/
@Override
public boolean paymentCallback(Transaction transaction) throws IOException {
System.out.println("---------------------------微信支付回调(开始)-------------------------------");
// 获取订单信息
String orderNumber = transaction.getOutTradeNo();
LambdaQueryWrapper<CourseOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CourseOrder::getOrderNumber, orderNumber);
CourseOrder courseOrder = courseOrderService.getOne(queryWrapper);
// 修改订单状态
LambdaUpdateWrapper<CourseOrder> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(CourseOrder::getId, courseOrder.getId())
.set(CourseOrder::getOrderStatus, OrderStatusConstant.SUCCESS);
courseOrderService.update(updateWrapper);
// 修改当前课程下单人数
Long courseId = courseOrder.getCourseId();
Course course = courseService.getById(courseId);
if (course != null) {
course.setOrderCount(course.getOrderCount() + 1);
courseService.updateById(course);
}
System.out.println("---------------------------微信支付回调(结束)-------------------------------");
return true;
}
/**
* 部分退款申请
*/
@Override
public Refund refundPartPayment(String orderId, BigDecimal refundAmount) {
// 获取订单
CourseOrder courseOrder = courseOrderService.getById(orderId);
ThrowUtils.throwIf(courseOrder == null, ErrorCode.OPERATION_ERROR, "订单不存在");
// 判断该订单是否已经退款
ThrowUtils.throwIf(courseOrder.getOrderStatus().equals(OrderStatusConstant.REFUNDED), ErrorCode.OPERATION_ERROR, "订单已退款");
String orderNumber = courseOrder.getOrderNumber();
// 退款请求
CreateRequest createRequest = new CreateRequest();
// 商户订单号
createRequest.setOutTradeNo(orderNumber);
// 商户退款单号
String outRefundNo = RefundUtils.generateRefundNo();
createRequest.setOutRefundNo(outRefundNo);
// 退款结果回调
createRequest.setNotifyUrl(wxPayConfig.getNotifyUrl() + "/wxPay/refund/part/callback");
// 退款金额
AmountReq amountReq = new AmountReq();
amountReq.setRefund(refundAmount.movePointRight(2).longValue());
amountReq.setTotal(courseOrder.getTotalAmount().movePointRight(2).longValue());
amountReq.setCurrency("CNY");
createRequest.setAmount(amountReq);
//TODO 生成退款记录
// 申请退款
System.out.println("退款请求:" + createRequest);
Refund refund = wxPayConfig.getRefundService().create(createRequest);
System.out.println("退款申请结果:" + refund);
return refund;
}
/**
* 部分退款回调
*/
@Override
public boolean refundPartCallback(RefundNotification refundNotification) {
System.out.println("---------------------------微信退款回调(开始)-------------------------------");
// 获取订单信息
String orderNumber = refundNotification.getOutTradeNo();
LambdaQueryWrapper<CourseOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CourseOrder::getOrderNumber, orderNumber);
CourseOrder courseOrder = courseOrderService.getOne(queryWrapper);
// 修改订单状态
LambdaUpdateWrapper<CourseOrder> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(CourseOrder::getId, courseOrder.getId())
.set(CourseOrder::getOrderStatus, OrderStatusConstant.REFUNDED);
courseOrderService.update(updateWrapper);
// 修改课程下单人数
Long courseId = courseOrder.getCourseId();
Course course = courseService.getById(courseId);
if (course != null) {
course.setOrderCount(course.getOrderCount() - 1);
courseService.updateById(course);
}
System.out.println("---------------------------微信退款回调(结束)-------------------------------");
return true;
}
/**
* 获取支付回调信息
*/
@Override
public Transaction getTransactionInfo(HttpServletRequest request) {
NotificationParser notificationParser = getNotificationParser(request);
return notificationParser.parse(requestParam, Transaction.class);
}
/**
* 获取退款回调信息
*/
@Override
public RefundNotification getRefundInfo(HttpServletRequest request) {
NotificationParser notificationParser = getNotificationParser(request);
return notificationParser.parse(requestParam, RefundNotification.class);
}
/**
* 根据微信官方发送的请求获取信息
*/
@SneakyThrows
public NotificationParser getNotificationParser(HttpServletRequest request) {
System.out.println("---------------------------获取信息-------------------------------");
// 获取RSA配置
NotificationParser notificationParser = new NotificationParser(wxPayConfig.getRSAConfig());
// 构建请求
StringBuilder bodyBuilder = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
bodyBuilder.append(line);
}
String body = bodyBuilder.toString();
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String singType = request.getHeader("Wechatpay-Signature-Type");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(singType)
.body(body)
.build();
System.out.println(requestParam.toString());
System.out.println("---------------------------信息获取完毕-------------------------------");
return notificationParser;
}
}

View File

@ -0,0 +1,59 @@
package com.greenorange.promotion.utils;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Data
@NoArgsConstructor
public class MultiDelayMessage<T> implements Serializable {
/**
* 消息体
*/
private T data;
/**
* 记录延时时间的集合
*/
private List<Long> delayMillis;
public MultiDelayMessage(T data, List<Long> delayMillis) {
this.data = data;
this.delayMillis = delayMillis;
}
public MultiDelayMessage(T data, Long...delayMillis) {
this.data = data;
this.delayMillis = new ArrayList<>(Arrays.asList(delayMillis));
}
/**
* 获取并移除下一个延迟时间
* @return 集合中第一个延迟时间
*/
public Long removeNextDelay() {
return delayMillis.remove(0);
}
/**
* 是否有下一个延迟时间
*/
public boolean hasNextDelay() {
return !delayMillis.isEmpty();
}
@Serial
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,21 @@
package com.greenorange.promotion.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class RefundUtils {
// 生成唯一的全额退款单号,格式为 yyyyMMddHHmmssSSS + 随机数
public static String generateRefundNo() {
// 获取当前时间的时间戳
String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
// 生成一个 4 位随机数,保证每次退款单号不同
int randomNum = new Random().nextInt(9000) + 1000; // 生成1000到9999之间的随机数
// 拼接退款单号
return timestamp + randomNum;
}
}