设计模式的应用-策略模式实现支付方式回调策略
简单了解下支付流程
支付宝支付流程
微信扫码支付流程
项目代码查看: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
注: 重构后的支付回调代码未测试