支付公司如何赚钱?支付网关如何设计?
移动支付目前在中国已经是很普及的业务了。
支付业务本身的门槛很高,在各个国家都有很强的限制。最高的门槛就是要获得支付的资质,也就是要从国家拿到支付牌照。没有支付牌照是不能管理资金的。传统的支付主要是国家银行在做,现在移动支付普及也是因为支付宝和微信拿到了支付牌照。
没有支付牌照,如何赚钱?
大家一定很好奇,现在市面上很多公司在做支付业务,这些公司没有支付牌照,为什么能展开支付业务呢?在支付业务中,涉及两点,一个是资金流,一个是信息流。资金流就是管理资金,比如将这个账户的资金转到另一个账户中,这是真正的资金管理;信息流就是,“这个账户的资金转到另一个账户中”这样的一条信息或指令,并不是真正的资金操作。有支付牌照的公司,即可以做资金流也可以做信息流,没有支付牌照的公司只能做信息流。比如市面上很多做聚合支付的公司,可以通过他们的pos机扫用户的支付宝或微信的支付码,完成支付,这个过程中,聚合支付的公司做的事情是,收集收款人和付款人的相关信息(信息流),然后调用支付宝、微信、银行的接口,告诉他们这个钱该怎么扣,真正扣钱的事就是支付宝、微信、银行干的(资金流)。所以说这些聚合支付的公司是支付宝和微信的服务商(ISV),是替支付宝和微信更好的服务用户的。
那么这些聚合支付的公司是如何赚钱的呢?如果没有这些聚合支付公司,商户可以直接申请支付宝、或微信的支付,这个时候,商户每从用户那里收一笔钱,支付宝或微信都会收手续费,这个手续费一般是千分之六。现在有了聚合支付公司,这些公司替支付宝、微信收钱,这个千分之六的手续费就由聚合支付公司和支付宝/微信之间分润。
支付宝/微信为什么愿意和这些聚合支付公司分润呢?自己做赚这千分之六不香吗?支付在商业活动中是最重要的一项业务,但不是唯一的业务,处除了支付,商业活动中商家还有很多其他的需求,比如开票、导流、卡券等,这些脏活累活很繁杂,对支付宝、微信这样体量的公司来说,自己做投入产出比并不高,所以需要有聚合支付这样的公司作为自己的ISV完成这些工作,这也是聚合支付公司的生存之道。
支付网关设计
上面简单介绍了支付的相关业务。下面介绍一下做聚合支付网关(时序图蓝色部分)的相关设计,这里的设计也是我在做支付时的相关思考。
对于一个公司来说,有很多业务线都可能会用到支付,所以我们可以做一个公用的支付网关模块,为其他业务提供支撑,这样其他业务需要使用支付功能,就可以直接对接这个支付网关了。支付网关里可以聚合各方的支付渠道,比如支付宝渠道、微信渠道等,可以考虑使用工厂模式。
很多时候,调用支付宝或微信的支付接口时,需要用户在手机上输入密码(小额可以免密支付),这个过程中,调用支付宝/微信的支付接口同步响应的结果是支付中,并不是最终的支付成功,所以我们需要调用支付宝或微信的查询接口轮询查询,直到查询到最终的结果(官方也是这样推荐的)。用户在手机上使用支付宝钱包或微信钱包完成支付后,支付渠道就会知道最终支付结果,同时支付渠道会将最终结果回调给我们的支付网关。在支付中我们一般不信任回调,回调只是一个触发支付网关查询的机制而已。
上游的业务调用方在调用完支付网关的支付接口后,也需要轮训查询支付网关是不是支付完成。支付网关拿到支付渠道的最终结果后,同样会回调给上游的调用方。
上面大概就是一个支付网关要做的事情。
这里面有几个比较关键的问题需要解决。
支付网关轮询查询支付渠道的最终支付状态该怎么做?轮训时间间隔是多少?轮询多少次?
支付网关通知上游业务方失败怎么办?
如果上游的业务方很多,上游的业务的前、后端都需要轮训查询最终状态,支付网关的压力大,怎么办?
1.轮训支付渠道查询最终结果
支付网关调用支付宝或微信等支付渠道的支付接口后,获得的同步结果很可能是支付中的状态,这是就需要主动轮询调用支付渠道的查询接口,进行轮训查询,直到获得本次支付的最终状态。
要轮询多久,轮询多少次?可以参考支付宝和微信官方文档中的轮询时间。比如微信的通知时间间隔(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3)。这里大家可以根据自己的业务需求进行设计, 比如0-120S,查询间隔:3s/7s/15s/15s/15s/15s/15s/15s/20s;120S以后,查询间隔: 2m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h,总共查询一天左右。
有了这个时间间隔,如何实现呢?
使用Java任务调度框架Quartz。可以使用quartz,可以定时从数据库中将支付状态不是最终结果的记录找到,然后调用支付渠道的查询接口进行查询。有什么问题呢?1.查询的间隔时间很难控制。2.在支付高峰时期,一次查询会查到很多支付中的记录,上一次任务还没处理完成,新的任务又启动了,会造成很多问题。3.使用quartz,项目分布式部署时,每台机器都会同时启动一个任务,难以控制。
从上面可以看出,传统的任务调度很难满足需求,这就需要设计方案了。这里给出一个方案参考,不是最好的。
每一次支付,都需要经过查询间隔为3s/7s/15s/15s/15s/15s/15s/15s/20s/2m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h的22次查询。这里就使用22个队列,每一个队列,表示该队列中的查询任务需要隔多久后进行查询。比如调用完成支付渠道的支付接口后,如果是中间支付结果,就把查询参数及需要查询的时间点放到第一个队列中,第一个队列要求3秒后查询,队列是先进先出的,队列中前一个查询任务没处理,后一个任务一定不用处理。当消费到这个队列中的任务时,比较任务需要处理的时间点和当前的时间点是不是一致,如果时间没到就sleep,直到这个时间满足消费的时间要求。当时间到了,查询完成后,如果还是支付中间状态,就把查询参数和下一次需要查询的时间点放入第二个队列等待下一次处理。如果查询到最终结果,这次支付的查询就结束了。其实这种队列就是延迟队列。
在上面的队列中,越靠近前面的队列,需要查询的任务越多,越靠近后面的队列,存储的任务越少,如何保证前面的队列中的任务没有积压快速消费也是个问题。
接下来就是技术选型的事情了。
队列可以使用redis的List结构,可以使用list的rpop/lpush/rpoplpush命令,做队列的入栈和出栈操作,将上一个队列右边的任务放到下一个队列的左边。前面的队列中任务比较多,可以考虑多生成几个,做数据分片,然后增加下游的消费线程,这部分需要自己代码实现。
也可以使用Kafka的topic,这种方式下,可以建20个topic,前面的topic可以使用更多的partition,后面的topic可以少一些partition,就可以解决数据分片的问题。同时Kafka的offset的提交使用手动提交,不要使用自动提交,因为可能消费的任务还没到处理时间,需要sleep。关于topic的生产和消费可以自己代码控制。
在我的实现中,选择使用Kafka。关于源码可以联系我。
2.支付网关通知上游业务方失败怎么办
在支付宝、微信的通知/回调业务中,如果通知失败,也会按一定的时间间隔重试通知的。
通知失败需要重试,重试的时间间隔也越来越大。这里其实和上面的轮询查询的队列实现差不多,上面轮询查询中,如果查询成功,就结束,如果查询失败,就放到下一个队列中。这里可以做一个通知模块,也使用一组延迟队列,如果通知成功就结束,通知失败就放入下一个队列等待重试。
3.上游轮询,支付网关的压力大怎么办?
在支付的业务中,上游业务方为了能让用户以最快的速度知道支付结果,提升用户体验,通常业务方的前端后端都会选择轮询下游拿到支付最终结果,这就回给下游带来很大的压力。为了减少上游的查询次数,我们使用MQTT的发布订阅做消息通知,可以减少上游的业务的前、后端轮训的次数。支付模块使用mqtt的好处一是,支付模块只需要向mqtt一个端发送消息即可,需要监听消息的上游业务的多个端可以都来订阅mqtt消息。如果页面需要监听mqtt消息,则需要考虑如何让mqtt的连接信息(IP、port、密码等)如何被前端页面安全的拿到。
mqtt在我之前的文章中也有写过,大家可以参考。
今天就和大家简单聊了一下支付网关相关的事项,要想真正做好一个支付网关,抗住上百万的并发,上面的简单介绍是远远不够的,同时还需要做各种优化,各种分布式处理。
完成,收工!
【传播知识,共享价值】,感谢小伙伴们的关注和支持,我是【诸葛小猿】,一个彷徨中奋斗的互联网民工!!!
版权声明: 本文为 InfoQ 作者【诸葛小猿】的原创文章。
原文链接:【http://xie.infoq.cn/article/17ba81bd7ba4ffeaf379868c7】。文章转载请联系作者。
评论