小探网页支付网关的架构设计与体验优化——以某企业支付平台为例

开题

现在网络支付方式越来越发达,已经不可避免地开始与各类 IT 系统集成。在财务制度比较规范的地方,这类入账自然需要统一管理,资金不能随意流动。如果由各个业务系统自己去申请对接网络支付接口,自然是很难实现统一管理的,而且也麻烦。

因此,就需要一个统一的企业级支付平台作为中间层,来负责各业务系统与网络支付的对接。这样,不仅方便了财务数据的统一提取、账户对账,也使企业支付平台可以代表所有的业务系统与网络支付接口进行交互,业务系统不会接触到企业的网络支付总密钥,较为安全可控。

这样的例子其实我们天天见。例如 12306 的车票支付教育部考试中心的考试费支付,都采用了中间层,来接入多个网络支付平台。

此时,企业级支付平台的角色可以认为是一个代理,协助业务系统完成支付流,并保存相关数据。目前这类“代理”的架构并没有标准,所以实现起来会各有小差别。如何去定义企业级支付平台(下称企业层)和业务系统各自的角色,就会决定架构的实现方式,相应就会影响到支付业务的实现情况。

WePay 的实现思路

下面介绍一款已经在运行中的企业支付平台 WePay,其目前向上对接了微信企业号的微信支付接口,远期规划可加入其他网络支付的支持。

WePay 会给每个业务系统分配一个“子商户”,对应会有 ID 和密钥,也就意味着业务系统通过 WePay 实现的子商户进行支付请求,WePay 自己处理网络支付接口的密钥,应用系统只能借助 WePay 获取网络支付的结果信息。

WePay 当前的架构和支付流程

上图的流程目前其实工作得非常好。应用系统需要完成的工作很简单,生成应用端的订单信息并保存订单号,然后把订单信息一股脑前台 POST 给 WePay,WePay 的网页会帮应用系统实现支付过程,应用系统只管收支付结果就可以了。

在这里,WePay 的角色并不只是“代理”那么简单了,因为要帮应用系统实现支付流程,所以中间还得控制支付过程。我就暂且叫它做“富代理”吧。

为了更好地说明纯粹的“代理”是个什么情况,我假设了新的流程。这里业务系统会把 WePay 当作是网络支付的服务器,所以业务系统传给 WePay 的 ID 可以是自编号的子商户,WePay 把请求递交给网络支付的时候再用上网络支付统一的密钥。

WePay 如果变成一个纯粹的代理,会变成这个流程

代理与否,利弊明显。实际上现在基本上都是“富代理”的解决方案。

  • 纯代理
    • 业务系统需要自己处理支付过程(图中,浏览器端中间的蓝栈页面均由应用服务器实现);
    • 不同系统的技术良莠不齐,支付过程的体验和安全性会有差别,不利于企业对支付过程进行标准化;
    • 但如果业务系统已经做了现有的网络支付对接(目前网络支付有大量的 SDK),技术上,纯代理模式下业务系统会更容易接入企业层系统。
  • 富代理
    • 支付过程由 WePay 实现(图中,浏览器端中间的蓝栈页面均由 WePay 实现);
    • 如果应用是从头开发的,接入的工作量就会比直接接入网络支付少(即便人家有 SDK 了还得自己实现前端呢);
    • 支付过程均由企业层实现,用户体验和安全性可以由企业层的统一实现来保证。

“富代理”角色可以多做这点判断

企业层需要做的事情多了,这样的角色定位下,每单支付业务系统该给企业层传多少参数呢?

根据内部公开资料得知,目前业务系统在创建支付订单时,需要传以下这些参数给这款 WePay 系统。我做了下分类:

  • 请求身份和请求校验
    • spbillCreateIp 发起订单的 IP
    • tenant 租户代码(由 WePay 分配)
    • timestamp 订单生成的时间戳
    • sign 所有参数加上密钥后的签名值
  • 订单内容(必须)
    • tradeType 交易类型
    • totalFee 支付金额(单位为分)
    • tenantTradeNumber 租户订单号(业务系统内部唯一)
    • redirectUrl 前端跳转确认地址
  • 订单内容(增强)
    • productBody, productDetail 交易内容和详情
    • tenantUserCode, tenantUserName 企业应用中用户的用户名、姓名

实际上这一整套接口的参数内容,多是直接借鉴微信支付的参数来着的,反正就是转发参数,用现成的参数名也没啥问题。

但注意到 tradeType 这个参数了吗?实际上这个参数是微信支付用的。微信那边目前支持的值有 JSAPI(微信内浏览器)、NATIVE(用户扫码付款)、APP(手机应用)、MWEB(微信外手机浏览器)、MICROPAY*(商户条码付款),而 WePay 实现了 JSAPINATIVE,根据业务系统发的参数来返回给浏览器不同的界面。

那么,这个参数真的要靠业务系统来给定吗?

  • 大多数情况下,业务系统需要根据客户端来提供不同的界面来适配,比如根据 UA 有没有 micromessenger,判断提供 PC 版还是微信版。业务系统很多时候似乎知道客户端属性了,让业务系统来指定这个类型,下不同的单,似乎顺理成章。
  • 然而,当今的网页技术早已可以实现一套前端模板,通吃 PC 移动(Bootstrap 框架 推了很多不会写响应式设计的人一把)。因此,有的业务系统根本没必要去判断客户端是不是微信(而提供不同的界面)。这时候让业务系统来下单,业务系统肯定下一个 PC 版的单了,除非非常考究,才会特意适配一下微信。
  • 此外前文提到,企业层在支付流程中,会协助应用系统实现面向用户的支付交互流程(前端)。而显然,JSAPINATIVEMWEB 这三种模式,只是触发支付的方式不同,并不会影响到支付业务的核心内容和最终结果。这种情况下,显然没有必要让应用去选择下单模式,而应该让企业层来统一判断,从而继续降低业务系统的集成难度。
    • JSAPI创建订单的时候需要 OPENID,可能有的业务会靠这个参数来限制只能某人支付这一订单。实际上这个需求并非必要,很多时候反而会造成业务规则不统一(支付页面如果可以分享,会导致 OPENID 不同而支付失败;不限制的话,虽然可能会有退款退不到账户本人的问题,但很多业务直接上 NATIVE 模式,谁扫码都行,完全没考虑这一点),确实有业务需要的话也可以保留,就是 WePay 多判断一下而已。
  • 更重要的是,这三种模式是高度与特定的网络支付端口耦合的。如果日后需要接入其它的网络支付,较好的办法是业务系统不用管网络支付端口的具体的支付模式参数,而靠 WePay 去给用户选择,否则业务系统全得再改一遍,多没意思。

结论就是,这个 tradeType 应当做一下整合。鉴于JSAPINATIVEMWEB 这三种模式本质上都是网页支付,可以在 WePay 端整合为一个名字(如 HTML),再由 WePay 进行选择,而不应该让业务系统来选。如果业务系统还发了这三个串进来,就统一定向到新的 HTML 模式就好。至于 APPMICROPAY,现在这个系统似乎没有用到,这些方式就预留着空位就好。

支付中间层如何判断支付模式?

既然提出了这个解决方案,自然也得考虑,没有业务系统的任何数据,支付层能不能高效率地自行判断支付模式呢?当然是可以的。

支付中间层最麻烦的事情,莫过于在开始让用户支付前,就得先跟网络支付开好订单。而开订单的时候,就必须决定具体的支付模式。

有种稳妥的方法,就是通过前端来检查微信的 JSAPI 能不能用、有没有手机(微信),再来按需异步开订单。不过这样当然太麻烦了,所以靠粗暴的后端 User Agent 检查,就可以实现绝大多数情况下的正确及时判断了。

所以是:

  • UA 判断是否包含了 micromessenger,是的话,默认在微信环境下,开 JSAPI 订单
    • 调 JSAPI 需要获取 OPENID,企业号的接口无论微信用户是否有关注企业号,都会返回一个 OPENID,而且鉴于支付是低频操作,这个时候让
      WePay 跳一次 OAuth 其实也并不太影响响应时间(OAuth 有两种方案,一种是 WePay 自己跟微信拿数据然后 Cookie 缓存,另一种是跟企业号应用主域名拿数据,由于企业号域名通常用户都有登陆,都带 Cookie,可能可以节省 OAuth 的开销)
  • 其他情况全部走 NATIVE 订单;或者,如果有闲心兼容下 MWEB(手机浏览器在这个模式下可以直接拉起微信,体验会更好),就再 UA 判断是否包含 Android 或 iPhone / iPad 串,有的话就进 MWEB,没有就进 NATIVE 扫码去
  • 要做到 100% 靠谱,就得考虑在 WePay 端提供切换模式的功能了,不过一切换模式,就得关闭订单,还得重新生成订单号给网络支付用,是挺烦的,所以没必要做

EOF

说明:本文资料均来自公网公开资料,图是自己用这个画的,并没有透露什么机密。终于可以安心学习了 = =

本文采用知识共享“署名-非商业性使用 4.0”许可协议授权,如需额外授权请与本人联系。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容