立诚勿怠,格物致知
It's all about connecting the dots

微信跨公众号支付功能开发总结

本周团队里我分到的开发任务之一微信公众号的支付功能,需要和北京的一个java同事合作完成。搞了两天终于搞定。怕忘记具体流程,这里简要记录一下:

本文所指的微信跨号支付指的是如下的场景:

用户小明在微信公众号A里成功走完流程提交了一笔订单后点击页面上的支付按钮,吊起微信支付弹框,确认金额后输入密码即可完成支付。不过通常情况下这笔钱是支付给当前微信账号的,我们现在的场景是公众号A所属公司的营业性质不具有这个支付业务的收款权限,同公司另一个子公司(公众号B所属公司)具备相应牌照可以进行该类性质款额的收取,所以用户吊起微信支付弹框后我们需要让实际的收款公众号为微信公众号B。

首先我们要知道微信测试号是没有微信支付权限的,所以我们必须用微信正式号来测这个功能。在开始开发微信支付功能之前,微信正式号上需要先申请微信支付功能,这个是开发的前提,因为给我的正式号已经申请了这个功能,所以具体的流程并不清楚,反正是掏钱的活。

下面我们分别对两个微信号进行配置,每个微信号会分别用到一个域名。这两个域名不能是IP地址,也不能是http/https默认端口号之外的各种端口号。

测试号(微信号A,发起付款操作的微信号)配置:

我们微信测试号的前端文件之前一直是直接用ip地址访问的,我相信类似情况的朋友应该也不少,但是用ip的话我们是没办法测微信支付功能的,以前负责这块的后端同事是在生产环境试的(那时候生产环境还未上线),所以没涉及到这个问题。我们测试环境用的是apache,搜了下网络上的文章,配置了下代理,绑定了一个生产环境用的已经备案了的主域名的二级域名到这个测试服务器上(apache设定代理的文章网络上已经很多了就不写了,这里只提醒一下没有域名绑定经验的人:绑定域名出了在服务器端做好代理配置,还需要登录域名管理系统上指定域名要绑定的ip,这样访问域名时才会访问到我们的服务器,然后服务器根据我们的配置和请求来源的域名来判断转发请求到哪个服务上)。

测试号上主要的信息和配置点如下:

1、测试号信息、接口配置信息、JS接口安全域名:

appId前端和后端都可能会用到,谁要弄微信跳转地址就必须用到这个值。appID和appsecret都是腾讯给你配好的固定的值,无法修改。

【接口配置信息】里的URL和Token是需要我们自己填写的信息。这个是微信和我们自己的服务器互相校验用的。简单说明一下,这里当我们填完URL和Token的值后点击确定想保存时,微信会发起一个GET请求到我们填写的URL地址上,我们需要在该URL的GET请求(POST请求另有他用,所以这个接口地址请务必对GET和POST请求区别对待)中校验微信发来的请求里的签名,校验通过才可以相信当前这个请求是微信官方发过来的,然后将微信请求传过来的echostr重新返回给微信服务器,至此双方服务器算是互相认可了-_-,只有在微信服务器成功接收到你在接口中返回的echostr后,你在【接口配置信息】里填写的Token值才会被保存起来,否则微信会在页面上提示保存失败的。

JS接口安全域名是用来指定允许调用微信JSSDK的域名,也就是我们前端页面所在域名(不需要包含url中的路径和http://或https://这种协议部分的内容)。在测试号中,此处可以是subdomain.domain.com这样的域名,也可以填写111.222.33.44这样的ip地址。微信JSSDK是什么东西?如果你做过iOS/android内嵌react-native/html页面的项目的话,你应该会碰到需要原生应用暴露一些方法给你调用的场景。微信JSSDK其实是差不多的东西,他提供了浏览器默认API之外的一些API,有些API比如本文提到的微信支付是浏览器默认API中不存在的新功能,有些API比如拍照的用户体验会比input[type=file]调起来的原生拍照的用户体验好很多。这里因为我们后面要用这个测试号进行微信支付,而发起微信支付需要使用域名(如果只是用用拍照这些API,用ip是没问题的),所以此处需要填写域名而非ip地址。

2、授权用户获取基本信息/网页授权回调域名

点击下图中【网页账号】=>【网页授权获取用户基本信息】右侧的【修改】按钮。

然后会出现这样的弹框,因为通常我们调用微信JSSDK是所在的域名跟我们获取用户信息所要跳转到域名是一致的,所以此处就按我们上面填写的JS接口安全域名来填写就行了,也是不需要包含url路径和http://或https://这种协议部分的。我们显然知道这个地址的用途,但是这个地址是如何使用的呢?微信网页授权获取用户信息有两种方式,一种是需要用户点击同意进行授权,一种是不需要用户手动点击同意的,两种授权方式都能获取到用户的部分信息,区别主要是前者能获取到的信息相对多一些,但是后者的优点是不扰民,我实际项目中用的都是后面这种“静默”方式来获取用户信息。微信网页授权获取用户信息需要使用下面这种微信跳转地址:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

这个地址中大写的APPID、REDIRECT_URI、SCOPE和STATE部分都只是占位符,需要根据实际情况进行替换。微信官方有关于微信网页授权的介绍,具体可以到这个地址去查看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

稍微备注下REDIRECT_URI,这个地方要填写的是编码后的地址,并且是允许带查询参数的。这个编码的操作,在浏览器端就是encodeURIComponent(‘http://www.baidu.com/test/index.html?q=example’)后得到的”http%3A%2F%2Fwww.baidu.com%2Ftest%2Findex.html%3Fq%3Dexample”这样的字符串。用户再点击微信跳转地址(一般直接绑定到微信菜单上)后,就会跳转到被我们编码的那个地址(http://www.baidu.com/test/index.html?q=example)上,并且还在会这个地址后面追加一个code查询参数。该参数每次都是随机生成的,并且每个code都只能被使用一次(如果不使用,code的有效期最长可以保留5分钟,超过这个时间也会过期作废),其作用是服务端可以根据code来查询用户的openid,openid是用户在某个公众号里的唯一id,并且用户取消关注该该公众号然后重新关注该公众号后,新的openid跟之前的openid也是一样的值——其实这个openid只跟公众号和用户微信号有关系,只要这两个变量不变,openid就不会变。还要提醒的是,获取用户openid只是需要跳转地址跳一下拿到用户code后就能拿到用户openid的,并不需要用户去关注公众号,本文提到的跨微信号支付功能也利用了这一点。

微信号B(授权的微信号)配置:

在这里也需要配置微信网页授权回调域名(假设此处我们配置的域名是pay.example.com),配置方式跟上面说的差不多,唯一的区别是页面上会给你一个当作证明文件的txt文本文件,你需要把他上传到你的服务器上,并且要可以访问,否则配置完后是无法保存的,还有就是这个保存操作一个月只有3次机会(保存失败不会计数的,保存成功了才算作一次)。

URL和Token这个地方,如果仅仅是问了完成本文提到的微信跨号支付功能的话,是不需要填写的,反正我们这次没填写,不影响微信支付。

然后是配置微信支付授权目录,如下图所示,最多可以添加5个。

这里填写的支付授权目录是你在微信号A里发起支付请求是页面所在url的完整路径,长得类似下面这种样子:

http://subdomain.domain.com/a/b/c/d/

几个需要注意的地方:

  • 路径必须以“/”结尾;
  • 这里只填写到路径部分,不填写资源文件名、查询参数等内容。比如如果url是http://subdomain.domain.com/a/b/c/d/index.html?q=example的话,我们要填写的授权目录指的是index.html前面的这一部分;
  • 网络上有文章指出这里的目录层级需要时二级或者三级,但是正如上面示例所示,我用四级并没有问题。具体应该用几级不是随意的,还是拿http://subdomain.domain.com/a/b/c/d/index.html?q=example这个url为例,这里如果你授权目录没填写到d这里而是填写到了a或b或c就无法进行支付操作了——对了,授权目录需要填写到页面url中最深的路径部分。如果url是http://subdomain.domain.com/a/b/c/test.html,我们的授权目录就需要填写http://subdomain.domain.com/a/b/c/;
  • 强调下,此处的授权目录是根据发起请求时页面所在url来填写的,跟我们在微信号B中配置的pay.example.com这个域名之间没有任何关系,两者甚至都不需要是同一个根域名。正如上面示例的一样,我们这里填写的授权目录里的域名部分是跟微信号A里的相一致的,而非微信号B。

总操作流程:

用户在支付页面点击我们页面里的支付按钮,然后前端使用微信跳转地址跳转到微信pay.example.com开头的一个后端提供的接口上去,这个时候相当于对这个后端接口发起了一个get请求,服务端根据微信在接口后面追加的code查询参数来获取微信号A的用户在微信号B中对应的openid(用户无需关注微信号B),然后服务端在响应前端get请求时进行重定向返回到用户先前访问的支付页面并在新的url后面追加openid参数参数(微信号A的用户在微信号B中的openid),然后前端页面检测url中是否带有openid来判断是否需要通过js自动触发请求到服务端查询一些微信统一支付的参数来在客户端调启微信支付控件。大概就是这么个意思,实际操作中我们会对接口追加一些诸如告诉服务端重定向要定向到的地址,支付页面所在订单的id之类的参数。下面这种吊起维系支付控件的方式是不需要经过微信JSSDK授权的,两个不是一回事,因为用的不是微信JSSDK里的支付API。

非微信JSSDK方式调启微信支付控件的代码示例:

doPost(endPoint, requestData).done(data => {
  if (data.responseBody && data.responseBody.appid) {
    const options = {
      appId: data.responseBody.appid,
      timeStamp: data.responseBody.timeStamp,
      nonceStr: data.responseBody.nonce_str,
      package: 'prepay_id=' + data.responseBody.prepay_id,
      signType: 'MD5',
      paySign: data.responseBody.sign
    }
    if (typeof window.WeixinJSBridge === 'undefined') {
      if (window.document.addEventListener) {
        window.document.addEventListener('WeixinJSBridgeReady', onBridgeReady.bind(null, options), false)
      } else if (window.document.attachEvent) {
        window.document.attachEvent('WeixinJSBridgeReady', onBridgeReady.bind(null, options))
        window.document.attachEvent('onWeixinJSBridgeReady', onBridgeReady.bind(null, options))
      }
    } else {
      onBridgeReady(options)
    }
  } else if (data.errorCode && data.errorMsg) {
    _this.alert({ text: data.errorMsg })
  } else {
    _this.alert({ text: '服务器响应异常' })
  }
}).fail((jqXHR, textStatus, errorThrown) => {
  _this.alert({ text: '微信支付失败' })
}).always(() => { _this.wait(false) })

function onBridgeReady (options) {
  window.WeixinJSBridge.invoke(
    'getBrandWCPayRequest', options, function (data) {
      if (data && data.err_msg === 'get_brand_wcpay_request:ok') {
        _this.$router.push({
          path: '/pay-success',
          query: { orderid: _this.order.orderId }
        })
      } else if (
        data &&
        (
          data.err_msg === 'get_brand_wcpay_request:cancel' ||
          data.err_msg === 'get_brand_wcpay_request:fail'
        )
      ) {
        _this.alert({ text: '未成功支付' })
      } else if (data && data.err_desc) {
        _this.alert({ text: data.err_desc })
      } else {
        _this.alert({ text: '支付失败' })
      }
    }
  )
}

 

赞(1) 打赏
文章名称:《微信跨公众号支付功能开发总结》
文章链接:https://www.orzzone.com/wechat-payment.html
商业联系:yakima.public@gmail.com

本站大部分文章为原创或编译而来,对于本站版权文章,未经许可不得用于商业目的,非商业性转载请以链接形式标注原文出处。
本站内容仅供个人学习交流,不做为任何投资、建议的参考依据,因此产生的问题需自行承担。

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力提供更多优质内容!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册