确认订单-提交订单之后,就是支付了。
用户点击提交订单之后,选择确定付款,此时进入支付界面
前端调用接口为/mall4cloud_payment/pay/order
,根据订单号进行支付,上一步提交订单返回的便是订单id列表
接口详情:
@PostMapping("/order")
@ApiOperation(value = "根据订单号进行支付", notes = "根据订单号进行支付")
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)
该方法由于涉及多个表库的增改,故此加了事务注解来保证一致性。
@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
便有用了
// 回调地址
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);
使其支付成功
@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
进行订单回调,此时将订单改为已支付状态,并且发送消息通知库存可以扣减了
@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);
}
}
将支付单号返回给前端即可。