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

本文介绍了一个支付回调处理的重构案例,通过策略模式和模板方法模式实现了支付宝和微信支付回调的通用处理逻辑,降低了代码的耦合度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单了解下支付流程

支付宝支付流程

支付宝支付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

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

转载于:https://www.cnblogs.com/jcook/p/6476731.html

### PHP 中实现支付功能的策略设计模式 在 PHP 开发中,使用 **策略模式** 可以有效地管理多种支付方式(如支付宝、财付通等),使代码结构清晰且易于扩展。以下是关于如何通过策略模式实现支付功能的最佳实践。 #### 策略模式简介 策略模式是一种行为型设计模式,它允许定义一系列算法并将它们封装到独立的类中,使得这些算法可以互换使用。这种模式非常适合于像支付这样的场景,因为不同的支付渠道可能有不同的逻辑和配置需求[^1]。 --- #### 示例代码:基于策略模式支付功能实现 下面是一个完整的示例代码,展示如何利用策略模式实现支持多支付渠道的功能: ```php <?php // 定义支付接口 interface PaymentStrategy { public function pay($amount); } // 支付支付策略 class AlipayStrategy implements PaymentStrategy { public function pay($amount) { echo "正在通过支付支付金额: $amount 元\n"; // 调用支付宝 API 进行实际支付操作... } } // 财付通支付策略 class TenpayStrategy implements PaymentStrategy { public function pay($amount) { echo "正在通过财付通支付金额: $amount 元\n"; // 调用财付通 API 进行实际支付操作... } } // 上下文类,用于设置具体的支付策略并执行支付 class PaymentContext { private $strategy; public function setPaymentStrategy(PaymentStrategy $strategy) { $this->strategy = $strategy; } public function executePayment($amount) { if ($this->strategy === null) { throw new Exception("未设置支付策略"); } $this->strategy->pay($amount); } } // 使用示例 try { $context = new PaymentContext(); // 设置为支付支付 $context->setPaymentStrategy(new AlipayStrategy()); $context->executePayment(100); // 切换为财付通支付 $context->setPaymentStrategy(new TenpayStrategy()); $context->executePayment(200); } catch (Exception $e) { echo '错误: ' . $e->getMessage(); } ?> ``` 上述代码展示了如何通过 `PaymentStrategy` 接口统一不同支付方式的行为,并通过上下文类动态切换具体使用支付策略[^2]。 --- #### 最佳实践建议 1. **抽象化支付逻辑** 将每种支付方式的具体实现细节隐藏在其对应的策略类内部,外部只需调用通用方法即可完成支付动作[^3]。 2. **增强可维护性** 如果未来需要新增其他支付渠道(例如微信支付或其他第三方服务),只需要创建一个新的策略类继承自 `PaymentStrategy` 即可,而不需要修改现有代码。 3. **安全性与合规性** 在实际应用中,除了基本的支付流程外,还需要特别注意数据加密、签名验证以及回调的安全校验等问题。 4. **日志记录与异常处理** 对每次支付请求都应做好详细的日志记录以便后续排查问题;同时针对可能出现的各种异常情况要有完善的捕获机制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值