作为开放式的 B/S 架构程序,无论所属电商,金融,机械制造,企业 OA , ERP , CRM , CMS 等等行业或系统中,第三方支付以及银联支付的业务一定是客户关心所在,也是保证客户系统盈利运营的一个重要保障。通常这种 B2C 或者 C2C 系统的开发,商户用户所关注的支付平台大多离不开 “ 阿里支付宝,快钱,腾讯财付通,易宝支付这种第三方支付平台以及中国银联 UnionPay....等等” 这些方式。

最近某项目中涉及到支付的模块与涉及流程,在此和大家分享一下。

1,名词释义

      商户网站:比如淘宝,聚美,唯品会这种B2C/C2C的网站及后台的管理系统,统称为商户网站;主要负责对买家订单数据的封装,加密,                         及支付平台回调的订单处理。

      支付平台:我们需要开发的支付平台,支付接口,支付模拟的Servlet,暴露出来的WebService接口url等;主要负责对买家请求来的                         加密后的订单数据进行解密,构造请求的URL,拼接参数,对Sign进行加密,对支付机构异步(或同步)请求回调的数据                         进行封装,解密回传给商户网站。

      支付机构:比如阿里支付宝,快钱,腾讯财付通,易宝支付这种第三方支付平台等支付机构。

      Sign:支付机构为商户分配的一把“密钥”与”合作者ID“同时分配,用做调用Base64,MD5等加密算法在加密解密时的一种私钥,通常                  与此相关联的还有signType,就是加密方式。

      回调:对上次请求端request中的url或指定的url进行http请求,或https请求

     支付平台请求,响应,及回调流程图:       

java 通联支付分账 通联支付接口文档_java 通联支付分账

2,业务流设计(本文只介绍alipay的即时到账接口:"create_direct_pay_by_user")

      2.1  商户网站对数据封装加密,调用支付接口:

                      )商户网站后台对买家的订单进行封装,插入商户网站db中的订单表(比如:xxx_order);

              

PayReturnVovo = new PayReturnVo();
                           vo.setOrderId("kuaiqian00232");         
                           vo.setOrderAmount("20");      
                           vo.setOrderTime("20140504121020"); 
                           vo.setProductName("3M网线,送水晶头");      
                            vo.setProductId("2213229319378");    
                            vo.setProductNum("2");         
                            vo.setPayType("00");*/          
                            //   把模拟的表单数据转成Json
                           StringorderJson= PaymentJsonUtil.beanToJson(vo);
                           //   通过db获取商家key密钥
                          
                           //   根据key使用base64加密算法对订单信息进行加密
                          
                      )于此同时调用dao层查询买家用户平台账户余额,并进行锁表:在SQL的select后加入forupdate wait n(最好
                              为1-5秒,此处的 数值为httpclient请求超时时长)为防止订单被多用户修改。

     2.2  支付平台响应请求及解密,调用支付机构接口:

                        2.2.1)支付平台响应请求,对数据进行解密;

获取输入参数
                           InputStreamis = request.getInputStream();
                           //把接收的加密流转成String类型
                           StringpayMsgJson = IOUtils.toString(is, "utf-8");
                          进行解密
                           byte[]byteJson = CryptUtil.decryptBASE64payMsgJson
                           StringstrJson = new String(byteJson,"UTF-8");
                           //把解密后的json转换成实体vo
                          
                                 
                            }catch (Exception e) {
                                   e.printStackTrace();
                                                              }

                        2.2.2)从db查询商户协议信息,构造不同方式的支付机构所需请求的url;

publicString CreateUrl(PayBankEntity payBankEntity) throws BankpayException,SQLException{        
                            
                            通过DB获取阿里支付Config信息
                             AliPayAccountDaoImplaccount = new AliPayAccountDaoImpl();
                             AliPayAccountVoaccVo = account.getAccountInfo(webPartentId);                    
                             //根据订单号区别b2a和b2c对partner参数设置
                             StringstrOrderNo = payBankEntity.getOrderNo();      
                            阿里支付合作伙伴ID
                             Stringpartner = accVo.getPaPartner();            
                             //阿里支付key
                             Stringkey= accVo.getPaKey();            
                            阿里支付接口
                             Stringpaygateway = accVo.getPaPayGateWay();         
                             //阿里支付服务名
                             Stringservice = accVo.getPaService();              
                            阿里支付签名Sign加密方式
                             Stringsign_type = accVo.getPaSignType();
                              卖家账号,邮箱
                             Stringseller_email = accVo.getPaSellerEmail();   
                             //###### Form Web ###### 商户网站订单
                             Stringout_trade_no = payBankEntity.getOrderNo();  
                             //###### Form Web ###### 交易总额
                             Stringtotal_fee = payBankEntity.getMoney(); 
                             //###### Form Web ######   商品名称
                             String subject= payBankEntity.getProductId();                        
                             //###### Form Web ######   商品展示地址
                             StringinputCharset = accVo.getPaInputCharset();     
                             //###### Form Web ###### 支付类型
                             Stringpayment_type = payBankEntity.getPaymentType();       
                             //超时时长
                             Stringit_b_pay = accVo.getPaItBBay();             
                             //!!! 在此修改参数为异步notify_url但是vo和db中显示为return_url
                             Stringreturn_url = accVo.getPaReturnUrl();
                            StringItemUrl="";

                      2.2.2.temp)PS:

ItemUrl= Payment.CreateUrl(paygateway,service,sign_type,inputCharset,payment_type,
                                                           
                            异步通知返回agbpay地址:"+ return_url); 
                                   returnItemUrl;
                            
                    )StringBuffer绘制跳转请求的html dom元素,把参数请求到支付机构;
                      
                                StringBuffer sbHtml = new StringBuffer();
                                    try {
                                          sbHtml.append("");
                                         
 
支付网关 
"); 
                                          sbHtml.append("
                                          sbHtml.append("
 
"); 
                                      
                                        系统异常,错误描述:" + e.getMessage());
                                      }
                                return sbHtml.toString(); 
                      }

                    )切记不要忘记设置支付机构回调支付平台的回调url,大多数支付机构的参数为同步和异步两种,设置支付机构的                              url目的在于它进行了我们的请求。处理之后对订单数据及订单等状态的回写,进而支付平台可以封装,                               加密成json串,继续调用商户网站,对这次支付的信息进行更改,执行具体业务。

                              下面是阿里的api,同步和异步回调路径不能同时为空。

               notify_url      服务器异步通知页面路径   String(160)     支付宝服务器主动通知商户网站里指定的页面Http路径                    可空

               returl_url      服务器同步通知页面路径   String(160)     支付宝完成处理后当前页面自动跳转到商户网站的Http路径              可空

                              下面是快钱的api,同步和异步回调路径不能同时为空。

               pageUrl     接受支付结果的页面地址   String(256)     需要是绝对地址,与bgUrl不能同时为空,当bgUrl为空时,生效          可空

               bgUrl       接受支付结果后台代码地址  String(256)    需要是绝对地址,与pageUrl不能同时为空,当pageUrl为空时,生效   可空

     2.3  支付平台响应支付机构回调:被支付机构接收的订单支付成功或失败之后,回调我们支付平台的接口。

                       1)把支付宝的请求输入流转成我们需要的vo对象,调用2)中的performTask()。

       

//获取输入参数
                              
                              转成String类型
                               String payMsgJson =IOUtils.toString(is, "utf-8");
                              
                               request.setAttribute("returnStr",vos);
                              newAliPayReturnBo().performTask(request, response);

                       2)把支付宝的请求输入流转成我们需要的vo对象,调用2)中的performTask()。                          

                      

                      

   

HttpServletResponseresponse) throws IOException, ServletException {
                      
                      
                      
                                   
                                   支付状态:TRADE_FINISHED(普通即时到账的交易成功状态)||TRADE_SUCCESS(开通
                                     了高级即时到账或机票分销产品后的交易成功状态)
                                   
                                   订单编号
                                   
                                   通知類型
                                   
                                   支付宝交易流水号
                                   
                                   订单总价
                                   
                                   
                                               
                                   
                                    StringalipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?"
                                                           
                                                           
                                                           
                                                           
                                   获取支付宝ATN返回结果,true是正确的订单信息,false 是无效的
                                   
                                   
                                   获得POST 过来参数设置到新的params中
                                   
                                                           
                                               
                                               
                                               
                                               
                                                           
                                               
                                               
                                   
                                   、校验支付结果
                                   
                                   
                                   验证
                                   
                                   获取支付交易状态
                                   
                                                           
                                                           
                                   
                                     {
                                                //TODO 调用agbweb接口告知支付结果
                                               
                                               
                                               通过DB获取阿里支付Config信息
                                                AliPayAccountDaoImplaccount = new AliPayAccountDaoImpl();
                                               
                                               
                                               
                                               
                                               
                                               
                                               
                                                //         支付银行
                                               
                                                           
                                               
                                                           
                                                //         支付结果
                                               
                                                //         阿里-支付成功
                                                 vos.setTradeFlag("ALIPAY_T");
                                               
                                                returnStr= PaymentJsonUtil.beanToJson(vos);
                                                //         原封Json+key
                                               
                                                //       加密
                                               
                                               
                                       返回的结果信息认证没有通过"
                                        //}else if (false) { // "AliPay返回的结果信息认证没有通过"
                                               支付返回失败");
                                       返回没有TRADE_FINISHED
                                               支付返回失败");

                                   

                                   

                              

                      3)回调商户网站的接口,告知支付状态以及回调的订单信息。

                      

                       

HttpServletResponseresponse, String strMsg , String strMD5)           
                      
                                   
                                    "http://10.1.126.10:8080/agb/payResponse.servlet?str="+ strMsg + "&strMD5=" + strMD5);
                                   
                                   
                                   
                                   
                                    System.setProperty("sun.net.client.defaultConnectTimeout","30000");// 连接超时30秒
                                    System.setProperty("sun.net.client.defaultReadTimeout","30000"); // 读取超时30秒
                                   
                                    //TODO 把数据回写到agbweb
                                   
                                   传入参数
                                   
                                   
                                   
                              
                                    e.printStackTrace();                                    throw(e);

                                  

                      

                      4)被支付机构接收的订单有可能存在回调失败等情况,虽然这种情况是百万分之一的机会,但为了防止交易过程没有                             进行回调,也可以通过Spring的定时任务注解:@Scheduled注解进行“对账接口”的定时对账,在此不进行详细                             介绍,接口名为“Sign_trade_query”。

    

2.4  商户网站响应支付平台回调:

                      1)流获取,转换String UTF-8;

                      2)解密,Json转化为Vo;

                      3)执行某个Service/Bo;

                      4)更新DB,订单表等;

                      5)回写页面,告知用户支付结果。

      本篇日志仅大致描述了支付宝交易的一次请求流程:             1)商户网站(订单加密)               2)订单解密)支付平台(构造url)              阿里接口

            封装订单vo -- 支付平台 -- 订单加密,模拟请求

            商户网站(db操作订单)的操作流程。其中包括其中的4次加密以及2次回调和两次模拟的http请求。其他第三方或银联支付平台与此结构大致一样,只是API中的参数或构造URL的方式,加密算法有个别差异。

       仅供参考,个人觉得bo中的业务逻辑处理得还不够细致,欢迎大家提出最宝贵的意见,一起探讨学习。以上。