通过Braintree接入PayPal支付的过程工作笔记

目前在国际互联网支付领域,PayPal无疑是占据主导地位的服务商。它拥有大量活跃用户,覆盖面广、处理交易效率高。但直接对接PayPal进行交易也存在一些问题,比如缺乏定制性,无法接入其他后续业务等。PayPal本身的技术文档也过于细节繁杂,每个API的参数都会细致列出,但是缺乏一个简单易用的Guide和Tutorial引导开发者自己选择必要的环节去做定制(提供的demo仅仅适合非常简易的网店购买场景,并不符合我们公司的情况)。我们选择引入Braintree作为技术中间层。Braintree提供灵活的支付网关接口,一方面可以对接PayPal等多种支付渠道,另一方面也可以通过软件化定制的SDK与产品后端对接。Braintree本身也提供简明易懂的文档和图表演示,文档结构清晰,接入的步骤也不多,便于新手快速学习和掌握。

实现整个支付流程需要三个环节: 第一步,从我们公司server获取token来初始化客户端,以此建立和我们公司的定向交易,同时也验证我们公司Braintree账户的有效性;第二步,用户点击支付按钮后填写自己的PayPal账号信息并且授权,这一部分由PayPal的控件来负责,并且在最后返回一个nonce(相当于是token)作为凭证;第三步, 把这个nonce传给我们公司server,由server向braintree端发起支付/订阅交易,支付完成后再更新我们这边的会员情况。

在走通三个环节之前,需要先做一些准备工作。首先,注册一个PayPal的开发者账号,会附带用于测试环境的Sandbox Accounts:包括个人账号和商家账号,需要进到sandbox dashboard获取,同时可以在里面查询虚拟账号的余额等各种信息。然后需要再注册一个Braintree的开发者账号,这个可以用于测试时使用的商家账号,用于发起交易和收款。同样Braintree也有dashboard,可以查看各种交易和订阅情况,这里也可以进行配置,将账号和前面的PayPal虚拟商家账号进行绑定。为了测试订阅,还需要预先在dashboard里配置好测试用的plan,确定每个plan的价格,折扣和试用天数等信息。

准备工作完成后,可以着手制作一个demo把简单的支付流程走通(不涉及公司这边的业务逻辑)。首先,需要一个简单的前端让用户完成支付的交互,这里可以用Braintree提供的PayPal控件库,不过库是要js写的,如果转成ts使用,需要做一些修改;然后需要实现一个简单的后端,为前端初始化提供token,还可以在后期进一步测试购买和订阅的功能。两部分都实现后,在本地启动网页,点击button,填写测试PayPal账号,确认支付, Bingo!

由于我们公司的产品分一次性购买(终身会员)和订阅(连续包月/包季/包年会员)两种,需要走通transaction和subscription两个渠道。transaction比较简单,demo已经实现,只需要获取用户授权的nonce就能完成交易,甚至连用户的相关信息也不需要提供。接下来研究subscription就遇到这次开发的第一个大坑,subscription的create需要传入两个参数:payment_method_tokenplan_id。这里的plan_id是什么呢,就是前面准备工作里说的在dashboard预先配置好的plan,这里的plan_id由我们配置的时候自己定义。但是payment_method_token却并不是前面提到的nonce,文档里是这么解释的:

One-time-use reference to a payment method provided by your customer, such as a credit card or PayPal account.

所以重新检索了文档,在customer下找到了对应的属性。这里又涉及到customer的创建,需要传入一些用户的个人信息和nonce,也可以传一些我们自定义的属性(例如我们glow的user id,用于数据库的匹配)。成功创建customer后,就会返回对应的payment method token,可以用于创建subscription。到这一步,订阅还没有完成。取决于plan配置的时候,有没有设置trial day,trial end时会自动进行扣款。如果没有trial day则会马上扣款。每次扣款都是一笔transaction,等于所有有金钱流动的操作都属于transaction,每个subscription都会和若干个transaction绑定(可能是0个,还在trial day;可能多个,对应过了几个billing cycle)。subscription和transaction并不是同一级的概念,甚至可以说不是交易的一种,它只是一种recurring billing的约定。

这里在前端又碰到了另一个坑:一次性购买和订阅所使用的前端也是不一样,初始化配置的参数不一样,展示UI时调用的方法也不一样。正如前面所说,本质上前者是购买,后者则是一种协议的签订。术语上,前者对应Checkout,后者则是Vault(保存用户的信息后,在后续购买中进行更快捷的扣款,不需要再次填写)。

购买完成后,还需要考虑退款的问题。这里有另一个坑,就是和一般概念上的退款更新相应transaction状态的想法不同,Braintree这里的退款是重新发起一笔transaction,可以理解成是反向交易,并且会在属性里和原先的订单关联在一起。

💡
注意⚠️
这里subscription是不能直接退款的,可以进行退款操作的只有transaction,所以可以选择subscription关联下的其中一笔或多笔订单进行退款。subscription本身只能进行cancel的操作。Braintree整个支付架构的核心元素都是transaction,所有交易操作都是围绕它展开。

经过前面这些尝试,所有关键性的功能都已经走通,接下来就要和公司服务器进行实际的对接。一方面需要提供一些必要的接口给前端每个步骤调用,另一方面就要设计合适的数据库表将所有关键性的交易信息和状态更新都记录下来,供以后查询和后台操作等。这里的表格设计借鉴了之前对接stripe支付平台的经验,对应每一次状态更新/操作都写进一条记录,保存所有的历史信息。但跟stripe又有不同,stripe基本都是基于webhook的事件驱动的,把每一个事件都转换成一条record即可,设计表字段也基本参考事件内包含的属性;Braintree则基本很少依赖webhook通知,按官方的说法是:

We don't offer webhooks for transactions - instead, our API is designed to give instant feedback via the response object. Because of this, we only provide webhooks for asynchronous events that aren't triggered by an API call (such as billing a subscription or disbursing funds). We encourage our customers to avail of our API responses, in addition to our reporting system and comprehensive search call options, for any extra functionality they require.

所有的操作是否成功基本都能从即时的response中得到反馈,所以这里我们根据每次主动操作和反馈到的结果去存写记录;唯一的例外是subscription的trial end后扣款的操作,还有一些subscription状态更新,依旧需要webhook来获取。这里一些后台的设计架构也借鉴了stripe的实现。

最后,测试环节也很重要。Braintree dashboard提供了查看所有transaction和subscription记录的功能,可以通过设置日期范围等查询条件进行快速搜索,还有report功能可以进行汇总总计和可视化的展示,便于在开发过程中查看操作是否成功,状态是否及时更新。Braintree的开发文档中也提供了测试使用的信用卡号,和不同类型的支付nonce用于测试,提供了极大的便利性。

整个开发过程中,不断学习了解之前Stripe接入工作的同时,对Stripe和Braintree两家也进行了比较。相较于Braintree, Stripe的优点在于其提供的SDK和API文档更加丰富、统一。开发者可以找到对各种语言平台的详细支持,文档实例也更多。这降低了接入Stripe的学习门槛。另外Stripe也在不断加强对第三方工具的支持,比如预构建的插件、集成方案等,可以减少重复工作。但是Braintree也有其独特优势,支持PayPal等多种支付渠道的对接,Stripe仅支持欧盟区的PayPal。Braintree 被PayPal收购后,技术变动也比较少,有利于维持用户的架构文档。整体来说,两家平台各有千秋,开发者可以根据自己的业务特点选择。但无论采用何种平台,做好准备工作,熟悉交易环节,了解具体的交易场景, 并且理解支付平台背后的技术原理,都是确保顺利对接的关键。这需要投入时间、学习成本,但会给产品增加核心竞争力。选择易用、可靠的支付平台,是每家公司都需要谨慎做出的选择。