zoukankan      html  css  js  c++  java
  • 第三方支付平台支付接口及回调接口开发

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

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

    1,名词释义

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

                            支付平台回调的订单处理。

           支付平台我们需要开发的支付平台,支付接口,支付模拟的Servlet,暴露出来的WebService接口url等;主要负责对买家请求来的

                            加密后的订单数据进行解密,构造请求的URL,拼接参数,对Sign进行加密,对支付机构异步(或同步)请求回调的数据

                            进行封装,解密回传给商户网站

     

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

           Sign支付机构为商户分配的一把密钥合作者ID“同时分配,用做调用Base64MD5等加密算法在加密解密时的一种私钥,通常

                 
     
     与此相关联的还有signType,就是加密方式。

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

     



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

       
      

     第三方支付平台支付接口及回调接口开发




     

     

     

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

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

                            2.1.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密钥

                                Stringkey = dao.getKeyByUserId(userId);

                                //   根据key使用base64加密算法对订单信息进行加密

                                StringSignedJson = CryptUtil.encryptBase64Des(orderJson, key); 

     

                            2.1.2)于此同时调用dao层查询买家用户平台账户余额,并进行锁表:在SQLselect后加入 forupdate wait n(最好

         
         
         
         
       
     1-5秒,此处的 数值为httpclient请求超时时长)为防止订单被多用户修改。

                           

     

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

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

                                //获取输入参数

                                InputStreamis = request.getInputStream();

                                //把接收的加密流转成String类型

                                StringpayMsgJson = IOUtils.toString(is, "utf-8");

                                //base64进行解密

                                byte[]byteJson = CryptUtil.decryptBASE64payMsgJson

                                StringstrJson = new String(byteJson,"UTF-8");

                                //把解密后的json转换成实体vo

                                try{

                                       pVo = (BankPayVo)PaymentJsonUtil.jsonToBean(strJson,BankPayVo.class);

                                 }catch (Exception e) {

                                        e.printStackTrace();

                                  
         
    throw(e);

                                }
     

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

                                publicString CreateUrl(PayBankEntity payBankEntity) throws BankpayException,SQLException{        

                                  StringwebPartentId = payBankEntity.getWebPartentId();

                                  //通过DB获取阿里支付Config信息

                                  AliPayAccountDaoImplaccount = new AliPayAccountDaoImpl();

                                  AliPayAccountVoaccVo = account.getAccountInfo(webPartentId);                    

                                  //根据订单号区别b2ab2cpartner参数设置

                                  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但是vodb中显示为return_url

                                  Stringreturn_url = accVo.getPaReturnUrl();

                                  StringItemUrl="";
                           2.2.2.temp
     PS:  下行代码的CreateUrl()是根据请求参数首字母降序排列,把参数重新构造成新的url。 

                                  ItemUrl= Payment.CreateUrl(paygateway,service,sign_type,inputCharset,payment_type,

                                                                 partner,key,out_trade_no,total_fee,return_url,seller_email,subject,it_b_pay);

                                  System.out.println("异步通知返回agbpay地址:"+ return_url); 

                                        returnItemUrl;

                                  }


                          2.2.3StringBuffer绘制跳转请求的html dom元素,把参数请求到支付机构

                            publicString getBankHtml(PayBankEntity payBankEntity) throws BankpayException {

                                     StringBuffer sbHtml = new StringBuffer();

                                         try {

                                               sbHtml.append("");

                                               sbHtml.append("

    支付网关");

                                               sbHtml.append("

                                               sbHtml.append("

    ");

                                            }catch (Exception e) {

                                              throw new BankpayException("系统异常,错误描述:" + e.getMessage());

                                           }

                                      return sbHtml.toString(); 

                           }
     

                          2.2.4切记不要忘记设置支付机构回调支付平台的回调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()

                                    //获取输入参数

                                    InputStreamis = request.getInputStream();

                                    //转成String类型

                                    String payMsgJson =IOUtils.toString(is, "utf-8");

                                    PayReturnVovos = PaymentJsonUtil.jsonToBean(payMsgJson, PayReturnVo.class);

                                    request.setAttribute("returnStr",vos);

                                    newAliPayReturnBo().performTask(request, response);

     

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

                               

                            @SuppressWarnings("unused")

                            publicstatic String performTask(HttpServletRequest request,

                                         HttpServletResponseresponse) throws IOException, ServletException {

                            StringreturnStr = "";

                            StringwebPartentId = "";

                            try{

                                         Stringsign = request.getParameter("sign");

                                         //支付状态:TRADE_FINISHED(普通即时到账的交易成功状态)||TRADE_SUCCESS(开通

                                          了高级即时到账或机票分销产品后的交易成功状态)

                                         StringtradeStatus = request.getParameter("trade_status");

                                         //订单编号

                                         StringorderNo = request.getParameter("out_trade_no");

                                         //通知類型

                                         Stringnotify_type = request.getParameter("notify_type");

                                         //支付宝交易流水号

                                         Stringtrade_no = "";

                                         //订单总价

                                         Stringamount = request.getParameter("total_fee");

                                         if(request.getParameter("trade_no") != null) {

                                                     trade_no= request.getParameter("trade_no");

                                         }

                                         StringalipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?"

                                                                 +"partner="

                                                                 +partner

                                                                 +"¬ify_id="

                                                                 +request.getParameter("notify_id");

                                         //获取支付宝ATN返回结果,true是正确的订单信息,false 是无效的

                                         //StringresponseTxt = CheckURL.check(alipayNotifyURL);

                                         Mapparams = new HashMap();

                                         //获得POST 过来参数设置到新的params

                                         for(Iterator iter = requestParams.keySet().iterator(); iter

                                                                 .hasNext();){

                                                     Stringname = (String) iter.next();

                                                     String[]values = (String[]) requestParams.get(name);

                                                     StringvalueStr = "";

                                                     for(int i = 0; i < values.length; i++) {

                                                                 valueStr= (i == values.length - 1) ? valueStr + values[i]  :valueStr + values[i] + ",";

                                                     }

                                                     params.put(name,valueStr);

                                         }

                                         //2、校验支付结果

                                         StringpayStatus = "1";

                                         Stringmysign = com.alipay.util.SignatureHelper.sign(params,privateKey);

                                         //验证

                                         booleanverifySuccess = mysign.equalsIgnoreCase(sign);

                                         //获取支付交易状态

                                         booleantradeFinished = tradeStatus

                                                                 .equalsIgnoreCase("TRADE_SUCCESS")

                                                                 ||tradeStatus.equalsIgnoreCase("TRADE_FINISHED");

                                         if(verifySuccess&& tradeFinished)

                                          {

                                                     //TODO 调用agbweb接口告知支付结果

                                                     PayReturnVovos = (PayReturnVo) request.getAttribute("returnStr");

                                                     StringwebPartengId = vos.getWebPartentId();

                                                     //通过DB获取阿里支付Config信息

                                                     AliPayAccountDaoImplaccount = new AliPayAccountDaoImpl();

                                                     AliPayAccountVoaccVo = account.getAccountInfo(webPartengId);

                                                     Stringkey = accVo.getWebKey();

                                                     vos.setOutTradeNo(vos.getBillNo());

                                                     vos.setTotal_free(vos.getTotal_free());

                                                     vos.setPrivate_key(key);

                                                     StringnotifyType = vos.getNotifyType();

                                                     StringpayStatuss = vos.getPay_status();

                                                     //         支付银行

                                                     if(notifyType.equals("trade_status_sync")) {

                                                                 vos.setBankName("ALIPAY");

                                                     }else

                                                                 vos.setBankName("QUICKMONEY");

                                                     //         支付结果

                                                     if(payStatuss.equals("TEADE_SUCCESS")|| payStatuss.equals("TEADE_FINISHED")){

                                                     //         阿里-支付成功

                                                      vos.setTradeFlag("ALIPAY_T");

                                                     }

                                                     returnStr= PaymentJsonUtil.beanToJson(vos);

                                                     //         原封Json+key

                                                     StringreturnStrWithKey = key + returnStr;

                                                     //        MD5加密

                                                     StringbyteMD5 = MD5Util.MD5Encode(returnStrWithKey);

                                                     returnMsg(request,response, returnStr , byteMD5);

     

                                             }else if (!verifySuccess) { // "AliPay返回的结果信息认证没有通过"

                                             //}else if (false) { // "AliPay返回的结果信息认证没有通过"

                                                     thrownew BankpayException("Alipay支付返回失败");

                                             }else { // AliPay返回没有TRADE_FINISHED

                                                     thrownew BankpayException("Alipay支付返回失败");

                                             }

                                             }catch (Exception e) {

                                                     e.printStackTrace();

                                         }

                                         return returnStr;

                                    }

     

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

                            publicstatic void returnMsg(HttpServletRequest request,

                                         HttpServletResponseresponse, String strMsg , String strMD5)            

                            try{

                                         URLurl = new URL(

                                         "http://10.1.126.10:8080/agb/payResponse.servlet?str="+ strMsg + "&strMD5=" + strMD5);

                                         HttpURLConnectionhttp = (HttpURLConnection) url.openConnection();

                                         http.setRequestMethod("POST");

                                         http.setDoOutput(true);

                                         http.setDoInput(true);

                                         System.setProperty("sun.net.client.defaultConnectTimeout","30000");// 连接超时30

                                         System.setProperty("sun.net.client.defaultReadTimeout","30000"); // 读取超时30

                                         http.connect();

                                         //TODO 把数据回写到agbweb

                                         OutputStreamos = http.getOutputStream();

                                         //os.write(strMsg.getBytes("UTF-8"));//传入参数

                                         os.flush();

                                         os.close();

                                         InputStreamis = http.getInputStream();

                                    }catch (IOException e) {

                                         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)

         
         
      3)
    里接口

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

                  5)商户网站(db操作订单)的操作流程。

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


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






    以上。 


  • 相关阅读:
    13.ng-value
    Android 下使用 JSON 实现 HTTP 请求,外加几个示例!
    PHP完整的AES加解密算法使用及例子(256位)
    常用对称加密算法(DES/AES)类(PHP)
    随机字符串生成算法
    JAVA实现AES加密
    Base64的好处
    什么是VC、PE、LP、GP?
    mysql update操作
    iOS开发:用DES对字符串加解密
  • 原文地址:https://www.cnblogs.com/jpfss/p/9354654.html
Copyright © 2011-2022 走看看