Airbnb 作为共享经济的典范,Airbnb 支付有怎样的故事,以及其架构正在经历的变革。
Airbnb 支付的重要性
当人们提起 Airbnb 的时候,人们不会觉得这是一家支付的公司。然而,支付是 Airbnb 最核心的部分之一。
回想一下用户预订 Airbnb 的流程:我们首先根据入住的天数和清洁费用等计算需要收取的金额。 然后计算相应地区的税率以及其他服务费。再用用户提供的付款方式进行收款,并在用户入住后付款给房东。
每个步骤看似很简单,其实支持它们的底层实现极其复杂,尤其是 Airbnb 对全球范围的双向支付,这个问题和我们一些同行如 Braintree 和 Stripe 要解决的问题其实很类似。
Airbnb 作为一个共享经济的平台,起源于三个支柱性的要素:信任、归属感、合法性。
信任是我们产品的基石,也是我们企业的基石。任何一个牵涉到现金交易的平台都需要强大的信任体系来支撑。而这样的信任就来自于支付的实现,来自于每笔交易的安全性和可靠性。
关于归属感,这意味着我们希望用户可以按他/她自己熟悉的方式来支付。这样我们才可能让他/她们更有产品本土化的体验。
最后,合法性意味着我们会遵循所有地区的法律法规,比如我们需要代表不同城市收取不同的出租房产需要缴纳的税收等等。
Airbnb 支付的起源
关于 Airbnb 支付的起源,有一个很好玩的故事。最早的 Airbnb 网站,并没有线上付款,只有定价。Guest 入住的时候交现金给房东。
在 2009 年的时候,有一次,我们的创始人之一 Brian Chesky 到德州 Austin 参加 SXSW 大会。他刚入住的时候,房东就按惯例跟他要现金。因为他当时身上没有,所以他就跟房东说他晚上出去会到 ATM 取钱回来。然而,那天晚上他却忘了这件事。所以第二天一早房东问他要钱他又没有。Brain 说 “今天我出去一定取钱给你”。繁忙的一天过去了,Brian 又忘了取钱。Brain 再三保证明天去 SXSW 的时候一定取钱。然而悲剧都是这么发生的,老大又忘了……这时候情况有点囧、老大有点尴尬。房东甚至开始怀疑他到底能不能拿到钱了。
这件事让 Brian 意识到我们的平台必须有支付的功能,帮房东在房客预定的时候就收取费用,而不是等到入住时再进行现金交易。于是不久后,我们的 CTO Nate 等人就完成了我们 Airbnb 支付的第一个版本。
Airbnb 支付的国际化之路
最早期我们的支付系统只支持 PayPal。后来又有了信用卡。这两个支付方式主要都是针对美国用户的。随着 Airbnb 扩展到越来越多的国家,我们的支付也一直作为国际化的先行军,前后打入了 191 个国家。
和 Uber 不同,Uber 的大部分交易司机和乘客是同一个国家的,而且付钱和收钱几乎同步进行。Airbnb 的很大一部分交易都是跨境的。而且 guest 在预定的时候付款,房东在入住第二天收款,中间还有一段时差。更别提我们还要兼顾各国的法律法规和税务条款。
除了大家熟悉的 PayPal、信用卡等支付方式,我们还支持一些非典型的支付方式,如基于现金的 Boletos 和 VaCuba。让世界上任意一个国家的人都可以使用 Airbnb 支付,对我们和我们的用户来说都很重要。即使是一些很偏远和不那么发达的国家,我们也想尽办法提升他/她们的支付体验。
关于 Airbnb 支付的国际化,说几个好玩的故事。
1、古巴
古巴是一个很奇特的国家。Airbnb 能够获取批准进入古巴市场,付出的努力也是很大的。很多现代的基础设施,尤其是支付相关的,在古巴都不存在。例如,他们完全不能支持信用卡或任何类似的支付方式。
为了让古巴人也可以在 Airbnb 上租房,我们使用了 VaCuba 这种支付方式,其本质就是人工开着游艇、驾着飞机、骑着自行车,将现金像送货那样送到房东的家里。这也是为什么去年 Airbnb 作为第一家美国公司将业务开展到古巴的时候,奥巴马也给予了很高的评价。
2、巴西
如果说古巴是没有一个信用卡网络,那么巴西的问题就是他们支持了太多的信用卡网络,每一个网络的覆盖面又很有限。此外,现金支付依然是巴西最常见的付款方式。
为了让巴西人民可以按照他们的方式自由付款,我们在支付系统里加入了一种新的支付方式 Boleto。Boleto 是一种基于异步收款单的一种支付方式。简单来说,当房客在我们的网站上预订 Airbnb 之后,我们通过 Boleto 为其生成一个交款凭证。用户可以到银行按该凭证付钱。付完钱以后,银行会给 Airbnb 发一个通知,告诉我们款项已到,我们就可以进一步处理这笔订单。
在此之后,我们也在另外几个国家使用这种类似的异步收款方式。
3、中国
中国最常见的线上支付方式要数银联、支付宝和微信支付了。目前为止,我们使用了一些 “非典型” 方式支持了银联支付和支付宝支付。
什么意思呢?拿支付宝来说,我们没有直接和阿里对接,而是通过一个叫做 Adyen 的第三方来对接的。可是 Adyen 有很多问题,造成了一些中国用户的支付宝支付的体验比较差。好在我们已经着手直接和支付宝对接了。微信支付未来也是有可能会支持的。
4、印度
印度是一个极其以移动应用为核心的国家。这就意味着所有的预订、支付、授权等等都必须在手机上支持。
此外,他们有严格的法规要求所有境内游的资金需要保证滞留在印度境内的账户上。因此对于印度,我们需要设置特殊的账户和支付路由选择。这在维系我们的账户收支上增加了一定的定制困难。
一些简单的支付常识
后面的部分我们会讲到支付的软件架构。在此之前,有一些常识和名词有必要先大概解释一下。
任何一个系统的支付,都应该(至少)包括下面三个部分:
结账和付款(checkout)流程。这包括很多的用户界面和交互部分。
平台和银行或者第三方的实际资金流动。
内部 auditing 和 reporting 系统产生所有财务报表。
第一部分很好理解。第二部分根据实际对接的金融机构的不同,实现起来有很大的差异。比如,这是一个基于银行账户的交易?还是基于 “钱包” 的交易?是同步的交易?还是异步的交易?有没有一个可以存下来以后再用的账户?还是每次都需要用户重新输入付款方式的信息?交易是一步完成?还是通过多个步骤完成?等等。
为了简单起见,我们在以下统称一种接入的支付方式为一个 processor。
而第三部分因为用户完全不可见,很多时候非支付行业的人对其了解比较少。而公司的财务报表其实是一个公司支付最最不可或缺的部分。尤其是对于一些已经上市或者有投资人的公司,这些报表是他们了解公司运营情况的主要信息来源。
老架构
相对应的,Airbnb 支付最核心的模块也是三个部分:
在 Airbnb 网上用户预订和付款流程和入住后我们向房东付款流程;
支持世界各地各种 Processor 的集成,可能是 Airbnb 收款,也可能是 Airbnb 付款;
产生所有的财务报表。
早期的 Airbnb 包括支付在内的所有的代码,都是在一个 Ruby on Rails(RoR)App 上的。每一种支付方式的对接,都是高度定制化的。这就意味着,每接入一种新的支付方式,需要的的工程师资源都很高。而且在我们接入二十多种不同支付后,代码逻辑变得异常复杂,可维护性极差。
此外,RoR 的数据访问都是通过 ActiveRecord 进行的。熟悉 RoR 的都知道,ActiveRecord 很难对数据访问进行限定和跟踪,所有的数据默认都是可任意修改的。而对历史事务的纪录和所有事务事件的可跟踪性,是支付最核心的需求之一。所以早期 Airbnb 的做法,是在所有支付相关的核心数据库表上面加了 DB trigger,以记录所有的数据变动,包括插入 / 更改 / 删除,并保存到另外的 audit table 中。
当我们生成财报的时候,我们就是基于这些这些 audit table,用层层迭代的 SQL 语句,来查询生成我们想要的各种数据。而因为 audit table 记录的仅仅是数据变动,不是事件,所以我们还需要根据这些数据变动去 reverse engineer 到底在整个支付流程中发生了什么。
以上种种可以看出,我们最核心的三个模块都有着极复杂的业务逻辑。复杂到全公司只有极少数几个工程师真正了解其中的逻辑。当我们处理的交易越来越多,业务逻辑越来越复杂,更多的新产品属性需要在支付流程上做相应的改动的同时,我们遇到的问题和可能犯的错误也就越多。
新架构
抽象成技术的问题来说,我们的老系统存在以下问题:
初始的支付流程是按照我们的传统的 Airbnb 订房流程来设计和实现的。每出现一个新的产品需要支付的支持,这个流程就会一定程度上被 hack 来满足需求。例如,我们如果找了一些第三方帮我们翻译一些网页,我们会 hack 我们的 “给房东付款” 的付款流程来对其进行支持。房客和住宿过程中如果需要别的付费服务,那我们的平台目前也不能很好地支持。
每个 processor 的集成都是高度定制的。考虑我们现在有 25 种 processor,这意味着所有的处理支付的路由部分都有很多的分支处理,简单说代码不可避免地出现无数的 if 语句和 case 语句。加上很多 processor 又有针对不同货币的处理逻辑。因此当出现 bug 的时候,理清这些千头万绪的交易路由成了高难度的任务。很多如 Boleto 和 VaCuba 的特殊性又增加更多的特殊处理。因此每个新的 processor 的集成的难度都变得越来越大。想做一些智能路由处理几乎不可能。
生成报表是基于 DB trigger 生成的 audit trail tables 来做的。Reverse Engineer 的过程也随着业务逻辑的复杂和各种 hack 支付的做法变得艰难而脆弱。同时,这种相关性也让对这些 table schema 的改动变得异常困难。我们因此增加了很多 “临时” 的方案,例如 EAV 模型的大量使用。然而这些方案一定程度上减弱了交易的事务性。相关数据以多种状态并存来增加了系统维护性的难度。
为了解决以上阐述的种种问题,我们的支付系统的新的架构会将一些主要的支付相关的功能用三个服务来实现。需要提出的是,所有 API 设计,一个很重要的设计理念,就是 API 将实现细节尽可能的通过抽象来隐藏,而只将干净统一的接口显露给调用者。
Panama,我们的新的 Billing API。用统一的结账流程来支持 Airbnb 所有的产品。Panama 通过对产品具体属性的抽象,而使用统一的如 price engine 和统一的 tax engine 来支持必须的产品共性。这样可以在我们的系统中快速的支持任意一种新产品的结账流程。此外,整个结账流程对所有产品保持统一化:在用户付款前统一确认库存状态;统一的支付流程;统一的 fulfillment(库存和出货处理)。最终的结账结果也是用统一的数据模型(Bill + LineItem)来表示。这些数据模型是之后所有对订单的修改和取消等操作的参照标准(source of truth)。操作产生相对应的 Platform Event 发送到 Kafka message bus上。
Gibraltar:Payment Gateway。Airbnb 的产品往往只关心 Payin 和 Payout 的结果,而不关心 Payin 和 Payout 的实现细节。Gibraltar 把所有不同 processor 的 Payin 和 Payout 抽象成统一的 API 接口。极大地简化了产品以及支付的实现复杂度。所有的实际交易 transaction(事务)都由该 API 层控制并记录。产生相对应的 Payment Event 发送到 Kafka message bus上。
Braavos:Financial Reporting Service:财务系统往往只关心一些统一的财务账户(如应收账款,应付账款,利润,税款等)以及这些账户之间的资金流动,而不关心产品的逻辑细节(例如 reservation 如何 block calendar,如何发通知 email,等等)。Braavos 收到来自于 Panama 的各种 Platform Event(产品事件)和来自于 Gibraltar 的 Payment Event(支付事件)之后,把上述事件中的财务账户信息统一表示成 subledger,这样就可以构建跨多个产品的统一的报表逻辑。
这里说一个题外话。虽说 naming 和 cache invalidation 是计算机科学里的两大难题,很明显 Airbnb 的工程师以为 service 取有典故又好玩的名字为乐。
Braavos 是 Game of Throne 里面的 home of Iron Bank,是一个很靠北面、很富足的城市。
Panama (巴拿马运河)是大西洋和太平洋之间的一条运河,控制两片水域之间的船只来往。Gibraltar (直布罗陀海峡)是 Spain 和 Morocco 之间的一个具有很重要地理位置的海峡。
这些名字也意味着这几个 service 在我们整个 Airbnb 系统中的举足轻重的地位。
展望未来
当所有的基础功能都实现了以后,我们就可以不用花费大量的工程师资源去维护和迭加各种定制的业务逻辑。整个系统变得更模块化,逻辑的复杂度大大降低。新产品或者新支付方式的支持也变得更加容易。于是我们开始着力于在系统上增加更多的智能,使用机器学习能来对一些数据流进行优化。
举几个例子:
费用定制的智能化。现有的所有 guest fee,host fee 等都是基本固定的。我们可以根据一些机器学习模型来调整价格,看什么样的 Airbnb 收费最合理。例如如果你是一个长期的租房,那么我们可以适当地降低 service rate 等等。
支付路由选取的优化。因为我们接入了多个 processor,所以所有的信用卡,都是可以通过不同的 processor 来处理。目前我们是根据发卡行和本地货币来决定 processor,以后我们还可以根据信用卡交易通过率、每笔收费等来动态调整 processor 的选择。降低每笔交易的成本,增加订单成功率,改善用户体验。
Credit 和 coupon 系统的完善。建立一个更方便的 stored value service。这有点类似 Airbnb 钱包。让一些长期使用 Airbnb 的用户的支付变得更方便。
最后,虽然目前我们的报表生成是基于批处理的,我们正和我们的数据组共同努力,希望将整个过程做成 streaming based 处理。这样我们就能更实时的自动生成我们的各种报表。