设计模式的应用-策略模式实现支付方式回调策略

简单了解下支付流程

支付宝支付流程

支付宝支付liuc

微信扫码支付流程

微信扫码支付流程

项目代码查看: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执行以上步骤,将具体怎么执行交给策略类来做,AliPayNotifyPloyImplWeChatPayNotifyPloyImpl, 这样在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提示发现---WeChatPayNotifyServletAliPayNotifyServlet代码重复,是时候应用使用模版方法重构了

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

注: 重构后的支付回调代码未测试

posted @ 2017-02-27 22:51  MarioLuo  阅读(3054)  评论(0编辑  收藏  举报