在JSAPI支付官方文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6里面,微信团队明确指出,在微信浏览器里面打开H5网页中执行JS调起支付时,代码里面res.err_msg这个值将在用户支付成功后返回ok,但并不保证它绝对可靠,因此,用户支付是否成功不能在这里进行判断,而应该通过接收微信的异步通知来实现。
在https://blog.csdn.net/hjfcgt123/article/details/104244974这篇微博中,我们配置文件中有一个属性名为:wechat.notifyUrl。这个参数的作用是什么呢?在生成预支付订单时,请求参数中有一个属性值叫做notify_url,此属性值必传,微信官方指出该属性为:异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
当支付成功之后,微信会向该url发起异步通知,返回订单的支付状态。我们需要在这里判断订单是否支付成功,继而做其他业务逻辑操作。
一、官方文档
官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
二、代码实现
1、controller
@Controller
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayService payService;
@PostMapping("notify")
public ModelAndView notify(@RequestBody String notifyDate) {
//接收微信支付异步通知
payService.notify(notifyDate);
//返回给微信处理结果
return new ModelAndView("pay/success");
}
}
2、service实现类
@Service
@Slf4j
public class PayServiceImpl implements PayService {
private static final String orderName = "微信点餐订单";
@Autowired
private BestPayServiceImpl bestPayService;
@Autowired
private OrderService orderService;
@Override
public PayResponse notify(String notifyDate) {
/**
* 接收微信支付异步通知
* 1、验证签名(sdk已经内部实现)
* 2、订单的支付状态(sdk已经内部实现)
*/
PayResponse payResponse = bestPayService.asyncNotify(notifyDate);
log.info("【微信支付】异步通知,payResponse={}", JsonUtils.toJson(payResponse));
/**
* 3、校验订单是否存在
*/
OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
if (ObjectUtils.isEmpty(orderDTO)) {
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
/**
* 4、校验订单是否已经被支付
*/
if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
return payResponse;
}
/**
* 5、判断金额是否一致
*/
if (!MathUtils.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
log.error("【微信支付】异步通知,订单金额不一致,orderId={},微信通知金额={},系统金额={}",
payResponse.getOrderId(),
payResponse.getOrderAmount(),
orderDTO.getOrderAmount());
throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
}
/**
* 5、修改订单的支付状态
*/
orderService.paid(orderDTO);
return payResponse;
}
}
同样的通知微信支付后台可能会多次发送给商户系统。所以我们在接收了微信的异步支付通知,处理自身的业务逻辑之后,需要给微信支付后台返回确认消息,这样微信就不会再重复发送同一个订单的支付结果。
因此在controller中,我们返回了一个ModelAndView,对应的视图为src\main\resources\templates\pay\success.ftl,文件内容如下
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>