<?php /** * 回复用户消息 - 客服消息 * 2015-3-31 */ class WeixinReply { /** * 回复消息调用接口 * @var type */ private $apiReplyUrl; /** * 获取access token 接口 * @var type */ private $apiGetAccessTokenUrl; /** * access token 获取到的凭证 * @var string */ private $accessToken; /** * accessToken memcache 缓存名称 * @var type */ private $accessTokenCacheName; const APPID = 'xxxx'; //AppID(应用ID) const APPSECRET = 'xxxxxx'; //AppSecret(应用密钥) public function __construct() { $this->accessTokenCacheName = 'weixin_AccessToken'; $this->apiGetAccessTokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . self::APPID . '&secret=' . self::APPSECRET; if (!$this->getAccessToken()) { echo "获取AccessToken失败!"; exit; } $this->apiReplyUrl = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' . $this->accessToken; } /** * 获取access token * @param type $force 是否强制获取access token */ private function getAccessToken($force = 0) { $objMemcached = new KIFCacheMemcached(); $accessToken = $objMemcached->get($this->accessTokenCacheName); if ($force) $accessToken = ''; if (!$accessToken) { $return = $this->curlGetData($this->apiGetAccessTokenUrl); $return = json_decode($return, true); $accessToken = $return['access_token']; if (!$accessToken) { return false; } $objMemcached->set($this->accessTokenCacheName, $accessToken, $return['expires_in'] - 600); } $this->accessToken = $accessToken; return true; } /** * 回复消息 * @param type $params */ public function reply($params) { $return = null; if ($params['type'] == WeixinAutoReply::TYPE_TEXT) { $return = $this->textMessage($params); } if (!$return) { return ''; } $return = json_decode($return, true); // access_token invalid if ($return['errmsg'] == 'invalid credential') { $this->getAccessToken(1); return ''; } if ($return['errcode']) { return $return['errmsg']; } /** * success { "errcode": 0, "errmsg": "ok" } */ return 'ok'; } /** * 回复文本消息 */ private function textMessage($params) { if (!isset($params['openid']) || !$params['openid'] || !isset($params['content']) || !$params['content']) { return 'openid 或 content为空'; } $params['content'] = htmlentities($params['content'], ENT_COMPAT, 'utf-8'); $jsonData = <<<EOF { "touser":"{$params['openid']}", "msgtype":"text", "text": { "content":"{$params['content']}" } } EOF; $return = $this->curlPostData($jsonData); return $return; } /** * 发送数据 */ private function curlPostData($jsonData) { $ch = curl_init($this->apiReplyUrl); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: ' . strlen($jsonData)) ); $result = curl_exec($ch); return $result; } /** * 获取数据 - get */ private function curlGetData($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array()); $result = curl_exec($ch); return $result; } /** * 对字符串进行加密和解密 * @param <string> $string * @param <string> $operation DECODE 解密 | ENCODE 加密 * @param <int> $expiry 有效期,单位秒 * @return <string> */ static public function authcode($string, $operation = 'DECODE', $expiry = 2592000) { $ckey_length = 4; $key = '08127b44d5a005a12b6e880'; $keya = md5(substr($key, 0, 16)); $keyb = md5(substr($key, 16, 16)); $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey = $keya . md5($keya . $keyc); $key_length = strlen($cryptkey); $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string; $string_length = strlen($string); $result = ''; $box = range(0, 255); $rndkey = array(); for ($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for ($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } for ($a = $j = $i = 0; $i < $string_length; $i++) { $a = ($a + 1) % 256; $j = ($j + $box[$a]) % 256; $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp; $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); } if ($operation == 'DECODE') { if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) { return substr($result, 26); } else { return ''; } } else { return $keyc . str_replace('=', '', base64_encode($result)); } } }
Template
.main{800px; margin:0 auto; position: relative;} .replybox{ display:none; 200px; height:120px; position: absolute; top:200px; right:-240px; background-color: #fff; padding:10px 20px 40px; border:1px solid #ccc; text-align:center; } .replybox textarea{ 200px; height:60px;} .replybox .tips{ display:block; text-align:left; margin-bottom:5px; color:#3c3c3c;} .btnon{ color:#f00;}
<div class="replybox"> <form action="http://xxxx/message.php" method="post"> <textarea name="content"></textarea> <span class="tips">获得奖品说明 《回车换行》微信试用活动链接</span> <input type="button" name="replysubmitbtn" value="提交" /> <input type="hidden" name="msgid" value="" /> </form> </div>
<script type="text/javascript"> $(function(){ //root_domain $(".replybtn").click(function() { $("input[name=msgid]").val($(this).attr('rel')); $(".replybox").show(); $(".replybtn").removeClass('btnon') $(this).addClass('btnon'); }); // reply submit $("input[name=replysubmitbtn]").click(function() { var id = $("input[name=msgid]").val(); if(!id) { alert('请选择回复项'); return false; } var content = $("textarea[name=content]").val(); $.ajax({ url: root_domain + '/admin/weixin/message.php?action=replymsg', data:{id:id, content:content}, dataType:'json', type:'GET', success:function(data) { if(data.ok) { $("textarea[name=content]").val(data.msg); alert('回复成功'); } else { alert(data.msg); } } }); }); }); </script>
Recive
$openid = WeixinReply::authcode(Request::g("e_user"), 'DECODE');
message.php
// ACTION 扩展 if(Request::g('action') == 'replymsg') { $id = Request::g('id'); $content = Request::g('content'); if(!$content) { ajax_fail_exit('回复内容为空'); } $message = $objWeixinChatsData->get($id); if(!$message) { ajax_fail_exit('没有获取到此消息内容'); } // 时间判断, 发布时间两小时内 if(time() - $message['create_time'] > 86400 * 2) { ajax_fail_exit('仅允许在消息发布后48小时内回复'); } $objWeixinReply = new WeixinReply(); $message['openid'] = 'xxxxxx'; if (preg_match('#http://xxx.xxxx.com/weixin/article/w+#i', $content, $match)) { $authOpenid = $objWeixinReply->authcode($message['openid'], 'ENCODE'); $suffix = '?page_type=true' . '&e_user=' . urlencode($authOpenid); $content = str_replace($match[0], $match[0]. $suffix, $content); } // 回复 $params = array( 'openid' => $message['openid'], 'content' => $content, 'type' => 'text', ); $return = $objWeixinReply->reply($params); if($return == 'ok') { // 回复记录... @todo ajax_success_exit($content); } else { ajax_fail_exit('回复失败 ' . $return); } }