it_lv 3 weeks ago
parent
commit
12f5d88b20
1 changed files with 93 additions and 101 deletions
  1. 93 101
      第8章 支付.md

+ 93 - 101
第8章 支付.md

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