Lumen/Laravel 中支付寶 / 微信第三方 App 登陸

woann發表於2020-03-03

介紹

    現在幾乎所有APP都整合了微信/支付寶登入,簡化使用者操作.這裡介紹下在lumen/laravel中如何進行微信/支付寶APP登陸。

  • 配置檔案 (config.woann.php 這裡是我自己建立的配置檔案,可以自行調整)

    <?php
    // +----------------------------------------------------------------------
    // | Created by PhpStorm
    // +----------------------------------------------------------------------
    // | Date: 2019-03-29
    // +----------------------------------------------------------------------
    // | Blog: ( http://www.woann.cn )
    // +----------------------------------------------------------------------
    // | Author: woann <www.woann.cn>
    // +----------------------------------------------------------------------
    return [
      //微信
      'wechat'    =>  [
          'app'   =>  [
              'appid'    =>  env('WECHAT_APPID', ''),
              'secret'    =>  env('WECHAT_APP_SECRET', '')
          ]
      ],
      //支付寶
      'alipay'    =>  [
          'app_id'        =>  env('ALI_APP_ID'),
          'pid'           =>  env('ALI_PID'),
          'private_key'   =>  env('ALI_PRIVATE_KEY')
      ]
    ];
  • 支付寶登入基類(app/utility/AliPayLogin.php 這個檔案隨便你放哪裡,能呼叫到即可)

    <?php
    // +----------------------------------------------------------------------
    // | Created by PhpStorm
    // +----------------------------------------------------------------------
    // | Date: 2019-05-24
    // +----------------------------------------------------------------------
    // | Blog: ( http://www.woann.cn )
    // +----------------------------------------------------------------------
    // | Author: woann <www.woann.cn>
    // +----------------------------------------------------------------------
    namespace App\Utility;
    use GuzzleHttp\Client;
    class AliPayLogin
    {
      protected $app_id;
      protected $pid;
      protected $private_key;
    
      public function __construct()
      {
          $config = config('woann.alipay');
          $this->app_id = $config['app_id'];
          $this->pid = $config['pid'];
          $this->private_key = $config['private_key'];
    
      }
    
      /**
       * InfoStr APP登入需要的的infostr
       *
       * @return String
       */
      public function infoStr()
      {
          $infoStr = http_build_query([
              'apiname' => 'com.alipay.account.auth',
              'method' => 'alipay.open.auth.sdk.code.get',
              'app_id' => $this->app_id,
              'app_name' => 'mc',
              'biz_type' => 'openservice',
              'pid' => $this->pid,
              'product_id' => 'APP_FAST_LOGIN',
              'scope' => 'kuaijie',
              'target_id' => getOrderNo(), //商戶標識該次使用者授權請求的ID,該值在商戶端應保持唯一
              'auth_type' => 'AUTHACCOUNT', // AUTHACCOUNT代表授權;LOGIN代表登入
              'sign_type' => 'RSA2',
          ]);
          $infoStr .= '&sign='.$this->enRSA2($infoStr);
          return $infoStr;
      }
    
      /**
       * AlipayToken 獲得使用者 請求token, 通過它獲得 使用者資訊
       *
       * 需要按照支付寶加簽流程來。
       */
      public function userInfo($app_auth_token)
      {
          $infoArr = [
              'method' => 'alipay.system.oauth.token',
              'app_id' => $this->app_id,
              'charset' => 'utf-8',
              'sign_type' => 'RSA2',
              'timestamp' => date('Y-m-d Hs'),
              'version' => '1.0',
              'code' => $app_auth_token,
              'grant_type' => 'authorization_code',
          ];
    
          $signStr = $this->myHttpBuildQuery($infoArr);
          $sign = urlencode($this->enRSA2($signStr));
          $qureStr = $signStr.'&sign='.$sign;
    
          $res = new Client();
          $body = $res->get('https://openapi.alipay.com/gateway.do?'.$qureStr)->getBody()->getContents();
          $body = json_decode($body);
          if (!isset($body->alipay_system_oauth_token_response->access_token)) {
              return false;
          } else {
              $autho_token = $body->alipay_system_oauth_token_response->access_token;
              $userinfo = $this->aliPayUserInfo($autho_token);
              return $userinfo; // 或則 返回 json_encode($userinfo) 根據實際需求來
          }
      }
    
      /**
       * AliPayUserInfo 通過 token 獲取使用者資訊
       */
      private function aliPayUserInfo($autho_token)
      {
          $infoArr = [
              'method' => 'alipay.user.info.share',
              'app_id' => $this->app_id,
              'charset' => 'utf-8',
              'sign_type' => 'RSA2',
              'timestamp' => date('Y-m-d Hs'),
              'version' => '1.0',
              'auth_token' => $autho_token,
          ];
    
          $signStr = $this->myHttpBuildQuery($infoArr);
          $sign = urlencode($this->enRSA2($signStr));
          $qureStr = $signStr.'&sign='.$sign;
    
          $res = new Client();
          $body = $res->get('https://openapi.alipay.com/gateway.do?'.$qureStr)->getBody()->getContents();
          $body = json_decode($body);
          if (!isset($body->alipay_user_info_share_response)) {
              return '介面異常';
          }
          $body = $body->alipay_user_info_share_response;
          return $body;
      }
    
      /**
       * enRSA2 RSA加密
       *
       * @param String $data
       * @return String
       */
      private function enRSA2($data)
      {
          $str = chunk_split(trim($this->private_key), 64, "\n");
          $key = "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n";
          // $key = file_get_contents(storage_path('rsa_private_key.pem')); 為檔案時這樣引入
          $signature = '';
          $signature = openssl_sign($data, $signature, $key, OPENSSL_ALGO_SHA256)?base64_encode($signature):NULL;
          return $signature;
      }
    
      /**
       * myHttpBuildQuery 返回一個 http Get 傳引數組
       * 之所以不用 自帶函式 http_build_query 時間帶 ‘:’ 會被轉換
       *
       * @param Array
       * @return String
       */
      private function myHttpBuildQuery($dataArr)
      {
          ksort($dataArr);
          $signStr = '';
          foreach ($dataArr as $key => $val) {
              if (empty($signStr)) {
                  $signStr = $key.'='.$val;
              } else {
                  $signStr .= '&'.$key.'='.$val;
              }
          }
          return $signStr;
      }
    }
  • 支付寶給客戶端提供的兩個介面

1.獲取code字串(客戶端通過這個字串獲取code)

 public function aliLoginInfoStr()
    {
        $ali = new AliPayLogin();//例項化支付
        $res = $ali->infoStr();
        return returnApi(200,'SUCCESS',['str' => $res]);
    }
    /*
    資料示例
        {
        "code":200,
        "msg":"SUCCESS",
        "data":{    "str":"apiname=com.alipay.account.auth&method=alipay.open.auth.sdk.code.get&app_id=2021001100636235&app_name=mc&biz_type=openservice&pid=2088631756058087&product_id=APP_FAST_LOGIN&scope=kuaijie&target_id=44039&auth_type=AUTHACCOUNT&sign_type=RSA2&sign=EXMs+NtovsZEKxcwjprKYwIJIbdeESLYxI/cjyqQ1Re8st0ejTktkX06V+gnyTiwfi9KtdSXbM1HMuC8hGyCEeMZb0IDSyOmOXp+f2dDJDXdlzoqyVowOxn7enTejXnngmu2WDuKQo2KcWfRVBKFlnYyA42QOUYw0PEKiXsgJYdjNPyVqKH9oTJJWIASJgZOgXbY4LSDh8gP+P/5QsuM41fTggeeRLJYHNlvbFuigSF+KpdcPiXXi6huI+4NQpN/7PLzXbVHzdeipkHeYTV6tMZY4njv5RvE4UH9ESd3urpoCgRJxriR/mUdGdQZCDHIBKMXTHmagRXH8mx70SFdpA=="
        }
    }
    */

2.通過code獲取使用者資訊進行登入

public function loginByAli(Request $request)
    {
        $code = $request->post('code');
        $ali = new AliPayLogin();//例項化支付寶登入基類
        $res = $ali->userInfo($code);//獲取使用者資訊
        //....根據業務自行處理
    }
  • 微信登入

1.微信通過code獲取使用者資訊(供微信登入介面呼叫)

private function getUserInfoByWx($code)
    {
        $app = config('woann.wechat.app');//獲取app_id,secret
        $auth_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$app['appid']."&secret=".$app['secret']."&code=".$code."&grant_type=authorization_code";
        $auth_res = httpRequest($auth_url);
        if(isset($auth_res['errcode'])){
            return ['code' => $auth_res['errcode'], 'msg' => $auth_res['errmsg']];
        }
        $info_url = "https://api.weixin.qq.com/sns/userinfo?access_token=".$auth_res['access_token']."&openid=".$auth_res['openid']."&lang=zh_CN";
        $userinfo = httpRequest($info_url);
        if(isset($userinfo['errcode'])){
            return ['code' => $userinfo['errcode'], 'msg' => $userinfo['errmsg']];
        }
        return ['code' => 200, 'msg' => $userinfo];
    }

2.登陸介面

public function loginByWechat(Request $reques)
{
    $code = $request->post('code');
  $userInfo = $this->getUserInfoByWx($code);
    if ($userInfo['code'] != 200) {
        return returnApi($userInfo['code'], $userInfo['msg']);
    }
    //以下根據自己業務進行調整
    DB::beginTransaction();
    //根據openid查詢使用者是否存在
  $user = $this->where('wechat_openid', $userInfo['msg']['openid'])->first();
  if (!$user) {
        $user = new User();
        $user->avatar = $userInfo['msg']['headimgurl'];
        $user->nickname = $userInfo['msg']['nickname'];
        $user->sex = $userInfo['msg']['sex'];
        $user->wechat_openid = $userInfo['msg']['openid'];
    }
    $res = $user->save();
    if($res){
        if (!$token = Auth::login($user)) {
            DB::rollBack();
            return returnApi(500, '未知錯誤');
        } else {
            DB::commit();
            return returnApi(200, '登入成功', [
                'access_token' => 'Bearer '.$token
                ]);
        }
    }else{
        DB::rollBack();
        return returnApi(500,'未知錯誤');
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章