记录余额支付后的订单状态更新、购买记录策略模式改造,以及微信支付发起、状态查询与回调处理。

本篇要点

  • 处理余额支付后的订单状态
  • 用策略模式整理购买记录
  • 梳理微信支付请求与回调

**学习提示:**微信支付相关代码和测试片段属于学习记录,不代表已经通过真实商户环境的端到端验收。

内容回顾

1、提交订单接口

  • 检查和扣减余额

  • 保存订单数据

  • 延迟消息(订单超时未支付自动取消)

– RabbitMQ的TTL+死信队列

– RabbitMQ延迟插件

– Redisson实现

  • 保存购买记录

今天内容

0、订单模块总结

第一,订单模块流程(图)

第二,细节或者技术等

  • 进行重复提交判断
  • 进行数据加密防止篡改
  • 使用策略模式对添加购买记录优化

1、余额支付完成修改订单状态

  • 使用余额支付成功之后,修改订单状态变成已经支付
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//根据订单编号修改订单状态
public void orderPaySuccess(String orderNo) {
//根据订单编号查询订单对象
OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
//订单状态是未支付修改
if (orderInfo.getOrderStatus().equals("0901")) {
//订单对象设置订单状态值 0902
//SystemConstant.ORDER_STATUS_PAID
orderInfo.setOrderStatus("0902");

//调用方法进行修改
orderInfoMapper.updateById(orderInfo);
}
}

//根据订单编号查询订单对象
public OrderInfo getOrderInfoByOrderNo(String orderNo) {
LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderInfo::getOrderNo, orderNo);
OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

//根据订单id查询明细数据
Long orderId = orderInfo.getId();
LambdaQueryWrapper<OrderDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderDetail::getOrderId, orderId);
List<OrderDetail> orderDetails = orderDetailMapper.selectList(wrapper);

//设置到订单对象里面
orderInfo.setOrderDetailList(orderDetails);

return orderInfo;
}

2、优化保存购买记录代码

设计模式

  • 软件设计模式(Design pattern)是一套被反复使用、经过分类编目的代码设计经验总结

  • 设计模式提升代码可重用性、可理解性与可靠性。

  • 有很多常见设计模型:单例模型、工厂模式、策略模式等

策略模式概念

  • 官方定义:它是将定义的算法家族分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户

  • 策略模式使用的就是面向对象的多态机制,从而实现同一行为在不同场景下具备不同实现。避免使用多重条件选择语句(if else),充分体现面向对象设计思想

  • 举例:添加购买记录

– 购买专辑、vip和声音都是添加购买记录,因为购买内容不同操作逻辑不同的

image-20251108093451786

策略模式实现

策略模式的主要角色如下:

  • 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
  • 环境(Context)类:用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略、算法的直接访问,封装可能存在的变化。

第一步 创建接口,定义保存购买记录方法

1
2
3
4
5
6
//策略接口
public interface ItemTypeStrategy {

//定义添加购买记录方法
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo);
}

第二步 创建多个策略类(专辑、vip、声音)实现接口方法

image-20251108095300555

  • 购买vip实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//购买vip实现类
@Component
public class VipItemTypeStrategyImpl implements ItemTypeStrategy {

@Autowired
private UserInfoMapper userInfoMapper;


@Autowired
private VipServiceConfigMapper vipServiceConfigMapper;


@Autowired
private UserVipServiceMapper userVipServiceMapper;

//添加vip购买记录
@Override
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
//3.判断购买项目类型-处理VIP会员-允许多次购买
//3.1 新增VIP购买记录
UserVipService userVipService = new UserVipService();
//3.1.1 根据VIP套餐ID查询套餐信息-得到VIP会员服务月数
Long vipConfigId = userPaidRecordVo.getItemIdList().get(0);
VipServiceConfig vipServiceConfig =
vipServiceConfigMapper.selectById(vipConfigId);
//3.1.2 获取用户身份,如果是VIP会员,则续费
UserInfo userInfo =
userInfoMapper.selectById(userPaidRecordVo.getUserId());

// 当前系统时间
Date currentTime = new Date();
if (userInfo.getIsVip().intValue() == 1
&& userInfo.getVipExpireTime().after(new Date())) {
// 设置vip 续费的起始时间
currentTime = userInfo.getVipExpireTime();
}
currentTime =
new LocalDateTime(currentTime)
.plusMonths(vipServiceConfig.getServiceMonth()).toDate();
// 设置过期时间
userVipService.setExpireTime(currentTime);
userVipService.setStartTime(new Date());
//3.1.4 构建VIP购买记录对象保存
userVipService.setUserId(userPaidRecordVo.getUserId());
userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
userVipServiceMapper.insert(userVipService);

//3.2 更新用户表中VIP状态及会员过期时间
userInfo.setIsVip(1);
userInfo.setVipExpireTime(userVipService.getExpireTime());
userInfoMapper.updateById(userInfo);
}
}
  • 购买专辑实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//购买专辑实现类
@Component
public class AlbumItemTypeStrategyImpl implements ItemTypeStrategy {

@Autowired
private UserPaidAlbumMapper userPaidAlbumMapper;

//添加专辑购买记录
@Override
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
//1.1 根据订单编号查询专辑购买记录
LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo());
Long count = userPaidAlbumMapper.selectCount(userPaidAlbumLambdaQueryWrapper);
if (count > 0) {
return;
}
//1.2 查询到专辑购买记录为空则新增购买记录
UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
userPaidAlbumMapper.insert(userPaidAlbum);
}
}
  • 购买声音实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//购买声音实现类
@Component
public class TrackItemTypeStrategyImpl implements ItemTypeStrategy {

@Autowired
private UserPaidTrackMapper userPaidTrackMapper;

@Autowired
private TrackInfoFeignClient trackInfoFeignClient;

//添加声音购买记录
@Override
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
//2.判断购买项目类型-处理声音
//2.1 根据订单编号查询声音购买记录
LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo());
Long count = userPaidTrackMapper.selectCount(userPaidTrackLambdaQueryWrapper);
if (count > 0) {
return;
}
//2.2 查询到声音购买记录为空则新增购买记录(循环批量新增)
//2.2.1 远程调用专辑服务-根据声音ID查询声音对象-获取声音所属专辑ID
TrackInfo trackInfo =
trackInfoFeignClient.getTrackInfo(
userPaidRecordVo.getItemIdList().get(0)).getData();
Long albumId = trackInfo.getAlbumId();

//2.2.2 遍历购买项目ID集合批量新增声音购买记录
userPaidRecordVo.getItemIdList().forEach(trackId -> {
UserPaidTrack userPaidTrack = new UserPaidTrack();
userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
userPaidTrack.setUserId(userPaidRecordVo.getUserId());
userPaidTrack.setAlbumId(albumId);
userPaidTrack.setTrackId(trackId);
userPaidTrackMapper.insert(userPaidTrack);
});
}
}

第三步 创建环境类,根据购买类型切换使用不同实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class StrategyFactory {

// 1001-专辑 1002-声音 1003-vip会员
// <bean id="albumItemTypeStrategyImpl"
// class="com.atguigu.tingshu.user.AlbumItemTypeStrategyImpl"/>

// Map<key,value>: key对象id值 value:对象

// * 将IOC容器中ItemTypeStrategy接口下所有实现类对象注入到Map中
// * Map中Key:Bean对象ID
// * Map中Value:实现类对象
// * {"1001":购买类型为专辑策略类对象}
// * {"1002":购买类型为声音策略类对象}
// * {"1003":购买类型为vip策略类对象}
@Autowired
private Map<String,ItemTypeStrategy> itemTypeStrategy;

//根据不同类型 返回不同对象的方法
public ItemTypeStrategy getStrategy(String itemType) {

if (itemTypeStrategy.containsKey(itemType)) {
return itemTypeStrategy.get(itemType);
}
throw new GuiguException(500, "该策略" + itemType + "实现类不存在");
}
}

第四步 使用策略模式修改调用地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//使用策略模式优化:添加购买记录
@Autowired
private StrategyFactory strategyFactory;
@Override
public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
//// 1001-专辑 1002-声音 1003-vip会员
String itemType = userPaidRecordVo.getItemType();

//1 根据购买类型,获取不同的实现类
ItemTypeStrategy strategy = strategyFactory.getStrategy(itemType);

//2 调用不同实现类里面添加购买记录方法
strategy.savePaidRecord(userPaidRecordVo);
}

3、订单其他接口

根据订单编号查询订单数据

1
2
3
4
5
6
7
@GuiguLogin
@Operation(summary = "根据订单号获取订单信息")
@GetMapping("getOrderInfo/{orderNo}")
public Result<OrderInfo> getOrderInfo(@PathVariable String orderNo) {
OrderInfo orderInfo = orderInfoService.getOrderInfoByOrderNo(orderNo);
return Result.ok(orderInfo);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//根据订单编号查询订单对象
public OrderInfo getOrderInfoByOrderNo(String orderNo) {
LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderInfo::getOrderNo, orderNo);
OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

//根据订单id查询明细数据
Long orderId = orderInfo.getId();
LambdaQueryWrapper<OrderDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderDetail::getOrderId, orderId);
List<OrderDetail> orderDetails = orderDetailMapper.selectList(wrapper);

//设置到订单对象里面
orderInfo.setOrderDetailList(orderDetails);

orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));

return orderInfo;
}

查询当前用户所有订单

1
2
3
4
5
6
7
8
9
10
11
12
13
@GuiguLogin
@Operation(summary = "获取用户订单")
@GetMapping("findUserPage/{page}/{limit}")
public Result index(@PathVariable Long page,
@PathVariable Long limit) {
// 获取到用户Id
Long userId = AuthContextHolder.getUserId();
Page<OrderInfo> pageParam = new Page<>(page, limit);
// 调用服务层方法
IPage<OrderInfo> pageModel = orderInfoService
.findUserPage(pageParam, userId);
return Result.ok(pageModel);
}
1
2
3
4
5
6
7
8
9
10
11
@Override
public IPage<OrderInfo> findUserPage(Page<OrderInfo> pageParam, Long userId) {
// 调用mapper 层方法
IPage<OrderInfo> infoIPage = orderInfoMapper.selectUserPage(pageParam,userId);
infoIPage.getRecords().forEach(item->{
// 设置状态名
item.setOrderStatusName(getOrderStatusName(item.getOrderStatus()));
item.setPayWayName(getPayWayName(item.getPayWay()));
});
return infoIPage;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.atguigu.tingshu.order.mapper.OrderInfoMapper">

<resultMap id="orderInfoMap" type="com.atguigu.tingshu.model.order.OrderInfo" autoMapping="true">
<id property="id" column="id"></id>
<collection property="orderDetailList"
ofType="com.atguigu.tingshu.model.order.OrderDetail" autoMapping="true">
<id property="id" column="detail_id"></id>
</collection>
</resultMap>
<!-- 用于select查询公用抽取的列 -->
<sql id="columns">
oi.id ,
oi.user_id,
oi.order_title,
oi.order_no,
oi.order_status,
oi.original_amount,
oi.derate_amount,
oi.order_amount,
oi.item_type,
oi.pay_way,
od.id detail_id,
od.item_id,
od.item_name,
od.item_url,
od.item_price
</sql>

<select id="selectUserPage" resultMap="orderInfoMap">
select
<include refid="columns"></include>
from order_info oi inner join order_detail od on oi.id = od.order_id
where oi.user_id = #{userId}
order by oi.id desc
</select>
</mapper>

4、微信支付

4.1 发起微信支付请求接口

  • 购买vip 或者 购买专辑,可以使用微信支付。但是购买声音只能余额!
  • 点击微信执行,首先创建未支付订单,之后调用接口发起微信支付请求
  • 发起微信支付请求接口传递两个参数:订单编号 和 支付种类(购买或者充值)
  • 1301 购买种类

image-20251108140519460

WxPayApiController

image-20251108142006291

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Tag(name = "微信支付接口")
@RestController
@RequestMapping("api/payment/wxPay")
@Slf4j
public class WxPayApiController {

@Autowired
private WxPayService wxPayService;

/**
* 微信支付
* @param paymentType 支付种类:购买 充值
* @param orderNo 订单编号
* @return
*/
@GuiguLogin
@Operation(summary = "微信下单")
@PostMapping("/createJsapi/{paymentType}/{orderNo}")
public Result createJsapi(@PathVariable String paymentType,
@PathVariable String orderNo) {
Long userId = AuthContextHolder.getUserId();
Map map = wxPayService.createJsapi(paymentType,orderNo,userId);
return Result.ok(map);
}
}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

@Autowired
private PaymentInfoService paymentInfoService;

@Autowired
private OrderInfoFeignClient orderInfoFeignClient;

@Autowired
private RSAAutoCertificateConfig rsaAutoCertificateConfig;

@Autowired
private WxPayV3Config wxPayV3Config;

@Autowired
private UserInfoFeignClient userInfoFeignClient;
//发起微信支付请求
@Override
public Map createJsapi(String paymentType, String orderNo, Long userId) {
//1 如果paymentType支付种类是购买,判断订单不能取消,如果订单状态是取消不支付
//// 支付类型 1301-订单 1302-充值
if(!paymentType.equals(SystemConstant.PAYMENT_TYPE_RECHARGE)) {
//根据订单编号获取订单信息
Result<OrderInfo> orderInfoResult = orderInfoFeignClient.getOrderInfo(orderNo);
OrderInfo orderInfo = orderInfoResult.getData();
Assert.notNull(orderInfo,"订单为空");
//订单状态
String orderStatus = orderInfo.getOrderStatus();
//如果订单取消状态,不需要支付
//订单状态 0901-正常 0902-已支付 0903-已取消
if (SystemConstant.ORDER_STATUS_CANCEL.equals(orderStatus)){
// 此时订单已取消
return null;
}
}

//2 添加支付记录到支付记录表(支付记录状态 - 未支付)
PaymentInfo paymentInfo =
paymentInfoService.savePaymentInfo(paymentType, orderNo);

//3 发起微信支付请求
// JsapiServiceExtension对象
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig).build();
//创建request对象,设置微信支付需要参数
PrepayRequest request = new PrepayRequest();
// 注意在这个包下 com.wechat.pay.java.service.payments.jsapi.model.Amount
//TODO 支付金额 支付0.01
Amount amount = new Amount();
amount.setTotal(1);
request.setAmount(amount);

//公众号id 商户号等信息
request.setAppid(wxPayV3Config.getAppid());
request.setMchid(wxPayV3Config.getMerchantId());
//微信显示信息
request.setDescription(paymentInfo.getContent());
//微信回调地址
request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
//订单编号
request.setOutTradeNo(paymentInfo.getOrderNo());
// 获取用户信息
Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(paymentInfo.getUserId());
Assert.notNull(userInfoVoResult,"返回用户结果集对象不能为空");
UserInfoVo userInfoVo = userInfoVoResult.getData();
Assert.notNull(userInfoVo,"用户对象不能为空");
String openid = userInfoVo.getWxOpenId();
Payer payer = new Payer();
payer.setOpenid(openid);
request.setPayer(payer);

//调用方法,传递request对象,发起微信支付请求
PrepayWithRequestPaymentResponse response =
service.prepayWithRequestPayment(request);
Map<String, Object> result = new HashMap();
result.put("timeStamp", response.getTimeStamp());
result.put("nonceStr", response.getNonceStr());
result.put("package", response.getPackageVal());
result.put("signType", response.getSignType());
result.put("paySign", response.getPaySign());
// 返回数据
return result;
}
}

保存支付记录方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Service
@SuppressWarnings({"all"})
public class PaymentInfoServiceImpl extends ServiceImpl<PaymentInfoMapper, PaymentInfo> implements PaymentInfoService {

@Autowired
private OrderInfoFeignClient orderInfoFeignClient;
@Autowired
private RechargeInfoFeignClient rechargeInfoFeignClient;

@Autowired
private PaymentInfoMapper paymentInfoMapper;

//保存支付记录
@Override
public PaymentInfo savePaymentInfo(String paymentType, String orderNo) {
//1 根据订单编号,查询支付记录表
LambdaQueryWrapper<PaymentInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PaymentInfo::getOrderNo,orderNo);
PaymentInfo paymentInfo = paymentInfoMapper.selectOne(wrapper);

//2 判断如果没有查到数据,第一次添加,如果查询到直接返回
if(paymentInfo == null) {
paymentInfo = new PaymentInfo();
//判断支付种类paymentType ,购买订单 或者 充值
//1301-订单
if(paymentType.equals(SystemConstant.PAYMENT_TYPE_ORDER)) {
// 远程调用获取到订单对象
Result<OrderInfo> orderInfoResult = orderInfoFeignClient.getOrderInfo(orderNo);
Assert.notNull(orderInfoResult,"返回订单对象不能为空");
OrderInfo orderInfo = orderInfoResult.getData();
Assert.notNull(orderInfo,"返回订单对象不能为空");
//支付金额
paymentInfo.setAmount(orderInfo.getOrderAmount());
//userId
paymentInfo.setUserId(orderInfo.getUserId());
paymentInfo.setContent(orderInfo.getOrderTitle());
} else { //1302-充值
//TODO
}
paymentInfo.setPaymentType(paymentType);
paymentInfo.setOrderNo(orderNo);
//状态:未支付
paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_UNPAID);
paymentInfoMapper.insert(paymentInfo);
}
return paymentInfo;
}
}

4.2 查询微信支付状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 查询支付状态 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_2.shtml
* @param orderNo
* @return
*/
@Operation(summary = "支付状态查询")
@GetMapping("/queryPayStatus/{orderNo}")
public Result queryPayStatus(@PathVariable String orderNo) {
//调用方法根据订单编号查询支付状态,返回Transaction
Transaction transaction = wxPayService.queryPayStatus(orderNo);
//Transaction对象判断是否成功
// "trade_state" : "SUCCESS",
if(transaction != null &&
transaction.getTradeState()==Transaction.TradeStateEnum.SUCCESS) {
//调用方法更新订单或者支付记录和购买记录
paymentInfoService.updatePaymentStatus(transaction);
return Result.ok(true);
}

return Result.ok(false);
}
  • 根据订单编号查询支付状态
1
2
3
4
5
6
7
8
9
10
11
12
//根据订单编号查询支付状态
@Override
public Transaction queryPayStatus(String orderNo) {
JsapiServiceExtension service = new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig).build();
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
queryRequest.setMchid(wxPayV3Config.getMerchantId());
queryRequest.setOutTradeNo(orderNo);

Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
return transaction;
}
  • 更新支付记录、订单状态、添加购买记录

image-20251108153610767

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//调用方法更新订单或者支付记录和购买记录
@Override
public void updatePaymentStatus(Transaction transaction) {
//获取订单编号
String orderNo = transaction.getOutTradeNo();
PaymentInfo paymentInfo =
this.getOne(new LambdaQueryWrapper<PaymentInfo>()
.eq(PaymentInfo::getOrderNo, orderNo));
if ("1402".equals(paymentInfo.getPaymentStatus())) {
return;
}
//SystemConstant.PAYMENT_STATUS_PAID
paymentInfo.setPaymentStatus("1402"); //已经支付
//订单编号
paymentInfo.setOrderNo(transaction.getOutTradeNo());
paymentInfo.setOutTradeNo(transaction.getTransactionId());
paymentInfo.setCallbackTime(new Date());
paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
this.updateById(paymentInfo);

//发送mq消息orderNo,更新订单状态
//1301-订单 1302-充值
String routing =
paymentInfo.getPaymentType().equals(SystemConstant.PAYMENT_TYPE_ORDER)
? MqConst.ROUTING_ORDER_PAY_SUCCESS
: MqConst.ROUTING_RECHARGE_PAY_SUCCESS;

rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,
routing,
paymentInfo.getOrderNo());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class OrderReceiver {

@Autowired
private OrderInfoService orderInfoService;

@SneakyThrows
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = MqConst.EXCHANGE_ORDER, durable = "true"),
value = @Queue(value = MqConst.QUEUE_ORDER_PAY_SUCCESS, durable = "true"),
key = {MqConst.ROUTING_ORDER_PAY_SUCCESS}
))
public void orderPaySuccess(String orderNo, Message message, Channel channel) {
if(StringUtils.hasText(orderNo)) {
orderInfoService.updateOrderSuccess(orderNo);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//根据订单编号修改订单状态(已经支付),远程调用添加购买记录
@Override
public void updateOrderSuccess(String orderNo) {
//根据订单编号修改订单状态(已经支付)
//根据订单编号查询订单对象
OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
//订单状态是未支付修改
if (orderInfo.getOrderStatus().equals("0901")) {
//订单对象设置订单状态值 0902
//SystemConstant.ORDER_STATUS_PAID
orderInfo.setOrderStatus("0902");

//调用方法进行修改
orderInfoMapper.updateById(orderInfo);
}

//远程调用添加购买记录
UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
userPaidRecordVo.setOrderNo(orderNo);
userPaidRecordVo.setUserId(orderInfo.getUserId());
userPaidRecordVo.setItemType(orderInfo.getItemType());
List<Long> itemIdList =
orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
userPaidRecordVo.setItemIdList(itemIdList);

userInfoFeignClient.savePaidRecord(userPaidRecordVo);
}

4.3 支付成功微信回调通知

  • 我们项目目前实现方式:

发起微信支付请求之后,前端每隔5s调用一次查询支付状态接口,如果支付中,前端继续调用,如果支付成功前端不再调用

  • 还有一种方式:

当微信支付成功之后,微信主动调用提供的接口,在这个接口获取微信传递过来成功信息,收到信息之后返回给微信收到标识。如果不给微信返回,微信继续通知,最多通知15次

image-20251108163303900

微信回调我们通知成功,需要有公网ip或者域名才可以,如果没有公网ip可以使用内网穿透工具

image-20251108164058862

5、测试微信支付方式

第一步 找到支付测试位置

  • 我的

image-20251108164240893

image-20251108164303549

第二步 使用微信支付方式生成订单,不支付

第三步 复制订单编号到支付测试里面

  • 出现错误

image-20251108165006369

第四步 修改订单编号,重新复制过来

image-20251108165058194

第五步 修改nacos微信相关值

image-20251108165227618

  • 注意证书路径
1
2
3
4
5
6
7
8
9
10
11
12
//
@GuiguLogin
@Operation(summary = "微信Native下单")
@Parameters({
@Parameter(name = "paymentType",description = "支付类型:1301-订单 1302-充值",in = ParameterIn.PATH,required = true),
@Parameter(name = "orderNo",description = "订单号",required = true,in = ParameterIn.PATH),
})
@PostMapping("/createNative/{paymentType}/{orderNo}")
public Result<Map<String, Object>> createNative(@PathVariable String paymentType, @PathVariable String orderNo) {
Map<String, Object> map = wxPayService.createNative(paymentType, orderNo, AuthContextHolder.getUserId());
return Result.ok(map);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public Map<String, Object>
createNative(String paymentType, String orderNo, Long userId) {
try {
//保存支付记录
PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo);

// 构建service
NativePayService service =
new NativePayService.Builder().config(rsaAutoCertificateConfig).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
amount.setTotal(1);
request.setAmount(amount);
request.setAppid(wxPayV3Config.getAppid());
request.setMchid(wxPayV3Config.getMerchantId());
request.setDescription(paymentInfo.getContent());
request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
request.setOutTradeNo(paymentInfo.getOrderNo());

// 调用下单方法,得到应答
com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = service.prepay(request);
// 使用微信扫描 code_url 对应的二维码,即可体验Native支付
System.out.println(response.getCodeUrl());

Map result = new HashMap<>();
result.put("codeUrl", response.getCodeUrl());
return result;
} catch (Exception e) {
e.printStackTrace();

}
return null;
}

image-20251108164832360