1、在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平台官網中的“開發 - 接口權限 - 網頁服務 - 網頁帳號 - 網頁授權獲取用戶基本信息”的配置選項中,修改授權回調域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協議頭; 比如需要網頁授權的域名為:www.qq.com,配置以后此域名下面的頁面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以進行OAuth2.0鑒權。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com無法進行OAuth2.0鑒權 (就是不能用)
2、關於網頁授權access_token和普通access_token的區別:我理解的就是 網頁授權的access_token沒什么特別的限制(雖然貌似也是2小時有效期)。大家隨便無限制用;但是除此之外用到的access_token是有限制的。真的是有效期2小時,而且每天有獲取access_token的限制次數。
因為有限制次數,所以我在做的時候是把獲取到的access_token保存在某個文件里,設置7000(小於一點點官方的2小時)秒過期,下次我再去獲取access_token的時候看這個設置的過期時間有沒有到。如果沒到。就直接獲取access_token值直接用。如果過期了,那么再次去獲取下。再次保存到這個文件里去;
另外普通過的access_token 是一個全局的共用的值,什么意思呢,比如你倆個模塊都用到了access_token 但是你 倆個模塊都單獨存了一份access_token的文件。那么恭喜你,你中招了。因為 每次用戶獲取一次access_token的時候微信的服務器是緩存記錄了最新access_token的最新值。比如你A模塊獲取更新了一次access_token,緊接着B模塊也獲取更新了一次access_token。那么,此時微信服務器緩存記錄的是B模塊獲取的access_token值,再接着A模塊去獲取一次access_token。因為是緊接着嘛,那么倆小時肯定沒到啊,也就是沒過期啊,那么自動從access_token保存文件里獲取值,可是實際此時微信服務器里緩存的access_token值 是B模塊最新更新的值啊,那你繼續進行你當前模塊下涉及到access_token的運算,肯定是要提示access_token錯誤的(跟服務器的不一致啊);
所以普通的access_token 一定要放在一個公共的,所有模塊都調用的同一個地方。這樣就避免了上面的錯誤;
好了,下面進入正題。先介紹下微信網頁授權的基本流程;
1.你進入到某個頁面,這個頁面先判斷地址url里有沒有code參數;如果有code參數直接調用請求以下鏈接獲取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code 可以獲取到
直接就可以獲取到openid的值。 也就是完成了網頁授權的基本流程,剩下你自己程序的操作了。
2.如果這個頁面沒有code參數,那么先組裝url到那個讓用戶點擊授權的頁面,
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect ;跳轉到這個url去,得到下圖
用戶點擊確認登錄之后。頁面自動貨跳轉到 redirect_uri/?code=CODE&state=STATE。這個頁面(redirect_uri是上面你自己設置url頁面,默認就是用戶剛開始進入第一個頁面的url)。
此時,等於又一次進入到當前頁面了。默認會執行第一步判斷操作(此時url獲取的code的值),完成授權基本流程。
getOpenId.php 代碼如下
<?php require_once "./lib/WxPay.JsApiPay.php"; $tools = new JsApiPay(); $openId = $tools->GetOpenid(); echo $openId; ?>
WxPay.Config.php代碼如下
<?php /** * 配置賬號信息 */ class WxPayConfig { //=======【基本信息設置】===================================== // /** * TODO: 修改這里配置為您自己申請的商戶信息 * 微信公眾號信息配置 * * APPID:綁定支付的APPID(必須配置,開戶郵件中可查看) * * MCHID:商戶號(必須配置,開戶郵件中可查看) * * KEY:商戶支付密鑰,參考開戶郵件設置(必須配置,登錄商戶平台自行設置) * 設置地址:https://pay.weixin.qq.com/index.php/account/api_cert * * APPSECRET:公眾帳號secert(僅JSAPI支付的時候需要配置, 登錄公眾平台,進入開發者中心可設置), * 獲取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN * @var string */ const APPID = '123123213213'; const MCHID = '123123123123123123'; const KEY = '13123123123213'; const APPSECRET = '123123213213213123213123'; //=======【證書路徑設置】===================================== /** * TODO:設置商戶證書路徑 * 證書路徑,注意應該填寫絕對路徑(僅退款、撤銷訂單時需要,可登錄商戶平台下載, * API證書下載地址:https://pay.weixin.qq.com/index.php/account/api_cert,下載之前需要安裝商戶操作證書) * @var path */ const SSLCERT_PATH = '../xxxx/apiclient_cert.pem'; const SSLKEY_PATH = '../xxxx/apiclient_key.pem'; //=======【curl代理設置】=================================== /** * TODO:這里設置代理機器,只有需要代理的時候才設置,不需要代理,請設置為0.0.0.0和0 * 本例程通過curl使用HTTP POST方法,此處可修改代理服務器, * 默認CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此時不開啟代理(如有需要才設置) * @var unknown_type */ const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220"; const CURL_PROXY_PORT = 0;//8080; //=======【上報信息配置】=================================== /** * TODO:接口調用上報等級,默認緊錯誤上報(注意:上報超時間為【1s】,上報無論成敗【永不拋出異常】, * 不會影響接口調用流程),開啟上報之后,方便微信監控請求調用的質量,建議至少 * 開啟錯誤上報。 * 上報等級,0.關閉上報; 1.僅錯誤出錯上報; 2.全量上報 * @var int */ const REPORT_LEVENL = 1; }
WxPay.JsApiPay.php代碼如下
<?php require_once "WxPay.Config.php"; /** * * JSAPI支付實現類 * 該類實現了從微信公眾平台獲取code、通過code獲取openid和access_token、 * 生成jsapi支付js接口所需的參數、生成獲取共享收貨地址所需的參數 * * 該類是微信支付提供的樣例程序,商戶可根據自己的需求修改,或者使用lib中的api自行開發 * * @author widy * */ class JsApiPay { /** * * 網頁授權接口微信服務器返回的數據,返回樣例如下 * { * "access_token":"ACCESS_TOKEN", * "expires_in":7200, * "refresh_token":"REFRESH_TOKEN", * "openid":"OPENID", * "scope":"SCOPE", * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" * } * 其中access_token可用於獲取共享收貨地址 * openid是微信支付jsapi支付接口必須的參數 * @var array */ public $data = null; /** * * 通過跳轉獲取用戶的openid,跳轉流程如下: * 1、設置自己需要調回的url及其其他參數,跳轉到微信服務器https://open.weixin.qq.com/connect/oauth2/authorize * 2、微信服務處理完成之后會跳轉回用戶redirect_uri地址,此時會帶上一些參數,如:code * * @return 用戶的openid */ public function GetOpenid() { //通過code獲得openid if (!isset($_GET['code'])){ //觸發微信返回code碼 $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']; $baseUrl = rtrim($url, '/'); $baseUrl = urlencode($baseUrl); $url = $this->__CreateOauthUrlForCode($baseUrl); Header("Location: $url"); exit(); } else { //獲取code碼,以獲取openid $code = $_GET['code']; $openid = $this->getOpenidFromMp($code); return $openid; } } /** * * 獲取jsapi支付的參數 * @param array $UnifiedOrderResult 統一支付接口返回的數據 * @throws WxPayException * * @return json數據,可直接填入js函數作為參數 */ public function GetJsApiParameters($UnifiedOrderResult) { if(!array_key_exists("appid", $UnifiedOrderResult) || !array_key_exists("prepay_id", $UnifiedOrderResult) || $UnifiedOrderResult['prepay_id'] == "") { throw new WxPayException("參數錯誤"); } $jsapi = new WxPayJsApiPay(); $jsapi->SetAppid($UnifiedOrderResult["appid"]); $timeStamp = time(); $jsapi->SetTimeStamp("$timeStamp"); $jsapi->SetNonceStr(WxPayApi::getNonceStr()); $jsapi->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']); $jsapi->SetSignType("MD5"); $jsapi->SetPaySign($jsapi->MakeSign()); $parameters = json_encode($jsapi->GetValues()); return $parameters; } /** * * 通過code從工作平台獲取openid機器access_token * @param string $code 微信跳轉回來帶上的code * * @return openid */ public function GetOpenidFromMp($code) { $url = $this->__CreateOauthUrlForOpenid($code); //初始化curl $ch = curl_init(); //設置超時 // curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE); curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" && WxPayConfig::CURL_PROXY_PORT != 0){ curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST); curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT); } //運行curl,結果以jason形式返回 $res = curl_exec($ch); curl_close($ch); //取出openid $data = json_decode($res,true); $this->data = $data; $openid = $data['openid']; return $openid; } /** * * 拼接簽名字符串 * @param array $urlObj * * @return 返回已經拼接好的字符串 */ private function ToUrlParams($urlObj) { $buff = ""; foreach ($urlObj as $k => $v) { if($k != "sign"){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff; } /** * * 獲取地址js參數 * * @return 獲取共享收貨地址js函數需要的參數,json格式可以直接做參數使用 */ public function GetEditAddressParameters() { $getData = $this->data; $data = array(); $data["appid"] = WxPayConfig::APPID; $data["url"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; $time = time(); $data["timestamp"] = "$time"; $data["noncestr"] = "1234568"; $data["accesstoken"] = $getData["access_token"]; ksort($data); $params = $this->ToUrlParams($data); $addrSign = sha1($params); $afterData = array( "addrSign" => $addrSign, "signType" => "sha1", "scope" => "jsapi_address", "appId" => WxPayConfig::APPID, "timeStamp" => $data["timestamp"], "nonceStr" => $data["noncestr"] ); $parameters = json_encode($afterData); return $parameters; } /** * * 構造獲取code的url連接 * @param string $redirectUrl 微信服務器回跳的url,需要url編碼 * * @return 返回構造好的url */ private function __CreateOauthUrlForCode($redirectUrl) { $urlObj["appid"] = WxPayConfig::APPID; $urlObj["redirect_uri"] = "$redirectUrl"; $urlObj["response_type"] = "code"; $urlObj["scope"] = "snsapi_base"; $urlObj["state"] = "STATE"."#wechat_redirect"; $bizString = $this->ToUrlParams($urlObj); return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; } /** * * 構造獲取open和access_toke的url地址 * @param string $code,微信跳轉帶回的code * * @return 請求的url */ private function __CreateOauthUrlForOpenid($code) { $urlObj["appid"] = WxPayConfig::APPID; $urlObj["secret"] = WxPayConfig::APPSECRET; $urlObj["code"] = $code; $urlObj["grant_type"] = "authorization_code"; $bizString = $this->ToUrlParams($urlObj); return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; } }
注意code只能用一次的。如果你第一次頁面獲取到openid了,你刷新這個頁面。就會報錯。