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了,你刷新这个页面。就会报错。