最近作微信PC端网页微信相关功能的开发,从一个新手的角度来讲,微信公众号的文档仍是很差理解的,网上找的帖子大都也都基本上是复制微信公众平台上给的文档,开发微信带参数二维码过程当中仍是遇到很多坑的,在此把个人开发过程比较详细的记录下,但愿对你们有所帮助。php
我本次开发使用的是认证服务号。 ajax
1 接入
首先进入微信公众号 -> 基本配置
下面是基本配置的页面,在URL中填写服务器地址,这个地址就是接受微信推送事件的一个接口,我是使用thinkPHP框架开发的程序,在其中一个Module(Decoration)的Action目录下新建一个类,好比叫: WechatAction.class.php ,在该Action中新建一个public方法,好比叫: URLRedirect() ,那么在这个URL中填写的就是 http://[IP]:[port]/index.php/Decoration/Wechat/UrlRedirect ,而后填写Token,Token随意填,EncodingAESKey要不要都行,而后点击确认,微信会往这个URL上发送一个get请求,里面包含不少参数,其中大部分都是让咱们本身核对此次访问是否是微信服务器请求的,我本身没有验证,他的要求是若是咱们核对成功,即原样返回get请求中的一个参数echostr,这里的返回不是return,也不是ajaxReturn,而使用echo,若是用thinkPHP开发的话,直接使用 echo I('echostr'); 便可。而后接口即验证成功了。 数据库
2 带参数二维码的做用
微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,可是永久二维码的生成是有个数限制的,我此次要实现的功能是用户未登陆的状况下在网站上使用产品,好比得到某商品的详细报价,可是又不想注册,然而又想保存这个报价单,这个时候网页能够生成一张二维码,用户只要用微信扫一扫这个二维码,官方公众号就会给这个用户发送一天图文消息,图文消息点开后就是用户刚刚得到的报价单,并且能够随时点击查看而且分享给朋友进行比价。因此临时二维码便可正常使用。
上面是我是怎么使用的,下面介绍一下整个交互的流程:json
当用户扫描这个二维码,若是用户关注了公众号,用户会直接进入与公众号的会话页面,微信服务器会给咱们在上一步设置的服务器URL中推送一条消息,其中能够携带一个咱们自定义的参数。若是用户未关注公众号,则用户首先会跳转到公众号关注页面,用户点击关注后,会直接进入公众号的会话页面,微信服务器这时也会给咱们设置的URL推送一个事件消息,携带咱们自定义参数,咱们能够根据这个参数和事件类型作控制下一步动做。api
3 具体开发过程 浏览器
3.1 获取access_token
这个access_token是咱们程序调用微信接口的凭证,目前的有效期是7200秒,因此咱们须要定时更新access_token。
得到方法:
方法 : GET
url :https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
其中的参数APPID和APPSECRET是咱们公众号的APPID和APPSECRET,在微信公众号 -> 基本配置中能够查到,调用成功会返回以下JSON数据:
{"access_token":"ACCESS_TOKEN","expires_in":7200} 服务器
其中access_token就是调用接口凭证,expire_in是token有效时间。
我本人是把access_token存在数据库中,同时保存过时时间,而后封装公用函数 getWechatAccessToken() ,每次先检查access_token是否过时,若是过时则从新获取,不然直接使用数据库保存的access_token便可,我忘了在哪儿看加过,这个access_token天天的获取次数应该是有限制的。下面是 getWechatAccessToken() 的具体实现:微信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//获取access_token
function
getWechatAccessToken(){
$wechatInfo
= M(
'wechat_info'
)->select();
$wechatInfo
=
array_reduce
(
$wechatInfo
, create_function(
'$result, $v'
,
'$result[$v["conf_name"]] = $v;return $result;'
));
$expireTime
=
$wechatInfo
[
'PUBLIC_WECHAT_ACCESSTOKEN_EXPIRES'
][
'conf_value'
];
//前面不用管,是我数据库相应设置
if
(time() <
$expireTime
){
//access_token未过时
return
$wechatInfo
[
'PUBLIC_WECHAT_ACCESSTOKEN'
][
'conf_value'
];
}
else
{
//access_token过时,从新获取
$baseUrl
= C(
'WECHAT_PUBLIC_GET_ACCESS_TOKEN'
);
$url
=
str_replace
(
"##APPSECRET##"
,
$wechatInfo
[
'PUBLIC_WECHAT_APPSECRET'
][
'conf_value'
],
str_replace
(
"##APPID##"
,
$wechatInfo
[
'PUBLIC_WECHAT_APPID'
][
'conf_value'
],
$baseUrl
));
$result
=
file_get_contents
(
$url
);
$result
= json_decode(
$result
, true);
if
(
array_key_exists
(
'errorcode'
,
$result
)){
//失败重试一次
return
false;
}
else
{
M(
'wechat_info'
)->where(
array
(
'conf_name'
=>
'PUBLIC_WECHAT_ACCESSTOKEN'
))->save(
array
(
'conf_value'
=>
$result
[
'access_token'
]));
M(
'wechat_info'
)->where(
array
(
'conf_name'
=>
'PUBLIC_WECHAT_ACCESSTOKEN_EXPIRES'
))->save(
array
(
'conf_value'
=> time()+
$result
[
'expires_in'
]-200));
return
$result
[
'access_token'
];
}
}
}
|
C('WECHAT_PUBLIC_GET_ACCESS_TOKEN') = https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET app
封装好这个以后,咱们每次就能够安心的使用了。微信公众平台
.2 建立临时二维码
3.2.1 获取ticket3
请求方式: POST
接口:https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据: {"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}
接口URL中的TOKEN即咱们在3.1中获取的access_token,post数据中expire_seconds是二维码的有效时间,最多为30天,action_name临时二维码的话固定就是QR_SCENE,scene_id即咱们自定义参数,是个32位非0整数,我在应用中把它设为订单的ID,微信服务器推送事件的时候会把这个值返回给咱们设置的接口中,而后我会根据这个值去拿相应的订单数据展现在网页上,这是后话。
下面是封装的生成临时二维码的方法:
1
2
3
4
5
6
7
8
9
|
//建立临时二维码
function
getTemporaryQrcode(
$orderId
){
$accessToken
= getWechatAccessToken();
$url
=
str_replace
(
"##TOKEN##"
,
$accessToken
, C(
'WECHAT_PUBLIC_GET_TEMPORARY_TICKET'
));
$qrcode
=
'{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": '
.
$orderId
.
'}}}'
;
$result
= api_notice_increment(
$url
,
$qrcode
);
$result
= json_decode(
$result
, true);
return
urldecode(
$result
[
'url'
]);
}
|
其中的方法 api_notice_increment() 是我封装的一个POST方法函数,我试过不少POST的方法,可能因为微信接口对POST方法和参数的限制比较严格,这个浪费了很久时间,最后在网上找到了一个可使用的封装好的POST方法,建议你们先本身试试,若是微信返回错误吗,就用这个吧,起码我测试微信这个接口的时候用postman测试返回的都是错误,并且必定要用JSON字符串,必定要是很是严格的JSON字符串。下面是这个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function
api_notice_increment(
$url
,
$data
){
$ch
= curl_init();
$header
=
"Accept-Charset: utf-8"
;
curl_setopt(
$ch
, CURLOPT_URL,
$url
);
curl_setopt(
$ch
, CURLOPT_CUSTOMREQUEST,
"POST"
);
curl_setopt(
$ch
, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt(
$ch
, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt(
$ch
, CURLOPT_HTTPHEADER,
$header
);
curl_setopt(
$ch
, CURLOPT_USERAGENT,
'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)'
);
curl_setopt(
$ch
, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt(
$ch
, CURLOPT_AUTOREFERER, 1);
curl_setopt(
$ch
, CURLOPT_POSTFIELDS,
$data
);
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, true);
$tmpInfo
= curl_exec(
$ch
);
if
(curl_errno(
$ch
)) {
curl_close(
$ch
);
return
$ch
;
}
else
{
curl_close(
$ch
);
return
$tmpInfo
;
}
}
|
getTemporaryQrcode() 中有一个在配置文件中的参数给你们看下,其实就是微信接口连接:
C('WECHAT_PUBLIC_GET_TEMPORARY_TICKET') = https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=##TOKEN##
这个接口的返回值是:
{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==","expire_seconds":60,"url":"http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI"}
其中ticket是让咱们用来进行下一步调用的凭证,expire_seconds是二维码的有效期,url是咱们生成的二维码扫描后打开的连接。因此若是咱们本身实现了生成二维码的方法,就不用再进行下一步调用,我本人即在这一步就中止了,直接返回url的值,而后利用这个url的值生成二维码存在本地便可。PHP生成二维码可使用phpqrcode,挺好用的。下一步也大体提一下:
3.2.2 获取二维码地址
请求方式: GET
接口:https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
这个接口的返回值是一张图片,能够直接展现或者下载,咱们有具体使用过,因此也不知道应该怎么展现。
3.3 用户扫描二维码以后发生的事情
3.3.1 扫描后发生了什么
上面提到了,用户扫描咱们生成的临时二维码,若是用户未关注公众号,则首先会跳转到公众号的关注页面,点击关注后,会进入公众号的会话页面,同时会给咱们设置的接口推送一个事件。若是用户已经关注了,用户微信会直接跳转到公众号会话页面,而后微信服务器会给咱们设置的接口推送一个事件。
用户关注与否微信服务器给咱们推送的事件是差很少的,只是新关注用户推送的事件中scene_id前面会加一个前缀。下面是微信公众平台文档的说明:
用户未关注时,进行关注后的事件推送
1
2
3
4
5
6
7
8
|
<
xml
><
ToUserName
>
<![CDATA[toUser]]>
</
ToUserName
> //开发者微信号
<
FromUserName
>
<![CDATA[FromUser]]>
</
FromUserName
> //发送者帐号(openid)
<
CreateTime
>123456789</
CreateTime
> //消息建立时间(整型)
<
MsgType
>
<![CDATA[event]]>
</
MsgType
> //消息类型 event
<
Event
>
<![CDATA[subscribe]]>
</
Event
> //事件类型(subscribe)
<
EventKey
>
<![CDATA[qrscene_123123]]>
</
EventKey
> //事件KEY值,qrscene_为前缀,后面为二维码参数值
<
Ticket
>
<![CDATA[TICKET]]>
</
Ticket
> //二维码ticke值,能够用来换取二维码图片
</
xml
>
|
用户已关注时的事件推送
1
2
3
4
5
6
7
8
9
|
<
xml
>
<
ToUserName
>
<![CDATA[toUser]]>
</
ToUserName
> //开发者微信号
<
FromUserName
>
<![CDATA[FromUser]]>
</
FromUserName
> //发送者帐号(openid)
<
CreateTime
>123456789</
CreateTime
> //消息建立时间
<
MsgType
>
<![CDATA[event]]>
</
MsgType
> //消息类型event
<
Event
>
<![CDATA[SCAN]]>
</
Event
> //事件类型 event
<
EventKey
>
<![CDATA[SCENE_VALUE]]>
</
EventKey
> //事件key值,是一个32位无符号整数,即建立二维码时的二维码scene_id
<
Ticket
>
<![CDATA[TICKET]]>
</
Ticket
> //二维码的ticke,能够用来换取二维码图片
</
xml
>
|
3.3.2 咱们要作些什么
咱们须要在本身填写的URL接口中接收这个事件,而后拿到咱们须要的东西作咱们想干的事儿。由于我要实现的功能比较简单,只须要拿到scene_id便可,由于这是我要展现给用户看的订单数据。下面是我写的接收和处理部分,比较简单,主要看一下应该怎么接收微信推送的事件:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
function
urlRedirect(){
$postStr
=
$GLOBALS
[
"HTTP_RAW_POST_DATA"
];
$postObj
= simplexml_load_string(
$postStr
,
'SimpleXMLElement'
, LIBXML_NOCDATA);
$fromUsername
= (string)
$postObj
->FromUserName;
$EventKey
= trim((string)
$postObj
->EventKey);
$keyArray
=
explode
(
"_"
,
$EventKey
);
if
(
count
(
$keyArray
) == 1){
//已关注者扫描
$this
->sendMessage(
$fromUsername
,
$EventKey
);
}
else
{
//未关注者关注后推送事件
$this
->sendMessage(
$fromUsername
,
$keyArray
[1]);
}
}
|
我没有使用其余参数,只是根据不一样的推送事件拿到我想要的订单ID,而后这时候其实至关于你在这里用公众号的客服在跟扫码的这个用户对话,上段代码中调用的sendMessage()是使用客户帐号给扫码用户发送一个图文消息,由于我在拿scen_id的同时也拿到了用户的openid,能够利用这个给用户发送消息。
下面是sendMessage()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//给用户发送图文消息,点击跳转到报价页面
public
function
sendMessage(
$openid
,
$orderId
){
$url
=
str_replace
(
'##TOKEN##'
, getWechatAccessToken(), C(
'WECHAT_SEND_MESSAGE'
));
$redirectUrl
=
str_replace
(
"##ORDERID##"
,
$orderId
,
str_replace
(
"##OPENID##"
,
$openid
, C(
'WECHAT_REDIRECT_URL_PRE'
)));
$orderInfo
= M(
'order'
)->where(
array
(
'orderid'
=>
$orderId
))->field(
array
(
'totalMoney'
,
'savedMoney'
,
'roomarea'
))->find();
$description
=
str_replace
(
"##ROOMAREA##"
,
intval
(
$orderInfo
[
'roomarea'
] * 1.25), C(
'WECHAT_MESSAGE_BRIEF'
));
$description
=
str_replace
(
"##TOTALBUDGET##"
,
$orderInfo
[
'totalMoney'
],
$description
);
$description
=
str_replace
(
"##MARKETBUDGET##"
,
$orderInfo
[
'totalMoney'
]+
$orderInfo
[
'savedMoney'
],
$description
);
$description
=
str_replace
(
"##SAVEMONEY##"
,
$orderInfo
[
'savedMoney'
],
$description
);
$dataStr
=
'{"touser":"'
.
$openid
.
'","msgtype":"news","news":{"articles":[{"title":"'
. C(
'WECHAT_MESSAGE_TITLE'
) .
'","description":"'
.
$description
.
'","url":"'
.
$redirectUrl
.
'","picurl":"'
. C(
'WECHAT_MESSAGE_PICURL'
) .
'""}]}}'
;
api_notice_increment(
$url
,
$dataStr
);
}
|
其中 C('WECHAT_SEND_MESSAGE') = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=##TOKEN##' 至于下面的一大段str_replace,就是在组给用户发送的文字而已,须要注意$dataStr的格式,这里面要求JSON字符串比较严格,必须全部的字符串都用双引号括起来。微信接口对POST参数的限制真心严格。
下面是微信公众平台开发者文档中要求发送图文消息的POST data格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
{
"touser"
:
"OPENID"
,
"msgtype"
:
"news"
,
"news"
:{
"articles"
: [
{
"title"
:
"Happy Day"
,
"description"
:
"Is Really A Happy Day"
,
"url"
:
"URL"
,
"picurl"
:
"PIC_URL"
},
{
"title"
:
"Happy Day"
,
"description"
:
"Is Really A Happy Day"
,
"url"
:
"URL"
,
"picurl"
:
"PIC_URL"
}
]
}
}
|
其中url是用户点击这个消息以后打开的地址,这个时候我就组了一个本身网站的地址,是一个get请求地址,里面携带参数是用户的openid和订单id,这样用户点击开图文消息就能够看到本身刚才下单的内容了,由于须要在网页上展现用户的微信头像和昵称,因此我把openid也放到参数里,在页面加载前先拿到用户的我的信息和订单数据,再展现网页。这样流程:用户未登陆下单 -> 生成微信二维码 -> 用户扫码关注公众号 -> 查看订单详细信息 就完成了。并且由于这个图文消息打开后的连接携带的参数是这个用户的额openid和其下单的订单ID,无论分享到哪儿,用什么浏览器打开都是能够访问的,且展现的也是这个用户的头像和昵称信息,这也是我要实现的一个效果。