大家好,我是烤鸭:

如果作为第三方支付平台,需要通知调用方付款成功。但是出现通知失败的情况,怎么处理。
    支付宝的异步通知,每个订单的异步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h。
    如果没有收到success,就会一直按上边的进行通知。

就上述的情景说一下想到的解决方案,并不一定是有效的,只是一些想法:

1. 定时任务

最开始想到的是用定时任务来做。通知后,如果没有收到结果,就会一直扫表。
    扫描状态是未通知的,下次通知的时间小于当前时间的,如果再通知再未送达到的话,
    更新下次通知时间和通知次数。
    这个做法有一些弊端,如果订单到达一定数量,一直扫表会对数据库压力。
    而且如果按照上面的时间间隔的话,在大量订单的情况下很难保证精度。

2. 定时任务 + redis实现

为了避免数据库的压力,想到的是用redis来代替。
    当第一次通知失败的时候,将失败的订单标识(+订单号)存到redis中。
    其中redis中存放的是两种数据结构,一种是Set集合,订单号集合。
    另一种String.key-value,key是前缀+订单号,value是已通知次数。
    还有一种是key是前缀+订单号,value是下次通知时间。

简易代码如下:
    第一次通知失败:

 //通知失败if (IDBConstant.RESULT_ERROR.equals(status)) {logger.info("返回结果为error 将订单id存到redis中  orderId===" + orderId);//将订单号放到redis中String key = IDBConstant.SCYD_NOTIFY_PREFIX_PRE + orderId;//集合的通用key,根据这个key能获取到需要通知的订单集合redisClient.hset(IDBConstant.SCYD_NOTIFY_AGAIN_PRE, key, key);//通知次数redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PRE + orderId, "2");//下一次通知时间,应该跟次数有关,可以写个枚举类,将次数和下次的加长时间对应redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PRE + orderId, System.currentTimeMillis() + 10 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "");}

定时任务每隔一分钟获取redis数据

  // 获取订单号Set<String> list = redisClient.hkeys(IDBConstant.SCYD_NOTIFY_AGAIN_PAY);    if (!list.isEmpty()) {list.forEach(item -> {String itemStr = (String) item;String[] strs = itemStr.split("_");String orderId = strs[1];String applyNum = redisClient.hget(IDBConstant.SCYD_NOTIFY_AGAIN_PAY, itemStr);System.out.println(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);// 订单发送的时间毫秒值String notifyTime = redisClient.get(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);//被锁不等待if (redisClient.tryLock(item, 0L, TimeUnit.SECONDS)) {// 如果通知时间 < 当前时间,发送通知if (Long.valueOf(notifyTime) < System.currentTimeMillis()) {// 通知次数String num = redisClient.get(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId);switch (num) {case "2":// 通知时间 + 10minnotifyTime = Long.valueOf(notifyTime)+ 20 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "3":// 通知时间 + 10minnotifyTime = Long.valueOf(notifyTime)+ 20 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "4":// 通知时间 + 15minnotifyTime = Long.valueOf(notifyTime)+ 30 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "5":// 通知时间 + 1h* DateConstant.SIXTY_MINUTESnotifyTime = Long.valueOf(notifyTime)+ 60 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;default:break;}}}});}

上面方法中doSCYDPayNotifyAgainHandler() 就是对当前的订单再次通知。
    如果通知失败,更新次数和下次通知时间。如果成功就移除。最后别忘记释放锁。
    方法如下:

public void doSCYDPayNotifyAgainHandler(String item, String notifyTime, String num,String applyNum) {taskAsyncPool.execute(new Runnable() {@Overridepublic void run() {String[] strs = item.split("_");String orderId = strs[1];logger.info("[通知]" + orderId + ":第" + num + "次任务启动");//根据orderId  获取订单信息//订单信息假装已经获取到了try {//发送请求//过程1//过程2//获取结果,成功的话if (IDBConstant.RESULT_SUCCESS.equals(status)) {//清空缓存数据redisClient.delKey(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId);redisClient.delKey(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);//移除操作成功的redisClient.hdel(IDBConstant.SCYD_NOTIFY_AGAIN_PAY, item);} else {int count = Integer.parseInt(num);if(count==5) {//回调第五次还是失败,直接返回return;}//如果还是没有回调,更新回调时间redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId, notifyTime);count += 1;//更新回调次数redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId, count + "");}} catch (Exception e) {logger.error("[回调通知]" + item + ":{}第" + num + "次任务异常:method{}" ,e);redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId, notifyTime);int count = Integer.parseInt(num);count += 1;//更新回调次数redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId, count + "");} finally {//解锁redisClient.unLock(item);}}});}    

这样多条线程执行,主线程从redis中获取待通知订单集合,另起线程做通知操作。
    每通知一单就是一条线程,延迟性也得到了比较好的解决,上面从数据库取的结果也可以多线程。
    线程池也是有上限的,无限获取很可能将内存和cpu耗尽。

推荐第三种方式。redis+队列

3. redis+队列

将已经获取到的订单扔到队列中,在队列里执行 doSCYDPayNotifyAgainHandler(String item, String notifyTime, String num,String applyNum) 
    这个方法,延时和cpu问题就能比较好的解决了。
    至于丢失问题,暂时没考虑过。就目前来说,第二种方式够用。其他的只是有一些想法,欢迎交流。

redis实践的一点思路,关于支付回调相关推荐

  1. 关于支付回调的一些思考

    前段时间在工作中又一次接触到了公司关于支付相关的业务领域,于是又一次重新回顾了工作中使用的支付框架. 之前写过的很多篇文章都是先介绍整体背景,再深入到每一点去做分析,今天打算换种方式,尝试从 why ...

  2. 微信支付:支付流程分析、微信扫码支付(HttpClient)、微信支付二维码生成、检测支付状态、订单状态操作准备工作、支付信息回调、MQ处理支付回调状态、定时处理订单状态

    微信支付 微信支付开发的整体思路 生成支付二维码 查询支付状态(微信的服务器) 实现订单状态的修改.删除订单 支付状态回查->微信服务器将支付状态返回给支付微服务 MQ处理支付回调状态 Rabb ...

  3. 节约内存:Instagram的Redis实践(转)

    一.问题: 数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求. 二.解决方案: 1.通过高速服务器Cache缓存数据库数据 2.内存数据库 三.主流解Cache和数据库对比: 从以上 ...

  4. 利用内网穿透实现无固定IP调试支付回调

    在家(使用NAT网)开发项目时,难免会涉及到第三方外部接口回调(如:支付回调.微信小程序登录),需要固定IP或域名解析,内网穿透是个不错的选择.常见的内网穿透工具有 向日葵.natapp.网云穿 等. ...

  5. 小程序支付回调(完整版)

    小程序支付回调 本次是关于统一下单的回调,支付成功后需要通知告知微信服务器.这里有一点必须注意,为后期退款功能提供保障,在回调中处理业务逻辑的时候一定要将商户订单号->订单金额 total_fe ...

  6. 使用NATAPP.cn测试微信支付回调接口

    个人资源与分享网站:http://xiaocaoshare.com/ 1.在工作中,开发微信支付有点的难度的地方就是微信支付回调接口的测试,现在推荐一款在线调试工具https://natapp.cn ...

  7. 支付宝支付 第十一集:支付回调成功后的监听

    支付宝支付 第十一集:支付回调成功后的监听 一.代码 目录结构 更新OrderDetailController.java package com.dzy.alipay.web.order; impor ...

  8. 支付宝支付 第十集:支付回调

    支付宝支付 第十集:支付回调 一.注意 这里的支付回调最好是自己有一个服务器(阿里云服务器一年70多也不是很贵),博主自己尝试了一下,本机和使用虚拟机模拟服务器的话,支付宝的授权回调信息是传不过来的, ...

  9. ios支付宝支付失败不回调_iOS 支付宝网页支付回调问题

    今天遇到支付宝网页支付回调的问题 当手机里面没有支付宝客户端的时候,会自动调起网页支付页面,但是我发现我原来写在AppDelegate.m里面的代码没走. 造成的结果是,不管是支付成功,还是退出支付, ...

最新文章

  1. 利用Python制作简单的小程序:IP查看器
  2. mysql 5.6 主从同步配置_Mysql 5.6主从同步配置
  3. Vue.js 条件渲染 v-if、v-show、v-else
  4. mmap函数_Linux中的mmap映射 [二]
  5. linux的引导过程和服务控制
  6. CS231n第一次作业_问题1
  7. BNUOJ-4049-四叉树
  8. python图像降噪
  9. Springboot+vue 增删改查的小项目
  10. Pepper停产之后,家庭服务机器人的未来产品形态,会是四足机器人吗
  11. 新型冠状病毒肺炎分析
  12. 基于QT Creator 5.14的仿QQ聊天系统【UDP通讯】
  13. 8大底层逻辑,提升思维能力
  14. 用Java测试电脑速度的小方法
  15. 9.15蚂蚁金服hr面
  16. redhat7安装oracle11gR2之动手安装
  17. protel dxp PCB使用技巧(摘)
  18. 深度学习 效果不好怎么办
  19. 便利的无线信号传输解决方案
  20. 北邮22信通:实验六 由运放器构成的音频放大电路设计、仿真、测试报告

热门文章

  1. 前端学习(3018):vue+element今日头条管理--反馈
  2. [html] HTML5如何使用音频和视频?
  3. [vue] 你认为vue的核心是什么?
  4. 工作47:继续理解父子组件
  5. 工作41:解决vuex刷新数据丢失
  6. 前端学习(2459):账户设置
  7. 前端学习(1402):多人管理22验证joi
  8. 前端学习(969):移动端300ms延时问题
  9. java面试题25 在程序代码中写的注释太多,会使编译后的程序尺寸变大。
  10. spring mvc学习(13)windows上安装maven