簡單了解下支付流程
支付寶支付流程
微信掃碼支付流程
項目代碼查看:https://git.oschina.net/lkqm/ploy
重構前的代碼:
Servlet
以下代碼有點亂,看注釋,了解這個步驟即可,執行回調的Servlet:
支付寶
/**
* 支付結果回調Servlet
*
* @author
*/
@WebServlet("/pay/notify/alipay.do")
public class AlipayNotifyServlet extends HttpServlet {
private static final long serialVersionUID = 8158440606464368180L;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// 1.獲得支付寶服務器post過來的參數
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
// 2.驗證參數的合法性(根據簽名驗證保證數據時,從支付寶平台的數據)
boolean verify_result = AlipayNotify.verify(params);
String trade_status = request.getParameter("trade_status");
if (verify_result
&& (trade_status.equals("TRADE_FINISHED") || trade_status
.equals("TRADE_SUCCESS"))) {
try {
// 3.支付成功,執行業務邏輯
String out_trade_no = request.getParameter("out_trade_no");
String total_fee = request.getParameter("total_fee");
// 修改訂單狀態
UserService userService = ServiceFactory.getService(UserService.class);
userService.paySuccess(out_trade_no,
String.valueOf(total_fee),
String.valueOf(Order.PAY_WAY_ALIPAY));
response.reset();
PrintWriter out = response.getWriter();
// 4.返回執行成功的標識與支付寶服務器通信,否則支付寶服務器會多次再POST數據
out.print("SUCCESS");
out.flush();
} catch (ServiceFailedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
微信
/**
* 微信支付回調地址
*
* @author
*
*/
public class WechatpayNotifyServlet extends HttpServlet {
private static final long serialVersionUID = 8158440606464368180L;
private UserService userService = ServiceFactory.getService(UserService.class);
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException{
// 1. 獲得請求參數(用戶支付成功后,微信服務器post數據過來)
InputStream inputStream = request.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
StringBuilder sb = new StringBuilder();
String s ;
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> parameterMap = null;
try {
parameterMap = XMLUtil.doXMLParse(sb.toString());
} catch (JDOMException e1) {
e1.printStackTrace();
}
//過濾空 設置 TreeMap
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
Iterator<String> it = parameterMap.keySet().iterator();
while (it.hasNext()) {
String parameter = it.next();
String parameterValue = parameterMap.get(parameter);
String v = "";
if(null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
// 賬號信息
String key = PayConfigUtil.API_KEY; // key
//2. 判斷簽名是否正確
if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
//------------------------------
//處理業務開始
//------------------------------
String resXml = "";
if("SUCCESS".equals(packageParams.get("result_code"))){
// 3.這里是支付成功
//////////執行自己的業務邏輯////////////////
String out_trade_no = (String)packageParams.get("out_trade_no");
String total_fee = (String)packageParams.get("total_fee");
try{
double money = Integer.valueOf(total_fee)/100.0; // 分轉化為員
userService.paySuccess(out_trade_no, String.valueOf(money), String.valueOf(Order.PAY_WAY_WECHAT));
} catch(ServiceFailedException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
//////////執行自己的業務邏輯////////////////
//4. 通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
System.out.println("支付失敗,錯誤信息:" + packageParams.get("err_code"));
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
}
//------------------------------
//處理業務完畢
//------------------------------
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else{
System.out.println("通知簽名驗證失敗");
}
}
}
開始重構
上面兩個Servlet像極了,獲取請求數據,驗證數據,支付成功判斷,執行成功業務邏輯...,這不是模版模式的應用嗎?對,但是這里先用策略模式重構下支付回調的問題!!!
定義一個支付工具類PayNotifyUtil
執行以上步驟,將具體怎么執行交給策略類來做,AliPayNotifyPloyImpl
和WeChatPayNotifyPloyImpl
, 這樣在Servlet中就無須關心是什么支付平台回調的。
目錄結構
Servlet代碼
只需要關注使用的什么支付策略!!!
**
* 支付寶支付回調通知
*/
@WebServlet("/pay/notify/alipay.do")
public class AlipayNotifyServlet extends HttpServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// 1. 獲得支付策略
PayNotifyPloy payNotifyPloy = new AliPayNotifyPloyImpl();
PayNotifyUtil payNotifyUtil = new PayNotifyUtil(payNotifyPloy);
// 2. 獲得請求參數
Map<String, String> payInfo = payNotifyUtil.loadPayData(request);
// 3. 驗證支付數據
String result="";
if( !payNotifyUtil.verifyData(payInfo)) {
System.out.println("驗證失敗");
} else if (!payNotifyUtil.isPaySuccess(payInfo) ) {
// 支付失敗
result = payNotifyUtil.getFailedResponseData();
} else {
//4. 執行業務邏輯
OrderMode orderMode = payNotifyUtil.getServiceMode(payInfo);
// 修改訂單狀態
UserService userService = ServiceFactory.getService(UserService.class);
try {
userService.paySuccess(orderMode.getTradeNo(), orderMode.getTotalFee(), PayWayEnum.ALIPAY);
// 支付成功數據
result = payNotifyUtil.getSuccessfulResponseData();
} catch (Exception e) {
// 邏輯執行失敗,等同於支付失敗,所以返回失敗數據
result = payNotifyUtil.getFailedResponseData();
}
}
response.reset();
PrintWriter out = response.getWriter();
// 返回數據
out.print(result);
out.flush();
}
}
將來的某一天,需要增加微信支付功能,對應微信支付回調,只需要copy上面代碼然后,修改策略的實現類即可。
這個時候IDEA IDE提示發現---WeChatPayNotifyServlet
和AliPayNotifyServlet
代碼重復,是時候應用使用模版方法重構了
PayNotifyUtil工具類
/**
* 支付之付款工具類
* Created by on 2017/2/25.
*/
public class PayNotifyUtil {
// 支付策略
private PayNotifyPloy payNotifyPloy;
public PayNotifyUtil(PayNotifyPloy payNotifyPloy) {
this.payNotifyPloy = payNotifyPloy;
}
// 加載支付信息
public Map<String, String> loadPayData(HttpServletRequest request) throws IOException {
return payNotifyPloy.loadPayInfo(request);
}
// 驗證參數
public boolean verifyData(Map<String, String> postData) {
return payNotifyPloy.verifyData(postData);
}
// 判斷是否支付成功
public boolean isPaySuccess(Map<String, String> data) {
return payNotifyPloy.isPaySuccess(data);
}
// 獲得支付成功的返回數據
public String getSuccessfulResponseData() {
return payNotifyPloy.getSuccessfulResponseData();
}
// 獲得支付失敗的返回數據
public String getFailedResponseData() {
return payNotifyPloy.getFailedResponseData();
}
// 獲得業務結果需要的數據
public OrderMode getServiceMode(Map<String, String> params) {
return payNotifyPloy.getServiceMode(params);
}
}
策略接口
/**
* 支付回調策略接口
* Created by on 2017/2/25.
*/
public interface PayNotifyPloy {
// 加載支付回調信息
Map<String, String> loadPayInfo(HttpServletRequest request) throws IOException;
// 獲得返回給支付平台代表成功的
String getSuccessfulResponseData();
// 獲得返回給支付平台代表失敗
String getFailedResponseData();
// 判斷是否支付成功
boolean isPaySuccess(Map<String, String> data);
// 驗證數據的合法性
boolean verifyData(Map<String, String> postData);
// 獲得需要的信息(比如支付的訂單號、支付的金額)
OrderMode getServiceMode(Map<String, String> params);
}
現在你可以定義你的具體平台的實現類了!!!
總結
由於經驗不足和對支付平台每種支付方式的接口了解不詳細,重構的代碼還有很多細節不足,比如異常的設計,代碼已經傳在git上,供參考,並求指點:https://git.oschina.net/lkqm/ploy
注: 重構后的支付回調代碼未測試