资讯专栏INFORMATION COLUMN

Java后端支付大杂烩之sps.controller(支付请求入口,配置文件)(五)

Joyven / 2216人阅读

摘要:重要的是学习过程,而不是结果。但,结果同样重要,加油。。在这提一点,由于网络原因等异常情况支付平台可能出现多次发送支付结果的情况,通知回调接口商户要注意做好接口幂等,其余的不再多说。

7、sps.controller.base,front. 说明

如果有幸能看到,其实只为自己记录,回头复习用

1、本文项目来自Martin404,自己只是临摹大佬的项目。

2、重要的是学习过程,而不是结果。但,结果同样重要,加油。gogogo。

3、框架搭建就略过了。配置文件太多。遇到的时候贴出来。也收藏起来,留着备用。

4、Gist、Insight.io for GitHub必备吧,划词翻译不懂的单词划一划。

5、代码提交到这里了GitHub。根据提交记录找自己想要的类库。

6、只为自己整理,大概的过了一边,在Service层哪里还需好好理解。。

目录

2、core.dao,service,web(重点是接口的设计)点这里

3、sps包~dto、enums、mq点这里

4、在Dao层定义了domian、mapper映射文件,想了解的可以去看看。有助于理解整个系统。点这里

5、pay.util.app。 pay.stratege,支付工具类,和支付策略点这里

6、sps.service及实现类(重点)点这里

首先看下支付的一些相关知识点

(1)、支付方案:

(2)、支付流程:

一般流程说明: 原作者

用户在商户网站选定商品并下单,在商户支付页面选择对应支付平台图标,进行支付;

商户按照文档提交支付请求接口组织报文,向支付平台发送支付请求;

如果是PC端,会跳转到对应支付平台的支付页面,如果是移动端,会唤起对应的支付工具,用户在支付平台输入支付信息,提交支付;

支付平台将支付结果通知商户;

若支付成功,则支付平台将交易结果异步发送给商户;

商户若未收到交易结果,则商户按照文档查询接口向支付平台发请求查询该交易,以确定消费交易的状态,支付平台收到 查询请求时,将同步返回该笔消费交易的交易结果;

商户若收到交易结果,如果未向支付平台反馈已收到交易结果,支付平台会重复发送交易结果。

在这提一点,由于网络原因等异常情况支付平台可能出现多次发送支付结果的情况,通知回调接口商户要注意做好接口幂等,其余的不再多说。

在线支付过程:

01)创建合法的商业购物网站,且在易宝支付平台申请成为商家,并提供商家银行卡号,等待审查

02)如果审查通过,和易宝支付签约,得向易宝支付一定的接口使用费或每笔交易的手续费,通常是交易额的1%左右

03)付款后,易宝会给每个商家一个唯一的商家编号,密钥,和接口文档和jar包

04)当用户请求来了,你得获取客户的相关信息,例如:订单号,金额,支付银行等14个信息

注意:其中hmac是由客户订单信息和商家密钥生成,通过易宝提供的工具包自动生成

05)用表单的方式,以POST或GET请求,使用GBK或GB2312向易宝发出支付请求,请求中带有14个参数

06)易宝如果验成功,即客户发送过来的信息与易宝生成的信息相同的话,易宝认为是合法用户请求,否则非法用户请求

注意:验证是否成功,主要通过hmac和密钥作用

07)如果是合法用户的支付请求的话,易宝再将请求转发到客户指定的银行,例如:招商银行

注意:易宝必须支持招商银行在线支付

08)凡是转账,查询,等等都由银行后台操作完成,是全封闭的,与易宝没有任何关系,千万不要认为是易宝在处理资金结算

09)银行处理完毕后,将响应结果转发到易宝在线支付平台

10)易宝在线支付经过加工处理后,再将结果响应到用户指定的外网可以访问的Servlet或Jsp页面

11)商家网站可以用GET方式接收易宝的响应数据,经过验证合法后,再将付款结果,显示在用户的浏览器

注意:验证是否成功,主要通过hmac和密钥作用

首先来看BaseController

public class BaseController {

    private Logger logger = LoggerFactory.getLogger(BaseController.class);

    /**
     * 获取用户ID,用户ID可能为NULL,需自行判断
     */
    protected Long getUserId(HttpServletRequest request) {
        String sId = request.getHeader("userId");
        if (!StringUtil.isEmpty(sId)) {
            try {
                Long userId = Long.parseLong(sId);
                return userId;
            } catch (NumberFormatException e) {
                logger.warn("请求头userId参数格式错误:{}", sId);
            }
        }
        return null;
    }

    /**
     * 获取用户ID,当userId为空的时候抛出异常
     */
    protected Long getNotNullUserId(HttpServletRequest request) throws BusinessException {
        Long userId = getUserId(request);
        if (userId == null) {
            throw new BusinessException("用户ID不能为空");
        }
        return userId;
    }

    /**
     * 获取请求来源类型
     */
    protected RequestFrom getRequestFrom(HttpServletRequest request) throws BusinessException {
        String from = request.getHeader("from");
        if (StringUtil.isEmpty(from)) {
            throw new BusinessException("请求头错误未包含来源字段");
        }
        try {
            int iFom = Integer.parseInt(from);
            return RequestFrom.getById(iFom);
        } catch (NumberFormatException e) {
            throw new BusinessException("请求头来源字段类型错误");
        }

    }

    /**
     * 获取移动端请求头信息
     */
    protected MobileInfo getMobileInfo(HttpServletRequest request) throws BusinessException {
        String appVersion = request.getHeader("appVersion");
        String systemVersion = request.getHeader("appSystemVersion");
        String deviceId = request.getHeader("appDeviceId");
        Integer width = null;
        Integer height = null;
        int night = 0;
        try {
            width = Integer.parseInt(request.getHeader("appDeviceWidth"));
            height = Integer.parseInt(request.getHeader("appDeviceHeight"));
            if (request.getHeader("nightMode") != null) {
                night = Integer.parseInt(request.getHeader("nightMode"));
            }
        } catch (NumberFormatException e) {
            throw new BusinessException("移动端请求头不符合约定");
        }
        if (StringUtil.isEmpty(appVersion) || width == null || height == null) {
            throw new BusinessException("移动端请求头不符合约定");
        }
        return new MobileInfo(appVersion, systemVersion, deviceId, width, height, night != 0);
    }

}

控制层异常统一处理

/**
 * 控制层异常统一处理
 */
public class RestErrorHandler {
    private static Logger logger = LoggerFactory.getLogger(RestErrorHandler.class);

    @ExceptionHandler(BindException.class)
    @ResponseBody
    public AjaxResult handleBindException(BindException exception) {
        AjaxResult result = AjaxResult.getError(ResultCode.ParamException);
        Set errors = new HashSet();
        for (FieldError er : exception.getFieldErrors()) {
            errors.add(new ValidationError(er.getObjectName(), er.getField(), er.getDefaultMessage()));
        }
        result.setData(errors);
        logger.warn("参数绑定错误:{}", exception.getObjectName());
        return result;
    }

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public AjaxResult handleBusinessException(BusinessException exception) {
        AjaxResult result = AjaxResult.getError(ResultCode.BusinessException);
        result.setMessage(exception.getMessage());
        logger.warn("业务错误:{}", exception.getMessage());
        return result;
    }

    @ExceptionHandler(SystemException.class)
    @ResponseBody
    public AjaxResult handleSystemException(SystemException exception) {
        AjaxResult result = AjaxResult.getError(ResultCode.SystemException);
        result.setMessage("系统错误");
        logger.error("系统错误:{}", exception);
        return result;
    }

    @ExceptionHandler(DBException.class)
    @ResponseBody
    public AjaxResult handleDBException(DBException exception) {
        AjaxResult result = AjaxResult.getError(ResultCode.DBException);
        result.setMessage("数据库错误");
        logger.error("数据库错误:{}", exception);
        return result;
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public AjaxResult handleException(Exception exception) {
        AjaxResult result = AjaxResult.getError(ResultCode.UnknownException);
        result.setMessage("服务器错误");
        logger.error("服务器错误:{}", exception);
        return result;
    }
}

支付通知入口:

/**
 * 支付通知入口
 * Created by Martin on 2016/7/01.
 */
@RequestMapping(value = "/open/payNotify")
public class PayNotifyController extends BaseController {

    private static Logger logger = LoggerFactory.getLogger(PayNotifyController.class);


    /**
     * 国内支付宝app通知回调
     * @param request
     * @param response
     * @throws SystemException
     * @throws BusinessException
     */
    @RequestMapping(value = "/alipayNotifyMainApp", method = RequestMethod.POST)
    public void alipayNotifyMainApp(HttpServletRequest request, HttpServletResponse response) throws SystemException, BusinessException {
        alipayNotifyService.alipayNotifyMainApp(request, response);
    }
    /**
     * 国内支付宝web通知回调
     * @param request
     * @param response
     * @throws SystemException
     * @throws BusinessException
     */
    @RequestMapping(value = "/alipayNotifyMain", method = RequestMethod.POST)
    public void alipayNotifyMain(HttpServletRequest request, HttpServletResponse response) throws SystemException, BusinessException {
        alipayNotifyService.alipayNotifyMain(request, response);
    }
    /**
     * 国际支付宝app通知回调
     * @param request
     * @param response
     * @throws SystemException
     * @throws BusinessException
     */
    @RequestMapping(value = "alipayNotifyGlobalApp", method = RequestMethod.POST)
    public void alipayNotifyGlobalApp(HttpServletRequest request, HttpServletResponse response) throws SystemException, BusinessException {
        alipayNotifyService.alipayNotifyGlobalApp(request, response);
    }
}

支付请求相关接口

/**
 * 支付请求相关接口
 */
@Controller
@RequestMapping("/app/payRequest")
public class PayRequestController extends BaseController {

    private static Logger logger = LoggerFactory.getLogger(PayRequestController.class);
    @Autowired
    private IPayRouteService payRouteService;

    /**
     * 组装支付请求报文
     * @param payRequestParam
     * @return
     * @throws BusinessException
     * @throws SystemException
     */
    @ResponseBody
    @RequestMapping(value = "/getPayParams", method = RequestMethod.POST)
    public AjaxResult getPayParams(@RequestBody PayRequestParam payRequestParam) throws BusinessException, SystemException {
        return AjaxResult.getOK(payRouteService.getPayRetMap(payRequestParam));
    }

}

接下来在看看配置文件,支付相关的暂时省略,因为俺没有。

generatorConfig


    
    

       

    
        
        
        
        
        
        
        
        

        
        
        


        
            
        
        
        
        
        
        

        
        

        

        

    

数据库配置:
#MySQL
mysql.jdbc.url=jdbc:mysql://127.0.0.1:3306/sps_db?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
mysql.jdbc.username=root
#连接数据库的密码.
mysql.jdbc.password=root
mysql.jdbc.initialSize=10
#连接池在空闲时刻保持的最大连接数.
mysql.jdbc.minIdle=10
#连接池在同一时刻内所提供的最大活动连接数。
mysql.jdbc.maxActive=100
#当发生异常时数据库等待的最大毫秒数 (当没有可用的连接时).
mysql.jdbc.maxWait=60000
mysql.jdbc.timeBetweenEvictionRunsMillis=60000
mysql.jdbc.minEvictableIdleTimeMillis=300000
mysql.jdbc.removeAbandonedTimeout=7200
mysql.jdbc.validationQuery=SELECT "x"
mysql.jdbc.testWhileIdle=true
mysql.jdbc.testOnBorrow=false
mysql.jdbc.testOnReturn=false
mysql.jdbc.filters=slf4j
mysql.jdbc.removeAbandoned=true
mysql.jdbc.logAbandoned=true

#Redis
redis.ip=127.0.0.1
redis.port=6379
redis.timeout=6000
#Redis-pool
redis.pool.maxTotal=10000
redis.pool.maxIdle=1000
redis.pool.testOnBorrow=true

#RabbitMQ
rabbitmq.master.ip=127.0.0.1
rabbitmq.master.port=5672
rabbitmq.master.username=guest
rabbitmq.master.password=guest

接着再看这几个

 applicationContext:



    
        
        
    

    
        
            
                classpath:server_config.properties
                classpath:sys_config.properties
            
        
    

    
    

    
    
        
        
        
    

    
        
        
        
        
        
    
    
    
    

    
        
        
        
        
        
    

    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

    
        
            ${shiro.guest.username}
        
    

    
    
        
    
    
    
        
        
        
    

    
        
            
            
            
            
            
            
            
            
            
        
    

    
        
        
        
        
        
        
        
    

    
        
    
mybatis_config




    
        
    
    
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
        
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
        
    


spring_mvc



    
    

    
    
        
    

    
    
        
        
        
    
    
    
        
        
    

    
        
    
    
    
        
    



spring_shiro



    
    
        
    
    
    
        
    
    
    
    
    
        
        
        
        
    

    
    
        
        
            
                
            
        
        
        
            
                /=anon
                /index.jsp=anon
                /app/**=stateless
            
        
    
    
    


Spring-rabbitmq



    

    
    
    

    
    
    
    
    
    
    
        
            
            
            
        
    
    
    
        
    
    
    
    
    


web.xml


    Pay Map Service
    
        webAppRootKey
        PayMap
    
    
    
        contextConfigLocation
        classpath:applicationContext.xml,classpath:spring_shiro.xml,classpath:spring_rabbitmq.xml
        
    

    
    
        MDCInsertingServletFilter
        
            ch.qos.logback.classic.helpers.MDCInsertingServletFilter
        
    
    
        MDCInsertingServletFilter
        /*
    

    
    
        shiroFilter
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        shiroFilter
        /*
    
    
    
    
        字符集过滤器
        encodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            字符集编码
            encoding
            UTF-8
        
    
    
        encodingFilter
        /*
    

    
        DruidWebStatFilter
        com.alibaba.druid.support.http.WebStatFilter
        
            exclusions
            *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
        
    
    
        DruidWebStatFilter
        /*
    

    
        loggingFilter
        com.guo.core.web.system.filters.LoggingFilter
    

    
        loggingFilter
        /*
    

    
        spring监听器
        org.springframework.web.context.ContextLoaderListener
    
    
        Introspector缓存清除监听器
        org.springframework.web.util.IntrospectorCleanupListener
    
    
        request监听器
        org.springframework.web.context.request.RequestContextListener
    
                                     c
        系统初始化监听器
        com.guo.core.web.system.listener.InitListener
    
    
        default
        /static/*
    
    
        spring mvc servlet
        springMvc
        org.springframework.web.servlet.DispatcherServlet
        
            spring mvc 配置文件
            contextConfigLocation
            classpath:spring_mvc.xml
        
        1
    
    
        springMvc
        /
    
    
        DruidStatView
        com.alibaba.druid.support.http.StatViewServlet
    
    
        DruidStatView
        /druid/*
    

    
        HiddenHttpMethodFilter
        org.springframework.web.filter.HiddenHttpMethodFilter
    

    
        HiddenHttpMethodFilter
        springMvc
    

    
    
        30
    

    
        index.jsp
    

这只是简单的过来一遍,对大概的流程有个印象。需要配置文件的时候能找到。如果你有幸能看到这段话,那就好好加油吧。gogogo。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68662.html

相关文章

  • Java后端支付杂烩core.dao,service,web(重点是接口的设计)(二)

    摘要:是一个使用语言集成三方支付的小,现已集成支付宝国内国际移动端端微信银联光大网关网页邮政支付,采用的技术栈为。系统初始化监听器在系统启动时运行进行一些初始化工作加载银联配置文件缓存初始化忽略过滤器我们先看关于日志的,真心看不懂,后面有一大堆。 PayMap PayMap是一个使用Java语言集成三方支付的小Demo,现已集成支付宝(国内、国际、移动端、PC端)、微信、银联(ACP、UPO...

    sourcenode 评论0 收藏0
  • Java后端支付杂烩core.common、utils(异常,ID,日期,字符串等工具类)(一)

    摘要:但,结果同样重要,加油。。而且可明确提出错误信息。业务异常的自定义封装类实现和一样,为了空间就不展示了。数据库异常在看两个类,验证信息异常。那就是字符串工具类。字符串处理及转换工具类,。 PayMap PayMap是一个使用Java语言集成三方支付的小Demo,现已集成支付宝(国内、国际、移动端、PC端)、微信、银联(ACP、UPOP)、光大(网关、网页)、邮政支付,采用的技术栈为:S...

    huayeluoliuhen 评论0 收藏0
  • Java 类文章 - 收藏集 - 掘金

    摘要:而调用后端服务就应用了的高级特分布式配置管理平台后端掘金轻量的分布式配置管理平台。关于网络深度解读后端掘金什么是网络呢总的来说,网络中的容器们可以相互通信,网络外的又访问不了这些容器。 在 Java 路上,我看过的一些书、源码和框架(持续更新) - 后端 - 掘金简书 占小狼转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注 物有本末,事有终始,知所先后,则近道矣 ......

    RayKr 评论0 收藏0
  • 这次要是讲不明白Spring Cloud核心组件,那我就白编这故事了

    摘要:我不听,我就是这么命名。任何服务启动以后,都会把自己注册到的注册表中当服务死亡的时候,也会通知。服务拿到结果后,会把结果缓存在本地的注册表里。根据负载均衡策略,从注册表中选择一个真正的实例地址。 原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。 这几天可真是热啊,泡个海澡是再好不过了。玩的正起劲,突然脚底绊上一股暗流,然后我就一直在水里旋转旋转旋转...终于...

    stdying 评论0 收藏0

发表评论

0条评论

Joyven

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<