支付模块重构整理与总结
来源:互联网 发布:dsdt editor mac 1.3 编辑:程序博客网 时间:2024/06/11 09:39
由于之前支付模块与订单耦合,也就是说如果要支付,必须要走一个订单流程,但是大家都知道:订单流程是很复杂的,而且订单只是支付的一种源头,因此就对这块代码进行了重构,解耦。
首先,我们系统支付的接口目前集成支付宝和财富通,而且整个支付过程涉及以下几个环节:
- 发请求,即向第三方支付接口发请求参数
- 正常返回的接收以及处理
- 对Notify请求的接收和处理。
首先,我们来抽象一下发请求的部分:
- 封装请求参数
public class PayInfoWrapper { private List<String> bankList = new LinkedList<String>(); private String defaultbank; private String callBackClass; /** * 交易总金额,以分为单位 */ private long totalFee; /** * 交易流水号 */ private String tradeNo = TradeSequenceUtil.getTradeNo(); /** *支付方式 */ private PayMethod payMethod;}
此处省去了必要的getter 和 setter。支付包括的参数: 交易金额,交易流水号,还有你的支付方式(财富通,支付宝,银行也隶属于支付宝,因为支付宝兼容银行),还有本次交易成功后的回调处理类。 - 封装发请求的类
我们抽象了一个父类PayRequestHandler,也就是支付请求处理类,从类图上可以看到,他有一个public方法,3个protected方法,很自然想到了是模板方式。让我们来看一下forwaridToPay方法里面的内容:
public abstract class PayRequestHandler { private static final String PAYMENT_PROPERTIES_LOCATION = "payment.properties"; private static Properties payProp = null; /** * 默认的reutrn url */ protected static final String DEFAULT_RETURN_URL_KEY = "return.url"; /** * 默认的notify url */ protected static final String DEFAULT_NOTIFY_URL_KEY = "notify.url"; /** * 初始化加载return.url和notify.url */ static { try { payProp = PropertiesUtil.loadProperties(PAYMENT_PROPERTIES_LOCATION); } catch (IOException e) { e.printStackTrace(); } } public final String forwardToPay(PayInfoWrapper payInfoWrapper) { Map<String, String> params = buildPayParam(payInfoWrapper); //返回处理的Url String returnUrl = payProp.getProperty(DEFAULT_RETURN_URL_KEY); //notify处理的Url String notifyUrl = payProp.getProperty(DEFAULT_NOTIFY_URL_KEY); Assert.notNull(returnUrl,"必须要在payment.properties中配置return url"); Assert.notNull(notifyUrl,"必须要在payment.properties中配置notify url"); params.put("return_url",returnUrl); params.put("notify_url",notifyUrl); //签名,并追加签名参数 doSign(params); return buildPaymentString(params); } protected String buildPaymentString(Map<String, String> params) { StringBuilder sbHtml = new StringBuilder(); sbHtml.append("<form id=\"payForm\" name=\"payForm\" action=\"" + getPaymentURL() + "\" " + "method=\"get\">"); for (Map.Entry<String, String> entry : params.entrySet()) { sbHtml.append("<input type=\"hidden\" name=\"" + entry.getKey() + "\" value=\"" + entry.getValue() + "\"/>"); } sbHtml.append("<input type=\"submit\" value=\"" + "确认" + "\" style=\"display:none;\"></form>"); sbHtml.append("<script>document.forms['payForm'].submit();</script>"); return sbHtml.toString(); } protected abstract Map<String, String> buildPayParam(PayInfoWrapper payInfoWrapper); protected abstract String getPaymentURL(); protected abstract void doSign(Map<String, String> params);}我们先是掉用buildPayParam,来获取支付参数,这个方法是一个抽象方法,由子类各自实现,因为每种支付方式都有各自不同的参数。 接着从配置文件中读取returnUrl 和notifyUrl。 也就是我们一开始提及的2个处理返回结果的地址。
buildPaymentString,就是构建一个自定提交的表单,这才是真正的发送支付请求的类。
接下的问题就是我们怎么知道,调用那个实现类来发送支付请求列?于是就写了一个管理类:
public class PaymentManager { private static Map<PayMethod,Class<? extends PayRequestHandler>> payServiceMap = new HashMap<PayMethod,Class<? extends PayRequestHandler>>(); /** *将默认的方式初始化管理,并提供 addPayService()以方便运时添加 */ static{ //支付宝,实现类 addPayService(PayMethod.directPay, AliPayRequestHandler.class); //银行,目前也是通过支付宝来实现 addPayService(PayMethod.bankPay, AliPayRequestHandler.class); //财富通,实现类 addPayService(PayMethod.Tenpay, TenPayRequestHandler.class); } /** * 得到支付实例 * @param payMethod * @return */ public static PayRequestHandler getPayRequestHandler(PayMethod payMethod){ try { return payServiceMap.get(payMethod).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } /** * 提供的对外接口,以方便运行时添加 * @param payMethod * @param payService */ public static void addPayService(PayMethod payMethod,Class<? extends PayRequestHandler> payService){ payServiceMap.put(payMethod,payService); }}
这样在Controller里面,你只需要封装好你的PayInfoWrapper, 然后得到payRequestHandler,然后调用支付接口。如下代码:
@RequestMapping("/test") public String testPay(Model model){ PayInfoWrapper wrapper = new PayInfoWrapper(); wrapper.setTotalFee(1); wrapper.setCallBackClass(OrderPayCallback.class.getName()); wrapper.setPayMethod(PayMethod.directPay); PayRequestHandler handler = PaymentManager.getPayRequestHandler(wrapper.getPayMethod()); model.addAttribute("form", handler.forwardToPay(wrapper)); return "payTo"; }
那么这整个发支付请求的过程,就完成了。
接下来,我们就看一下支付完成后,回调部分如何处理了,我们要达到用一个Controller就能处理所有的返回结果。
经常一系列的抽象,抽象出了几个对象: 交易信息,返回接收类,返回后的业务处理类,业务处理后的返回结果。
请求的统一处理地方
public class PayResponseHandler { private static Logger logger = Logger.getLogger(PayResponseHandler.class); //交易信息 private TradeInfo tradeInfo; //送过去的交易回调处理类 private String callBackHandlerClass; private TradeInfoBuilder builder; //所有返回的参数,原样封装 private Map<String,String> backParams; public PayResponseHandler(HttpServletRequest request) { String extra_common_param = request.getParameter("extra_common_param");//阿里的支付回传参数 String attach = request.getParameter("attach");//腾讯的回传参数 if (!StringUtils.isEmpty(extra_common_param)) { this.builder = new AliTradeInfoBuilder(); this.callBackHandlerClass = extra_common_param.trim(); } if (!StringUtils.isEmpty(attach)) { this.builder = new TenTradeInfoBuilder(); this.callBackHandlerClass = attach.trim(); } Assert.notNull(this.callBackHandlerClass,"没有定义支付回调处理接口类"); this.tradeInfo = this.builder.buildFromRequest(request); this.backParams = buildParam(request); } public CallBackResult handleCallback() { CallBackResult result ; try { Class<PayCallback> classz = (Class<PayCallback>) Class.forName(callBackHandlerClass); result = classz.newInstance().doAfterSuccess(this.tradeInfo,this.backParams); return result; } catch (ClassNotFoundException e) { logger.error("未定义的支付返回处理类:" + callBackHandlerClass, e); e.printStackTrace(); } catch (Exception e) { logger.error("支付返回处理时发生了错误", e); e.printStackTrace(); } result = new CallBackResult(); result.setResult(false); return result; } private Map<String, String> buildParam(HttpServletRequest request) { //获取GET过来反馈信息 Map<String, String> params = new HashMap<String, String>(); Map requestParams = request.getParameterMap(); for (Object oName : requestParams.keySet()) { String name = (String) oName; 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); } return params; }}
TradeInfo,就是封装的交易信息
TradeInfoBuilder,顾名思义就是构建TradeInfo的, 因为每种支付方式的参数不同,所以构建方式必须不同。
public interface TradeInfoBuilder { public abstract TradeInfo buildFromRequest(HttpServletRequest request);}
业务处理类的接口:
/** * 支付请求成功返回后的业务处理接口 * User: amos.zhou * Date: 13-10-24 * Time: 上午11:54 */public interface PayCallback { /** * 在支付请求成功返回后,处理自己的回调业务 * @param tradeInfo 封装好的交易信息 * @param backParams 所有返回的参数的 Map,方便特殊的自定义处理 * @return CallBackResult,包括支付结果(true || false),以及需要返回给调用端(Controller,页面)的参数,以及需要跳转的页面等。 */ public CallBackResult doAfterSuccess(TradeInfo tradeInfo, Map<String, String> backParams);}
业务处理类的返回值(getter,setter省去):
public class CallBackResult { private boolean result; private Map<String, Object> data = new HashMap<String, Object>(); private String successUrl; private String failureUrl; /** * 跳转至下一个处理流程 * @return */ public String skipToNextProcess() { if(StringUtils.isEmpty(this.successUrl) || StringUtils.isEmpty(this.failureUrl)){ throw new IllegalArgumentException("没有设置待跳转的Url,不能进行跳转。请确保successUrl与failureUrl已被正确初始化"); } if (success()) { return this.successUrl; } return this.failureUrl; }}
那么统一接收返回值的Controller就很明了:
public class PayCallBackController { @RequestMapping(value = "/return") @RenderHeaderFooter public String normalReturn(HttpServletRequest request, Model model) { PayResponseHandler handler = new PayResponseHandler(request); CallBackResult result = handler.handleCallback(); model.addAllAttributes(result.getData()); return result.skipToNextProcess(); } @RequestMapping(value = "/notify") public void notify(HttpServletRequest request, HttpServletResponse response) throws IOException { PayResponseHandler handler = new PayResponseHandler(request); CallBackResult result = handler.handleCallback(); response.getWriter().write(result.success() ? "success":"fail"); }}
一个处理return,一个处理notify
那么至此,整个支付流程就抽象完成了,也基本上没与其它业务耦合在一起。 那么我们开篇说的订单支付功能如何实现列?
public class OrderPayCallback implements PayCallback { @Override public CallBackResult doAfterSuccess(TradeInfo tradeInfo, Map<String, String> backParams) {}}
交易信息 与 所有参数都已经给到你了,那么仅需要在此处完成的业务逻辑就可以了。
那么以后需要调用支付模块时仅需:
1.统写回调业务处理类
2.在发送支付请求时用: wrapper.setCallBackClass(OrderPayCallback.class.getName());
那么就OK了。 至此整个支付流程就完全独立了。
好了,细节就不一一堆述了。大致思想就是如此。由于整个重构,从接手这个模块到现在为止也才四天,肯定还有很多可以改进和完善的地方,慢慢来吧。 以后当交易量大的时候,可以考虑做队列,做池等手段,来解决。只不过目前 还没有需要,所以不在本次重构的范围内。
- 支付模块重构整理与总结
- 整理下载文件与重构
- ios客户端发现_动画屋后期页面重构与悬浮评论分享模块开发项目总结
- pyodbc模块简单总结整理
- 重构 Airport 模块
- 考试系统总结与下一步重构
- 模式与重构 总结列表
- 重定向的解释总结整理
- UITextField 整理与总结
- 基础知识总结与整理
- 机房重构-封装模块
- 【重构】重构概要--六大重构模块
- 支付宝在线支付接口开发教程与总结
- 支付宝在线支付接口开发教程与总结
- Python 与时间相关模块整理
- itertools模块知识与整理删减【Python】
- 重构与领域模型设计一点总结
- 机房重构总结--界面与功能篇
- Cow Picnic (P3256)
- 控件随窗体大小改变而改变大小和位置
- Java 回调机制样例
- Android内存泄漏分析及调试
- 域名,IP地址相互转换等
- 支付模块重构整理与总结
- DrawDIBDraw显示图像颠倒的解决方法
- 利用photoshop给淘宝店的宝贝鞋子图片美化教程
- 非法探取密码的原理及其防范
- 33岁老程序员传递正能量
- @OneToOne讲解
- android让图片右边对齐的方法layout_gravity="right"
- 嵌入式系统RTEMS剖析(1)
- paip.python 执行shell 带空格命令行attilax总结