微信公众号支付实现详解(前端)

9,241 阅读3分钟

前言

完成微信h5支付的你,继续公众号的支付也许更简单哦。

场景

微信浏览器中的应用支付必须依赖于公众号支付,下面就公众号支付中的一些技术点进行详细的解析。

准备工作

基本配置申请

参考资料:微信公众号开通支付功能--百度经验教程

基本信息

  • 服务号,服务号绑定的管理员号
  • 开通支付账号,并记住支付账号,与支付账号绑定的微信号
  • appid,秘钥
  • 支付账号开通支付目录(直接支付地址的上一级目录
  • 设置了页面授权域名,并且是你的站点域名地址
  • 基本接口权限,尤其是jssdk部分权限,保证尽可能都开通

业务流程图解以及时序图

与微信h5基本相同,唯一不同的是这次微信返回的需要唤起微信sdk支付的参数列表。

技术问题

获取openid

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。获取openid分为两步,获取code,然后根据code获取openid,建议这两部分请求由后端发起,前端直接请求会涉及到跨域问题。后端直接把这两个方法定义为工具方法,使用方便,便于其他场景的复用。

一 :请求获取code,如果不想浪费时间请直接复制粘贴使用

  • 标准格式拼接代码:

    let encodeUrl=encodeURIComponent(`http://xxx/xhxwxpay?productId=${productId}&orderNo=${orderNo}`)
    let tempUrl=`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx6f5de09c8ef178a7&redirect_uri=${encodeUrl}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
    
  • 请求参数说明

    参数 是否必须 说明
    appid 应用唯一标识
    redirect_uri 请使用urlEncode对链接进行处理
    response_type 填code
    scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可,这里用的 snsapi_userinfo
    state 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
  • 返回说明: 用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数 redirect_uri?code=CODE&state=STATE 若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数 redirect_uri?state=STATE

  • 参考文档:微信中web网站获取code参考文档

二 根据code获取openid

  • 一个用户针对一个公众号openid是固定的,所以获取到一样的不用怀疑,涉及到部分敏感的公众号的秘钥等,建议是后端处理发起请求,这样也可以避免前端跨域的问题。

    //网关或者后端的设置(以koa框架的为例)
    *post_getOpenId(){
            let reqData = this.request.body;
            let param = {
                appid:'wxxxx',
                secret:'affsdcsdvdsvfv6',
                code:reqData.code,
                grant_type:'authorization_code'
            }
            let result = yield this.api.getOpenId(param);
            this.body = result;
        }
    // 获取openid 传入对象的形式,改造通用的api方法
    getOpenId: function* (apiParam, json = true) {
        // 获取token的地址
        let apiUrl='https://api.weixin.qq.com/sns/oauth2/access_token'
        let response = yield request.get(apiUrl, { qs: apiParam, json: json });
        return responseHandle(response, apiUrl, apiParam);
    }.bind(this),
    
    //前端的写法,好处是避免暴露公众号的相关信息
    this.$api.post("order/getOpenId", { code: this.code }).then(res => {
          //  正确获取openid的情况下 请求后台参数得到对应的返回参数,目前只需要openid
          if (res.openid) {
            this.openId = res.openid;
            //准备条件足够的话 可以唤起支付
            this.topay()
         }else {
           //请求失败或者没有对应的字段
           }
    
  • 请求参数说明,通过code获取access_token

    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
    
    
    参数 是否必须 说明
    appid 应用唯一标识,在微信开放平台提交应用审核通过后获得
    secret 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
    code 填写第一步获取的code参数
    grant_type 填authorization_code
  • 返回结果说明

    //正确的返回
    { 
    "access_token":"ACCESS_TOKEN", 
    "expires_in":7200, 
    "refresh_token":"REFRESH_TOKEN",
    "openid":"OPENID", 
    "scope":"SCOPE",
    "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
    }
    //错误的返回
    {"errcode":40029,"errmsg":"invalid code"}
    
  • 刷新fresh-token有效期 如果token失效了,可以用refresh_token重新获取一个。

微信支付sdk

方式一 :官网的方式

invoke方法 ,简单有效,直接根据接口返回参数唤起。以下代码实例是vue环境下的,其他环境请自行匹配,仅供参考。

// 准备好微信sdk部分  
    jsSdk(){
          // 判断微信的WeixinJSBridge
    if (typeof WeixinJSBridge == "undefined"){
        if( document.addEventListener ){
            document.addEventListener('WeixinJSBridgeReady', this.onBridgeReady, false);
        }else if (document.attachEvent){
            document.attachEvent('WeixinJSBridgeReady', this.onBridgeReady); 
            document.attachEvent('onWeixinJSBridgeReady', this.onBridgeReady);
        }
    }else{
      this.onBridgeReady();
    }
    },
    // 支付sdk准备完成
    onBridgeReady() {

    // 触发微信支付
          WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
       appId: this.payOption.appId, //公众号名称,由商户传入
        timeStamp: this.payOption.timeStamp, //时间戳,自1970年以来的秒数
        nonceStr: this.payOption.nonceStr, //随机串
        package: this.payOption.package, //prepay_id用等式的格式
        signType: this.payOption.signType, //微信签名方式:
        paySign: this.payOption.paySign, //微信签名
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {
             // 支付成功 返回成功页 
             let tempUrl="//paysucc"
            location.href=tempUr
           } else{
            //  取消支付或者其他情况 get_brand_wcpay_request:cancel get_brand_wcpay_request:fail
            let tempUrl='//topay'
            location.href=tempUrl
           }    
       }
   ); 
    },

方式二 : 需要引入js-weixin的模块,流程如下:

引入模块--ready--获取access-token--获取ticket--生成签名(wx.config需要)--结合接口返回参数--唤起wxpay。(比较麻烦,不推荐使用) 参考文档:

参考文档