Explorar el Código

fix 下单统一订单

zoujiajian hace 14 horas
padre
commit
1e5f016564

+ 17 - 5
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -1112,7 +1112,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			orderDon.setType(OrderDon.Type.WeChat);
 			orderDon.setShopId(shopId);
 			orderDon.setIsWebPay(false);
-			this.updateById(orderDon);
+            OrderDon patch = new OrderDon();
+            patch.setType(orderDon.getType());
+            patch.setShopId(orderDon.getShopId());
+            patch.setIsWebPay(orderDon.getIsWebPay());
+            if (!this.update(patch, Wrappers.lambdaQuery(OrderDon.class)
+                    .eq(OrderDon::getId, orderId).eq(OrderDon::getStatus, OrderDon.Status.noPayment))) {
+                throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
+            }
 		}
 		H5JsPayParams params = weChatService.getJsPayParams(
 				weChatConfig.getAppid(),
@@ -1159,7 +1166,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			orderDon.setShopId(shopId);
 			orderDon.setType(OrderDon.Type.WeChat);
 			orderDon.setIsWebPay(true);
-			this.updateById(orderDon);
+            OrderDon patch = new OrderDon();
+            patch.setType(orderDon.getType());
+            patch.setShopId(orderDon.getShopId());
+            patch.setIsWebPay(orderDon.getIsWebPay());
+            if (!this.update(patch, Wrappers.lambdaQuery(OrderDon.class)
+                    .eq(OrderDon::getId, orderId).eq(OrderDon::getStatus, OrderDon.Status.noPayment))) {
+                throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
+            }
 		}
 
 		H5JsPayParams params = weChatService.getJsPayParams(
@@ -1181,12 +1195,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private void validateMultiQuantityPrepay(OrderDon orderDon, OrderDon.Type requestedType,
 			boolean multiQuantity) {
-		if (!multiQuantity) {
-			return;
-		}
 		if (orderDon.getStatus() != OrderDon.Status.noPayment) {
 			throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
 		}
+        if (!multiQuantity) return;
 		if (orderDon.getType() != null && orderDon.getType() != requestedType) {
 			throw BusinessRuntimeException.getInstance("订单支付方式已确定,请勿切换支付方式");
 		}

+ 9 - 4
netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java

@@ -191,7 +191,14 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 		if (!multiQuantity) {
 			orderDon.setType(OrderDon.Type.ALI_PAY);
 			orderDon.setShopId(appId);
-			orderDonMapper.updateById(orderDon);
+            OrderDon patch = new OrderDon();
+            patch.setType(orderDon.getType());
+            patch.setShopId(orderDon.getShopId());
+            patch.setIsWebPay(orderDon.getIsWebPay());
+            if (orderDonMapper.update(patch, Wrappers.lambdaQuery(OrderDon.class)
+                    .eq(OrderDon::getId, orderId).eq(OrderDon::getStatus, OrderDon.Status.noPayment)) != 1) {
+                throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
+            }
 		}
 		String notifyPath = BaseZfbConfig.notifyUrl;
 		Boolean isUserBenefits = re.getIsUserBenefits();
@@ -245,12 +252,10 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 	}
 
 	private void validateMultiQuantityPrepay(OrderDon orderDon, boolean multiQuantity) {
-		if (!multiQuantity) {
-			return;
-		}
 		if (orderDon.getStatus() != OrderDon.Status.noPayment) {
 			throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
 		}
+        if (!multiQuantity) return;
 		if (orderDon.getType() != null && orderDon.getType() != OrderDon.Type.ALI_PAY) {
 			throw BusinessRuntimeException.getInstance("订单支付方式已确定,请勿切换支付方式");
 		}

+ 66 - 26
netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java

@@ -163,7 +163,9 @@ public class OrderController {
     @PostMapping("/post/submit")
     public Result<OrderDon> submit(@Validated @RequestBody OrderPayRequest payRequest, HttpServletResponse response) throws Exception {
         final String methodName = "吊起微信支付";
-        Long userId = StpUserUtil.getUserIdAfterLogin();
+        Long userId = StpUserUtil.getLoginIdAsLong();
+        validateOrderPhone(payRequest, userId);
+        payRequest.setIsNoLogin(false);
         payRequest.setUserId(userId);
         log.info("{}[start] params:{}", methodName, payRequest);
 
@@ -182,26 +184,6 @@ public class OrderController {
 
         OrderDon orderDon = orderDonService.submit(payRequest);
 
-        //无登录订单 预留手机号免登录
-        if (userId == null && payRequest.getPopularizeId() != null && StrUtil.isNotBlank(payRequest.getPhone())) {
-            User user = userMapper.selectById(orderDon.getUserId());
-            //写回登陆信息
-            StpUserUtil.login(user.getId());
-            response.addHeader("satoken-user", StpUserUtil.getTokenValue());
-
-            Cookie cookie = new Cookie("satoken-user", StpUserUtil.getTokenValue());
-            cookie.setMaxAge(31536000);
-            cookie.setPath("/");
-            response.addCookie(cookie);
-            JSONObject jsonObject = new JSONObject();
-            jsonObject.putOpt("showId", user.getShowId());
-            jsonObject.putOpt("nickName", user.getNickname());
-
-            Cookie userInfo = new Cookie("info", Base64.encode(jsonObject.toString()));
-            cookie.setMaxAge(31536000);
-            cookie.setPath("/");
-            response.addCookie(userInfo);
-        }
         return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
     }
 
@@ -213,6 +195,8 @@ public class OrderController {
         final String methodName = "套餐吊起支付";
         //无需免登录
         Long userId = StpUserUtil.getLoginIdAsLong();
+        validateOrderPhone(payRequest, userId);
+        payRequest.setIsNoLogin(false);
         payRequest.setUserId(userId);
         log.info("{}[start] params:{}", methodName, payRequest);
 
@@ -231,6 +215,7 @@ public class OrderController {
     @PostMapping("/post/order/status/{orderId}")
     @Deprecated
     public Result<String> zeroOrderConfirm(@PathVariable Long orderId) {
+        authorizePayment(orderId, false);
         orderDonService.zeroOrderConfirm(orderId);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
@@ -241,6 +226,7 @@ public class OrderController {
     @PostMapping("/post/upgrade/order/status/{orderId}")
     @Deprecated
     public Result<String> zeroUpgradeOrderConfirm(@PathVariable Long orderId) {
+        authorizePayment(orderId, true);
         orderDonService.zeroUpgradeOrderConfirm(orderId);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
@@ -251,12 +237,62 @@ public class OrderController {
      * @author chan
      * @date 2021-10-11 下午4:56
      */
+    @org.springframework.beans.factory.annotation.Autowired
+    private com.cyksj.redis.RedisService paymentGuardRedis;
+
+    @org.springframework.beans.factory.annotation.Autowired
+    private com.cyksj.mapper.UpgradeOrderDonMapper paymentUpgradeMapper;
+
+    private void validateOrderPhone(OrderPayRequest req, Long userId) {
+        if (StrUtil.isBlank(req.getPhone())) return;
+        User user = userMapper.selectById(userId);
+        if (user == null || !java.util.Objects.equals(user.getLoginPhone(), req.getPhone())) {
+            throw BusinessRuntimeException.getInstance("下单手机号必须与当前登录账号一致");
+        }
+    }
+
+    @FunctionalInterface
+    private interface PaymentCall<T> { T run() throws Exception; }
+
+    private <T> T invokePayment(Long orderId, PaymentCall<T> call) {
+        try {
+            return call.run();
+        } catch (Exception error) {
+            String reference = java.util.UUID.randomUUID().toString();
+            // Do not echo provider payloads / merchant configuration to clients.
+            log.error("Prepay failed orderId:{} reference:{} errorType:{}", orderId, reference,
+                    error.getClass().getName());
+            throw BusinessRuntimeException.getInstance("支付发起失败,请稍后重试,错误编号:" + reference);
+        }
+    }
+
+    private void authorizePayment(Long orderId, boolean upgrade) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        if (orderId == null || orderId <= 0) throw BusinessRuntimeException.getInstance("订单不存在或无权操作");
+        boolean payable;
+        if (upgrade) {
+            UpgradeOrderDon order = paymentUpgradeMapper.selectById(orderId);
+            if (order == null || !java.util.Objects.equals(order.getUserId(), userId))
+                throw BusinessRuntimeException.getInstance("订单不存在或无权操作");
+            payable = order.getStatus() == UpgradeOrderDon.Status.noPayment;
+        } else {
+            OrderDon order = orderDonMapper.selectById(orderId);
+            if (order == null || !java.util.Objects.equals(order.getUserId(), userId))
+                throw BusinessRuntimeException.getInstance("订单不存在或无权操作");
+            payable = order.getStatus() == OrderDon.Status.noPayment;
+        }
+        if (!payable) throw BusinessRuntimeException.getInstance("订单已处理,请勿重复支付");
+        if (!paymentGuardRedis.setNx("security:prepay:" + upgrade + ":" + orderId, "1", 5L))
+            throw BusinessRuntimeException.getInstance("支付请求过于频繁,请稍后重试");
+    }
+
     @PostMapping("/post/pay/{orderId}")
     public Result<H5JsPayParams> pay(@PathVariable Long orderId) throws Exception {
+        authorizePayment(orderId, false);
         final String methodName = "吊起微信支付";
         log.info("{}[start] orderId:{}",methodName,orderId);
 
-        H5JsPayParams payParams = orderDonService.pay(orderId, "JSAPI");
+        H5JsPayParams payParams = invokePayment(orderId, () -> orderDonService.pay(orderId, "JSAPI"));
 
         return GatewayResponse.SUCCESS.newBuilder().toResult(payParams);
     }
@@ -267,8 +303,9 @@ public class OrderController {
      */
     @PostMapping("/get/wx/orderQrCode/{orderId}")
     public Result<String> createUnifiedOrderQr(@PathVariable Long orderId) throws Exception {
+        authorizePayment(orderId, false);
         //预下单
-        H5JsPayParams payParams = orderDonService.wxPcWebPay(orderId, "NATIVE");
+        H5JsPayParams payParams = invokePayment(orderId, () -> orderDonService.wxPcWebPay(orderId, "NATIVE"));
         //获取二维码链接code_url
         String codeUrl = payParams.getCodeUrl();
         QrConfig qrConfig = new QrConfig(300, 300);
@@ -349,7 +386,8 @@ public class OrderController {
 	 */
 	@PostMapping("/post/pay/zfb")
     public Result<String> aliPay(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
-        AliPayParams aliPayParams = orderDonService.aliPay(re, ZfbProductCode.MOBILE_WEB.getProductCode(), null);
+        authorizePayment(re.getOrderId(), false);
+        AliPayParams aliPayParams = invokePayment(re.getOrderId(), () -> orderDonService.aliPay(re, ZfbProductCode.MOBILE_WEB.getProductCode(), null));
 //        response.setContentType("text/html;charset=" + BaseZfbConfig.charset);
         //直接将完整的表单html输出到页面
 //        response.getWriter().write(aliPayParams.getBody());
@@ -363,7 +401,8 @@ public class OrderController {
      */
     @PostMapping("/post/pay/upgrade/zfb")
     public Result<String> aliPayUpgrade(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
-        AliPayParams aliPayParams = orderDonService.aliPayUpgrade(re, ZfbProductCode.MOBILE_WEB.getProductCode(),null);
+        authorizePayment(re.getOrderId(), true);
+        AliPayParams aliPayParams = invokePayment(re.getOrderId(), () -> orderDonService.aliPayUpgrade(re, ZfbProductCode.MOBILE_WEB.getProductCode(),null));
         return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
     }
 
@@ -374,7 +413,8 @@ public class OrderController {
      */
     @PostMapping("/get/zfb/orderQrCode")
     public Result<String> zfbWebQrCode(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
-        AliPayParams aliPayParams = orderDonService.aliPay(re, ZfbProductCode.PC_WEB.getProductCode(), BaseZfbConfig.qrPayMode_orderCode);
+        authorizePayment(re.getOrderId(), false);
+        AliPayParams aliPayParams = invokePayment(re.getOrderId(), () -> orderDonService.aliPay(re, ZfbProductCode.PC_WEB.getProductCode(), BaseZfbConfig.qrPayMode_orderCode));
 //        response.setContentType("text/html;charset=" + BaseZfbConfig.charset);
         //直接将完整的表单html输出到页面
 //        response.getWriter().write(aliPayParams.getBody());