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