精华内容
下载资源
问答
  • 来源:cnblogs.com/cjsblog/p/14516909.html概述为了防止掉单,这里可以这样处理:为了防止订单重复提交,可以这样处理:附上微信支付最佳实践:概述如图是一个简化...

    来源:cnblogs.com/cjsblog/p/14516909.html

    • 概述

    • 为了防止掉单,这里可以这样处理:

    • 为了防止订单重复提交,可以这样处理:

    • 附上微信支付最佳实践:


    概述

    如图是一个简化的下单流程,首先是提交订单,然后是支付。

    支付的话,一般是走支付网关(支付中心),然后支付中心与第三方支付渠道(微信、支付宝、银联)交互。

    支付成功以后,异步通知支付中心,支付中心更新自身支付订单状态,再通知业务应用,各业务再更新各自订单状态。

    这个过程中经常可能遇到的问题是掉单,无论是超时未收到回调通知也好,还是程序自身报错也好。

    总之由于各种各样的原因,没有如期收到通知并正确的处理后续逻辑等等,都会造成用户支付成功了,但是服务端这边订单状态没更新。

    这个时候有可能产生投诉,或者用户重复支付。

    由于③⑤造成的掉单称之为外部掉单,由④⑥造成的掉单我们称之为内部掉单

    为了防止掉单,这里可以这样处理:

    1、支付订单增加一个中间状态“支付中”,当同一个订单去支付的时候,先检查有没有状态为“支付中”的支付流水,当然支付(prepay)的时候要加个锁。支付完成以后更新支付流水状态的时候再讲其改成“支付成功”状态。

    2、支付中心这边要自己定义一个超时时间(比如:30秒),在此时间范围内如果没有收到支付成功回调,则应调用接口主动查询支付结果,比如10s、20s、30s查一次,如果在最大查询次数内没有查到结果,应做异常处理

    3、支付中心收到支付结果以后,将结果同步给业务系统,可以发MQ,也可以直接调用,直接调用的话要加重试(比如:SpringBoot Retry)

    4、无论是支付中心,还是业务应用,在接收支付结果通知时都要考虑接口幂等性,消息只处理一次,其余的忽略

    5、业务应用也应做超时主动查询支付结果

    对于上面说的超时主动查询可以在发起支付的时候将这些支付订单放到一张表中,用定时任务去扫

    为了防止订单重复提交,可以这样处理:

    1、创建订单的时候,用订单信息计算一个哈希值,判断redis中是否有key,有则不允许重复提交,没有则生成一个新key,放到redis中设置个过期时间,然后创建订单。

    其实就是在一段时间内不可重复相同的操作

    附上微信支付最佳实践:

    往期推荐

    应用卡死的bug竟然是因为Redis使用不当?

    SpringBoot+webSocket实现扫码登录功能

    7000+字,30+张图!Java线上故障排查思路全部总结在这里了

    TCP3次握手为啥挥手却要4次,这下解释明白了

    SpringCloud微服务的熔断机制和熔断的意义?

    java项目线上JVM调优实践,FullGC大大减少

    非常nice的分布式事务方案总结

    【干货】6500字全面字讲解 Redis 性能优化点!

    精髓!深入浅出剖析12个Zookeeper知识点

    Spring+SpringMVC+Mybatis分布式敏捷开发系统架构(附源码)

    Java大文件HTTP断点续传到服务器该怎么做?

    面试官:请你讲讲Thread.sleep(0) 的作用?

    展开全文
  • Java防止表单重复提交

    2014-06-30 12:05:14
    WAP商城订单重复提交解决方法,步骤: 1.

    WAP商城订单重复提交解决方法,步骤:

    1.javascript脚本控制法:提交按钮提交之后变为不可用状态(此步骤掩人耳目,只是对不懂技术的人而言能起一点作用)

    2.令牌控制法:在跳转到订单提交按钮的页面时,给页面添加一个唯一值,在订单提交过程中,确认这个唯一值是否已经用过(这样当第一次提交时会认为没有提交过,后来重复提交的订单执行到这一步会跳出)

    此处为第二种方法,以下是代码:

    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Random;
    
    import javax.servlet.http.HttpServletRequest;
    
    import sun.misc.BASE64Encoder;
    
    /**
     * @description: 令牌处理,防止重复提交
     * @author: shaojie
     * @date: 2014-5-21 下午02:32:49
     * @version: 1.0
     */
    public class TokenProcessor {
    	
    	/**
    	 * 生成一个令牌
    	 * @return String
    	 */
    	public String generateToken()  {  
    		String token=System.currentTimeMillis()+new Random().nextInt()+"";  
    		try {  
    			MessageDigest md=MessageDigest.getInstance("md5");  
    			byte[] md5=md.digest(token.getBytes());  
    			//base64编码  
    			BASE64Encoder encoder=new BASE64Encoder();  
    			return encoder.encode(md5);  
    		} catch (NoSuchAlgorithmException e) {  
    			// TODO Auto-generated catch block  
    			throw new RuntimeException(e);  
    		}  
    	}  
    	
    	/**
    	 * 管理令牌
    	 * @param request
    	 * @param mType 管理类型 p:存放 d:删除
    	 * @param void
    	 */
    	public void managerToken(HttpServletRequest request,String mType){
    		if("p".equals(mType)){
    			//产生随机数   
    			String token=generateToken();  
    			request.getSession().setAttribute("token", token); 
    		}else if("d".equals(mType)){
    			request.getSession().removeAttribute("token"); 
    		}
    	}
    	
    	/**
    	 * 令牌是否验证通过
    	 * @param request
    	 * @return boolean
    	 */
    	public boolean isTokenValid(HttpServletRequest request) {
    		String client_token = request.getParameter("token");
    		if (client_token == null) {
    			return false;
    		}
    		String server_token = (String) request.getSession().getAttribute("token");
    		if (server_token == null) {
    			return false;
    		}
    		if (!client_token.equals(server_token)) {
    			return false;
    		}
    		return true;
    	} 
    }

    然后在适当的位置存放令牌

    token.managerToken(request, "p");//存放令牌

    然后提交订单时删除对应的令牌存放值

    				if(!token.isTokenValid(request)){
    					logger.info("重复提交,phoneNo:"+phoneNo);
    					request.setAttribute(Constant.RETURNMSG, "不能重复提交同一订单");
    					return checkresult;
    				}
    				token.managerToken(request, "d");//删除令牌





    
    
    展开全文
  • 防止订单重复提交

    2019-09-29 22:41:37
    这时有一点需要我们考虑到:防止订单重复提交。 也就是说,我下单成功后,刷新页面,再次生成订单就会产生误会了。用户明明下了一次单,你却给人家发两次货,给人家收两次钱。 那么我们分析一下造成订单重复提交的...

    电商项目中,用户可能经常要下订单。这时有一点需要我们考虑到:防止订单重复提交。

    也就是说,我下单成功后,刷新页面,再次生成订单就会产生误会了。用户明明下了一次单,你却给人家发两次货,给人家收两次钱。


    那么我们分析一下造成订单重复提交的原因有哪些?

    一是转发引起的。

    我们知道页面跳转有两种方式:转发和重定向。所谓转发是在服务器端进行跳转,对用户时透明的,这时,浏览器中的地址是不会改变的。所以,如果此时刷新页面,会再次对服务器请求一次,造成多次下单;

    而重定向,是在客户端发生跳转,跳转时浏览器中的地址会发生改变。也就是说,我下单成功后,会马上重定向到一个新页面,浏览器地址栏改变,这是刷新,只会请求显示的地址,而不会请求上一次的地址。


    *注:重定向如果需要传递参数,可以采用下面方式

    ?
    1
    2
    3
         <result name= "success" type= "redirect" >/test/addSuccess.action?productId=${productId}</result>
    </action>



    二是服务器响应慢引起的。

    当用户点击下订单后,由于服务器反应时间过长没能及时看到响应信息,或者出于其它目的,多次点击“提 交”按钮,从而导致在服务器端接收到两条或多条相同的信息。如果信息需要存储到后台数据库中,如此以来就会产生数据库操作异常提示信息,以至于给用户带来 错误信息提示,从而给用户的使用带来不便。


    如何解决?

    以下思路,可以当个参考:

    1、下单成功后,重定向到其他页面

    这种方式,只能解决刷新时,生成订单的可能,但是不能防止多次点击的情况。

    2、利用struts2的token拦截器


    原理:

    Struts 2已经内置了能够防止用户重复提交同一个HTML表单的功能。它的工作原理:让服务器生成一个唯一标记,并在服务器和表单里各保存一份这个标记的副本。此 后,在用户提交表单的时候,表单里的标记将随着其他请求参数一起发送到服务器,服务器将对他收到的标记和它留存的标记进行比较。如果两者匹配,这次提交的 表单被认为是有效的,服务器将对之做出必要的处理并重新设置一个新标记。随后,提交相同的表单就会失败,因为服务器上的标记已经重置。

    Struts 2标签中的token标签,可以用来生成一个独一无二的标记。这个标记必须嵌套在form标签中使用,它会在表单里插入一个隐藏字段并把标记保存到 HttpSession对象里。toke标签必须与Token或Token Session拦截器配合使用,两个拦截器都能对token标签进行处理。Token拦截器遇到重复提交表单的情况,会返回一 个"invalid.token"结果并加上一个动作级别的错误。Token Session拦截器扩展了Token拦截器并提供了一种更复杂的服务,它采取的做法与Token拦截器不同,它只是阻断了后续的提交,这样用户不提交多 少次,就好像只是提交了一次。


    附Struts2的token拦截器demo一个:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <%@ page language= "java" contentType= "text/html; charset=UTF-8"
         pageEncoding= "UTF-8" %>
            <%
         String path = request.getContextPath();
         String basePath = request.getScheme()+ "://" +request.getServerName()+ ":" +request.getServerPort
     
    ()+path+ "/" ;
    %>
    <% @taglib uri= "/struts-tags" prefix= "s" %>
     
     
         
    <meta charset= "utf-8"
     
     
     
         <s:form id= "payFrm" action= "" >
             <s:hidden type= "hidden" name= "bankCardsId" id= "bankCardsId" value= "'0'/" >
             <s:hidden type= "hidden" name= "productId" value= "%{#request.productId}" >
             <s:token></s:token>
         </s:hidden></s:hidden></s:form>
         <span class = "sureBuy" >确认订单</span>
     
    <script type= "text/javascript" src= "<%=basePath %>weixin/js/fx.js" ></script>
      <script type= "text/javascript" src= "<%=basePath %>weixin/js/fx_methods.js" ></script>
    <script>
    $(function(){
    $( ".sureBuy" ).click(function(){
         var form = document.getElementById( "payFrm" );           
             form.action = "<%=path%>" +/test/sureOrder.action;
             form.method = "post" ;
             form.submit();
    });
         
    </script>


    struts2配置文件:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    < package name= "mallOrderAction" extends = "json-default" namespace= "/test" >
             
                 <interceptor-ref name= "defaultStack" >
                 <interceptor-ref name= "token" >
                 <result name= "invalid.token" type= "redirect" >/test/payRepeat.action</result>
                 <result name= "error" >/test/payError.jsp</result>
                 <result name= "success" type= "redirect" >/test/orderSuccess.action?productId=${productId}</result>
             </interceptor-ref></interceptor-ref></action>
             
                 <result name= "success" >/test/payRepeat.jsp</result>
             </action>
             
                 <result name= "success" >/test/orderSuccess.jsp</result>
             </action>
     
    </ package >


    action类文件:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class orderAction extends ActionSupport{
         /**
          * @MethodName  : sureOrder
          * @Description : 添加订单
          * @return
          * @throws Exception
          */
         public String sureOrder() throws Exception{
             
             //………下单的各种步骤…………………
             return SUCCESS;
             
         }
         
         /**
          * @MethodName  : payRepeat
          * @Description : 重复提交
          * @return
          */
         public String payRepeat(){
             return SUCCESS;
         }
         
         /**
          * @MethodName  : addSuccess
          * @Description : 下单成功
          * @return
          */
         public String addSuccess(){
             HttpServletRequest request = ServletActionContext.getRequest();
             Product product = product.getproductId(productId);
             request.setAttribute( "product" , product);
             return SUCCESS;
         }
    }


    转载于:https://www.cnblogs.com/jpfss/p/8072484.html

    展开全文
  • WAP商城订单重复提交解决方法,步骤: 1.javascript脚本控制法:提交按钮提交之后变为不可用状态(此步骤掩人耳目,只是对不懂技术的人而言能起一点作用) 2.令牌控制法:在跳转到订单提交按钮的页面时,给页面添加...

    转载来源:http://blog.csdn.net/u011410116/article/details/35989151

    WAP商城订单重复提交解决方法,步骤:

    1.javascript脚本控制法:提交按钮提交之后变为不可用状态(此步骤掩人耳目,只是对不懂技术的人而言能起一点作用)

    2.令牌控制法:在跳转到订单提交按钮的页面时,给页面添加一个唯一值,在订单提交过程中,确认这个唯一值是否已经用过(这样当第一次提交时会认为没有提交过,后来重复提交的订单执行到这一步会跳出)

    此处为第二种方法,以下是代码:

    1. import java.security.MessageDigest;  
    2. import java.security.NoSuchAlgorithmException;  
    3. import java.util.Random;  
    4.   
    5. import javax.servlet.http.HttpServletRequest;  
    6.   
    7. import sun.misc.BASE64Encoder;  
    8.   
    9. /** 
    10.  * @description: 令牌处理,防止重复提交 
    11.  * @author: shaojie 
    12.  * @date: 2014-5-21 下午02:32:49 
    13.  * @version: 1.0 
    14.  */  
    15. public class TokenProcessor {  
    16.       
    17.     /** 
    18.      * 生成一个令牌 
    19.      * @return String 
    20.      */  
    21.     public String generateToken()  {    
    22.         String token=System.currentTimeMillis()+new Random().nextInt()+“”;    
    23.         try {    
    24.             MessageDigest md=MessageDigest.getInstance(”md5”);    
    25.             byte[] md5=md.digest(token.getBytes());    
    26.             //base64编码    
    27.             BASE64Encoder encoder=new BASE64Encoder();    
    28.             return encoder.encode(md5);    
    29.         } catch (NoSuchAlgorithmException e) {    
    30.             // TODO Auto-generated catch block    
    31.             throw new RuntimeException(e);    
    32.         }    
    33.     }    
    34.       
    35.     /** 
    36.      * 管理令牌 
    37.      * @param request 
    38.      * @param mType 管理类型 p:存放 d:删除 
    39.      * @param void 
    40.      */  
    41.     public void managerToken(HttpServletRequest request,String mType){  
    42.         if(“p”.equals(mType)){  
    43.             //产生随机数     
    44.             String token=generateToken();    
    45.             request.getSession().setAttribute(”token”, token);   
    46.         }else if(“d”.equals(mType)){  
    47.             request.getSession().removeAttribute(”token”);   
    48.         }  
    49.     }  
    50.       
    51.     /** 
    52.      * 令牌是否验证通过 
    53.      * @param request 
    54.      * @return boolean 
    55.      */  
    56.     public boolean isTokenValid(HttpServletRequest request) {  
    57.         String client_token = request.getParameter(”token”);  
    58.         if (client_token == null) {  
    59.             return false;  
    60.         }  
    61.         String server_token = (String) request.getSession().getAttribute(”token”);  
    62.         if (server_token == null) {  
    63.             return false;  
    64.         }  
    65.         if (!client_token.equals(server_token)) {  
    66.             return false;  
    67.         }  
    68.         return true;  
    69.     }   
    70. }  
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Random;
    
    import javax.servlet.http.HttpServletRequest;
    
    import sun.misc.BASE64Encoder;
    
    /**
     * @description: 令牌处理,防止重复提交
     * @author: shaojie
     * @date: 2014-5-21 下午02:32:49
     * @version: 1.0
     */
    public class TokenProcessor {
    
        /**
         * 生成一个令牌
         * @return String
         */
        public String generateToken()  {  
            String token=System.currentTimeMillis()+new Random().nextInt()+"";  
            try {  
                MessageDigest md=MessageDigest.getInstance("md5");  
                byte[] md5=md.digest(token.getBytes());  
                //base64编码  
                BASE64Encoder encoder=new BASE64Encoder();  
                return encoder.encode(md5);  
            } catch (NoSuchAlgorithmException e) {  
                // TODO Auto-generated catch block  
                throw new RuntimeException(e);  
            }  
        }  
    
        /**
         * 管理令牌
         * @param request
         * @param mType 管理类型 p:存放 d:删除
         * @param void
         */
        public void managerToken(HttpServletRequest request,String mType){
            if("p".equals(mType)){
                //产生随机数   
                String token=generateToken();  
                request.getSession().setAttribute("token", token); 
            }else if("d".equals(mType)){
                request.getSession().removeAttribute("token"); 
            }
        }
    
        /**
         * 令牌是否验证通过
         * @param request
         * @return boolean
         */
        public boolean isTokenValid(HttpServletRequest request) {
            String client_token = request.getParameter("token");
            if (client_token == null) {
                return false;
            }
            String server_token = (String) request.getSession().getAttribute("token");
            if (server_token == null) {
                return false;
            }
            if (!client_token.equals(server_token)) {
                return false;
            }
            return true;
        } 
    }

    然后在适当的位置存放令牌

    1. token.managerToken(request, “p”);//存放令牌  
    token.managerToken(request, "p");//存放令牌

    然后提交订单时删除对应的令牌存放值

    1. if(!token.isTokenValid(request)){  
    2.     logger.info(”重复提交,phoneNo:”+phoneNo);  
    3.     request.setAttribute(Constant.RETURNMSG, ”不能重复提交同一订单”);  
    4.     return checkresult;  
    5. }  
    6. token.managerToken(request, ”d”);//删除令牌  
                   if(!token.isTokenValid(request)){
                        logger.info("重复提交,phoneNo:"+phoneNo);
                        request.setAttribute(Constant.RETURNMSG, "不能重复提交同一订单");
                        return checkresult;
                    }
                    token.managerToken(request, "d");//删除令牌





    
    


    展开全文
  • 电商中防止订单重复提交

    千次阅读 多人点赞 2019-01-08 19:01:59
    1、在网络延迟的情况下让用户有时间点击多次提交按钮导致表单重复提交。 2、表单提交后用户点击浏览器的刷新导致表单重复提交 3、用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交   1、...
  • 1、在第一次进入页面是生成令牌 createToken( "Paytoken" , 30 *60 ); 2、添加拦截器,在下一个Controller前添加 @Before ( SubmitValidator . class ) ..."请不要重复提交" ); } }
  • java如何防止重复提交

    万次阅读 2018-01-22 14:44:22
    重复提交 比较常见的重复提交的方式有: 1、多次点击提交按钮...1、对要防止重复提交的操作,页面放置服务器Session的Token值  2、提交到后台后做验证 @RequestMapping("/add") public void add(HttpServletReque
  • 防止订单重复提交或者缓存数据时使用Redis的一些基本用法和配置 ​ redis作为一种非关系型数据库,它有很多用法,这次就简单说下spring boot集成redis时简单的配置并简单介绍防止订单重复提交的做法和缓存的用法. 首先...
  • 防止订单重复提交的几种方法

    千次阅读 2018-10-31 18:07:52
    // 在提交函数外面定义个变量防止重复提交   //提交函数 function sublimit() {  if(flag){  return; // 如果存在就返回  }  flag = true;    $.post(url, {'data':id}, function(res){  ...
  • 防止订单重复提交面试 帕梅拉·福克斯(Pamela Fox)在最近的博客文章《 技术面试让我哭泣》中讲述了一个技术专家和会议发言人的个人故事,他们因梦想的工作而接受Skype / Stypi采访,陷入技术问题,流泪,几乎放弃...
  • 出现这种问题的原因主要在于短时间内...解决这种问题的方法一般是从解决订单重复提交+防止高并发情况下账户余额为负数的情况解决方案。(类似于商品库存超卖的解决方案) 一般完整的订单支付流程是这样的 1.后端生成...
  • Java语言防止表单重复提交token机制

    千次阅读 2018-08-07 17:10:53
    防止用户注册信息或者订单信息重复提交写入数据库,使用session的token令牌机制 也叫做令牌机制,除了重定向以外的另一种处理表单重复提交的方法 login.jsp================================================ &...
  • java,redis防止重复提交

    千次阅读 2019-01-16 15:30:11
    1.用户只发送了一次请求,但是因为网络问题或者其他各种各样的原因导致我们接收了两次所导致的重复提交 解决方案:提交时传入uuid,根据uuid是否相同来判断是否重复提交 try { DoubleSubmitHelperEntity ...
  • 利用redis+token+拦截器+注解(只在需要防止重复的接口上添加该注解即可)实现防止重复订单.
  • 在Web / App项目中,有一些请求或操作会对数据产生影响(比如新增、删除、修改),针对这类请求一般都需要做一些保护,以防止用户有意或无意的重复发起这样的请求导致的数据错乱。 常见处理方案 1.客户端  例如...
  • 通过自定义注解来标识哪些方法需要防止重复提交,比如下订单时保存订单数据只需要保存一次,但由于用户连续点击两次可能会造成多次保存,所以需要防止表单重复提交。 /** * 自定义注解防止表单重复提交 * */ @...
  • 重复提交的后果 用户在界面看到两个一模一样的订单,不知道应该支付哪个; 系统出现异常数据,影响正常的校验 解决方法 服务器A接收到请求之后,获取锁,获取成功 , 服务器A进行业务处理,订单提交成功; 服务器B接收到...
  • java后端解决重复提交问题

    万次阅读 2017-10-02 18:10:15
    一、为什么会出现重复提交?...对于前端的办法这里就不做演示了,因为前端的控制虽然能够防止数据的重复提交但是治标不治本。这里主要介绍第二种方法。 3.2 后端解决: 思路:主要是利用唯一Token
  • 并发情况下lua脚本防止重复提交订单 在商城系统结算时,生成交易码,存入redis和页面中,防止浏览器页面回退重复提交订单,重复与数据库做IO操作;但是在并发情况依然存在 多个请求同时验证交易码成功情况; 解决...
  • 1.防止表单重复提交 1.通过JavaScript屏蔽提交按钮(不推荐) 2.给数据库增加唯一键约束(简单粗暴) 3 利用Session防止表单重复提交(推荐) 第一次提交数据进入控制层 session.setAttribute(“test”,“test”...
  • 在开发产品,交付给用户使用之后,重复提交的现象偶尔或经常出现。开发人员觉得数据重复,用户觉得订单多了一个,产品觉得有bug。这可不是好现象。 下面以创建订单为例: 原因: 1、误操作两次点击下单按钮; 2、...
  • 如何防止form表单重复提交
  • 防止并发重复提交

    2020-01-13 21:16:05
    阅读文本大概需要6分钟。0x01:背景在业务开发中,我们常会面对防止重复请求的问题。当服务端对于请求的响应涉及数据的修改,或状态的变更时,可能会造成极大的危害。重复请求的后果在交易系统、...
  • JavaWeb防止表单重复提交的几种方式

    千次阅读 2019-06-27 22:21:02
    一、表单重复提交的常见应用场景 网络延迟的情况下用户多次点击submit按钮导致表单重复提交 用户提交表单后,点击【刷新】按钮...二、防止防止表单重复提交的方式 1、利用JavaScript防止表单重复提交 (1)、用Java...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,131
精华内容 2,852
关键字:

java防止订单重复提交

java 订阅