对接上游支付接口,定义一个BaseHandler,其中getPayType用于获取当前Handler的支付类型。支付的核心逻辑和公共处理都在BaseHandler,个性化的处理交给各个实现类。
public abstract class BaseHandler {
/**
* 获取支付类型
* @return
*/
public abstract PayType getPayType();
...
}
不同的支付渠道,有不同的Handler,如AHandler、BHandler、CHandler,由于每个支付渠道的参数不同还有可能随时改动,所以我们使用了spring cloud的分布式配置中心,自然就用到了@RefreshScope注解。
@RefreshScope
@Component
public class AHandler extends BaseHandler {
...
}
@RefreshScope
@Component
public class BHandler extends BaseHandler {
...
}
@RefreshScope
@Component
public class CHandler extends BaseHandler {
...
}
支付工厂类进行接口分发:
如下的PayFactory,通过getHandler来获取处理器,不同的支付渠道有不同的处理器。springboot项目启动时接收到ApplicationReadyEvent事件,则通过获取所有继承了BaseHandler的类的相关实例,并缓存到handlerMap中。
@Component
public class PayFactory implements ApplicationListener<ApplicationReadyEvent>{
protected static Logger log = LoggerFactory.getLogger("run") ;
/**支付处理器map*/
private Map<Integer, BaseHandler> handlerMap = new HashMap<Integer, BaseHandler>();
@Autowired
private ApplicationContext applicationContext;
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info(" ==============初始化支付接口处理器开始 ==============");
Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class, false, false);
//用下面的方式获取bean,可能会获取到同一个类的多个实例
//Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class);
for(BaseHandler handler : handlerBeanMap.values()){
log.info("初始化支付接口处理器:{}" , handler.getClass().getName());
PayType payType = handler.getPayType();
if(payType == null){
throw new EcommerceException("支付处理器未明确支付类型");
}else if(handlerMap.containsKey(payType.getCode())){
throw new EcommerceException("同一种支付方式发现多个处理器," + payType.getCode());
}else{
handlerMap.put(payType.getCode(), handler);
}
}
log.info(" ==============初始化支付接口处理器结束 ==============");
}
public PayResult pay(PayContext context) throws EcommerceException {
BaseHandler handler = getHandler(context.getPayTypeCode());
return handler.pay(context);
}
public RefundResult refund(RefundContext context) throws EcommerceException {
BaseHandler handler = getHandler(context.getPayTypeCode());
return handler.refund(context);
}
/**
* 同步调用
*
* @param request
* @param response
* @throws IOException
*/
public void synNotify(PayType payType, HttpServletRequest request, HttpServletResponse response) throws IOException {
BaseHandler handler = getHandler(payType.getCode());
handler.synNotify(request, response);
}
/**
* 向第三方发起请求,检查支付状态或退款状态,若状态发生变化,则进行
* 数据库更新以及业务通知处理
* @param payInfo
* @return 返回true则状态有变化,返回false检测结果无变化
*/
public boolean check(PayInfo payInfo) {
BaseHandler handler = getHandler(payInfo.getPayType());
return handler.check(payInfo);
}
/**
* 异步调用
*
* @param request
* @param response
* @throws IOException
*/
public void asynNotify(PayType payType, HttpServletRequest request, HttpServletResponse response) throws IOException {
BaseHandler handler = getHandler(payType.getCode());
handler.asynNotify(request, response);
}
/**
* 支付报关接口,跨境电商使用
* @param query
* @return
* @throws EcommerceException
*/
public PayDeclareResult customsDeclare(PayDeclareQuery query) throws EcommerceException{
BaseHandler handler = getHandler(query.getPayTypeCode());
return handler.customsDeclare(query);
}
public PayDeclareResult getCustomsDeclareResult(PayDeclareQuery query) throws EcommerceException {
BaseHandler handler = getHandler(query.getPayTypeCode());
return handler.getCustomsDeclareResult(query);
}
/**
* 根据支付类型code获取处理器
* @param payTypeCode
* @return
*/
private BaseHandler getHandler(Integer payTypeCode){
BaseHandler handler = handlerMap.get(payTypeCode);
if(handler == null){
throw new EcommerceException("不支持的支付类型:" + payTypeCode);
}
return handler;
}
}
@RefreshScope的相关注意事项:
在PayFactory中的onApplicationEvent方法中,最开始没有使用分布式配置中心时,时这样获取bean的:
Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class);
使用了分布式配置中心后,报错如下:
2023-03-31 14:34:57.143 |-INFO [main] run [cn.x.x.pay.factory.PayFactory-64] -| 初始化支付接口处理器:cn.x.x.handler.AHandlerEnhancerBySpringCGLIBb9a535ff
2023-03-31 14:34:57.182 |-INFO [main] run [cn.x.x.pay.factory.PayFactory-64] -| 初始化支付接口处理器:cn.x.x.pay.handler.AHandlerEnhancerBySpringCGLIB20480174
2023-03-31 14:34:57.205 |-ERROR [main] org.springframework.boot.SpringApplication [org.springframework.boot.SpringApplication-830] -| Application run failed
cn.x.x.pay.common.exception.GlobleException: 同一种支付方式发现多个处理器,1
原因是创建Bean的时候如果是RefreshScope就缓存在一个专门管理的ScopeMap中,这样就可以管理Scope是Refresh的Bean的生命周期了(所以含RefreshScope的其实一共创建了两个bean)。
所以如果使用了RefreshScope,我们获取bean的时候使用下入方式就可以了!
Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class, false, false);