下单接口防重提交问题

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 下单接口防重提交问题

公众号merlinsea


  • 背景


  • 用户下单的时候,可能由于网络延迟导致用户多次提交的是同一笔订单,但作为业务方需要保证用户只会支付一次。即用户多次点击提交支付的时候下单接口应该具有幂等性。
  • 幂等性介绍
  • 幂等即如果用户同一个请求多次到达,那么我们只能处理一次,即默认第一次会处理,后续的请求会忽略。
  • 对于输出,同一个请求不论来多少次,返回的结果都必须是相同的。
  • 因此幂等通常会选取请求中可以唯一标识这个请求的参数作为幂等key,幂等请求的结果和幂等key应该存储在redis中,后序相同的请求到来可以直接冲redis中获取。


  • 用户下单流程
  • 1、选中购物车中的商品,点击结算 ------->后端返回一个提交令牌token同时跳转到提交订单支付页面【提交订单】
  • 2、用户点击提交订单,进行支付【确认支付】


  • 说明:下单流程中第1步提交订单实际上是直接和我们的后台进行响应,速度是比较快的,因此在这一步是不会出现重复下单,但对于第2步由于支付需要和第三方交互,用户可能由于延迟多次支付,因此在第1步的时候我们会生成一个唯一标识这个订单的令牌token交给用户【同时token会在后端保存一份】,用户在第2步的时候会携带token进行支付【支付完后端就删除了这个token】,这样我们就可以保证不论用户支付多少次,实际上的付款只会执行一次。


  • 解决重复下单问题的思路
  • 将用户下单过程分为两步执行,第一步中会生成一个提交令牌token,并将这个提交的令牌token存到redis中,在第二步中用户点击提交订单进行支付的时候会携带上这个提交令牌token,如果发现redis中有这个token说明是第一次提交,调用第三方支付接口同时删除redis中的这个令牌。这样在第二个步骤中不管用户点击了多少次都只会支付一次(因为后续的提交都找不到这个token了)。

640.jpg


  • 第一步:点击购物车结算按钮,获取提交订单的唯一令牌【这步是和本项目的微服务交互,故速度会比较快】


@ApiOperation("获取提交订单令牌")
@GetMapping("get_token")
public JsonData getOrderToken(){
    LoginUser loginUser = LoginInterceptor.threadLocal.get();
    String key = String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY,loginUser.getId());
    String token = CommonUtil.getStringNumRandom(32);
    redisTemplate.opsForValue().set(key,token,30,TimeUnit.MINUTES);
    return JsonData.buildSuccess(token);
}

640.jpg

  • 第二步:点击提交订单接口并携带token【这一步要和第三方支付交互,速度可能会比较慢,关键是在这步容易出现重复提交】


/**
 * * 防重提交
 * * 用户微服务-确认收货地址
 * * 商品微服务-获取最新购物项和价格
 * * 订单验价
 *   * 优惠券微服务-获取优惠券
 *   * 验证价格
 * * 锁定优惠券
 * * 锁定商品库存
 * * 创建订单对象
 * * 创建子订单对象
 * * 发送延迟消息-用于自动关单
 * * 创建支付信息-对接三方支付
 *
 * @param orderRequest
 * @return
 */
@Override
@Transactional
public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
    LoginUser loginUser = LoginInterceptor.threadLocal.get();
    String orderToken = orderRequest.getToken();
    if(StringUtils.isBlank(orderToken)){
        throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
    }
    //原子操作 校验令牌,删除令牌
    String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
    Long result = redisTemplate.execute(new DefaultRedisScript<>(script,Long.class), Arrays.asList(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY,loginUser.getId())),orderToken);
    //redis中找不到提交token 说明是重复提交,直接抛出异常
    if(result == 0L){
        throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
    }
    //说明是第一次提交,执行下单支付相关业务todo【调用第三方支付接口】
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
8月前
|
存储 缓存 NoSQL
防止订单重复提交或支付分布式锁方案设计
防止订单重复提交或支付分布式锁方案设计
325 0
|
NoSQL Java Redis
服务端如何防止订单重复支付!
如图是一个简化的下单流程,首先是提交订单,然后是支付。 支付的话,一般是走支付网关(支付中心),然后支付中心与第三方支付渠道(微信、支付宝、银联)交互。 支付成功以后,异步通知支付中心,支付中心更新自身支付订单状态,再通知业务应用,各业务再更新各自订单状态。
服务端如何防止订单重复支付!
|
10月前
|
Java Spring 容器
通过注解开发来实现下单接口的防重提交
通过注解开发来实现下单接口的防重提交
|
5月前
|
消息中间件 NoSQL 中间件
关于实现订单超时的几种方案(详细细节版)
关于实现订单超时的几种方案(详细细节版)
105 0
关于实现订单超时的几种方案(详细细节版)
|
10月前
|
NoSQL Java 数据库
java接口防重提交如何处理
举一个最简单的例子:日常开发中crud在业务系统中普遍存在,在服务端没有做任何处理,客户端没有做节流、防抖等限流操作时,同一秒一个用户点了两次新增按钮,导致数据库中存在同样两条数据,其结果可想而知,同理修改、删除同样的道理;查询本身具有幂等性,但是在同一秒钟同样的操作,查询多次和一次,有区别吗?区别大了去了,不谈用户体验如何,光是网络开销、流量占用、带给服务器的压力等等,生产中一点小的问题,如何不及时处理,可能会引发灾难性bug。
|
消息中间件 Java 程序员
订单支付超时,自动关闭订单实现
今天跟大家一起探讨一个场景:用户对商品下单,约定30分钟没支付,超时订单将被系统自动关闭。
334 0
订单支付超时,自动关闭订单实现
|
消息中间件 存储 资源调度
订单超时怎么处理?我们用这种方案
在电商业务下,许多订单超时场景都在24小时以上,对于超时精度没有那么敏感,并且有海量订单需要批处理,推荐使用基于定时任务的跑批解决方案。
1030 0
订单超时怎么处理?我们用这种方案
|
前端开发 应用服务中间件 API
订单异步通知修改订单状态
订单异步通知修改订单状态
订单异步通知修改订单状态
|
消息中间件 Dubbo 测试技术
Rest 方式测试支付下单和支付回调|学习笔记
快速学习 Rest 方式测试支付下单和支付回调
162 0
Rest 方式测试支付下单和支付回调|学习笔记
|
消息中间件 数据库 RocketMQ
回退库存幂等性处理|学习笔记
快速学习回退库存幂等性处理
68 0
回退库存幂等性处理|学习笔记