## 订单接口-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<Long> segmentIdResponse = segmentFeignClient.getSegmentId(PayInfo.DISTRIBUTED_ID_KEY);
    if (!segmentIdResponse.isSuccess()) {
        throw new mall4cloudException(ResponseEnum.EXCEPTION);
    }
    Long payId = segmentIdResponse.getData();
    List<Long> orderIds = payParam.getOrderIds();
    // 如果订单没有被取消的话，获取订单金额，否之会获取失败
    ServerResponseEntity<OrderAmountVO> 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<Long> 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);
    }
}
```

将支付单号返回给前端即可。
