|
@@ -784,15 +784,14 @@ public Boolean queryPayStatus(String orderNo) {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 下单或充值微信支付成功后,收到来自微信支付结果通知
|
|
|
- *
|
|
|
+ * 提供给微信支付调用:用户微信付款成功后,微信通知商户支付结果
|
|
|
+ * @param request
|
|
|
* @return {code:"SUCCESS",message:"成功"}
|
|
|
*/
|
|
|
-@Operation(summary = "下单或充值微信支付成功后,收到来自微信支付结果通知")
|
|
|
+@Operation(summary = "提供给微信支付调用:用户微信付款成功后,微信通知商户支付结果")
|
|
|
@PostMapping("/wxPay/notify")
|
|
|
-public Result<Map<String, String>> wxPaySuccessNotify(HttpServletRequest request) {
|
|
|
- Map<String, String> map = wxPayService.wxPaySuccessNotify(request);
|
|
|
- return Result.ok(map);
|
|
|
+public Map<String, String> notifyPayResult(HttpServletRequest request) {
|
|
|
+ return wxPayService.notifyPayResult(request);
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -800,11 +799,11 @@ public Result<Map<String, String>> wxPaySuccessNotify(HttpServletRequest request
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 下单或充值微信支付成功后,收到来自微信支付结果通知
|
|
|
- *
|
|
|
+ * 提供给微信支付调用:用户微信付款成功后,微信通知商户支付结果
|
|
|
+ * @param request
|
|
|
* @return {code:"SUCCESS",message:"成功"}
|
|
|
*/
|
|
|
-Map<String, String> wxPaySuccessNotify(HttpServletRequest request);
|
|
|
+Map<String, String> notifyPayResult(HttpServletRequest request);
|
|
|
```
|
|
|
|
|
|
**WxPayServiceImpl实现类**:
|
|
@@ -814,14 +813,14 @@ Map<String, String> wxPaySuccessNotify(HttpServletRequest request);
|
|
|
private RedisTemplate redisTemplate;
|
|
|
|
|
|
/**
|
|
|
- * 下单或充值微信支付成功后,收到来自微信支付结果通知
|
|
|
+ * 提供给微信支付调用:用户微信付款成功后,微信通知商户支付结果
|
|
|
*
|
|
|
- * @return {code:"SUCCESS",message:"成功"}
|
|
|
+ * @param request
|
|
|
+ * @return
|
|
|
*/
|
|
|
@Override
|
|
|
-@GlobalTransactional(rollbackFor = Exception.class)
|
|
|
-public Map<String, String> wxPaySuccessNotify(HttpServletRequest request) {
|
|
|
- //1.商户验证签名,以确认请求来自微信,而不是其他的第三方,杜绝“假通知”出现;得到支付信息
|
|
|
+public Map<String, String> notifyPayResult(HttpServletRequest request) {
|
|
|
+ //1.验签-避免出现虚假通知或支付结果在网络传输中被恶意篡改
|
|
|
//1.1 从请求头中获取封装请求参数对象数据
|
|
|
String signature = request.getHeader("Wechatpay-Signature");
|
|
|
String serial = request.getHeader("Wechatpay-Serial");
|
|
@@ -829,45 +828,48 @@ public Map<String, String> wxPaySuccessNotify(HttpServletRequest request) {
|
|
|
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
String signaureType = request.getHeader("Wechatpay-Signature-Type");
|
|
|
log.info("签名:{},序列号:{},随机数:{},时间戳:{},签名类型:{}", signature, serial, nonce, timestamp, signaureType);
|
|
|
- //1.2 获取请求体中所有业务数据(被加密后)
|
|
|
- String requestBody = PayUtil.readData(request);
|
|
|
- //1.3 构造 RequestParam请求参数对象
|
|
|
+ //1.2 获取请求体中参数
|
|
|
+ String body = PayUtil.readData(request);
|
|
|
+ log.info("请求体:{}", body);
|
|
|
+ //2.参数解密-解析获取请求体中业务参数(支付结果业务数据)
|
|
|
+ //2.1 构造RequestParam
|
|
|
RequestParam requestParam = new RequestParam.Builder()
|
|
|
.serialNumber(serial)
|
|
|
.nonce(nonce)
|
|
|
.signature(signature)
|
|
|
.timestamp(timestamp)
|
|
|
- .body(requestBody)
|
|
|
+ .body(body)
|
|
|
.build();
|
|
|
- //1.4 初始化 NotificationParser 通知解析器对象
|
|
|
+ //2.2 初始化 NotificationParser解析器对象
|
|
|
NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
|
|
|
- //1.5 验签、解密并转换成 Transaction
|
|
|
+ //2.3 解密并转换成 Transaction交易对象
|
|
|
Transaction transaction = parser.parse(requestParam, Transaction.class);
|
|
|
|
|
|
- //2.确保幂等性 采用redis提供String set k v ex nx 来实现
|
|
|
- //2.1 获取交易对象中订单编号
|
|
|
- String orderNo = transaction.getOutTradeNo();
|
|
|
- //2.2 尝试存入Redis
|
|
|
- String key = RedisConstant.BUSINESS_PREFIX + orderNo;
|
|
|
- Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 25, TimeUnit.HOURS);
|
|
|
- try {
|
|
|
+ //3.幂等性处理:微信会多次重复发送通知共计时间24h4m
|
|
|
+ if (transaction != null) {
|
|
|
+ //3.1 微信支付系统生成的订单号
|
|
|
+ String transactionId = transaction.getTransactionId();
|
|
|
+ //3.2 采用Redis进行幂等性处理
|
|
|
+ String key = "pay:notify:" + transactionId;
|
|
|
+ Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, transactionId, 25, TimeUnit.HOURS);
|
|
|
if (flag) {
|
|
|
- //3.TODO 先检查对应业务数据:支付状态需要已支付且用户付款金额跟商户侧金额是否一致
|
|
|
- Transaction.TradeStateEnum tradeState = transaction.getTradeState();
|
|
|
- if (Transaction.TradeStateEnum.SUCCESS == tradeState) {
|
|
|
- //3.1 说明用户支付成功,进一步判断商户侧金额跟微信交易对象中支付金额是否一致
|
|
|
- Integer payerTotal = transaction.getAmount().getPayerTotal();
|
|
|
- //3.2 付款金额是一致
|
|
|
- if (payerTotal.intValue() == 1) {
|
|
|
- //4.核心业务处理
|
|
|
- log.info("[支付系统]收到来自微信异步回到,开始处理核心业务");
|
|
|
- paymentInfoService.updatePaymentInfo(transaction);
|
|
|
+ try {
|
|
|
+ //4.校验通知的信息是否与商户侧的信息一致:校验应付金额跟实付金额是否一致
|
|
|
+ if (transaction.getAmount().getTotal().intValue() == 1) {
|
|
|
+ //5.TODO 核心业务:更新本地交易及订单或者充值记录状态-存在分布式事务问题
|
|
|
+ paymentInfoService.handlerPaymentInfo(transaction);
|
|
|
+ //6.返回微信正确应答
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
+ map.put("code", "SUCCESS");
|
|
|
+ map.put("message", "成功");
|
|
|
+ return map;
|
|
|
}
|
|
|
+ } catch (Exception e) {
|
|
|
+ //如果业务发生异常无法正确应答微信,删除key,等待微信下次通知
|
|
|
+ redisTemplate.delete(key);
|
|
|
+ throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- redisTemplate.delete(key);
|
|
|
- throw new RuntimeException(e);
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
@@ -879,55 +881,57 @@ public Map<String, String> wxPaySuccessNotify(HttpServletRequest request) {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 处理用户付款成功后两条业务分之后续业务完善(1.订单 2.充值)
|
|
|
+ * 用户付款成功后,更新本地交易记录及关联的订单或充值相关业务处理
|
|
|
* @param transaction
|
|
|
*/
|
|
|
-void updatePaymentInfo(Transaction transaction);
|
|
|
+void handlerPaymentInfo(Transaction transaction);
|
|
|
```
|
|
|
|
|
|
**PaymentInfoServiceImpl**
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 处理用户付款成功后两条业务分之后续业务完善(1.订单 2.充值)
|
|
|
+ * 用户付款成功后,更新本地交易记录及关联的订单或充值相关业务处理
|
|
|
*
|
|
|
- * @param transaction 微信支付交易对象
|
|
|
+ * @param transaction
|
|
|
*/
|
|
|
@Override
|
|
|
-public void updatePaymentInfo(Transaction transaction) {
|
|
|
- //1.判断并修改本地交易记录状态:已支付 及更新回调信息
|
|
|
- //1.1 查询本地交易记录状态 判断是否为未支付
|
|
|
+@GlobalTransactional(rollbackFor = Exception.class)
|
|
|
+public void handlerPaymentInfo(Transaction transaction) {
|
|
|
+ //1.核心业务1:更新本地交易-支付状态、回调内容、回调时间、微信端交易编号
|
|
|
+ //1.1 获取交易对应商户订单编号
|
|
|
String orderNo = transaction.getOutTradeNo();
|
|
|
- LambdaQueryWrapper<PaymentInfo> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(PaymentInfo::getOrderNo, orderNo);
|
|
|
- PaymentInfo paymentInfo = this.getOne(queryWrapper);
|
|
|
- if (SystemConstant.PAYMENT_STATUS_UNPAID.equals(paymentInfo.getPaymentStatus())) {
|
|
|
- //1.2 更新支付状态、回调时间及内容、微信端交易号
|
|
|
+ //1.2 根据订单编号查询本地交易记录
|
|
|
+ PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, orderNo));
|
|
|
+ if (paymentInfo != null && SystemConstant.PAYMENT_STATUS_UNPAID.equals(paymentInfo.getPaymentStatus())) {
|
|
|
+ //1.3 获取到微信交易订单编号
|
|
|
String transactionId = transaction.getTransactionId();
|
|
|
+ paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_PAID);
|
|
|
paymentInfo.setOutTradeNo(transactionId);
|
|
|
paymentInfo.setCallbackTime(new Date());
|
|
|
paymentInfo.setCallbackContent(transaction.toString());
|
|
|
- paymentInfo.setPaymentStatus(SystemConstant.PAYMENT_STATUS_PAID);
|
|
|
- this.updateById(paymentInfo);
|
|
|
+ paymentInfoMapper.updateById(paymentInfo);
|
|
|
|
|
|
- //2.处理交易类型为:订单 业务线
|
|
|
+ //支付类型:1301-订单 1302-充值
|
|
|
String paymentType = paymentInfo.getPaymentType();
|
|
|
+ //2.核心业务2-如果交易类型是订单,远程调用订单服务更新订单并且完成虚拟物品发货
|
|
|
if (SystemConstant.PAYMENT_TYPE_ORDER.equals(paymentType)) {
|
|
|
- //2.1 远程调用订单服务修改订单支付状态包含虚拟物品发货
|
|
|
+ //2.1 远程调用订单服务修改订单状态:已支付
|
|
|
Result result = orderFeignClient.orderPaySuccess(orderNo);
|
|
|
- if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())) {
|
|
|
- throw new GuiguException(500, "修改订单状态异常");
|
|
|
+ if (!result.getCode().equals(200)) {
|
|
|
+ throw new RuntimeException("远程调用-更新订单状态异常");
|
|
|
}
|
|
|
-
|
|
|
- } else if (SystemConstant.PAYMENT_TYPE_RECHARGE.equals(paymentType)) {
|
|
|
- //3.处理交易类型为:充值 业务线
|
|
|
- //3.1 远程调用账户服务完成充值业务
|
|
|
- Result result = accountFeignClient.rechargePaySuccess(orderNo);
|
|
|
+ }
|
|
|
+ //3.TODO 核心业务3-如果交易类型是充值,远程调用账户服务完成充值业务
|
|
|
+ if (SystemConstant.PAYMENT_TYPE_RECHARGE.equals(paymentType)) {
|
|
|
+ Result result = accountFeignClient.rechargePaySuccess(orderNo);
|
|
|
if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())) {
|
|
|
throw new GuiguException(500, "充值异常");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -939,12 +943,11 @@ public void updatePaymentInfo(Transaction transaction) {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 当用户微信支付成功后,修改订单支付状态且完成虚拟物品发货
|
|
|
- * 业务源头发起为微信支付,不需要登录
|
|
|
+ * 用户付款成功后,处理订单相关业务
|
|
|
* @param orderNo
|
|
|
* @return
|
|
|
*/
|
|
|
-@Operation(summary = "当用户微信支付成功后,修改订单支付状态且完成虚拟物品发货")
|
|
|
+@Operation(summary = "用户付款成功后,处理订单相关业务")
|
|
|
@GetMapping("/orderInfo/orderPaySuccess/{orderNo}")
|
|
|
public Result orderPaySuccess(@PathVariable String orderNo){
|
|
|
orderInfoService.orderPaySuccess(orderNo);
|
|
@@ -956,8 +959,7 @@ public Result orderPaySuccess(@PathVariable String orderNo){
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 当用户微信支付成功后,修改订单支付状态且完成虚拟物品发货
|
|
|
- * 业务源头发起为微信支付,不需要登录
|
|
|
+ * 用户付款成功后,处理订单相关业务
|
|
|
* @param orderNo
|
|
|
* @return
|
|
|
*/
|
|
@@ -968,44 +970,35 @@ void orderPaySuccess(String orderNo);
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 用户微信支付成功后,修改订单支付状态且完成虚拟物品发货
|
|
|
+ * 用户付款成功后,处理订单相关业务
|
|
|
*
|
|
|
* @param orderNo
|
|
|
+ * @return
|
|
|
*/
|
|
|
@Override
|
|
|
-@Transactional(rollbackFor = Exception.class)
|
|
|
public void orderPaySuccess(String orderNo) {
|
|
|
- //1.更新订单状态:已支付
|
|
|
- LambdaUpdateWrapper<OrderInfo> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
- //设置修改条件 where 部分
|
|
|
- updateWrapper.eq(OrderInfo::getOrderNo, orderNo);
|
|
|
- //设置更新字段 set 部分
|
|
|
- updateWrapper.set(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_PAID);
|
|
|
- this.update(updateWrapper);
|
|
|
-
|
|
|
- //2.远程调用用户服务-虚拟物品发货 订单中可以获取到购买商品ID
|
|
|
- //2.1 构建发货VO对象
|
|
|
- //2.1.1 查询订单信息
|
|
|
- LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(OrderInfo::getOrderNo, orderNo);
|
|
|
- OrderInfo orderInfo = this.getOne(queryWrapper);
|
|
|
+ //1.修改订单状态改为已支付
|
|
|
+ OrderInfo orderInfo = this.getOrderInfo(orderNo);
|
|
|
+ orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
|
|
|
+ orderInfoMapper.updateById(orderInfo);
|
|
|
+
|
|
|
+ //2.远程调用用户服务完成虚拟物品发货
|
|
|
+ //2.1 构建虚拟物品发货VO对象
|
|
|
UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
|
|
|
- userPaidRecordVo.setOrderNo(orderNo);
|
|
|
+ userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
|
|
|
userPaidRecordVo.setUserId(orderInfo.getUserId());
|
|
|
+ //2.1.1 购买项目类型:付款项目类型: 1001-专辑 1002-声音 1003-vip会员'
|
|
|
userPaidRecordVo.setItemType(orderInfo.getItemType());
|
|
|
- //2.1.2 根据订单ID查询订单明细获取购买项目ID列表
|
|
|
- LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
- orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId, orderInfo.getId());
|
|
|
- orderDetailLambdaQueryWrapper.select(OrderDetail::getItemId);
|
|
|
- List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailLambdaQueryWrapper);
|
|
|
- List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
|
|
|
- userPaidRecordVo.setItemIdList(itemIdList);
|
|
|
- //2.2 远程调用用户服务虚拟物品发货
|
|
|
- Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
|
|
|
-
|
|
|
- //2.3 判断远程调用结果对象业务状态码是否为200
|
|
|
- if (ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())) {
|
|
|
- throw new GuiguException(500, "虚拟物品发货异常");
|
|
|
+ //2.1.2 从订单中获取订单明细得到购买项目ID列表
|
|
|
+ List<OrderDetail> orderInfoOrderDetailList = orderInfo.getOrderDetailList();
|
|
|
+ if (CollectionUtil.isNotEmpty(orderInfoOrderDetailList)) {
|
|
|
+ List<Long> itemIdList = orderInfoOrderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
|
|
|
+ userPaidRecordVo.setItemIdList(itemIdList);
|
|
|
+ //2.2 判断业务状态码
|
|
|
+ Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
|
|
|
+ if (!result.getCode().equals(200)) {
|
|
|
+ throw new RuntimeException("远程调用-虚拟物品发货异常");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
```
|
|
@@ -1014,8 +1007,7 @@ public void orderPaySuccess(String orderNo) {
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
- * 当用户微信支付成功后,修改订单支付状态且完成虚拟物品发货
|
|
|
- * 业务源头发起为微信支付,不需要登录
|
|
|
+ * 用户付款成功后,处理订单相关业务
|
|
|
* @param orderNo
|
|
|
* @return
|
|
|
*/
|
|
@@ -1028,7 +1020,7 @@ public Result orderPaySuccess(@PathVariable String orderNo);
|
|
|
```java
|
|
|
@Override
|
|
|
public Result orderPaySuccess(String orderNo) {
|
|
|
- log.error("[订单服务]提供远程调用方法orderPaySuccess执行服务降级");
|
|
|
+ log.error("[订单服务]提供远程调用orderPaySuccess方法执行服务降级");
|
|
|
return null;
|
|
|
}
|
|
|
```
|