## 订单接口-3 确认订单-提交订单之后,就是支付了。 用户点击提交订单之后,选择确定付款,此时进入支付界面 前端调用接口为`/mall4cloud_payment/pay/order`,根据订单号进行支付,上一步提交订单返回的便是订单id列表 接口详情: ```java @PostMapping("/order") @Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付") public ServerResponseEntity pay(HttpServletRequest request, @Valid @RequestBody PayInfoDTO payParam) { // 这里的地址是网关通过转发过来的时候,获取到当前服务器的地址,测试环境要用测试环境的uri String gatewayUri = "http://192.168.1.17:8126/mall4cloud_payment"; UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); Long userId = userInfoInTokenBO.getUserId(); PayInfoBO payInfo = payInfoService.pay(userId, payParam); payInfo.setBizUserId(userInfoInTokenBO.getBizUserId()); ... } ``` 这里关键的是调用了`payInfoService.pay(userId, payParam)` 该方法由于涉及多个表库的增改,故此加了事务注解来保证一致性。 ```java @Override @Transactional(rollbackFor = Exception.class) public PayInfoBO pay(Long userId, PayInfoDTO payParam) { // 支付单号 ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(PayInfo.DISTRIBUTED_ID_KEY); if (!segmentIdResponse.isSuccess()) { throw new mall4cloudException(ResponseEnum.EXCEPTION); } Long payId = segmentIdResponse.getData(); List orderIds = payParam.getOrderIds(); // 如果订单没有被取消的话,获取订单金额,否之会获取失败 ServerResponseEntity ordersAmountAndIfNoCancelResponse = orderFeignClient.getOrdersAmountAndIfNoCancel(orderIds); // 如果订单已经关闭了,此时不能够支付了 if (!ordersAmountAndIfNoCancelResponse.isSuccess()) { throw new mall4cloudException(ordersAmountAndIfNoCancelResponse.getMsg()); } //将数据存到数据库中 OrderAmountVO orderAmount = ordersAmountAndIfNoCancelResponse.getData(); PayInfo payInfo = new PayInfo(); payInfo.setPayId(payId); payInfo.setUserId(userId); //支付的金额是从数据库查询的,并非前端传过来的值 payInfo.setPayAmount(orderAmount.getPayAmount()); payInfo.setPayStatus(PayStatus.UNPAY.value()); payInfo.setSysType(AuthUserContext.get().getSysType()); payInfo.setVersion(0); // 保存多个支付订单号 payInfo.setOrderIds(StrUtil.join(StrUtil.COMMA, orderIds)); // 保存预支付信息 payInfoMapper.save(payInfo); PayInfoBO payInfoDto = new PayInfoBO(); payInfoDto.setBody("商城订单"); payInfoDto.setPayAmount(orderAmount.getPayAmount()); payInfoDto.setPayId(payId); //返回支付信息 return payInfoDto; } ``` `controller`方法剩下的便是支付回调了,前面的`gatewayUri`便有用了 ```java // 回调地址 payInfo.setApiNoticeUrl(gatewayUri + "/notice/pay/order"); payInfo.setReturnUrl(payParam.getReturnUrl()); payNoticeController.submit(payInfo.getPayId()); return ServerResponseEntity.success(payInfo.getPayId()); ``` 执行完`pay`方法后,由于没有对接微信和支付宝接口,故直接调用submit中`payInfoService.paySuccess(payInfoResult,orderIdList);`使其支付成功 ```java @Override @Transactional(rollbackFor = Exception.class) public void paySuccess(PayInfoResultBO payInfoResult, List orderIds) { // 标记为支付成功状态 PayInfo payInfo = new PayInfo(); payInfo.setPayId(payInfoResult.getPayId()); payInfo.setBizPayNo(payInfoResult.getBizPayNo()); payInfo.setCallbackContent(payInfoResult.getCallbackContent()); payInfo.setCallbackTime(new Date()); payInfo.setPayStatus(PayStatus.PAYED.value()); payInfoMapper.update(payInfo); // 发送消息,订单支付成功 SendStatus sendStatus = orderNotifyTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_TOPIC, new GenericMessage<>(new PayNotifyBO(orderIds))).getSendStatus(); if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) { // 消息发不出去就抛异常,因为订单回调会有多次,几乎不可能每次都无法发送出去,发的出去无所谓因为接口是幂等的 throw new mall4cloudException(ResponseEnum.EXCEPTION); } } ``` 使用`rocketMq`来发送支付成功的消息,`OrderNotifyConsumer`进行订单回调,此时将订单改为已支付状态,并且发送消息通知库存可以扣减了 ```java @Override public void onMessage(PayNotifyBO message) { LOG.info("订单回调开始... message: " + Json.toJsonString(message)); orderService.updateByToPaySuccess(message.getOrderIds()); // 发送消息,订单支付成功,通知库存扣减 SendStatus sendStockStatus = orderNotifyStockTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC, new GenericMessage<>(message)).getSendStatus(); if (!Objects.equals(sendStockStatus,SendStatus.SEND_OK)) { throw new mall4cloudException(ResponseEnum.EXCEPTION); } } ``` 将支付单号返回给前端即可。