0%

生成聚合收款二维码:支付宝、微信、QQ 钱包

今天在浏览 V2EX 时,看到了一个令人啼笑皆非的帖子。OP 在 GitHub 上开源了一个能将支付宝和微信收款码二合一的项目。由于在项目中,OP 默认填写了自己的支付宝和微信信息;当项目被 fork 出去后,很多人没有修改其中的信息。特别地,有一些被用在恶意用途上的 fork 分支也没有修改。这样一来,就有不少人扫描这些生成的二维码而受骗,最终导致 OP 的微信被封禁了收款功能——连红包都收不了。

虽然故事本身令人啼笑皆非,但是自己动手将支付宝和微信收款码合二为一的想法还是很有趣的。在 Google 上搜索了一番后,我发现收款码合并的套路大概有两种。

其一是以芝麻收款为代表的第三方解决方案。这些第三方解决方案要求用户分别上传支付宝和微信的支付码,而后返回一个新的聚合收款码。按检索到的信息,芝麻收款以前是不收费的,但现在要收费 4.5 元。考虑到收款码涉及到资金流动,交予第三方本身是不安全的;并且,这样一个简单的事情还要收钱,未免有点「故意利用技术壁垒」的意思。作为一个更乐于自己动手丰衣足食的人,妥妥是忍不了的。

其二是以上述 OP 为代表的开源解决方案。这些解决方案对用户动手的能力更高,并且要求有一个可被公共访问的服务器来执行判断脚本。在这种套路里,还可以细分流派。一派是以 PHP 等为代表的解决方案,这要求上述服务器能够执行 PHP 等脚本。这一派的解决方案不适用于 GitHub Pages 等静态页面博客,因而使用范围受限。一派则是以静态 HTML 附议 CSS 和 JavaScript 为解决方案。这一派的方案适用面就广泛得多了。

本文介绍一种以静态 HTML 为基础,辅以 CSS 和 JavaScript 的方案,用以生成聚合收款码。

二维码收款的真相

二维码事实上只是一种编码手段,它将信息编码成了正方形的二维码的形状。只不过,在二维码协议中定义了许多用于诸如抗干扰、放破损的冗余等内容。但事实上二维码只是编码了一些信息而已。

我们使用微信、支付宝等手机 App 扫描二维码后,这些 App 的行为取决于二维码编码的信息。App 首先会解码二维码,而后根据这些信息采取相应的动作。具体到二维码收款时,二维码至少应当包含转账的目标人的 uid,以及(若需要)包含要求的金额。

开源中国的工具集中有针对二维码的解码工具。利用这些工具,我们就可以读取二维码中编码的信息了。以下是从我的支付宝、微信、QQ 钱包的收款码中读出的信息(关键信息已隐藏)。

可见,无论是支付宝、微信还是 QQ 钱包,其收款二维码背后实质都是一个 URL。但实际测试表明,只有支付宝能直接由 URL 出发付款动作;微信和 QQ 钱包应当是在此处做了额外的判断保护,以防用户误点链接进行付款。

于是,解决方案就变成了:

  • 当检测到用户使用支付宝扫描聚合码后,返回一个支付宝的收款 URL。
  • 当检测到用户使用微信或者 QQ 扫描聚合码后,返回微信或 QQ 的收款二维码,由相应的二维码再触发后续动作。

显而易见,该判断动作必然发生在一个可被公共访问的服务器上。于是,问题变成了如何使 App 在扫描相应二维码后访问公共服务器上的某个位置;以及如何在该位置判断用户使用哪一个 App 扫描聚合码,并返回合适的内容。前一个问题很容易解决,它是 App 扫描二维码后的默认动作。因此,我们集中解决后面的问题。

UserAgent

HTTP 协议报文的头部有所谓用户代理(UserAgent,简称 UA)的信息。UA 实际上就是用户使用的浏览器。因此,可以想见,当用户使用不同 App 扫描二维码并访问公共服务器时,HTTP 报文头部载有的 UA 是不一样的。我们可以据此判断用户使用的 App。

为此,我们需要探测支付宝、微信、QQ 钱包的 UA 中有哪些内容可以作为特征字段。详细做法将在后续文章中阐述,此处给出结论:

  • 支付宝:AlipayClient
  • 微信:MicroMessenger
  • QQ 钱包:QQ/

在 HTML 中,我们可以使用 navigator.userAgent.match(/foobar/i) 来判断 UA 中是否包含有 foobar 这一特征字符串。据此我们有:

1
2
3
4
5
6
7
8
9
if (navigator.userAgent.match(/Alipay/i)) {
// 返回支付宝链接
} else if (navigator.userAgent.match(/MicroMessenger\//i)) {
// 返回微信链接对应的二维码
} else if (navigator.userAgent.match(/QQ\//i)) {
// 返回 QQ 钱包链接对应的二维码
} else {
// 不支持的 App
}

在线生成二维码

由于聚合码被微信或 QQ 钱包扫描时需返回相应链接对应的二维码,我们要不然应保存相应二维码,要不然应想办法在线生成并将二维码返回给用户。在线生成二维码又有两个办法,其一是使用 JQuery 之类工具中的 QRCode 接口,直接由用户浏览器(手机 App)生成二维码,其二是调用现成的接口。此处我们通过调用现成的借口来生成二维码。

此处我们借用百度云盘的接口来生成二维码。具体来说

1
https://pan.baidu.com/share/qrcode?w=150&h=150&url=Liam%20loves%20Sophia!

返回的二维码中隐藏的信息是:

1
Liam loves Sophia!

GitHub Repository

至此,解决方案的主要思想就已经阐述清楚了;完整的代码可见 GitHub Repository

使用时,首先应当获取支付宝、微信和 QQ 钱包的二维码后对应的链接。三个 App 的操作路径分别是:

  • 支付宝:首页 -> 收钱 -> 保存图片
  • 微信:首页 -> + -> 收付款 -> 二维码收款 -> 保存收款码
  • QQ 钱包:手机 QQ 首页 -> + -> 付款 -> ... -> 我要收款 -> 截屏保存

将上述保存的二维码上传至开源中国,即可获取收款二维码背后的链接。而后,应当把这些链接填空到 index.html 头部的配置当中。最后只需将该 HTML 文档上传至一个公共服务器即可。如此给出的二维码如下,可供扫描测试——当然如果最后转账就最好啦!

致谢

GitHub 项目的原作者应当是孟坤,在此感谢!

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。