需求:
完成多通道支付系统,例如支持 微信支付,支付宝支付等,后续会持续集成其他支付通道。
条件:
为下游商户提供一个统一的下单接口,根据参数、通道标识码 来调用对就的通道完成下单。
思路:
遇到这种情景,首先想到的是使用 适配模式来完成。项目框架是spring 体系,所以要在pring下来做适配模式。解决各种 if else ,使不同通道代码解耦,并易扩展其他支付通道
1.定义一个下单的service接口,每一个支付通道就是一个独立的实现类。因为不同通道中具有相同的业务逻辑,所以上层在抽象一个父类。
2.创建一个集中管理和路由的上下文,来管理和路由各个通道实现类。
3.因为不同的支付传递的参数也不同,所以可根据接收参数的实体来进行路由。
代码:
注:按照代码执行顺序列出。
package com.zzkx.pay.wechat.controller.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.google.gson.Gson;
import com.zzkx.pay.common.enums.ExpCodeEnum;
import com.zzkx.pay.common.exception.CommonBizException;
/**
* @author lyy
*/
public class OrderRequestParamResolver implements HandlerMethodArgumentResolver {
private final Logger LOG = LoggerFactory.getLogger(OrderRequestParamResolver.class);
private final Map> map = new HashMap<>();
private final Reflections reflection = new Reflections("com.zzkx");
private final Gson gson = new Gson();
public OrderRequestParamResolver() {
Set> clazzs = reflection.getTypesAnnotatedWith(PayAnnotation.class);
for(Class> clazz : clazzs) {
PayAnnotation annotation = AnnotationUtils.getAnnotation(clazz, PayAnnotation.class);
String code = annotation.value();
map.put(code, clazz);
}
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasMethodAnnotation(PayAnnotation.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String value = parameter.getMethodAnnotation(PayAnnotation.class).value();
Object object = requestToBean(httpServletRequest,value);
return object;
}
private Object requestToBean(HttpServletRequest request,String name) throws UnsupportedEncodingException, IOException {
try {
BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
JSONObject jsonObject = new JSONObject(responseStrBuilder.toString());
String code = jsonObject.getString(name);
if(!StringUtils.isNotBlank(code))throw new CommonBizException(ExpCodeEnum.PARAM_INVALID);
Class> clazz = map.get(code);
if(clazz == null)throw new CommonBizException(ExpCodeEnum.CHANNEL_WITHOUT);
Object object = gson.fromJson(jsonObject.toString(), clazz);
return object;
} catch (UnsupportedEncodingException e) {
LOG.error(e.getMessage(),e);
new CommonBizException();
} catch(IOException e) {
LOG.error(e.getMessage(),e);
new CommonBizException();
} catch (JSONException e) {
throw new CommonBizException(ExpCodeEnum.JSON_INVALID);
}
return null;
}
}
package com.zzkx.pay.wechat.controller.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author lyy
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PayAnnotation {
String value();
}
@RestController
public class TestController {
@Autowired
private TestService testService;
@Autowired
private PayService
package com.zzkx.pay.wechat.controller.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* @author lyy
*/
@Component
public class PayContext implements BeanFactoryPostProcessor {
private static ConfigurableListableBeanFactory beanFactory;
private Map m = new HashMap<>();
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
PayContext.beanFactory = beanFactory;
init();
}
private void init() {
Class> clazz = PayService.class;
Object instance = Proxy.newProxyInstance(clazz.getClassLoader(),
new Class>[]{clazz},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = args[0].getClass().getSimpleName();
CommonPayService service = m.get(name);
return method.invoke(service, args);
}
});
beanFactory.registerSingleton(clazz.getSimpleName(), instance);
Map beansOfType = beanFactory.getBeansOfType(CommonPayService.class);
System.out.println(beansOfType);
for(Entry entry : beansOfType.entrySet()) {
ParameterizedType type = (ParameterizedType) entry.getValue().getClass().getGenericSuperclass();
Type[] arguments = type.getActualTypeArguments();
Class> claz = (Class>) arguments[0];
m.put(claz.getSimpleName(), entry.getValue());
}
}
}
public interface PayService {
public Object createOrder(T t);
}
public interface CommonPayService extends PayService{
}
public abstract class AbstractPayServiceImpl implements CommonPayService {
//公共部分代码
}
@Service
public class AlipayServiceImpl extends AbstractPayServiceImpl {
@Override
public Object createOrder(AlipayEntity t) {
System.out.println(t.toString());
return null;
}
}
@Service
public class WechatServiceImpl extends AbstractPayServiceImpl {
@Override
public Object createOrder(WechatPayEntity t) {
System.out.println(t.toString());
return null;
}
}
@PayAnnotation("alipay")
public class AlipayEntity implements EntityBase{
private String orderId;
private String goodsName;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
@Override
public String toString() {
return "AlipayEntity [orderId=" + orderId + ", goodsName=" + goodsName + "]";
}
}
@PayAnnotation("wx")
public class WechatPayEntity implements EntityBase {
private String orderId;
private String mchId;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
@Override
public String toString() {
return "WechatPayEntity [orderId=" + orderId + ", mchId=" + mchId + "]";
}
}
/**
* @author lyy
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List resolvers) {
resolvers.add(new OrderRequestParamResolver());
}
}
测试:
返回结果打印:
WechatPayEntity [orderId=1, mchId=null]
type换成 alipay 打印:
AlipayEntity [orderId=1, goodsName=nan]