马蜂窝支付中心架构演进

为了更好地支持交易业务的快速发展,马蜂窝支付中心从最初只支持基础支付和退款的「刀耕火种」阶段,经历了架构调整的「刮骨疗伤」阶段,完成了到实现综合产品平台形态的「沉淀蓄力」阶段的演进。 目前,马蜂窝支付中心集成了包括基础订单、收银台、路由管理、支付通道、清算核对、报表统计等多种能力,为马蜂窝度假(平台、定制)、交通(机票、火车票、用车)、酒店(开放平台、代理商)等近 20 条业务线提供服务。本文将围绕支付中心整体演变过程中不同阶段的核心部分进行简要介绍。 一、支付中心 1.0初期为快速响应业务的支付、退款以及一些基础需求,支付中心主要负责接入支付通道(支付宝、微信、连连等),由各业务线分别实现收银台,然后调用支付中心进行支付。业务系统、支付中心和第三方通道的交互流程图如下: 各系统交互流程为: 业务线将订单信息封装后请求到支付中心支付中心对订单信息简要处理后增加支付信息请求到第三方支付通道第三方支付通道将支付结果异步回调到支付中心支付中心将第三方响应的数据简易处理后同步通知到各业务系统业务系统进行逻辑处理、用户通知及页面跳转等业务发展初期,业务量较小,交易场景也比较单一,这样的设计可以快速响应业务需求,实现功能。但当业务复杂性不断提高,接入的业务也越来越多时,该架构就显得力不从心了。各业务线需要重复开发一些功能,并且支付中心不具备整体管控能力,开发维护成本越来越大。主要的问题包括: 维护成本高:各业务线需单独维护收银台,调用支付系统完成支付,需分别保证幂等、安全等问题容灾能力差:所有功能集中在一个大模块里,某个功能出问题,直接影响全部结构不合理:架构单一,不能满足复杂业务场景系统职责乱:收银台维护了收款方式及部分业务路由,缺乏统一的管控为了兼顾对快速发展中的业务的需求响应和系统的高可用性,保证线上服务的质量,我们快速进行了架构调整,开始了向支付中心 2.0 的演进。 二、支付中心 2.02.0 架构将各业务的公共交易、支付、财务等沉淀到支付中心,并主要解决了以下三个主要问题: 建立基础订单、支付、财务统一体系,抽象和封装公共处理逻辑,形成统一的基础服务,降低业务的接入成本及重复研发成本;构建安全、稳定、可扩展的系统,为业务的快速发展和创新需求提供基础支撑,解决业务「快」和支付「稳」之间的矛盾;沉淀核心交易数据,同时为用户、商家、财务提供大数据支撑。2.1 核心能力支付中心 2.0 是整个交易系统快速发展的重要时段。在此过程中,不仅要进行架构的升级,还要保证服务的稳定。 目前支付中心对业务提供的主要能力包括: 平台支付:用户可以使用微信、支付宝等第三方平台来完成支付快捷支付:用户提供银行卡信息,进行便捷支付协议支付:用户完成授权后,可以在不打断用户体验的场景下进行便捷支付信用支付:用户可以选择花呗等分期产品进行透支支付境外支付:用户可以选择境外支付通道完成境外产品的购买线下支付:用户可以选择 ToB 通道完成特定场景的支付针对马蜂窝业务的特点,目前支持的核心交易场景包括: 支付和退款:适用于普通商品的购买及退款拆分支付:适用于限额或金额较大场景合单支付:适用于保险等分账到不同收款账号的场景2.2 架构设计演进过程中,首先是对相对独立,同时作为统一体系基础的网关进行模块化。支付网关对外抽象出支付、退款、查询这些标准请求,然后在网关基础上逐步梳理各支付通道,并逐步抽取出基础订单模块,解耦业务功能与支付功能,同时可支持复杂的业务场景。目前的系统功能整体架构如下: 如图所示,从架构上主要分为三个层次: 产品层:组合核心层提供的支付能力,对终端用户提供收银台、对运营财务人员提供运营财务系统核心层:支付中心核心模块,包括基础订单、支付路由、支付通道等支撑层:用来支撑整个系统的基础设施,包括监控报警、日志、消息队列等2.2.1 产品层产品层主要包含消费者可见的收银台、支付管理后台和财务核算、对账的财会系统。本文重点介绍收银台的设计思路。 收银台收银台包含 H5 收银台和 PC 收银台两部分: 移动端: PC端: 如上图所示,收银台主要由三部分组成:订单基本信息(含订单号及支付金额)、订单详情(含日期信息、商品信息及基础信息)、支付方式(平台支付、信用支付等)。 由于收银台是整个支付中心面向用户的唯一入口,用户体验及安全性至关重要。为同时支持业务个性化和用户的一致性体验,收银台主要是通过定制化和配置化的方式实现。对业务同学来讲接入也非常简单,仅需通过订单号跳转至收银台页面,后续流程均由支付中心完成。  用户下单后到达收银台页面,收银台通过订单所属业务线、支付金额、是否合单等信息,展示可用的支付通道。同时风控系统会从商品、订单、用户行为等维度进行监控,屏蔽高风险的支付渠道。支付渠道出现故障时可在收银台暂停展示。 (1)定制化 为支持统一收银台下各业务线不同模式、不同展示的特性,使用工厂类继承的模式实现各业务数据及展示样式。收银台主要属性分为展示模块和通道路由,其中重复及默认功能的模块由抽象类用模板的方式实现,子类使用默认方法或者重写父类方法即可达到自定义的实现。收银台展示实现类已经实现了一套默认的收银台,其中包含大多数必须的组件(如倒计时,头部定制,订单详情等)。一般情况下,各个业务线仅需简单添加特定的实现类,即可生成一个清晰又丰富的页面(2)配置化 收银台的配置化主要根据各业务的属性(业务类型、品类等)对后续操作做一定的流程处理配置化,比如: 基于后端路由对收银台展示层做不同的处理,用户看到的可支持的通道列表(微信、支付宝等),以及排序置顶打标记等满足不同场景、不同业务在同一种支付方式下收款到不同的收款账号根据场景不同,走不同的结算方式,以及结算渠道等2.2.2 核心层支付中心中的核心模块,包括基础订单、支付路由、支付通道等。 基础订单基础订单系统是连接交易业务线、支付中心和结算系统的桥梁,实现了业务和支付结算解耦。主要涵盖了业务创建订单、关单、支付、退款、回调通知等 API 模块。基础数据支持普通支付、合单支付、拆分支付、保险支付等多种场景的支付功能,各个系统的交互流程如下: 目前基础订单系统可支持如下两种特殊场景: (1)一订单 VS 多商品 创建一个基础订单可以包含 N 个商品(商品信息包含商品名称、商品 ID、单价、数量、折扣等,订单信息包含用户 UID、手机号、支付金额、订单折扣等汇总基础信息),N 个商品对应 M 个业务子订单 (M≦N),所有业务子订单的业务类型若一样则为普通模式,否则为搭售模式;每个业务订单对应一个对账单元(支付成功后会将支付信息同步给对账系统),一订单 VS 多商品的创单模式基本支持目前所有场景,包括未来可能的购物车模式。 (2)一订单 VS 多支付单 ...

July 5, 2019 · 1 min · jiezi

组件化实践

最近想了解一些组件化的知识,去看了Casa写的iOS应用架构谈 组件化方案这篇文章,Casa在文中针对蘑菇街的组件化方案提出了一些不同的观点,陈述了自己的组件化方案。 大神们讨论具体的实施方案,是对理论的描述,在架构层面来分析利弊,我看过之后感觉还是有点晦涩,具体的方案异同之处我们先不说,今天我们先从应用着手,在自己当前的工程实施组件化。 当然了,我们选择使用的方案是Casa的CTMediator。 准备首先我们得先了解组件化这个概念,其实通俗的讲,就是把我们的项目拆解成一个一个的小组件分别管理。我们平时使用cocoapods继承的三方的库,可以理解成是一个公有的组件。我们项目中,也可以把一些模块拆解出来,使用cocoapods来集成。这样拆解成一个个的组件的好处有很多,比如说业务模块之间解耦,复用模块,节省编译时间等等。 所以我们要先学会创建cocoapods私有库。 这里多说一句,Casa的组件化方案在实施的时候,每独立出来一个组件,就会相应的创建一个Category工程,作为中间的调度,所以说,我们每做一个组件,就要创建两个私有的pod工程。 我们结合Casa这篇在现有工程中实施基于CTMediator的组件化方案,来做一下补充或者说是注解吧,本文中的流程取自于上文。 创建私有Pod工程1. 先去开一个repo,这个repo就是我们私有Pod源仓库2. pod repo add [私有Pod源仓库名字] [私有Pod源的repo地址]3. 创立一个文件夹,例如Project。把我们的主工程文件夹放到Project下:~/Project/MainProject4. 在~/Project下clone快速配置私有源的脚本repo:git clone git@github.com:casatwy/ConfigPrivatePod.git5. 将ConfigPrivatePod的template文件夹下Podfile中source 'https://github.com/ModulizationDemo/PrivatePods.git'改成第一步里面你自己的私有Pod源仓库的repo地址6. 将ConfigPrivatePod的template文件夹下upload.sh中PrivatePods改成第二步里面你自己的私有Pod源仓库的名字首先我们先创建一个名为Project的文件,然后把我们项目的主程序,我们叫做MainProject放到Project路径下,然后在Project路径下clone出我们需要的脚本(Casa提供) 在~/Project下clone快速配置私有源的脚本:git clone git@github.com:casatwy/ConfigPrivatePod.git现在我们的文件目录结构是这样的。 Project├── ConfigPrivatePod(脚本文件)└── MainProject在Project路径下创建我们的组件工程(一个普通的iOS工程),我们把这个工程名字叫PayComponents (模拟抽取项目中的支付模块)。 当前目录结构 Project├── ConfigPrivatePod├── MainProject└── PayComponents有了本地的工程之后,我们现在需要创建一个repo,作为我们的私有pod源仓库。也就是在github,或者gitee(码云)上面创建一个项目,放我们的项目代码,命名PayComponents。 然后呢,我们还需要创建一个东西,就是私有Pod源仓库名字。 pod repo add [私有Pod源仓库名字] [私有Pod源的repo地址]落实到我们这个项目中,我们应该这样写。 pod repo add PayComponents https://gitee.com/LittleBin/PayComponents.git那这到底代表着我们创建了什么? 我们打开finder->前往->前往文件夹,然后输入~/.cocoapods/repos 可以看到目录是这样子的 repos路径下面有一个master,一个payComponents。这两个文件夹我们可以粗略的认为他和pod search还有install有关。 打个比方就拿search来说,我们查询一个库的时候会用下面这个命令 pod search AFNetworking然后会从master路径下找到AFNetworking,然后列出来他有哪些版本什么的。我们有的时候会发现一个库其实已经跟新到2.x.x版本,但是我们search出来只有1.x.x,这也可能是我们的cocoapods没有更新,我们的master路径下没有新的版本。 这个PayComponents文件夹,就代表我们本地有一个私有的pod库。我们search的时候,也会查这些本地的私有库。 下面把远程的这个repo和我们本地创建的项目关联到一起,这个工作Casa给我们提供的脚本就可以完成,顺便还会帮我们生成.podspec的文件,具体这个文件的作用我们后面再说,还会初始化podfile。 我们进到ConfigPrivatePod文件中,执行config.sh脚本,然后在终端根据提示输入就行了。 [localhost:ConfigPrivatePod sunxiaobin$ ./config.sh Enter Project Name: PayComponentsEnter HTTPS Repo URL: https://gitee.com/LittleBin/PayComponents.gitEnter SSH Repo URL: git@gitee.com:LittleBin/PayComponents.gitEnter Home Page URL: https://gitee.com/LittleBin/PayComponents================================================ Project Name : PayComponents HTTPS Repo : https://gitee.com/LittleBin/PayComponents.git SSH Repo : git@gitee.com:LittleBin/PayComponents.git Home Page URL : https://gitee.com/LittleBin/PayComponents================================================confirm? (y/n):ycopy to ../PayComponents/FILE_LICENSEcopy to ../PayComponents/.gitignorecopy to ../PayComponents/PayComponents.podspeccopy to ../PayComponents/readme.mdcopy to ../PayComponents/upload.shcopy to ../PayComponents/Podfileediting...edit finishedcleaning...Initialized empty Git repository in /Users/fmb/Documents/LEARN/Project_test/PayComponents/.git/clean finishedfinishedlocalhost:ConfigPrivatePod sunxiaobin$ Enter Project Name:的时候,后面的名字一定要跟我们创建的PayComponents工程一样,要不然脚本找不到文件,就配置不了。 ...

April 28, 2019 · 2 min · jiezi