小程序
小游戏
企业微信
微信支付
扫描小程序码分享
大佬们好
现在开发遇到一个情况,当微信支付完成后,会在同时[时间跨度1秒内]收到2个异步通知,这种情况下应该如何拦截掉多余通知呢?
我的业务逻辑是在收到异步通知后就执行一次,执行前会先去判断支付是否成功,并推送消息给客服,但是由于这个时间过短,所以每一次的判断都会是成功的,都会执行一次业务逻辑,一下会推送两条一样的消息
后端开发框架是larave
有没有什么好的办法屏蔽一次
4 个回答
加粗
标红
插入代码
插入链接
插入图片
上传视频
微信支付为保证回调通知触达有效性,会有保障策略,在第一次通知如果网络链路返回无法连接或者状态不明,微信支付会换一条链路进行通知。此时可能会造成您这边收到两次通知。微信文档已强调同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。需要正确对自己数据进行状态唯一性处理。
支付结果通知:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。
注意:
1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。
3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【查询订单API】确认订单状态。
特别提醒:
1、商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
3、技术人员可登进微信商户后台扫描加入接口报警群,获取接口告警信息。
参考
你好,麻烦通过点击下方“反馈信息”按钮,提供出现问题的。
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
可以使用Redis的setnx命令来解决
@Override public void processOrder(Map<String, Object> plainTextMap) { String orderNo = (String) plainTextMap.get("out_trade_no"); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Boolean isFlag = redisTemplate.opsForValue().setIfAbsent(orderNo, "已支付"); log.info("还没修改~~~"); if (isFlag) { //修改订单支付成功 log.info("修改订单状态"); QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_no", orderNo); OrderInfo orderInfo = new OrderInfo(); orderInfo.setOrderStatus(OrderStatus.SUCCESS.getType()); baseMapper.update(orderInfo, queryWrapper); //记录支付日志 paymentInfoService.createPaymentInfo(plainTextMap); String redisKey = (String) plainTextMap.get("attach"); redisTemplate.delete(redisKey); } }
刚遇到同样问题,不想用redis缓存机制,使用map的key值唯一性,解决方法有待验证 使用全局变量,一个订单号只能用一次,用完后就不怕了。大佬们,看看这样会有其他问题吗? //通知结果高并发情况下拦截.全局变量 private Map repeatRequstMap = new ConcurrentHashMap<>(); public String xxx(String orderNo
){
if (repeatRequstMap.get(orderNo) == null) { repeatRequstMap.put(orderNo, orderNo); returnMsg = receiveBaseService.syncFinancial(financeUnifiedOrder); if (returnMsg != null) { logger.error("同步财务接口失败-{}-{}", orderNo, returnMsg); repeatRequstMap.remove(orderNo); return returnMsg; } else { if (repeatRequstMap.size() >= 60) { repeatRequstMap = new ConcurrentHashMap<>(); } } //修改同步系统状态 FinanceUnifiedOrder sync = new FinanceUnifiedOrder(); sync.setId(financeUnifiedOrder.getId()); sync.setSyncStatus("1"); financeUnifiedOrderMapper.updateByPrimaryKeySelective(sync); } return null;
}
基本没办法屏蔽。
将业务逻辑代码改成适应这种情况吧。
关注后,可在微信内接收相应的重要提醒。
请使用微信扫描二维码关注 “微信开放社区” 公众号
微信支付为保证回调通知触达有效性,会有保障策略,在第一次通知如果网络链路返回无法连接或者状态不明,微信支付会换一条链路进行通知。此时可能会造成您这边收到两次通知。微信文档已强调同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。需要正确对自己数据进行状态唯一性处理。
支付结果通知:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
应用场景
支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。
注意:
1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。
3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【查询订单API】确认订单状态。
特别提醒:
1、商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
3、技术人员可登进微信商户后台扫描加入接口报警群,获取接口告警信息。
参考
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
可以使用Redis的setnx命令来解决
@Override public void processOrder(Map<String, Object> plainTextMap) { String orderNo = (String) plainTextMap.get("out_trade_no"); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Boolean isFlag = redisTemplate.opsForValue().setIfAbsent(orderNo, "已支付"); log.info("还没修改~~~"); if (isFlag) { //修改订单支付成功 log.info("修改订单状态"); QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_no", orderNo); OrderInfo orderInfo = new OrderInfo(); orderInfo.setOrderStatus(OrderStatus.SUCCESS.getType()); baseMapper.update(orderInfo, queryWrapper); //记录支付日志 paymentInfoService.createPaymentInfo(plainTextMap); String redisKey = (String) plainTextMap.get("attach"); redisTemplate.delete(redisKey); } }
刚遇到同样问题,不想用redis缓存机制,使用map的key值唯一性,解决方法有待验证 使用全局变量,一个订单号只能用一次,用完后就不怕了。大佬们,看看这样会有其他问题吗? //通知结果高并发情况下拦截.全局变量 private Map repeatRequstMap = new ConcurrentHashMap<>(); public String xxx(String orderNo
){
if (repeatRequstMap.get(orderNo) == null) { repeatRequstMap.put(orderNo, orderNo); returnMsg = receiveBaseService.syncFinancial(financeUnifiedOrder); if (returnMsg != null) { logger.error("同步财务接口失败-{}-{}", orderNo, returnMsg); repeatRequstMap.remove(orderNo); return returnMsg; } else { if (repeatRequstMap.size() >= 60) { repeatRequstMap = new ConcurrentHashMap<>(); } } //修改同步系统状态 FinanceUnifiedOrder sync = new FinanceUnifiedOrder(); sync.setId(financeUnifiedOrder.getId()); sync.setSyncStatus("1"); financeUnifiedOrderMapper.updateByPrimaryKeySelective(sync); } return null;
}
//通知结果高并发情况下拦截
private Map<String, String> repeatRequstMap = new ConcurrentHashMap<>();
/**
* @throws
* @Title: notifyStuchargePay
* @Description: 交费订单支付结果回调处理
* @param: @return
* @return: ResultJSON
*/
@RequestMapping(value = "/notifyStuchargePay", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public void notifyStuchargePay(HttpServletRequest request, HttpServletResponse response) {
logger.info("交费订单支付结果回调处理-{}", request.getRequestURI());
Map<String, Object> result = new HashMap<>();
try {
logger.info("微信支付异步通知结果:{}", sb.toString());
Map<String, String> notifyMap = WxPayUtil.doXMLParse(sb.toString());
String orderNo = notifyMap.get("out_trade_no");
if (repeatRequstMap.get(orderNo) == null) {
repeatRequstMap.put(orderNo, orderNo);//占用
//todo 支付结果处理,处理完后再重复调用就有数据库拦截了
repeatRequstMap.remove(orderNo);//释放
}
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
String resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
out.write(resXml.getBytes());
out.flush();
out.close();
} catch (Exception e) {
logger.info("微信支付异步通知异常-", e);
}
}
基本没办法屏蔽。
将业务逻辑代码改成适应这种情况吧。