第三方支付平臺支付接口及回調接口開發

做爲開放式的B/S架構程序,不管所屬電商,金融,機械製造,企業OA,ERP,CRM,CMS等等行業或系統中,第三方支付以及銀聯支付的業務必定是客戶關心所在,也是保證客戶系統盈利運營的一個重要保障。一般這種B2C或者C2C系統的開發,商戶用戶所關注的支付平臺大多離不開「阿里支付寶,快錢,騰訊財付通,易寶支付這種第三方支付平臺以及中國銀聯UnionPay....等等」這些方式。
最近某項目中涉及到支付的模塊與涉及流程,在此和你們分享一下。
1,名詞釋義html

商戶網站:好比淘寶,聚美,惟品會這種B2C/C2C的網站及後臺的管理系統,統稱爲商戶網站;主要負責對買家訂單數據的封裝,加密,
及支付平臺回調的訂單處理。
支付平臺:咱們須要開發的支付平臺,支付接口,支付模擬的Servlet,暴露出來的WebService接口url等;主要負責對買家請求來的
加密後的訂單數據進行解密,構造請求的URL,拼接參數,對Sign進行加密,對支付機構異步(或同步)請求回調的數據
進行封裝,解密回傳給商戶網站。

支付機構:好比阿里支付寶,快錢,騰訊財付通,易寶支付這種第三方支付平臺等支付機構。
Sign:支付機構爲商戶分配的一把「密鑰」與」合做者ID「同時分配,用作調用Base64,MD5等加密算法在加密解密時的一種私鑰,一般
與此相關聯的還有signType,就是加密方式。
回調:對上次請求端request中的url或指定的url進行http請求,或https請求
git

支付平臺請求,響應,及回調流程圖:web

2,業務流設計(本文只介紹alipay的即時到帳接口:"create_direct_pay_by_user")算法

       2.1  商戶網站對數據封裝加密,調用支付接口:json

                        2.1.1)商戶網站後臺對買家的訂單進行封裝,插入商戶網站db中的訂單表(好比:xxx_order);api

                         PayReturnVovo = new PayReturnVo();服務器

                            vo.setOrderId("kuaiqian00232");         架構

                            vo.setOrderAmount("20");      app

                            vo.setOrderTime("20140504121020"); dom

                            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層查詢買家用戶平臺帳戶餘額,並進行鎖表:在SQL的select後加入 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);                    

                              //根據訂單號區別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:  下行代碼的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.3)StringBuffer繪製跳轉請求的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);

                                     }

                                     //二、校驗支付結果

                                     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的方式,加密算法有個別差別。

相關文章
相關標籤/搜索