分享增加获取微信官方的获取手机号的功能,以及一些微信小程序的小Bug。

功能发布 · Chen · 于 3年前 发布 · 2443 次阅读

线上效果图:

操作如下:

在\fecshop_win\addons\fecmall\fecyo\lib\wxapp\文件夹放入微信官方的Demo,三个php文件

在fecshop\fecshop_wxapp\fecyo\pages\login\login.wxml文件的47行加上:

<button type="primary" class="save-btn" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" >{{language.read_form_weixin}}</button>

在fecshop\fecshop_wxapp\fecyo\pages\login\login.js文件

18行:去除默认手机号。

102行:只要将下面的代码替换掉从102行开始执行的函数即可。主要是增加保存session_key,顺带修复无伤大雅的小Bug,即:失败重复请求由7次改为作者备注的5次,

	success: function (res) {
        wx.hideLoading();
        app.saveReponseHeader(res);
        //console.log(res);
        if (res.data.code == '1100025') { // 没有相关用户,需要绑定
          that.setData({
            loadAccount: 2,
            userinfo:res.data.data,
          });
          //wx.setStorageSync('userinfo',userinfo);
    
          return
        }
        if (res.data.code == '200') { // 登陆成功,进行跳转
          wx.navigateBack({
            delta: 1
          });
          //that.globalData.uid = res.data.data.uid;
          //wx.navigateTo({
          //  url: "/pages/my/my"
          //})
        } else { // 其他的失败情况,重新进行登陆操作,最大5次
          // 如果获取微信数据失败,则重复获取,最大5次
          
          var reRequestCount = that.data.reRequestCount;
          that.setData({
            reRequestCount: reRequestCount + 1,
          });
          if (reRequestCount < 4) {//此逻辑是第五次执行后才跳过
            // wx.showModal({
            //   title: "提示",
            //   content: "微信登录失败,重试中",
            //   showCancel: false
            // });
            that.wxLogin();
          } else {
            wx.showModal({
              title: "提示",
              content: "微信登录失败",
              showCancel: false
            });
          }
          that.setData({
            loadAccount: 3,
          });
          return;
        }
      }

在文件末尾 }) 前面加上:

getPhoneNumber(e) {
var that = this,
session_key=that.data.userinfo.session_key,
encryptedData=e.detail.encryptedData,
iv=e.detail.iv,
openid=that.data.userinfo.openid;

if (e.detail.errMsg == 'getPhoneNumber:ok') {
  wx.checkSession({
    success:function(){
      that.deciyption(session_key,encryptedData,iv,openid);
    },
    fail:function(){
      that.wxLogin();
      that.deciyption(this.data.userinfo.session_key,encryptedData,iv,openid);  
    }
  })
}
},
deciyption(session_key,encryptedData,iv,openid){
var that = this;
wx.request({
  url: app.globalData.urls + "/customer/login/bindaccount2",  // "/user/wxapp/login",
  header: app.getPostRequestHeader(),
  method: 'POST',
  data: {
    session_key: session_key,
    encryptedData: encryptedData,
    iv: iv,
    openid: openid,
  },
  success: function (res) {
    wx.hideLoading();
    app.saveReponseHeader(res);
    //console.log(res);
    if (res.data.code == '1100026') {  // 无法通过微信api获取信息
      wx.showModal({
        title: "提示",
        content: "无法从Session中获取微信Openid,请重新绑定",
        showCancel: false
      });
      that.wxLogin();
      return;
    } else if (res.data.code == '1100027') { // 登陆成功,进行跳转
      wx.showModal({
        title: "提示",
        content: "您已经有绑定的账户",
        showCancel: false
      });
      return;
    } else if (res.data.code == '1100007') { // 没有相关用户,需要绑定
      wx.showModal({
        title: "提示",
        content: "注册账户失败: " + res.data.data.errors,
        showCancel: false
      });

    } else if (res.data.code == '1100029') { // 没有相关用户,需要绑定
      wx.showModal({
        title: "提示",
        content: "该邮箱已经存在,请点击`已有账户`,输入邮箱密码进行账户绑定",
        showCancel: false
      });
    } else if (res.data.code == '1100028') { // 没有相关用户,需要绑定
      wx.showModal({
        title: "提示",
        content: "用户的账户密码不正确,请重新输入",
        showCancel: false
      });
    } else if (res.data.code == '200') { // 没有相关用户,需要绑定
      //wx.navigateTo({
      //  url: "/pages/my/my"
      //})
      wx.navigateBack({
        delta: 1
      });
    } else {
      wx.showModal({
        title: "提示",
        content: "绑定失败,请重新绑定",
        showCancel: false
      });
      that.wxLogin();

    }

  }
});
},

在文件:\fecshop\fecshop_win\addons\fecmall\fecyo\app\appserver\modules\Customer\controllers\LoginController.php 在文件末尾 } 前加上下面代码:

    // 绑定账户
    public function actionBindaccount2()
    {   
        $encryptedData = Yii::$app->request->post('encryptedData');
        $iv = Yii::$app->request->post('iv');
        $wx_session_key = Yii::$app->request->post('session_key');
        $wx_openid = Yii::$app->request->post('openid');

        if (!$wx_openid || !$wx_session_key || !$encryptedData || !$iv) {
            $code = Yii::$service->helper->appserver->no_account_openid_and_session_key;
            $data = [ ];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }
        
        if (Yii::$service->customer->getByWxOpenid($wx_openid)) {
            // 已经存在绑定的用户,绑定失败
            $code = Yii::$service->helper->appserver->account_has_account_openid;
            $data = [ ];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }
        
        $resultData = $this->PhoneIpn($encryptedData,$iv,$wx_session_key);
        if (is_array($resultData)) {
             $phone = $resultData['phoneNumber'];
        } 

        if (!$phone) {
            $code = Yii::$service->helper->appserver->account_register_fail;
            $data = $resultData;//[ ];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }    

        $param = [
            'phone' => $phone,
            'openid' => $wx_openid,
            'session_key' => $wx_session_key,
        ];
        $access_token = Yii::$service->customer->phoneRegisterAndGetLoginAccessToken($param);
        if (!$access_token) {
            $code = Yii::$service->helper->appserver->account_register_fail;
            $data = [
                'errors' => Yii::$service->helper->errors->get(),
            ];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }
        $code = Yii::$service->helper->appserver->status_success;
        $data = [ ];
        $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
        
        return $responseData;
        
    }

    public function PhoneIpn($encryptedData,$iv,$sessionKey)
    {
        $wxBizDataCrypt = Yii::getAlias('@fecyo/lib/wxapp/wxBizDataCrypt.php');
        require_once($wxBizDataCrypt);
        
        $pc = new \WXBizDataCrypt(Yii::$app->store->get('payment_wxpay', 'wechat_micro_app_id' ),$sessionKey);
        $decryptData = "";  //解密后的明文
        $errCode = $pc->decryptData($encryptedData, $iv, $decryptData);
        if ($errCode == 0) {
            return json_decode($decryptData,true);
        } else {
            return $errCode;
        }       
    }

在61行左右,有个小瑕疵顺便说下:


        $phone = $wxCode = Yii::$app->request->post('phone');
        $captcha = $wxCode = Yii::$app->request->post('captcha');

改为:

        $phone = Yii::$app->request->post('phone');
        $captcha = Yii::$app->request->post('captcha');

框架作者如果要改,那就顺便把文件: fecshop\fecshop_win\vendor\fancyecommerce\fecshop\app\appserver\modules\Customer\controllers\LoginController.php

207行 到 209行的也改下:

        $isBindNew = $wxCode = Yii::$app->request->post('isBindNew');
        $email = $wxCode = Yii::$app->request->post('email');
        $password = $wxCode = Yii::$app->request->post('password');

同一文件的288行左右,将空数组改为含有session_key和openid信息的数组:

		// 如果$openid 没有 对应的customer,则需要先绑定或者创建相应的账户
        if (!$customer) {
            $code = Yii::$service->helper->appserver->account_wx_get_customer_by_openid_fail;
            $data = [];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }

改为:

		// 如果$openid 没有 对应的customer,则需要先绑定或者创建相应的账户
        if (!$customer) {
            $code = Yii::$service->helper->appserver->account_wx_get_customer_by_openid_fail;
            $data = $wxUserInfo;
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }

在文件:fecshop\fecshop_win\addons\fecmall\fecyo\services\Customer.php

218行:

$session_key = $param['openid'];

Bug修复改为:

$session_key = $param['session_key'];

在文件:\fecshop\fecshop_win\vendor\fancyecommerce\fec\helpers\CApi.php

53行加入,这样本地开发才能获取openID。

            if (stripos ( $url, "https://api.weixin.qq.com" ) !== false) { 
            	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//绕过ssl验证
        		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            }

本文由 Chen 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。

共收到 11 条回复 技术分享
Fecmall#13年前 0 个赞

你这里加了一个 包:@fecyo/lib/wxapp/wxBizDataCrypt.php

Fecmall#23年前 0 个赞

另外

1.把session_key传递给微信小程序不安全吧?

2.本地开发微信小程序,可以关掉https,不用更改capi是基层函数

多谢分享,我改进一下,

赞一下您的分享。

Chen#33年前 0 个赞

@Fecmall #1楼

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html

这个包是微信官方的Demo包,你点下链接:加密数据解密算法,里面有下载链接

我原本按你的路子走,在后端生成session_key,但是微信那边会报错,因为wx.login,会刷新登陆态,由于getPhoneNumber所生成的encryptedData和iv参数,依赖前提是wx.login所产生的session_key,所以后端再生成就不是对应匹配的。

不过session_key只是相对于操作用户,感觉没什么不安全的

Chen#43年前 0 个赞

@Fecmall #2楼

我本地是关掉https的,但是本地开发,用户登录获取不了openid,需要绕过ssl证书,举个例子路径:

\fecshop\fecshop_win\addons\fecmall\fecyo\app\appserver\modules\Customer\controllers\LoginController.php

actionBindaccount()函数

\fecshop\fecshop_win\vendor\fancyecommerce\fecshop\services\helper\Wx.php

getUserInfoByCode()函数

\fecshop\fecshop_win\vendor\fancyecommerce\fec\helpers\CApi.php

getCurlData()函数

我不知道你怎么测试的,反正我微信开发工具也是设置了不检验合法证书,不检验https,你测试的可能是之前的缓存数据,建议你可以注册个新用户试试

Fecmall#53年前 0 个赞

1.getPhoneNumber所生成的encryptedData和iv参数,依赖前提是wx.login所产生的session_key

回:获取的时候不需要session_key参数吧?

session_key是php和微信端通信的session,刷新应该也没有关系,这个值也保存到了数据库,我测试通过,没有问题

2.我本地https没有问题,先这样吧。

绑定手机号,还是用

$wxCode = Yii::$app->request->post('code');
        //echo $wxCode;
        // 通过code 和 微信的一些验证信息,得到微信的信息uid
        $wxUserInfo = Yii::$service->helper->wx->getUserInfoByCode($wxCode);
        // 如果通过code获取微信信息(api获取)失败
        if (!$wxUserInfo) {
            // code  获取openid失败
            $code = Yii::$service->helper->appserver->account_wx_get_user_info_fail;
            $data = [ ];
            $responseData = Yii::$service->helper->appserver->getResponseData($code, $data);
            
            return $responseData;
        }
        
        // 得到 openid  和  session_key
        $wx_openid = $wxUserInfo['openid'];
        $wx_session_key = $wxUserInfo['session_key'];

这个获取出来

Chen#63年前 0 个赞

@Fecmall #5楼

只要encryptedData和iv参数和session_key对应匹配就行,因为加解密,是需要同一个code获取到的session_key,存储到服务器那当然最好了

我的起初想法是,新用户注册前,还没有账号,就没去保存session_key,当然,你可以走另外的路子。

附上微信官方的说明:

使用方法

需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。 注意

在回调中调用 wx.login 登录,可能会刷新登录态。此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。建议开发者提前进行 login;或者在回调中先使用 checkSession 进行登录态检查,避免 login 刷新登录态。

Chen#73年前 0 个赞

@Fecmall #5楼

哈哈哈哈哈我走过这个坑,留贴告知,奈何你还要再趟一遍这个坑。

winintel#83年前 0 个赞

我小程序 发送 验证码 失败 也是这个问题吗?最新版 feyco 小程序

Fecmall#93年前 0 个赞

现在这个没有问题了

qwzjoyr#102年前 0 个赞

在第三方服务器上解密出来的手机号该如何传回小程序中并展示?

rem486#117个月前 0 个赞

为什么我的微信小程序点击微信获取一直弹出微信登录失败

添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册
Your Site Analytics