WildCat's Blog

钝鸟先飞,大器晚成

Designer + Programmer


微信支付开发踩坑与吐槽

微信支付作为国内非常便捷的支付系统,自然有着很大的吸引力。然而,与之规模不相称的是,微信支付的开发是一个非常痛苦的过程。自己就面临着资料少且不全、文档不专业不具体的种种问题,甚至还有一些不为人知的隐秘 bug。本文将从最常用的微信支付渠道之一——微信公众号支付的开发讲起,抛砖引玉,总(tu)结(cao)开发中遇到的坑,以节约大家宝贵时间,避免重复踩坑。特别鸣谢:@yelo

让人迷惑的 key,key,key

微信支付大概需要 4 种密钥(参考来源):

  1. APPID:对于公众号,就是公众号的“开发者ID(AppID)”
  2. 微信支付商户号微信支付商户平台中获取,目前也可以在微信服务号后台的“微信支付”页面获取。
  3. API密钥:在微信支付商户平台中获取。一定注意这个 API 密钥不是公众号的 secret
  4. Appsecret:就是公众号的“开发者密码(AppSecret)”

一个支付,弄这么多密钥,真的是让人头大。

不专业且不责任的文档与 API

公众号支付的文档有两处:微信网页 SDK 中的一个章节微信支付文档。 这两处文档都有很多槽点:

  1. 这两处文档中的代码使用的前端支付接口不一样。虽然后面有了解到,微信网页 SDK 调用支付时,本质也是通过 WeixinJSBridge.invoke() 实现的,但是微信文档似乎没有对此做一个详尽的比较,真的是让人感到困惑与选择困难。
  2. 两处请求参数的 JSON 结构有一处不一致:timeStamp vs timestamp。这一点可谓非常不专业。对比如下:
      // 微信支付文档:公众号支付
      function onBridgeReady(){
      	WeixinJSBridge.invoke(
      		'getBrandWCPayRequest', {
      			"appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入     
      			"timeStamp":"1395712654",         //时间戳,自1970年以来的秒数     
      			// ...省略
      }
      // 微信公众号网页 SDK 文档
      wx.chooseWXPay({
      	timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
      	// ...省略
      });
    
  3. 微信公众号网页 SDK 文档中只有支付成功的回调函数,并没有失败的。

坑人的“JSAPI 支付授权目录”

对于重前端的 SPA 应用,支付时有一个大坑:https://www.example.com/#/foo-pathhttps://www.example.com/?#/foo-path 并不是一个 URL,微信似乎会把前者当作 https://www.example.com/foo-path。 举个例子,如果你在微信支付后台配置的“JSAPI 支付授权目录”为 https://www.example.com,那么:

URL 结果
https://www.example.com/#/foo-path 支付失败,配置的支付授权目录错误
https://www.example.com/?#/foo-path 支付成功

也就是说,在 hashtag 之前,必须有一个问号(?),否则不会支付成功。

粗暴的解决方案,判断 URL 中是否含有问号,不含问号时添加一个:

private hackWechatPay() {
	const url = window.location.href;
	// 判断当前url是否存在?参数匹配符
	if (!url.match(/\?#/)) {
		location.replace(window.location.href.split('#')[0] + '?' + window.location.hash);
		return;
	}
	// Reference: https://www.jianshu.com/p/1ace87d3c1fc
}

奇怪的沙箱模式与开发环境

开始开发的时候,以为微信支付的沙箱环境就是用于测试的环境。但是其实用处不大:

  1. 支付金额仅是固定的几个,比如 1.01 等等。
  2. 在自己的开发模式,即使可以签名成功,也会提示诸如“没有提供 out_trade_no“或者“调用支付JSAPI缺少参数:total_fee”之类的驴唇不对马嘴的奇怪错误,前端代码无法获取到支付成功的回调。
  3. 即使沙箱模式,也必需使用生产环境绑定的域名,无法在本地测试。
  4. 前端开发方面,即使用官方的“微信开发者工具”,也没有办法进行模拟支付(生产环境也不行),给调试造成了巨大不便。个人在生产环境调试方法是,判断 URL 中是否有某一个特殊的 flag,如果有的话,就加载诸如 eruda 或者 vconsole 这样的调试工具。

一个小总结就是,不要使用沙箱模式进行开发,没有必要,完全浪费时间。直接线上环境肉测吧。什么自动化测试,TDD,见鬼去吧。

结语

目前认为,微信公众号支付的开发最佳实践是:

  1. 使用生产环境进行测试,如果生产环境不能用来测试,那么就需要额外注册一个支持微信支付的服务号进行测试。并且,生产环境尽量要加入允许调出调试工具的 URL param flag,随时调试(参考上一节第四点)。
  2. 服务端进行支付验证时,注意不要用 cash_fee,这个是使用优惠券后的实际支付价格。订单价格验证要用 total_fee
  3. 官方文档看看就行了,不如社区的踩坑经历有价值。但是注意踩坑经历的时效性。

本文目的在于节约大家的宝贵开发时间,同时指出微信团队的可改进之处。目前微信支付的开发体验,真的是让人无法更失望。从微信支付系统的开发到上线,简直是像下图一样的过程:

wechat (来源 @jaceju

作为母公司两万多亿港元市值的企业,拿出一小部分资源优化这个核心业务,总比 PayPal(近 $1,000 亿市值) 和 Stripe 要容易很多。大家可以对比下这些支付系统的文档和开发体验,高下立判。微信支付的文档水平,甚至不如国内某些支付创业公司。另外,服务端的 SDK,也基本是开发者们各自为战,重复造轮子。这不是能力问题,是态度问题。优化开发流程与体验,对腾讯(微信)和开发者还有商户是多赢的——提高项目上线速度、上线后的稳定性与支付成功率,对大家都是好事,何乐而不为呢?

(未完待续)

参考资料

  1. 微信支付的沙箱环境能否使用?https://github.com/Wechat-Group/weixin-java-tools/issues/668#issuecomment-409504376
  2. Stripe on GitHub:https://github.com/stripe
  3. PayPal SDKs: https://github.com/paypal?utf8=✓&q=sdk&type=&language=
  4. 微信支付小坑:http://kinoandworld.github.io/blog/2014/09/17/weixinpayforios/
  5. 谈谈那些年微信支付踩过的坑:https://www.ieclipse.cn/2017/05/24/Android/quickaf-wechat-pay/
  6. 一次痛苦又甜蜜的微信支付踩坑之旅:https://segmentfault.com/a/1190000013460422

wechat-tip-code

更早的文章

简明深度学习工作站常用技巧和软件配置

使用自己的 PC 作为深度学习工作站已经有一段时间了。由于硬件配置是根据自己预算量力而行(当然是越贵越好 🤷‍♂️),所以这里只讨论软件配置。 由于笔者经验较少,能力有限,本文目的在于抛砖引玉而非最佳实践,帮助大家构建易用的深度学习工作站。深度学习工具相关 Python 包管理工具:这里推荐还是 Anaconda,如果很想保守一点,可以在 pyenv 里安装 miniconda。当然这个问题是见仁见智,Google 开发者的一期视频大概也是这么讲的:https://www.bilibi...…

继续阅读