作为开放式的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.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);

                                    
}

                                    
//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中的业务逻辑处理得还不够细致,欢迎大家提出最宝贵的意见,一起探讨学习。

以上。 


版权声明:本文为jpfss原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/jpfss/p/9354654.html