前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何防止订单二次重复支付?

如何防止订单二次重复支付?

原创
作者头像
JavaEdge
修改2024-07-23 17:32:41
2220
修改2024-07-23 17:32:41
举报
文章被收录于专栏:JavaEdge

1 背景

用户第一次点击下单操作时,会弹出支付页面待支付。但可能存在用户在支付时发现账户金额不够,后续选择:

  • 其他渠道支付(如微信支付转为支付宝支付)
  • 或采用不同终端来支付(如由电脑端支付转为app端支付)

这时就面临二次支付场景。

2 方案1

由于用户支付的时候的支付页面是html文件或是一个支付二维码,可将支付页面先存储一份在数据库中,用户二次支付时通过查询数据库来重新返回用户原来的支付页面。

2.1 缺点

需注意支付页面是否过期,若支付页面过期,需二次调用第三方支付

  • 后台需维护用户第一次调用时的支付页面,增加开发成本
  • 需要注意幂等性,即能唯一标识用户的多次请求

2.2 优点

规定时间内,不论用户多少次调用,后台只需要调用一次第三方支付。

2.3 流程图

3 方案2

用户第二次支付的时,继续调用第三方支付,让第三方根据是否超时等情况判断是:

  • 返回原来的支付页面
  • or生成一个新的支付页面返回

3.1 优点

便于实现,减轻自己后台下单的维护成本。【推荐】

用户二次支付时,订单微服务中存储了用户第一次下单支付的基本信息。因此第二次支付时,可通过查询第一次支付的一些基本信息来调用第三方支付。这样设计可告诉第三方支付平台这是一个订单,尤其是该订单的【剩余过期时间】。

剩余过期时间

后台调用第三方支付,第三方支付从收到请求信息->处理请求信息->响应请求信息是存在一定的时延的,因此一定不能死死卡住过期时间来调用第三方支付。需要预留一些时间给第三方支付处理。如支付过期时间是30分钟,当用户二次支付到达我们下单服务的时候是29分钟那么就拒绝支付。

用户超时支付的拒绝策略

策略一

前端显示订单30分钟内需要支付,后端中对第三方支付实际上是31分钟内不能支付 【预留时间给后端和第三方支付交互】

策略二

前端显示订单30分钟内需要支付,后端对第三方的支付实际上是当用户支付请求在地29分钟到后端就不给支付了。

代码语言:java
复制
@Override
@Transactional
public JsonData repay(RepayOrderRequest repayOrderRequest) {
    LoginUser loginUser = LoginInterceptor.threadLocal.get();
    // 根据订单流水号查询第一次支付的订单信息
    ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",repayOrderRequest.getOutTradeNo()).eq("user_id",loginUser.getId()));

    log.info("订单状态:{}",productOrderDO);

    if(productOrderDO==null){
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
    }

    // 订单状态不对,不是NEW状态
    if(!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())){
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
    }else {
        //订单创建到现在的存活时间
        long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime();
        //创建订单是临界点在预留一分钟时间,比如订单实际已经存活了28分钟了,我们就对外说订单已经存活了29分钟。
        orderLiveTime = orderLiveTime + 60*1000;

        //大于订单超时时间,则失效
        if(orderLiveTime>TimeConstant.ORDER_PAY_TIMEOUT_MILLS){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
        }else {

            //记得更新DB订单支付参数 payType,还可以增加订单支付信息日志  TODO
            //总时间-存活的时间 = 剩下的有效时间
            long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
            //创建支付
            PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(),
                    productOrderDO.getPayAmount(),repayOrderRequest.getPayType(),
                    repayOrderRequest.getClientType(), productOrderDO.getOutTradeNo(),"",timeout);

            log.info("payInfoVO={}",payInfoVO);
            //调用第三方支付
            String payResult = payFactory.pay(payInfoVO);
            if(StringUtils.isNotBlank(payResult)){
                log.info("创建二次支付订单成功:payInfoVO={},payResult={}",payInfoVO,payResult);
                return JsonData.buildSuccess(payResult);
            }else {
                log.error("创建二次支付订单失败:payInfoVO={},payResult={}",payInfoVO,payResult);
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
            }
        }
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 背景
  • 2 方案1
    • 2.1 缺点
      • 2.2 优点
        • 2.3 流程图
        • 3 方案2
          • 3.1 优点
            • 剩余过期时间
              • 用户超时支付的拒绝策略
                • 策略一
                • 策略二
            相关产品与服务
            云支付
            云支付(Cloud Pay,CPay)为您提供开放、可靠的聚合收款技术服务和商户管理功能。云支付支持刷卡支付、扫码支付、一码多付多种支付方式。服务商也可使用云支付提供的 SDK 和 HTTPS 接口,将云支付集成进自己的系统中,为商户提供的个性化解决方案。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档