├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src
├── Atan
│ └── Wechat
│ │ ├── Facades
│ │ └── Wechat.php
│ │ ├── Wechat.php
│ │ └── WechatServiceProvider.php
└── config
│ ├── .gitkeep
│ └── wechat.php
└── tests
└── .gitkeep
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - travis_retry composer self-update
11 | - travis_retry composer install --prefer-source --no-interaction --dev
12 |
13 | script: phpunit
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 atan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 本项目已停止开发
2 |
3 |
4 | #微信公众平台 Laravel SDK
5 |
6 | ##使用帮助
7 |
8 | ###0、说明
9 |
10 | 用 Laravel Facade 封装了 [dodgepudding/wechat-php-sdk](https://github.com/dodgepudding/wechat-php-sdk),目前只添加了 wechat.class 官方API类库,尽量做到及时更新,还未做全部测试,欢迎提交issue。
11 |
12 | ###1、安装
13 |
14 | 请先确认已经安装Composer. 编辑 `composer.json` 文件,然后加入 `atan/wechat`.
15 |
16 | ```
17 | "require": {
18 | "atan/wechat": "dev-master"
19 | }
20 | ```
21 |
22 | 更新包 `composer update` 。
23 |
24 | 需要添加以下服务到系统。
25 |
26 | 打开 `app/config/app.php` , 在`'providers'` 下添加:
27 |
28 | ```
29 | 'Atan\Wechat\WechatServiceProvider',
30 | ```
31 |
32 | 在`'aliases'` 下添加:
33 |
34 | ```
35 | 'Wechat' => 'Atan\Wechat\Facades\Wechat',
36 | ```
37 |
38 | 执行 `php artisan config:publish atan/wechat` ,然后修改 `app/config/packages/atan/wechat` 中的配置文件 `wechat.php` 。
39 |
40 | 把微信公众号的 'token', 'encodingaeskey', 'appid', 'appsecret' 改为对应的。
41 |
42 | ##2、使用
43 |
44 |
45 | ```php
46 | // 获取用户列表
47 | Route::get('/users', function(){
48 | return Wechat::getUserList();
49 | });
50 |
51 | // 回复文本消息
52 | Route::post('/', function(){
53 | $weObj = new Atan\Wechat\Wechat();
54 | $type = $weObj->getRev()->getRevType();
55 | $weObj->text("hello, it's wechat")->reply();
56 | });
57 | ```
58 |
59 |
60 | ##3、主要功能
61 |
62 | - 接入验证 **(初级权限)**
63 | - 自动回复(文本、图片、语音、视频、音乐、图文) **(初级权限)**
64 | - 菜单操作(查询、创建、删除) **(菜单权限)**
65 | - 客服消息(文本、图片、语音、视频、音乐、图文) **(认证权限)**
66 | - 二维码(创建临时、永久二维码,获取二维码URL) **(服务号、认证权限)**
67 | - 长链接转短链接接口 **(服务号、认证权限)**
68 | - 分组操作(查询、创建、修改、移动用户到分组) **(认证权限)**
69 | - 网页授权(基本授权,用户信息授权) **(服务号、认证权限)**
70 | - 用户信息(查询用户基本信息、获取关注者列表) **(认证权限)**
71 | - 多客服功能(客服管理、获取客服记录、客服会话管理) **(认证权限)**
72 | - 媒体文件(上传、获取) **(认证权限)**
73 | - 调用地址组件 **(支付权限)**
74 | - 生成订单签名数据 **(支付权限)**
75 | - 订单成功回调 **(支付权限)**
76 | - 发货通知 **(支付权限)**
77 | - 支付订单查询 **(支付权限)**
78 | - 高级群发 **(认证权限)**
79 | - 模板消息(设置所属行业、添加模板、发送模板消息) **(服务号、认证权限)**
80 | - 卡券管理(创建、修改、删除、发放、门店管理等) **(认证权限)**
81 | - 语义理解 **(服务号、认证权限)**
82 | - 获取微信服务器IP列表 **(初级权限)**
83 |
84 | ##4、被动接口方法
85 |
86 | * valid() 验证连接,被动接口处于加密模式时必须调用
87 | * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用
88 | * getRevData() 返回微信服务器发来的信息(数组)
89 | * getRevFrom() 返回消息发送者的userid
90 | * getRevTo() 返回消息接收者的id(即公众号id)
91 | * getRevType() 返回接收消息的类型
92 | * getRevID() 返回消息id
93 | * getRevCtime() 返回消息发送事件
94 | * getRevContent() 返回消息内容正文或语音识别结果(文本型)
95 | * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''}
96 | * getRevLink() 接收消息链接(链接型信息) 返回数组{'url'=>'','title'=>'','description'=>''}
97 | * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''}
98 | * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''}
99 | * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''}
100 | * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123')
101 | * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明
102 | * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明
103 | * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''}
104 | * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''}
105 | * getRevTicket() 返回接收TICKET(扫描带参数二维码,关注或SCAN事件) 返回二维码的ticket值
106 | * getRevSceneId() 返回二维码的场景值(扫描带参数二维码的关注事件) 返回二维码的参数值
107 | * getRevTplMsgID() 返回主动推送的消息ID(群发或模板消息事件) 返回MsgID值
108 | * getRevStatus() 返回模板消息发送状态(模板消息事件) 返回文本:success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败(非用户拒绝))
109 | * getRevResult() 返回群发或模板消息发送结果(群发或模板消息事件) 返回数组,内容依事件类型而不同,参考开发文档中群发、模板消息推送事件
110 | * getRevKFCreate() 返回多客服-接入会话的客服账号(多客服-接入会话事件) 返回文本型
111 | * getRevKFClose() 返回多客服-处理会话的客服账号(多客服-接入会话事件) 返回文本型
112 | * getRevKFSwitch() 返回多客服-转接会话信息(多客服-转接会话事件) 返回数组 {'FromKfAccount' => '','ToKfAccount' => ''}
113 | * getRevCardPass() 返回卡券-审核通过的卡券ID(卡券-卡券审核事件) 返回文本型
114 | * getRevCardGet() 返回卡券-用户领取卡券的相关信息(卡券-领取卡券事件) 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''}
115 | * getRevCardDel() 返回卡券-用户删除卡券的相关信息(卡券-删除卡券事件) 返回数组{'CardId' => '','UserCardCode' => ''}
116 | *
117 | * text($text) 设置文本型消息,参数:文本内容
118 | * image($mediaid) 设置图片型消息,参数:图片的media_id
119 | * voice($mediaid) 设置语音型消息,参数:语音的media_id
120 | * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要
121 | * music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐,参数:音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id
122 | * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明
123 | * image($mediaid) 设置图片型消息,参数:图片的media_id
124 | * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法)
125 | * transfer_customer_service($customer_account = '') 转接多客服,如不指定客服可不提供参数,参数:指定客服的账号
126 | * reply() 将以上已经设置好的消息,回复给微信服务器
127 |
128 | ##5、主动接口方法:
129 |
130 | * checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌
131 | * resetAuth($appid='') 删除验证数据
132 | * resetJsTicket($appid='') 删除JSAPI授权TICKET
133 | * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET
134 | * getJsSign($url, $timeStamp, $nonceStr, $appid='') 获取JsApi使用签名
135 | * createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)**
136 | * getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1')
137 | * getMenu() 获取菜单
138 | * deleteMenu() 删除菜单
139 | * uploadMedia($data, $type) 上传多媒体文件(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时)
140 | * getMedia() 获取接收到的音频、视频媒体文件
141 | * uploadMpVideo($data) 上传视频素材,当需要群发视频时,必须使用此方法得到的MediaID,否则无法显示
142 | * uploadArticles($data) 上传图文消息素材
143 | * sendMassMessage($data) 高级群发消息
144 | * sendGroupMassMessage($data) 高级群发消息(全体或分组群发)
145 | * deleteMassMessage($msg_id) 删除群发图文消息
146 | * previewMassMessage($data) 预览群发消息
147 | * queryMassMessage($msg_id) 查询群发消息发送状态
148 | * getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串
149 | * getQRUrl($ticket) 获取二维码图片地址
150 | * getShortUrl($long_url) 长链接转短链接接口
151 | * getUserList($next_openid) 批量获取关注用户列表
152 | * getUserInfo($openid) 获取关注者详细信息
153 | * updateUserRemark($openid,$remark) 设置用户备注名
154 | * getGroup() 获取用户分组列表
155 | * getUserGroup($openid) 获取用户所在分组
156 | * createGroup($name) 新增自定分组
157 | * updateGroup($groupid,$name) 更改分组名称
158 | * updateGroupMembers($groupid,$openid) 移动用户分组
159 | * sendCustomMessage($data) 发送客服消息
160 | * getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址
161 | * getOauthAccessToken() 通过回调的code获取网页授权access_token
162 | * getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期
163 | * getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料
164 | * getOauthAuth($access_token,$openid) 检验授权凭证access_token是否有效
165 | * getSignature($arrdata,'sha1') 生成签名字串
166 | * generateNonceStr($length) 获取随机字串
167 | * createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type="WX",$input_charset="UTF-8",$time_start="",$time_expire="",$transport_fee="",$product_fee="",$goods_tag="",$attach="") 生成订单package字符串
168 | * getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法
169 | * checkOrderSignature($orderxml='') 回调通知签名验证
170 | * sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知
171 | * getPayOrder($out_trade_no) 查询订单信息
172 | * getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名
173 | * setTMIndustry($id1,$id2='') 模板消息,设置所属行业
174 | * addTemplateMessage($tpl_id) 模板消息,添加消息模板
175 | * sendTemplateMessage($data) 发送模板消息
176 | * getCustomServiceMessage($data) 获取多客服会话记录
177 | * transfer_customer_service($customer_account) 转发多客服消息
178 | * getCustomServiceKFlist() 获取多客服客服基本信息
179 | * getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息
180 | * createKFSession($openid,$kf_account,$text='') 创建指定多客服会话
181 | * closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话
182 | * getKFSession($openid) 获取用户会话状态
183 | * getKFSessionlist($kf_account) 获取指定客服的会话列表
184 | * getKFSessionWait() 获取未接入会话列表
185 | * addKFAccount($account,$nickname,$password) 添加客服账号
186 | * updateKFAccount($account,$nickname,$password) 修改客服账号信息
187 | * deleteKFAccount($account) 删除客服账号
188 | * setKFHeadImg($account,$imgfile) 上传客服头像
189 | * querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region="") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)**
190 | * createCard($data) 创建卡券
191 | * updateCard($data) 修改卡券
192 | * delCard($card_id) 删除卡券
193 | * getCardInfo($card_id) 查询卡券详情
194 | * getCardColors() 获取颜色列表
195 | * getCardLocations() 拉取门店列表
196 | * addCardLocations($data) 批量导入门店信息
197 | * createCardQrcode($card_id) 生成卡券二维码
198 | * consumeCardCode($code) 消耗 code
199 | * decryptCardCode($encrypt_code) code 解码
200 | * checkCardCode($code) 获取 code 的有效性
201 | * getCardIdList($data) 批量查询卡列表
202 | * updateCardCode($code,$code_id,$new_code) 更改 code
203 | * unavailableCardCode($code) 设置卡券失效**(不可逆)**
204 | * modifyCardStock($data) 库存修改
205 | * activateMemberCard($data) 激活/绑定会员卡,参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节
206 | * updateMemberCard($data) 会员卡交易,参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节
207 | * updateLuckyMoney($code,$balance,$card_id='') 更新红包金额
208 | * setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单
209 |
210 |
211 | ##6、环境
212 | PHP >= 5.4
213 | Laravel >= 4.2
214 |
215 | ##7、License
216 | This is free software distributed under the terms of the MIT license
217 | 感谢 [dodgepudding/wechat-php-sdk](https://github.com/dodgepudding/wechat-php-sdk) 和 [huanghua581/laravel-wechat-sdk](https://github.com/huanghua581/laravel-wechat-sdk)
218 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atan/wechat",
3 | "keywords": ["laravel", "weixin", "wechat"],
4 | "license": "MIT",
5 | "description": "Wechat SDK for Laravel 4",
6 | "authors": [
7 | {
8 | "name": "atan",
9 | "email": "tanshiqi@gmail.com"
10 | }
11 | ],
12 | "require": {
13 | "php": ">=5.4.0",
14 | "illuminate/support": "4.2.*"
15 | },
16 | "autoload": {
17 | "psr-0": {
18 | "Atan\\Wechat\\": "src/"
19 | }
20 | },
21 | "minimum-stability": "stable"
22 | }
23 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Atan/Wechat/Facades/Wechat.php:
--------------------------------------------------------------------------------
1 | array(
119 | 'summary' => '/datacube/getusersummary?', //获取用户增减数据(getusersummary)
120 | 'cumulate' => '/datacube/getusercumulate?', //获取累计用户数据(getusercumulate)
121 | ),
122 | 'article' => array( //图文分析
123 | 'summary' => '/datacube/getarticlesummary?', //获取图文群发每日数据(getarticlesummary)
124 | 'total' => '/datacube/getarticletotal?', //获取图文群发总数据(getarticletotal)
125 | 'read' => '/datacube/getuserread?', //获取图文统计数据(getuserread)
126 | 'readhour' => '/datacube/getuserreadhour?', //获取图文统计分时数据(getuserreadhour)
127 | 'share' => '/datacube/getusershare?', //获取图文分享转发数据(getusershare)
128 | 'sharehour' => '/datacube/getusersharehour?', //获取图文分享转发分时数据(getusersharehour)
129 | ),
130 | 'upstreammsg' => array( //消息分析
131 | 'summary' => '/datacube/getupstreammsg?', //获取消息发送概况数据(getupstreammsg)
132 | 'hour' => '/datacube/getupstreammsghour?', //获取消息分送分时数据(getupstreammsghour)
133 | 'week' => '/datacube/getupstreammsgweek?', //获取消息发送周数据(getupstreammsgweek)
134 | 'month' => '/datacube/getupstreammsgmonth?', //获取消息发送月数据(getupstreammsgmonth)
135 | 'dist' => '/datacube/getupstreammsgdist?', //获取消息发送分布数据(getupstreammsgdist)
136 | 'distweek' => '/datacube/getupstreammsgdistweek?', //获取消息发送分布周数据(getupstreammsgdistweek)
137 | 'distmonth' => '/datacube/getupstreammsgdistmonth?', //获取消息发送分布月数据(getupstreammsgdistmonth)
138 | ),
139 | 'interface' => array( //接口分析
140 | 'summary' => '/datacube/getinterfacesummary?', //获取接口分析数据(getinterfacesummary)
141 | 'summaryhour' => '/datacube/getinterfacesummaryhour?', //获取接口分析分时数据(getinterfacesummaryhour)
142 | )
143 | );
144 |
145 |
146 | private $token;
147 | private $encodingAesKey;
148 | private $encrypt_type;
149 | private $appid;
150 | private $appsecret;
151 | private $access_token;
152 | private $jsapi_ticket;
153 | private $user_token;
154 | private $partnerid;
155 | private $partnerkey;
156 | private $paysignkey;
157 | private $postxml;
158 | private $_msg;
159 | private $_funcflag = false;
160 | private $_receive;
161 | private $_text_filter = true;
162 | public $debug = false;
163 | public $errCode = 40001;
164 | public $errMsg = "no access";
165 | public $logcallback;
166 |
167 | public function __construct($options = 'default')
168 | {
169 | $opt = is_array($options)?$options:Config::get('wechat::wechat.'.$options);
170 | $this->token = isset($opt['token'])?$opt['token']:'';
171 | $this->encodingAesKey = isset($opt['encodingaeskey'])?$opt['encodingaeskey']:'';
172 | $this->appid = isset($opt['appid'])?$opt['appid']:'';
173 | $this->appsecret = isset($opt['appsecret'])?$opt['appsecret']:'';
174 | $this->debug = isset($opt['debug'])?$opt['debug']:false;
175 | $this->logcallback = isset($opt['logcallback'])?$opt['logcallback']:false;
176 | }
177 |
178 | /**
179 | * For weixin server validation
180 | */
181 | private function checkSignature($str='')
182 | {
183 | $signature = isset($_GET["signature"])?$_GET["signature"]:'';
184 | $signature = isset($_GET["msg_signature"])?$_GET["msg_signature"]:$signature; //如果存在加密验证则用加密验证段
185 | $timestamp = isset($_GET["timestamp"])?$_GET["timestamp"]:'';
186 | $nonce = isset($_GET["nonce"])?$_GET["nonce"]:'';
187 |
188 | $token = $this->token;
189 | $tmpArr = array($token, $timestamp, $nonce,$str);
190 | sort($tmpArr, SORT_STRING);
191 | $tmpStr = implode( $tmpArr );
192 | $tmpStr = sha1( $tmpStr );
193 |
194 | if( $tmpStr == $signature ){
195 | return true;
196 | }else{
197 | return false;
198 | }
199 | }
200 |
201 | /**
202 | * For weixin server validation
203 | * @param bool $return 是否返回
204 | */
205 | public function valid($return=false)
206 | {
207 | $encryptStr="";
208 | if ($_SERVER['REQUEST_METHOD'] == "POST") {
209 | $postStr = file_get_contents("php://input");
210 | $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
211 | $this->encrypt_type = isset($_GET["encrypt_type"]) ? $_GET["encrypt_type"]: '';
212 | if ($this->encrypt_type == 'aes') { //aes加密
213 | $this->log($postStr);
214 | $encryptStr = $array['Encrypt'];
215 | $pc = new Prpcrypt($this->encodingAesKey);
216 | $array = $pc->decrypt($encryptStr,$this->appid);
217 | if (!isset($array[0]) || ($array[0] != 0)) {
218 | if (!$return) {
219 | die('decrypt error!');
220 | } else {
221 | return false;
222 | }
223 | }
224 | $this->postxml = $array[1];
225 | if (!$this->appid)
226 | $this->appid = $array[2];//为了没有appid的订阅号。
227 | } else {
228 | $this->postxml = $postStr;
229 | }
230 | } elseif (isset($_GET["echostr"])) {
231 | $echoStr = $_GET["echostr"];
232 | if ($return) {
233 | if ($this->checkSignature())
234 | return $echoStr;
235 | else
236 | return false;
237 | } else {
238 | if ($this->checkSignature())
239 | die($echoStr);
240 | else
241 | die('no access');
242 | }
243 | }
244 |
245 | if (!$this->checkSignature($encryptStr)) {
246 | if ($return)
247 | return false;
248 | else
249 | die('no access');
250 | }
251 | return true;
252 | }
253 |
254 | /**
255 | * 设置发送消息
256 | * @param array $msg 消息数组
257 | * @param bool $append 是否在原消息数组追加
258 | */
259 | public function Message($msg = '',$append = false){
260 | if (is_null($msg)) {
261 | $this->_msg =array();
262 | }elseif (is_array($msg)) {
263 | if ($append)
264 | $this->_msg = array_merge($this->_msg,$msg);
265 | else
266 | $this->_msg = $msg;
267 | return $this->_msg;
268 | } else {
269 | return $this->_msg;
270 | }
271 | }
272 |
273 | /**
274 | * 设置消息的星标标志,官方已取消对此功能的支持
275 | */
276 | public function setFuncFlag($flag) {
277 | $this->_funcflag = $flag;
278 | return $this;
279 | }
280 |
281 | /**
282 | * 日志记录,可被重载。
283 | * @param mixed $log 输入日志
284 | * @return mixed
285 | */
286 | protected function log($log){
287 | if ($this->debug && function_exists($this->logcallback)) {
288 | if (is_array($log)) $log = print_r($log,true);
289 | return call_user_func($this->logcallback,$log);
290 | }
291 | }
292 |
293 | /**
294 | * 获取微信服务器发来的信息
295 | */
296 | public function getRev()
297 | {
298 | if ($this->_receive) return $this;
299 | $postStr = !empty($this->postxml)?$this->postxml:file_get_contents("php://input");
300 | //兼顾使用明文又不想调用valid()方法的情况
301 | $this->log($postStr);
302 | if (!empty($postStr)) {
303 | $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
304 | }
305 | return $this;
306 | }
307 |
308 | /**
309 | * 获取微信服务器发来的信息
310 | */
311 | public function getRevData()
312 | {
313 | return $this->_receive;
314 | }
315 |
316 | /**
317 | * 获取消息发送者
318 | */
319 | public function getRevFrom() {
320 | if (isset($this->_receive['FromUserName']))
321 | return $this->_receive['FromUserName'];
322 | else
323 | return false;
324 | }
325 |
326 | /**
327 | * 获取消息接受者
328 | */
329 | public function getRevTo() {
330 | if (isset($this->_receive['ToUserName']))
331 | return $this->_receive['ToUserName'];
332 | else
333 | return false;
334 | }
335 |
336 | /**
337 | * 获取接收消息的类型
338 | */
339 | public function getRevType() {
340 | if (isset($this->_receive['MsgType']))
341 | return $this->_receive['MsgType'];
342 | else
343 | return false;
344 | }
345 |
346 | /**
347 | * 获取消息ID
348 | */
349 | public function getRevID() {
350 | if (isset($this->_receive['MsgId']))
351 | return $this->_receive['MsgId'];
352 | else
353 | return false;
354 | }
355 |
356 | /**
357 | * 获取消息发送时间
358 | */
359 | public function getRevCtime() {
360 | if (isset($this->_receive['CreateTime']))
361 | return $this->_receive['CreateTime'];
362 | else
363 | return false;
364 | }
365 |
366 | /**
367 | * 获取接收消息内容正文
368 | */
369 | public function getRevContent(){
370 | if (isset($this->_receive['Content']))
371 | return $this->_receive['Content'];
372 | else if (isset($this->_receive['Recognition'])) //获取语音识别文字内容,需申请开通
373 | return $this->_receive['Recognition'];
374 | else
375 | return false;
376 | }
377 |
378 | /**
379 | * 获取接收消息图片
380 | */
381 | public function getRevPic(){
382 | if (isset($this->_receive['PicUrl']))
383 | return array(
384 | 'mediaid'=>$this->_receive['MediaId'],
385 | 'picurl'=>(string)$this->_receive['PicUrl'], //防止picurl为空导致解析出错
386 | );
387 | else
388 | return false;
389 | }
390 |
391 | /**
392 | * 获取接收消息链接
393 | */
394 | public function getRevLink(){
395 | if (isset($this->_receive['Url'])){
396 | return array(
397 | 'url'=>$this->_receive['Url'],
398 | 'title'=>$this->_receive['Title'],
399 | 'description'=>$this->_receive['Description']
400 | );
401 | } else
402 | return false;
403 | }
404 |
405 | /**
406 | * 获取接收地理位置
407 | */
408 | public function getRevGeo(){
409 | if (isset($this->_receive['Location_X'])){
410 | return array(
411 | 'x'=>$this->_receive['Location_X'],
412 | 'y'=>$this->_receive['Location_Y'],
413 | 'scale'=>$this->_receive['Scale'],
414 | 'label'=>$this->_receive['Label']
415 | );
416 | } else
417 | return false;
418 | }
419 |
420 | /**
421 | * 获取上报地理位置事件
422 | */
423 | public function getRevEventGeo(){
424 | if (isset($this->_receive['Latitude'])){
425 | return array(
426 | 'x'=>$this->_receive['Latitude'],
427 | 'y'=>$this->_receive['Longitude'],
428 | 'precision'=>$this->_receive['Precision'],
429 | );
430 | } else
431 | return false;
432 | }
433 |
434 | /**
435 | * 获取接收事件推送
436 | */
437 | public function getRevEvent(){
438 | if (isset($this->_receive['Event'])){
439 | $array['event'] = $this->_receive['Event'];
440 | }
441 | if (isset($this->_receive['EventKey'])){
442 | $array['key'] = $this->_receive['EventKey'];
443 | }
444 | if (isset($array) && count($array) > 0) {
445 | return $array;
446 | } else {
447 | return false;
448 | }
449 | }
450 |
451 | /**
452 | * 获取自定义菜单的扫码推事件信息
453 | *
454 | * 事件类型为以下两种时则调用此方法有效
455 | * Event 事件类型,scancode_push
456 | * Event 事件类型,scancode_waitmsg
457 | *
458 | * @return: array | false
459 | * array (
460 | * 'ScanType'=>'qrcode',
461 | * 'ScanResult'=>'123123'
462 | * )
463 | */
464 | public function getRevScanInfo(){
465 | if (isset($this->_receive['ScanCodeInfo'])){
466 | if (!is_array($this->_receive['SendPicsInfo'])) {
467 | $array=(array)$this->_receive['ScanCodeInfo'];
468 | $this->_receive['ScanCodeInfo']=$array;
469 | }else {
470 | $array=$this->_receive['ScanCodeInfo'];
471 | }
472 | }
473 | if (isset($array) && count($array) > 0) {
474 | return $array;
475 | } else {
476 | return false;
477 | }
478 | }
479 |
480 | /**
481 | * 获取自定义菜单的图片发送事件信息
482 | *
483 | * 事件类型为以下三种时则调用此方法有效
484 | * Event 事件类型,pic_sysphoto 弹出系统拍照发图的事件推送
485 | * Event 事件类型,pic_photo_or_album 弹出拍照或者相册发图的事件推送
486 | * Event 事件类型,pic_weixin 弹出微信相册发图器的事件推送
487 | *
488 | * @return: array | false
489 | * array (
490 | * 'Count' => '2',
491 | * 'PicList' =>array (
492 | * 'item' =>array (
493 | * 0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'),
494 | * 1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'),
495 | * ),
496 | * ),
497 | * )
498 | *
499 | */
500 | public function getRevSendPicsInfo(){
501 | if (isset($this->_receive['SendPicsInfo'])){
502 | if (!is_array($this->_receive['SendPicsInfo'])) {
503 | $array=(array)$this->_receive['SendPicsInfo'];
504 | if (isset($array['PicList'])){
505 | $array['PicList']=(array)$array['PicList'];
506 | $item=$array['PicList']['item'];
507 | $array['PicList']['item']=array();
508 | foreach ( $item as $key => $value ){
509 | $array['PicList']['item'][$key]=(array)$value;
510 | }
511 | }
512 | $this->_receive['SendPicsInfo']=$array;
513 | } else {
514 | $array=$this->_receive['SendPicsInfo'];
515 | }
516 | }
517 | if (isset($array) && count($array) > 0) {
518 | return $array;
519 | } else {
520 | return false;
521 | }
522 | }
523 |
524 | /**
525 | * 获取自定义菜单的地理位置选择器事件推送
526 | *
527 | * 事件类型为以下时则可以调用此方法有效
528 | * Event 事件类型,location_select 弹出地理位置选择器的事件推送
529 | *
530 | * @return: array | false
531 | * array (
532 | * 'Location_X' => '33.731655000061',
533 | * 'Location_Y' => '113.29955200008047',
534 | * 'Scale' => '16',
535 | * 'Label' => '某某市某某区某某路',
536 | * 'Poiname' => '',
537 | * )
538 | *
539 | */
540 | public function getRevSendGeoInfo(){
541 | if (isset($this->_receive['SendLocationInfo'])){
542 | if (!is_array($this->_receive['SendLocationInfo'])) {
543 | $array=(array)$this->_receive['SendLocationInfo'];
544 | if (empty($array['Poiname'])) {
545 | $array['Poiname']="";
546 | }
547 | if (empty($array['Label'])) {
548 | $array['Label']="";
549 | }
550 | $this->_receive['SendLocationInfo']=$array;
551 | } else {
552 | $array=$this->_receive['SendLocationInfo'];
553 | }
554 | }
555 | if (isset($array) && count($array) > 0) {
556 | return $array;
557 | } else {
558 | return false;
559 | }
560 | }
561 |
562 | /**
563 | * 获取接收语音推送
564 | */
565 | public function getRevVoice(){
566 | if (isset($this->_receive['MediaId'])){
567 | return array(
568 | 'mediaid'=>$this->_receive['MediaId'],
569 | 'format'=>$this->_receive['Format'],
570 | );
571 | } else
572 | return false;
573 | }
574 |
575 | /**
576 | * 获取接收视频推送
577 | */
578 | public function getRevVideo(){
579 | if (isset($this->_receive['MediaId'])){
580 | return array(
581 | 'mediaid'=>$this->_receive['MediaId'],
582 | 'thumbmediaid'=>$this->_receive['ThumbMediaId']
583 | );
584 | } else
585 | return false;
586 | }
587 |
588 | /**
589 | * 获取接收TICKET
590 | */
591 | public function getRevTicket(){
592 | if (isset($this->_receive['Ticket'])){
593 | return $this->_receive['Ticket'];
594 | } else
595 | return false;
596 | }
597 |
598 | /**
599 | * 获取二维码的场景值
600 | */
601 | public function getRevSceneId (){
602 | if (isset($this->_receive['EventKey'])){
603 | return str_replace('qrscene_','',$this->_receive['EventKey']);
604 | } else{
605 | return false;
606 | }
607 | }
608 |
609 | /**
610 | * 获取主动推送的消息ID
611 | * 经过验证,这个和普通的消息MsgId不一样
612 | * 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH
613 | */
614 | public function getRevTplMsgID(){
615 | if (isset($this->_receive['MsgID'])){
616 | return $this->_receive['MsgID'];
617 | } else
618 | return false;
619 | }
620 |
621 | /**
622 | * 获取模板消息发送状态
623 | */
624 | public function getRevStatus(){
625 | if (isset($this->_receive['Status'])){
626 | return $this->_receive['Status'];
627 | } else
628 | return false;
629 | }
630 |
631 | /**
632 | * 获取群发或模板消息发送结果
633 | * 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH,即高级群发/模板消息
634 | */
635 | public function getRevResult(){
636 | if (isset($this->_receive['Status'])) //发送是否成功,具体的返回值请参考 高级群发/模板消息 的事件推送说明
637 | $array['Status'] = $this->_receive['Status'];
638 | if (isset($this->_receive['MsgID'])) //发送的消息id
639 | $array['MsgID'] = $this->_receive['MsgID'];
640 |
641 | //以下仅当群发消息时才会有的事件内容
642 | if (isset($this->_receive['TotalCount'])) //分组或openid列表内粉丝数量
643 | $array['TotalCount'] = $this->_receive['TotalCount'];
644 | if (isset($this->_receive['FilterCount'])) //过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数
645 | $array['FilterCount'] = $this->_receive['FilterCount'];
646 | if (isset($this->_receive['SentCount'])) //发送成功的粉丝数
647 | $array['SentCount'] = $this->_receive['SentCount'];
648 | if (isset($this->_receive['ErrorCount'])) //发送失败的粉丝数
649 | $array['ErrorCount'] = $this->_receive['ErrorCount'];
650 | if (isset($array) && count($array) > 0) {
651 | return $array;
652 | } else {
653 | return false;
654 | }
655 | }
656 |
657 | /**
658 | * 获取多客服会话状态推送事件 - 接入会话
659 | * 当Event为 kfcreatesession 即接入会话
660 | * @return string | boolean 返回分配到的客服
661 | */
662 | public function getRevKFCreate(){
663 | if (isset($this->_receive['KfAccount'])){
664 | return $this->_receive['KfAccount'];
665 | } else
666 | return false;
667 | }
668 |
669 | /**
670 | * 获取多客服会话状态推送事件 - 关闭会话
671 | * 当Event为 kfclosesession 即关闭会话
672 | * @return string | boolean 返回分配到的客服
673 | */
674 | public function getRevKFClose(){
675 | if (isset($this->_receive['KfAccount'])){
676 | return $this->_receive['KfAccount'];
677 | } else
678 | return false;
679 | }
680 |
681 | /**
682 | * 获取多客服会话状态推送事件 - 转接会话
683 | * 当Event为 kfswitchsession 即转接会话
684 | * @return array | boolean 返回分配到的客服
685 | * {
686 | * 'FromKfAccount' => '', //原接入客服
687 | * 'ToKfAccount' => '' //转接到客服
688 | * }
689 | */
690 | public function getRevKFSwitch(){
691 | if (isset($this->_receive['FromKfAccount'])) //原接入客服
692 | $array['FromKfAccount'] = $this->_receive['FromKfAccount'];
693 | if (isset($this->_receive['ToKfAccount'])) //转接到客服
694 | $array['ToKfAccount'] = $this->_receive['ToKfAccount'];
695 | if (isset($array) && count($array) > 0) {
696 | return $array;
697 | } else {
698 | return false;
699 | }
700 | }
701 |
702 | /**
703 | * 获取卡券事件推送 - 卡卷审核是否通过
704 | * 当Event为 card_pass_check(审核通过) 或 card_not_pass_check(未通过)
705 | * @return string|boolean 返回卡券ID
706 | */
707 | public function getRevCardPass(){
708 | if (isset($this->_receive['CardId']))
709 | return $this->_receive['CardId'];
710 | else
711 | return false;
712 | }
713 |
714 | /**
715 | * 获取卡券事件推送 - 领取卡券
716 | * 当Event为 user_get_card(用户领取卡券)
717 | * @return array|boolean
718 | */
719 | public function getRevCardGet(){
720 | if (isset($this->_receive['CardId'])) //卡券 ID
721 | $array['CardId'] = $this->_receive['CardId'];
722 | if (isset($this->_receive['IsGiveByFriend'])) //是否为转赠,1 代表是,0 代表否。
723 | $array['IsGiveByFriend'] = $this->_receive['IsGiveByFriend'];
724 | if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。
725 | $array['UserCardCode'] = $this->_receive['UserCardCode'];
726 | if (isset($array) && count($array) > 0) {
727 | return $array;
728 | } else {
729 | return false;
730 | }
731 | }
732 |
733 | /**
734 | * 获取卡券事件推送 - 删除卡券
735 | * 当Event为 user_del_card(用户删除卡券)
736 | * @return array|boolean
737 | */
738 | public function getRevCardDel(){
739 | if (isset($this->_receive['CardId'])) //卡券 ID
740 | $array['CardId'] = $this->_receive['CardId'];
741 | if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。
742 | $array['UserCardCode'] = $this->_receive['UserCardCode'];
743 | if (isset($array) && count($array) > 0) {
744 | return $array;
745 | } else {
746 | return false;
747 | }
748 | }
749 |
750 | public static function xmlSafeStr($str)
751 | {
752 | return '';
753 | }
754 |
755 | /**
756 | * 数据XML编码
757 | * @param mixed $data 数据
758 | * @return string
759 | */
760 | public static function data_to_xml($data) {
761 | $xml = '';
762 | foreach ($data as $key => $val) {
763 | is_numeric($key) && $key = "item id=\"$key\"";
764 | $xml .= "<$key>";
765 | $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val);
766 | list($key, ) = explode(' ', $key);
767 | $xml .= "$key>";
768 | }
769 | return $xml;
770 | }
771 |
772 | /**
773 | * XML编码
774 | * @param mixed $data 数据
775 | * @param string $root 根节点名
776 | * @param string $item 数字索引的子节点名
777 | * @param string $attr 根节点属性
778 | * @param string $id 数字索引子节点key转换的属性名
779 | * @param string $encoding 数据编码
780 | * @return string
781 | */
782 | public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {
783 | if(is_array($attr)){
784 | $_attr = array();
785 | foreach ($attr as $key => $value) {
786 | $_attr[] = "{$key}=\"{$value}\"";
787 | }
788 | $attr = implode(' ', $_attr);
789 | }
790 | $attr = trim($attr);
791 | $attr = empty($attr) ? '' : " {$attr}";
792 | $xml = "<{$root}{$attr}>";
793 | $xml .= self::data_to_xml($data, $item, $id);
794 | $xml .= "{$root}>";
795 | return $xml;
796 | }
797 |
798 | /**
799 | * 过滤文字回复\r\n换行符
800 | * @param string $text
801 | * @return string|mixed
802 | */
803 | private function _auto_text_filter($text) {
804 | if (!$this->_text_filter) return $text;
805 | return str_replace("\r\n", "\n", $text);
806 | }
807 |
808 | /**
809 | * 设置回复消息
810 | * Example: $obj->text('hello')->reply();
811 | * @param string $text
812 | */
813 | public function text($text='')
814 | {
815 | $FuncFlag = $this->_funcflag ? 1 : 0;
816 | $msg = array(
817 | 'ToUserName' => $this->getRevFrom(),
818 | 'FromUserName'=>$this->getRevTo(),
819 | 'MsgType'=>self::MSGTYPE_TEXT,
820 | 'Content'=>$this->_auto_text_filter($text),
821 | 'CreateTime'=>time(),
822 | 'FuncFlag'=>$FuncFlag
823 | );
824 | $this->Message($msg);
825 | return $this;
826 | }
827 | /**
828 | * 设置回复消息
829 | * Example: $obj->image('media_id')->reply();
830 | * @param string $mediaid
831 | */
832 | public function image($mediaid='')
833 | {
834 | $FuncFlag = $this->_funcflag ? 1 : 0;
835 | $msg = array(
836 | 'ToUserName' => $this->getRevFrom(),
837 | 'FromUserName'=>$this->getRevTo(),
838 | 'MsgType'=>self::MSGTYPE_IMAGE,
839 | 'Image'=>array('MediaId'=>$mediaid),
840 | 'CreateTime'=>time(),
841 | 'FuncFlag'=>$FuncFlag
842 | );
843 | $this->Message($msg);
844 | return $this;
845 | }
846 |
847 | /**
848 | * 设置回复消息
849 | * Example: $obj->voice('media_id')->reply();
850 | * @param string $mediaid
851 | */
852 | public function voice($mediaid='')
853 | {
854 | $FuncFlag = $this->_funcflag ? 1 : 0;
855 | $msg = array(
856 | 'ToUserName' => $this->getRevFrom(),
857 | 'FromUserName'=>$this->getRevTo(),
858 | 'MsgType'=>self::MSGTYPE_VOICE,
859 | 'Voice'=>array('MediaId'=>$mediaid),
860 | 'CreateTime'=>time(),
861 | 'FuncFlag'=>$FuncFlag
862 | );
863 | $this->Message($msg);
864 | return $this;
865 | }
866 |
867 | /**
868 | * 设置回复消息
869 | * Example: $obj->video('media_id','title','description')->reply();
870 | * @param string $mediaid
871 | */
872 | public function video($mediaid='',$title='',$description='')
873 | {
874 | $FuncFlag = $this->_funcflag ? 1 : 0;
875 | $msg = array(
876 | 'ToUserName' => $this->getRevFrom(),
877 | 'FromUserName'=>$this->getRevTo(),
878 | 'MsgType'=>self::MSGTYPE_VIDEO,
879 | 'Video'=>array(
880 | 'MediaId'=>$mediaid,
881 | 'Title'=>$title,
882 | 'Description'=>$description
883 | ),
884 | 'CreateTime'=>time(),
885 | 'FuncFlag'=>$FuncFlag
886 | );
887 | $this->Message($msg);
888 | return $this;
889 | }
890 |
891 | /**
892 | * 设置回复音乐
893 | * @param string $title
894 | * @param string $desc
895 | * @param string $musicurl
896 | * @param string $hgmusicurl
897 | * @param string $thumbmediaid 音乐图片缩略图的媒体id,非必须
898 | */
899 | public function music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') {
900 | $FuncFlag = $this->_funcflag ? 1 : 0;
901 | $msg = array(
902 | 'ToUserName' => $this->getRevFrom(),
903 | 'FromUserName'=>$this->getRevTo(),
904 | 'CreateTime'=>time(),
905 | 'MsgType'=>self::MSGTYPE_MUSIC,
906 | 'Music'=>array(
907 | 'Title'=>$title,
908 | 'Description'=>$desc,
909 | 'MusicUrl'=>$musicurl,
910 | 'HQMusicUrl'=>$hgmusicurl
911 | ),
912 | 'FuncFlag'=>$FuncFlag
913 | );
914 | if ($thumbmediaid) {
915 | $msg['Music']['ThumbMediaId'] = $thumbmediaid;
916 | }
917 | $this->Message($msg);
918 | return $this;
919 | }
920 |
921 | /**
922 | * 设置回复图文
923 | * @param array $newsData
924 | * 数组结构:
925 | * array(
926 | * "0"=>array(
927 | * 'Title'=>'msg title',
928 | * 'Description'=>'summary text',
929 | * 'PicUrl'=>'http://www.domain.com/1.jpg',
930 | * 'Url'=>'http://www.domain.com/1.html'
931 | * ),
932 | * "1"=>....
933 | * )
934 | */
935 | public function news($newsData=array())
936 | {
937 | $FuncFlag = $this->_funcflag ? 1 : 0;
938 | $count = count($newsData);
939 |
940 | $msg = array(
941 | 'ToUserName' => $this->getRevFrom(),
942 | 'FromUserName'=>$this->getRevTo(),
943 | 'MsgType'=>self::MSGTYPE_NEWS,
944 | 'CreateTime'=>time(),
945 | 'ArticleCount'=>$count,
946 | 'Articles'=>$newsData,
947 | 'FuncFlag'=>$FuncFlag
948 | );
949 | $this->Message($msg);
950 | return $this;
951 | }
952 |
953 | /**
954 | *
955 | * 回复微信服务器, 此函数支持链式操作
956 | * Example: $this->text('msg tips')->reply();
957 | * @param string $msg 要发送的信息, 默认取$this->_msg
958 | * @param bool $return 是否返回信息而不抛出到浏览器 默认:否
959 | */
960 | public function reply($msg=array(),$return = false)
961 | {
962 | if (empty($msg)) {
963 | if (empty($this->_msg)) //防止不先设置回复内容,直接调用reply方法导致异常
964 | return false;
965 | $msg = $this->_msg;
966 | }
967 | $xmldata= $this->xml_encode($msg);
968 | $this->log($xmldata);
969 | if ($this->encrypt_type == 'aes') { //如果来源消息为加密方式
970 | $pc = new Prpcrypt($this->encodingAesKey);
971 | $array = $pc->encrypt($xmldata, $this->appid);
972 | $ret = $array[0];
973 | if ($ret != 0) {
974 | $this->log('encrypt err!');
975 | return false;
976 | }
977 | $timestamp = time();
978 | $nonce = rand(77,999)*rand(605,888)*rand(11,99);
979 | $encrypt = $array[1];
980 | $tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文
981 | sort($tmpArr, SORT_STRING);
982 | $signature = implode($tmpArr);
983 | $signature = sha1($signature);
984 | $xmldata = $this->generate($encrypt, $signature, $timestamp, $nonce);
985 | $this->log($xmldata);
986 | }
987 | if ($return)
988 | return $xmldata;
989 | else
990 | echo $xmldata;
991 | }
992 |
993 | /**
994 | * xml格式加密,仅请求为加密方式时再用
995 | */
996 | private function generate($encrypt, $signature, $timestamp, $nonce)
997 | {
998 | //格式化加密信息
999 | $format = "
1000 |
1001 |
1002 | %s
1003 |
1004 | ";
1005 | return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
1006 | }
1007 |
1008 | /**
1009 | * GET 请求
1010 | * @param string $url
1011 | */
1012 | private function http_get($url){
1013 | $oCurl = curl_init();
1014 | if(stripos($url,"https://")!==FALSE){
1015 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
1016 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
1017 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
1018 | }
1019 | curl_setopt($oCurl, CURLOPT_URL, $url);
1020 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
1021 | $sContent = curl_exec($oCurl);
1022 | $aStatus = curl_getinfo($oCurl);
1023 | curl_close($oCurl);
1024 | if(intval($aStatus["http_code"])==200){
1025 | return $sContent;
1026 | }else{
1027 | return false;
1028 | }
1029 | }
1030 |
1031 | /**
1032 | * POST 请求
1033 | * @param string $url
1034 | * @param array $param
1035 | * @param boolean $post_file 是否文件上传
1036 | * @return string content
1037 | */
1038 | private function http_post($url,$param,$post_file=false){
1039 | $oCurl = curl_init();
1040 | if(stripos($url,"https://")!==FALSE){
1041 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
1042 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
1043 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
1044 | }
1045 | if (is_string($param) || $post_file) {
1046 | $strPOST = $param;
1047 | } else {
1048 | $aPOST = array();
1049 | foreach($param as $key=>$val){
1050 | $aPOST[] = $key."=".urlencode($val);
1051 | }
1052 | $strPOST = join("&", $aPOST);
1053 | }
1054 | curl_setopt($oCurl, CURLOPT_URL, $url);
1055 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
1056 | curl_setopt($oCurl, CURLOPT_POST,true);
1057 | curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
1058 | $sContent = curl_exec($oCurl);
1059 | $aStatus = curl_getinfo($oCurl);
1060 | curl_close($oCurl);
1061 | if(intval($aStatus["http_code"])==200){
1062 | return $sContent;
1063 | }else{
1064 | return false;
1065 | }
1066 | }
1067 |
1068 | /**
1069 | * 设置缓存,按需重载
1070 | * @param string $cachename
1071 | * @param mixed $value
1072 | * @param int $expired
1073 | * @return boolean
1074 | */
1075 | protected function setCache($cachename,$value,$expired){
1076 | Cache::put($cachename,$value,$expired/60);
1077 | return false;
1078 | }
1079 |
1080 | /**
1081 | * 获取缓存,按需重载
1082 | * @param string $cachename
1083 | * @return mixed
1084 | */
1085 | protected function getCache($cachename){
1086 | return Cache::get($cachename);
1087 | // return false;
1088 | }
1089 |
1090 | /**
1091 | * 清除缓存,按需重载
1092 | * @param string $cachename
1093 | * @return boolean
1094 | */
1095 | protected function removeCache($cachename){
1096 | Cache::forget($cachename);
1097 | return false;
1098 | }
1099 |
1100 | /**
1101 | * 获取access_token
1102 | * @param string $appid 如在类初始化时已提供,则可为空
1103 | * @param string $appsecret 如在类初始化时已提供,则可为空
1104 | * @param string $token 手动指定access_token,非必要情况不建议用
1105 | */
1106 | public function checkAuth($appid='',$appsecret='',$token=''){
1107 | if (!$appid || !$appsecret) {
1108 | $appid = $this->appid;
1109 | $appsecret = $this->appsecret;
1110 | }
1111 | if ($token) { //手动指定token,优先使用
1112 | $this->access_token=$token;
1113 | return $this->access_token;
1114 | }
1115 |
1116 | $authname = 'wechat_access_token'.$appid;
1117 | if ($rs = $this->getCache($authname)) {
1118 | $this->access_token = $rs;
1119 | return $rs;
1120 | }
1121 |
1122 | $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret);
1123 | if ($result)
1124 | {
1125 | $json = json_decode($result,true);
1126 | if (!$json || isset($json['errcode'])) {
1127 | $this->errCode = $json['errcode'];
1128 | $this->errMsg = $json['errmsg'];
1129 | return false;
1130 | }
1131 | $this->access_token = $json['access_token'];
1132 | $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;
1133 | $this->setCache($authname,$this->access_token,$expire);
1134 | return $this->access_token;
1135 | }
1136 | return false;
1137 | }
1138 |
1139 | /**
1140 | * 删除验证数据
1141 | * @param string $appid
1142 | */
1143 | public function resetAuth($appid=''){
1144 | if (!$appid) $appid = $this->appid;
1145 | $this->access_token = '';
1146 | $authname = 'wechat_access_token'.$appid;
1147 | $this->removeCache($authname);
1148 | return true;
1149 | }
1150 |
1151 | /**
1152 | * 删除JSAPI授权TICKET
1153 | * @param string $appid 用于多个appid时使用
1154 | */
1155 | public function resetJsTicket($appid=''){
1156 | if (!$appid) $appid = $this->appid;
1157 | $this->jsapi_ticket = '';
1158 | $authname = 'wechat_jsapi_ticket'.$appid;
1159 | $this->removeCache($authname);
1160 | return true;
1161 | }
1162 |
1163 | /**
1164 | * 获取JSAPI授权TICKET
1165 | * @param string $appid 用于多个appid时使用,可空
1166 | * @param string $jsapi_ticket 手动指定jsapi_ticket,非必要情况不建议用
1167 | */
1168 | public function getJsTicket($appid='',$jsapi_ticket=''){
1169 | if (!$this->access_token && !$this->checkAuth()) return false;
1170 | if (!$appid) $appid = $this->appid;
1171 | if ($jsapi_ticket) { //手动指定token,优先使用
1172 | $this->jsapi_ticket = $jsapi_ticket;
1173 | return $this->access_token;
1174 | }
1175 | $authname = 'wechat_jsapi_ticket'.$appid;
1176 | if ($rs = $this->getCache($authname)) {
1177 | $this->jsapi_ticket = $rs;
1178 | return $rs;
1179 | }
1180 | $result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=jsapi');
1181 | if ($result)
1182 | {
1183 | $json = json_decode($result,true);
1184 | if (!$json || !empty($json['errcode'])) {
1185 | $this->errCode = $json['errcode'];
1186 | $this->errMsg = $json['errmsg'];
1187 | return false;
1188 | }
1189 | $this->jsapi_ticket = $json['ticket'];
1190 | $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;
1191 | $this->setCache($authname,$this->jsapi_ticket,$expire);
1192 | return $this->jsapi_ticket;
1193 | }
1194 | return false;
1195 | }
1196 |
1197 |
1198 | /**
1199 | * 获取JsApi使用签名
1200 | * @param string $url 网页的URL,自动处理#及其后面部分
1201 | * @param string $timestamp 当前时间戳 (为空则自动生成)
1202 | * @param string $noncestr 随机串 (为空则自动生成)
1203 | * @param string $appid 用于多个appid时使用,可空
1204 | * @return array|bool 返回签名字串
1205 | */
1206 | public function getJsSign($url, $timestamp=0, $noncestr='', $appid=''){
1207 | if (!$this->jsapi_ticket && !$this->getJsTicket($appid) || !$url) return false;
1208 | if (!$timestamp)
1209 | $timestamp = time();
1210 | if (!$noncestr)
1211 | $noncestr = $this->generateNonceStr();
1212 | $ret = strpos($url,'#');
1213 | if ($ret)
1214 | $url = substr($url,0,$ret);
1215 | $url = trim($url);
1216 | if (empty($url))
1217 | return false;
1218 | $arrdata = array("timestamp" => $timestamp, "noncestr" => $noncestr, "url" => $url, "jsapi_ticket" => $this->jsapi_ticket);
1219 | $sign = $this->getSignature($arrdata);
1220 | if (!$sign)
1221 | return false;
1222 | $signPackage = array(
1223 | "appid" => $this->appid,
1224 | "noncestr" => $noncestr,
1225 | "timestamp" => $timestamp,
1226 | "url" => $url,
1227 | "signature" => $sign
1228 | );
1229 | return $signPackage;
1230 | }
1231 |
1232 | /**
1233 | * 微信api不支持中文转义的json结构
1234 | * @param array $arr
1235 | */
1236 | static function json_encode($arr) {
1237 | $parts = array ();
1238 | $is_list = false;
1239 | //Find out if the given array is a numerical array
1240 | $keys = array_keys ( $arr );
1241 | $max_length = count ( $arr ) - 1;
1242 | if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
1243 | $is_list = true;
1244 | for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position
1245 | if ($i != $keys [$i]) { //A key fails at position check.
1246 | $is_list = false; //It is an associative array.
1247 | break;
1248 | }
1249 | }
1250 | }
1251 | foreach ( $arr as $key => $value ) {
1252 | if (is_array ( $value )) { //Custom handling for arrays
1253 | if ($is_list)
1254 | $parts [] = self::json_encode ( $value ); /* :RECURSION: */
1255 | else
1256 | $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */
1257 | } else {
1258 | $str = '';
1259 | if (! $is_list)
1260 | $str = '"' . $key . '":';
1261 | //Custom handling for multiple data types
1262 | if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)
1263 | $str .= $value; //Numbers
1264 | elseif ($value === false)
1265 | $str .= 'false'; //The booleans
1266 | elseif ($value === true)
1267 | $str .= 'true';
1268 | else
1269 | $str .= '"' . addslashes ( $value ) . '"'; //All other things
1270 | // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
1271 | $parts [] = $str;
1272 | }
1273 | }
1274 | $json = implode ( ',', $parts );
1275 | if ($is_list)
1276 | return '[' . $json . ']'; //Return numerical JSON
1277 | return '{' . $json . '}'; //Return associative JSON
1278 | }
1279 |
1280 | /**
1281 | * 获取签名
1282 | * @param array $arrdata 签名数组
1283 | * @param string $method 签名方法
1284 | * @return boolean|string 签名值
1285 | */
1286 | public function getSignature($arrdata,$method="sha1") {
1287 | if (!function_exists($method)) return false;
1288 | ksort($arrdata);
1289 | $paramstring = "";
1290 | foreach($arrdata as $key => $value)
1291 | {
1292 | if(strlen($paramstring) == 0)
1293 | $paramstring .= $key . "=" . $value;
1294 | else
1295 | $paramstring .= "&" . $key . "=" . $value;
1296 | }
1297 | $Sign = $method($paramstring);
1298 | return $Sign;
1299 | }
1300 |
1301 | /**
1302 | * 生成随机字串
1303 | * @param number $length 长度,默认为16,最长为32字节
1304 | * @return string
1305 | */
1306 | public function generateNonceStr($length=16){
1307 | // 密码字符集,可任意添加你需要的字符
1308 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1309 | $str = "";
1310 | for($i = 0; $i < $length; $i++)
1311 | {
1312 | $str .= $chars[mt_rand(0, strlen($chars) - 1)];
1313 | }
1314 | return $str;
1315 | }
1316 |
1317 | /**
1318 | * 获取微信服务器IP地址列表
1319 | * @return array('127.0.0.1','127.0.0.1')
1320 | */
1321 | public function getServerIp(){
1322 | if (!$this->access_token && !$this->checkAuth()) return false;
1323 | $result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token);
1324 | if ($result)
1325 | {
1326 | $json = json_decode($result,true);
1327 | if (!$json || isset($json['errcode'])) {
1328 | $this->errCode = $json['errcode'];
1329 | $this->errMsg = $json['errmsg'];
1330 | return false;
1331 | }
1332 | return $json['ip_list'];
1333 | }
1334 | return false;
1335 | }
1336 |
1337 | /**
1338 | * 创建菜单(认证后的订阅号可用)
1339 | * @param array $data 菜单数组数据
1340 | * example:
1341 | * array (
1342 | * 'button' => array (
1343 | * 0 => array (
1344 | * 'name' => '扫码',
1345 | * 'sub_button' => array (
1346 | * 0 => array (
1347 | * 'type' => 'scancode_waitmsg',
1348 | * 'name' => '扫码带提示',
1349 | * 'key' => 'rselfmenu_0_0',
1350 | * ),
1351 | * 1 => array (
1352 | * 'type' => 'scancode_push',
1353 | * 'name' => '扫码推事件',
1354 | * 'key' => 'rselfmenu_0_1',
1355 | * ),
1356 | * ),
1357 | * ),
1358 | * 1 => array (
1359 | * 'name' => '发图',
1360 | * 'sub_button' => array (
1361 | * 0 => array (
1362 | * 'type' => 'pic_sysphoto',
1363 | * 'name' => '系统拍照发图',
1364 | * 'key' => 'rselfmenu_1_0',
1365 | * ),
1366 | * 1 => array (
1367 | * 'type' => 'pic_photo_or_album',
1368 | * 'name' => '拍照或者相册发图',
1369 | * 'key' => 'rselfmenu_1_1',
1370 | * )
1371 | * ),
1372 | * ),
1373 | * 2 => array (
1374 | * 'type' => 'location_select',
1375 | * 'name' => '发送位置',
1376 | * 'key' => 'rselfmenu_2_0'
1377 | * ),
1378 | * ),
1379 | * )
1380 | * type可以选择为以下几种,其中5-8除了收到菜单事件以外,还会单独收到对应类型的信息。
1381 | * 1、click:点击推事件
1382 | * 2、view:跳转URL
1383 | * 3、scancode_push:扫码推事件
1384 | * 4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框
1385 | * 5、pic_sysphoto:弹出系统拍照发图
1386 | * 6、pic_photo_or_album:弹出拍照或者相册发图
1387 | * 7、pic_weixin:弹出微信相册发图器
1388 | * 8、location_select:弹出地理位置选择器
1389 | */
1390 | public function createMenu($data){
1391 | if (!$this->access_token && !$this->checkAuth()) return false;
1392 | $result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
1393 | if ($result)
1394 | {
1395 | $json = json_decode($result,true);
1396 | if (!$json || !empty($json['errcode'])) {
1397 | $this->errCode = $json['errcode'];
1398 | $this->errMsg = $json['errmsg'];
1399 | return false;
1400 | }
1401 | return true;
1402 | }
1403 | return false;
1404 | }
1405 |
1406 | /**
1407 | * 获取菜单(认证后的订阅号可用)
1408 | * @return array('menu'=>array(....s))
1409 | */
1410 | public function getMenu(){
1411 | if (!$this->access_token && !$this->checkAuth()) return false;
1412 | $result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token);
1413 | if ($result)
1414 | {
1415 | $json = json_decode($result,true);
1416 | if (!$json || isset($json['errcode'])) {
1417 | $this->errCode = $json['errcode'];
1418 | $this->errMsg = $json['errmsg'];
1419 | return false;
1420 | }
1421 | return $json;
1422 | }
1423 | return false;
1424 | }
1425 |
1426 | /**
1427 | * 删除菜单(认证后的订阅号可用)
1428 | * @return boolean
1429 | */
1430 | public function deleteMenu(){
1431 | if (!$this->access_token && !$this->checkAuth()) return false;
1432 | $result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token);
1433 | if ($result)
1434 | {
1435 | $json = json_decode($result,true);
1436 | if (!$json || !empty($json['errcode'])) {
1437 | $this->errCode = $json['errcode'];
1438 | $this->errMsg = $json['errmsg'];
1439 | return false;
1440 | }
1441 | return true;
1442 | }
1443 | return false;
1444 | }
1445 |
1446 | /**
1447 | * 上传多媒体文件(认证后的订阅号可用)
1448 | * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时
1449 | * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义
1450 | * @param array $data {"media":'@Path\filename.jpg'}
1451 | * @param type 类型:图片:image 语音:voice 视频:video 缩略图:thumb
1452 | * @return boolean|array
1453 | */
1454 | public function uploadMedia($data, $type){
1455 | if (!$this->access_token && !$this->checkAuth()) return false;
1456 | $result = $this->http_post(self::UPLOAD_MEDIA_URL.self::MEDIA_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true);
1457 | if ($result)
1458 | {
1459 | $json = json_decode($result,true);
1460 | if (!$json || !empty($json['errcode'])) {
1461 | $this->errCode = $json['errcode'];
1462 | $this->errMsg = $json['errmsg'];
1463 | return false;
1464 | }
1465 | return $json;
1466 | }
1467 | return false;
1468 | }
1469 |
1470 | /**
1471 | * 根据媒体文件ID获取媒体文件(认证后的订阅号可用)
1472 | * @param string $media_id 媒体文件id
1473 | * @return raw data
1474 | */
1475 | public function getMedia($media_id){
1476 | if (!$this->access_token && !$this->checkAuth()) return false;
1477 | $result = $this->http_get(self::UPLOAD_MEDIA_URL.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id);
1478 | if ($result)
1479 | {
1480 | $json = json_decode($result,true);
1481 | if (isset($json['errcode'])) {
1482 | $this->errCode = $json['errcode'];
1483 | $this->errMsg = $json['errmsg'];
1484 | return false;
1485 | }
1486 | return $result;
1487 | }
1488 | return false;
1489 | }
1490 |
1491 | /**
1492 | * 上传图文消息素材(认证后的订阅号可用)
1493 | * @param array $data 消息结构{"articles":[{...}]}
1494 | * @return boolean|array
1495 | */
1496 | public function uploadArticles($data){
1497 | if (!$this->access_token && !$this->checkAuth()) return false;
1498 | $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOADNEWS_URL.'access_token='.$this->access_token,self::json_encode($data));
1499 | if ($result)
1500 | {
1501 | $json = json_decode($result,true);
1502 | if (!$json || !empty($json['errcode'])) {
1503 | $this->errCode = $json['errcode'];
1504 | $this->errMsg = $json['errmsg'];
1505 | return false;
1506 | }
1507 | return $json;
1508 | }
1509 | return false;
1510 | }
1511 |
1512 | /**
1513 | * 上传视频素材(认证后的订阅号可用)
1514 | * @param array $data 消息结构
1515 | * {
1516 | * "media_id"=>"", //通过上传媒体接口得到的MediaId
1517 | * "title"=>"TITLE", //视频标题
1518 | * "description"=>"Description" //视频描述
1519 | * }
1520 | * @return boolean|array
1521 | * {
1522 | * "type":"video",
1523 | * "media_id":"mediaid",
1524 | * "created_at":1398848981
1525 | * }
1526 | */
1527 | public function uploadMpVideo($data){
1528 | if (!$this->access_token && !$this->checkAuth()) return false;
1529 | $result = $this->http_post(self::UPLOAD_MEDIA_URL.self::MEDIA_VIDEO_UPLOAD.'access_token='.$this->access_token,self::json_encode($data));
1530 | if ($result)
1531 | {
1532 | $json = json_decode($result,true);
1533 | if (!$json || !empty($json['errcode'])) {
1534 | $this->errCode = $json['errcode'];
1535 | $this->errMsg = $json['errmsg'];
1536 | return false;
1537 | }
1538 | return $json;
1539 | }
1540 | return false;
1541 | }
1542 |
1543 | /**
1544 | * 高级群发消息, 根据OpenID列表群发图文消息(订阅号不可用)
1545 | * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成,
1546 | * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。
1547 | * @param array $data 消息结构
1548 | * {
1549 | * "touser"=>array(
1550 | * "OPENID1",
1551 | * "OPENID2"
1552 | * ),
1553 | * "msgtype"=>"mpvideo",
1554 | * // 在下面5种类型中选择对应的参数内容
1555 | * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId")
1556 | * // text => array ( "content" => "hello")
1557 | * }
1558 | * @return boolean|array
1559 | */
1560 | public function sendMassMessage($data){
1561 | if (!$this->access_token && !$this->checkAuth()) return false;
1562 | $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));
1563 | if ($result)
1564 | {
1565 | $json = json_decode($result,true);
1566 | if (!$json || !empty($json['errcode'])) {
1567 | $this->errCode = $json['errcode'];
1568 | $this->errMsg = $json['errmsg'];
1569 | return false;
1570 | }
1571 | return $json;
1572 | }
1573 | return false;
1574 | }
1575 |
1576 | /**
1577 | * 高级群发消息, 根据群组id群发图文消息(认证后的订阅号可用)
1578 | * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成,
1579 | * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。
1580 | * @param array $data 消息结构
1581 | * {
1582 | * "filter"=>array(
1583 | * "is_to_all"=>False, //是否群发给所有用户.True不用分组id,False需填写分组id
1584 | * "group_id"=>"2" //群发的分组id
1585 | * ),
1586 | * "msgtype"=>"mpvideo",
1587 | * // 在下面5种类型中选择对应的参数内容
1588 | * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId")
1589 | * // text => array ( "content" => "hello")
1590 | * }
1591 | * @return boolean|array
1592 | */
1593 | public function sendGroupMassMessage($data){
1594 | if (!$this->access_token && !$this->checkAuth()) return false;
1595 | $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data));
1596 | if ($result)
1597 | {
1598 | $json = json_decode($result,true);
1599 | if (!$json || !empty($json['errcode'])) {
1600 | $this->errCode = $json['errcode'];
1601 | $this->errMsg = $json['errmsg'];
1602 | return false;
1603 | }
1604 | return $json;
1605 | }
1606 | return false;
1607 | }
1608 |
1609 | /**
1610 | * 高级群发消息, 删除群发图文消息(认证后的订阅号可用)
1611 | * @param int $msg_id 消息id
1612 | * @return boolean|array
1613 | */
1614 | public function deleteMassMessage($msg_id){
1615 | if (!$this->access_token && !$this->checkAuth()) return false;
1616 | $result = $this->http_post(self::API_URL_PREFIX.self::MASS_DELETE_URL.'access_token='.$this->access_token,self::json_encode(array('msg_id'=>$msg_id)));
1617 | if ($result)
1618 | {
1619 | $json = json_decode($result,true);
1620 | if (!$json || !empty($json['errcode'])) {
1621 | $this->errCode = $json['errcode'];
1622 | $this->errMsg = $json['errmsg'];
1623 | return false;
1624 | }
1625 | return true;
1626 | }
1627 | return false;
1628 | }
1629 |
1630 | /**
1631 | * 高级群发消息, 预览群发消息(认证后的订阅号可用)
1632 | * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成,
1633 | * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。
1634 | * @param array $data 消息结构
1635 | * {
1636 | * "touser"=>"OPENID",
1637 | * "msgtype"=>"mpvideo",
1638 | * // 在下面5种类型中选择对应的参数内容
1639 | * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId")
1640 | * // text => array ( "content" => "hello")
1641 | * }
1642 | * @return boolean|array
1643 | */
1644 | public function previewMassMessage($data){
1645 | if (!$this->access_token && !$this->checkAuth()) return false;
1646 | $result = $this->http_post(self::API_URL_PREFIX.self::MASS_PREVIEW_URL.'access_token='.$this->access_token,self::json_encode($data));
1647 | if ($result)
1648 | {
1649 | $json = json_decode($result,true);
1650 | if (!$json || !empty($json['errcode'])) {
1651 | $this->errCode = $json['errcode'];
1652 | $this->errMsg = $json['errmsg'];
1653 | return false;
1654 | }
1655 | return $json;
1656 | }
1657 | return false;
1658 | }
1659 |
1660 | /**
1661 | * 高级群发消息, 查询群发消息发送状态(认证后的订阅号可用)
1662 | * @param int $msg_id 消息id
1663 | * @return boolean|array
1664 | * {
1665 | * "msg_id":201053012, //群发消息后返回的消息id
1666 | * "msg_status":"SEND_SUCCESS" //消息发送后的状态,SENDING表示正在发送 SEND_SUCCESS表示发送成功
1667 | * }
1668 | */
1669 | public function queryMassMessage($msg_id){
1670 | if (!$this->access_token && !$this->checkAuth()) return false;
1671 | $result = $this->http_post(self::API_URL_PREFIX.self::MASS_QUERY_URL.'access_token='.$this->access_token,self::json_encode(array('msg_id'=>$msg_id)));
1672 | if ($result)
1673 | {
1674 | $json = json_decode($result,true);
1675 | if (!$json || !empty($json['errcode'])) {
1676 | $this->errCode = $json['errcode'];
1677 | $this->errMsg = $json['errmsg'];
1678 | return false;
1679 | }
1680 | return $json;
1681 | }
1682 | return false;
1683 | }
1684 |
1685 | /**
1686 | * 创建二维码ticket
1687 | * @param int $scene_id 自定义追踪id
1688 | * @param int $type 0:临时二维码;1:永久二维码(此时expire参数无效)
1689 | * @param int $expire 临时二维码有效期,最大为1800秒
1690 | * @return array('ticket'=>'qrcode字串','expire_seconds'=>1800,'url'=>'二维码图片解析后的地址')
1691 | */
1692 | public function getQRCode($scene_id,$type=0,$expire=1800){
1693 | if (!$this->access_token && !$this->checkAuth()) return false;
1694 | $data = array(
1695 | 'action_name'=>$type?"QR_LIMIT_SCENE":"QR_SCENE",
1696 | 'expire_seconds'=>$expire,
1697 | 'action_info'=>array('scene'=>array('scene_id'=>$scene_id))
1698 | );
1699 | if ($type == 1) {
1700 | unset($data['expire_seconds']);
1701 | }
1702 | $result = $this->http_post(self::API_URL_PREFIX.self::QRCODE_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
1703 | if ($result)
1704 | {
1705 | $json = json_decode($result,true);
1706 | if (!$json || !empty($json['errcode'])) {
1707 | $this->errCode = $json['errcode'];
1708 | $this->errMsg = $json['errmsg'];
1709 | return false;
1710 | }
1711 | return $json;
1712 | }
1713 | return false;
1714 | }
1715 |
1716 | /**
1717 | * 获取二维码图片
1718 | * @param string $ticket 传入由getQRCode方法生成的ticket参数
1719 | * @return string url 返回http地址
1720 | */
1721 | public function getQRUrl($ticket) {
1722 | return self::QRCODE_IMG_URL.$ticket;
1723 | }
1724 |
1725 | /**
1726 | * 长链接转短链接接口
1727 | * @param string $long_url 传入要转换的长url
1728 | * @return boolean|string url 成功则返回转换后的短url
1729 | */
1730 | public function getShortUrl($long_url){
1731 | if (!$this->access_token && !$this->checkAuth()) return false;
1732 | $data = array(
1733 | 'action'=>'long2short',
1734 | 'long_url'=>$long_url
1735 | );
1736 | $result = $this->http_post(self::API_URL_PREFIX.self::SHORT_URL.'access_token='.$this->access_token,self::json_encode($data));
1737 | if ($result)
1738 | {
1739 | $json = json_decode($result,true);
1740 | if (!$json || !empty($json['errcode'])) {
1741 | $this->errCode = $json['errcode'];
1742 | $this->errMsg = $json['errmsg'];
1743 | return false;
1744 | }
1745 | return $json['short_url'];
1746 | }
1747 | return false;
1748 | }
1749 |
1750 | /**
1751 | * 获取统计数据
1752 | * @param string $type 数据分类(user|article|upstreammsg|interface)分别为(用户分析|图文分析|消息分析|接口分析)
1753 | * @param string $subtype 数据子分类,参考 DATACUBE_URL_ARR 常量定义部分 或者README.md说明文档
1754 | * @param string $begin_date 开始时间
1755 | * @param string $end_date 结束时间
1756 | * @return boolean|array 成功返回查询结果数组,其定义请看官方文档
1757 | */
1758 | public function getDatacube($type,$subtype,$begin_date,$end_date=''){
1759 | if (!$this->access_token && !$this->checkAuth()) return false;
1760 | if (!isset(self::$DATACUBE_URL_ARR[$type]) || !isset(self::$DATACUBE_URL_ARR[$type][$subtype]))
1761 | return false;
1762 | $data = array(
1763 | 'begin_date'=>$begin_date,
1764 | 'end_date'=>$end_date?$end_date:$begin_date
1765 | );
1766 | $result = $this->http_post(self::API_URL_PREFIX.self::$DATACUBE_URL_ARR[$type][$subtype].'access_token='.$this->access_token,self::json_encode($data));
1767 | if ($result)
1768 | {
1769 | $json = json_decode($result,true);
1770 | if (!$json || !empty($json['errcode'])) {
1771 | $this->errCode = $json['errcode'];
1772 | $this->errMsg = $json['errmsg'];
1773 | return false;
1774 | }
1775 | return isset($json['list'])?$json['list']:$json;
1776 | }
1777 | return false;
1778 | }
1779 |
1780 | /**
1781 | * 批量获取关注用户列表
1782 | * @param unknown $next_openid
1783 | */
1784 | public function getUserList($next_openid=''){
1785 | if (!$this->access_token && !$this->checkAuth()) return false;
1786 | $result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&next_openid='.$next_openid);
1787 | if ($result)
1788 | {
1789 | $json = json_decode($result,true);
1790 | if (isset($json['errcode'])) {
1791 | $this->errCode = $json['errcode'];
1792 | $this->errMsg = $json['errmsg'];
1793 | return false;
1794 | }
1795 | return $json;
1796 | }
1797 | return false;
1798 | }
1799 |
1800 | /**
1801 | * 获取关注者详细信息
1802 | * @param string $openid
1803 | * @return array {subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}
1804 | * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
1805 | */
1806 | public function getUserInfo($openid){
1807 | if (!$this->access_token && !$this->checkAuth()) return false;
1808 | $result = $this->http_get(self::API_URL_PREFIX.self::USER_INFO_URL.'access_token='.$this->access_token.'&openid='.$openid);
1809 | if ($result)
1810 | {
1811 | $json = json_decode($result,true);
1812 | if (isset($json['errcode'])) {
1813 | $this->errCode = $json['errcode'];
1814 | $this->errMsg = $json['errmsg'];
1815 | return false;
1816 | }
1817 | return $json;
1818 | }
1819 | return false;
1820 | }
1821 |
1822 | /**
1823 | * 设置用户备注名
1824 | * @param string $openid
1825 | * @param string $remark 备注名
1826 | * @return boolean|array
1827 | */
1828 | public function updateUserRemark($openid,$remark){
1829 | if (!$this->access_token && !$this->checkAuth()) return false;
1830 | $data = array(
1831 | 'openid'=>$openid,
1832 | 'remark'=>$remark
1833 | );
1834 | $result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATEREMARK_URL.'access_token='.$this->access_token,self::json_encode($data));
1835 | if ($result)
1836 | {
1837 | $json = json_decode($result,true);
1838 | if (!$json || !empty($json['errcode'])) {
1839 | $this->errCode = $json['errcode'];
1840 | $this->errMsg = $json['errmsg'];
1841 | return false;
1842 | }
1843 | return $json;
1844 | }
1845 | return false;
1846 | }
1847 |
1848 | /**
1849 | * 获取用户分组列表
1850 | * @return boolean|array
1851 | */
1852 | public function getGroup(){
1853 | if (!$this->access_token && !$this->checkAuth()) return false;
1854 | $result = $this->http_get(self::API_URL_PREFIX.self::GROUP_GET_URL.'access_token='.$this->access_token);
1855 | if ($result)
1856 | {
1857 | $json = json_decode($result,true);
1858 | if (isset($json['errcode'])) {
1859 | $this->errCode = $json['errcode'];
1860 | $this->errMsg = $json['errmsg'];
1861 | return false;
1862 | }
1863 | return $json;
1864 | }
1865 | return false;
1866 | }
1867 |
1868 | /**
1869 | * 获取用户所在分组
1870 | * @param string $openid
1871 | * @return boolean|int 成功则返回用户分组id
1872 | */
1873 | public function getUserGroup($openid){
1874 | if (!$this->access_token && !$this->checkAuth()) return false;
1875 | $data = array(
1876 | 'openid'=>$openid
1877 | );
1878 | $result = $this->http_post(self::API_URL_PREFIX.self::USER_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data));
1879 | if ($result)
1880 | {
1881 | $json = json_decode($result,true);
1882 | if (!$json || !empty($json['errcode'])) {
1883 | $this->errCode = $json['errcode'];
1884 | $this->errMsg = $json['errmsg'];
1885 | return false;
1886 | } else
1887 | if (isset($json['groupid'])) return $json['groupid'];
1888 | }
1889 | return false;
1890 | }
1891 |
1892 | /**
1893 | * 新增自定分组
1894 | * @param string $name 分组名称
1895 | * @return boolean|array
1896 | */
1897 | public function createGroup($name){
1898 | if (!$this->access_token && !$this->checkAuth()) return false;
1899 | $data = array(
1900 | 'group'=>array('name'=>$name)
1901 | );
1902 | $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
1903 | if ($result)
1904 | {
1905 | $json = json_decode($result,true);
1906 | if (!$json || !empty($json['errcode'])) {
1907 | $this->errCode = $json['errcode'];
1908 | $this->errMsg = $json['errmsg'];
1909 | return false;
1910 | }
1911 | return $json;
1912 | }
1913 | return false;
1914 | }
1915 |
1916 | /**
1917 | * 更改分组名称
1918 | * @param int $groupid 分组id
1919 | * @param string $name 分组名称
1920 | * @return boolean|array
1921 | */
1922 | public function updateGroup($groupid,$name){
1923 | if (!$this->access_token && !$this->checkAuth()) return false;
1924 | $data = array(
1925 | 'group'=>array('id'=>$groupid,'name'=>$name)
1926 | );
1927 | $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
1928 | if ($result)
1929 | {
1930 | $json = json_decode($result,true);
1931 | if (!$json || !empty($json['errcode'])) {
1932 | $this->errCode = $json['errcode'];
1933 | $this->errMsg = $json['errmsg'];
1934 | return false;
1935 | }
1936 | return $json;
1937 | }
1938 | return false;
1939 | }
1940 |
1941 | /**
1942 | * 移动用户分组
1943 | * @param int $groupid 分组id
1944 | * @param string $openid 用户openid
1945 | * @return boolean|array
1946 | */
1947 | public function updateGroupMembers($groupid,$openid){
1948 | if (!$this->access_token && !$this->checkAuth()) return false;
1949 | $data = array(
1950 | 'openid'=>$openid,
1951 | 'to_groupid'=>$groupid
1952 | );
1953 | $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_MEMBER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
1954 | if ($result)
1955 | {
1956 | $json = json_decode($result,true);
1957 | if (!$json || !empty($json['errcode'])) {
1958 | $this->errCode = $json['errcode'];
1959 | $this->errMsg = $json['errmsg'];
1960 | return false;
1961 | }
1962 | return $json;
1963 | }
1964 | return false;
1965 | }
1966 |
1967 | /**
1968 | * 发送客服消息
1969 | * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}}
1970 | * @return boolean|array
1971 | */
1972 | public function sendCustomMessage($data){
1973 | if (!$this->access_token && !$this->checkAuth()) return false;
1974 | $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));
1975 | if ($result)
1976 | {
1977 | $json = json_decode($result,true);
1978 | if (!$json || !empty($json['errcode'])) {
1979 | $this->errCode = $json['errcode'];
1980 | $this->errMsg = $json['errmsg'];
1981 | return false;
1982 | }
1983 | return $json;
1984 | }
1985 | return false;
1986 | }
1987 |
1988 | /**
1989 | * oauth 授权跳转接口
1990 | * @param string $callback 回调URI
1991 | * @return string
1992 | */
1993 | public function getOauthRedirect($callback,$state='',$scope='snsapi_userinfo'){
1994 | return self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect';
1995 | }
1996 |
1997 | /**
1998 | * 通过code获取Access Token
1999 | * @return array {access_token,expires_in,refresh_token,openid,scope}
2000 | */
2001 | public function getOauthAccessToken(){
2002 | $code = isset($_GET['code'])?$_GET['code']:'';
2003 | if (!$code) return false;
2004 | $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_TOKEN_URL.'appid='.$this->appid.'&secret='.$this->appsecret.'&code='.$code.'&grant_type=authorization_code');
2005 | if ($result)
2006 | {
2007 | $json = json_decode($result,true);
2008 | if (!$json || !empty($json['errcode'])) {
2009 | $this->errCode = $json['errcode'];
2010 | $this->errMsg = $json['errmsg'];
2011 | return false;
2012 | }
2013 | $this->user_token = $json['access_token'];
2014 | return $json;
2015 | }
2016 | return false;
2017 | }
2018 |
2019 | /**
2020 | * 刷新access token并续期
2021 | * @param string $refresh_token
2022 | * @return boolean|mixed
2023 | */
2024 | public function getOauthRefreshToken($refresh_token){
2025 | $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_REFRESH_URL.'appid='.$this->appid.'&grant_type=refresh_token&refresh_token='.$refresh_token);
2026 | if ($result)
2027 | {
2028 | $json = json_decode($result,true);
2029 | if (!$json || !empty($json['errcode'])) {
2030 | $this->errCode = $json['errcode'];
2031 | $this->errMsg = $json['errmsg'];
2032 | return false;
2033 | }
2034 | $this->user_token = $json['access_token'];
2035 | return $json;
2036 | }
2037 | return false;
2038 | }
2039 |
2040 | /**
2041 | * 获取授权后的用户资料
2042 | * @param string $access_token
2043 | * @param string $openid
2044 | * @return array {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]}
2045 | * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
2046 | */
2047 | public function getOauthUserinfo($access_token,$openid){
2048 | $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_USERINFO_URL.'access_token='.$access_token.'&openid='.$openid);
2049 | if ($result)
2050 | {
2051 | $json = json_decode($result,true);
2052 | if (!$json || !empty($json['errcode'])) {
2053 | $this->errCode = $json['errcode'];
2054 | $this->errMsg = $json['errmsg'];
2055 | return false;
2056 | }
2057 | return $json;
2058 | }
2059 | return false;
2060 | }
2061 |
2062 | /**
2063 | * 检验授权凭证是否有效
2064 | * @param string $access_token
2065 | * @param string $openid
2066 | * @return boolean 是否有效
2067 | */
2068 | public function getOauthAuth($access_token,$openid){
2069 | $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_AUTH_URL.'access_token='.$access_token.'&openid='.$openid);
2070 | if ($result)
2071 | {
2072 | $json = json_decode($result,true);
2073 | if (!$json || !empty($json['errcode'])) {
2074 | $this->errCode = $json['errcode'];
2075 | $this->errMsg = $json['errmsg'];
2076 | return false;
2077 | } else
2078 | if ($json['errcode']==0) return true;
2079 | }
2080 | return false;
2081 | }
2082 |
2083 | /**
2084 | * 模板消息 设置所属行业
2085 | * @param int $id1 公众号模板消息所属行业编号,参看官方开发文档 行业代码
2086 | * @param int $id2 同$id1。但如果只有一个行业,此参数可省略
2087 | * @return boolean|array
2088 | */
2089 | public function setTMIndustry($id1,$id2=''){
2090 | if ($id1) $data['industry_id1'] = $id1;
2091 | if ($id2) $data['industry_id2'] = $id2;
2092 | if (!$this->access_token && !$this->checkAuth()) return false;
2093 | $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SET_INDUSTRY_URL.'access_token='.$this->access_token,self::json_encode($data));
2094 | if($result){
2095 | $json = json_decode($result,true);
2096 | if (!$json || !empty($json['errcode'])) {
2097 | $this->errCode = $json['errcode'];
2098 | $this->errMsg = $json['errmsg'];
2099 | return false;
2100 | }
2101 | return $json;
2102 | }
2103 | return false;
2104 | }
2105 |
2106 | /**
2107 | * 模板消息 添加消息模板
2108 | * 成功返回消息模板的调用id
2109 | * @param string $tpl_id 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式
2110 | * @return boolean|string
2111 | */
2112 | public function addTemplateMessage($tpl_id){
2113 | $data = array ('template_id_short' =>$tpl_id);
2114 | if (!$this->access_token && !$this->checkAuth()) return false;
2115 | $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_ADD_TPL_URL.'access_token='.$this->access_token,self::json_encode($data));
2116 | if($result){
2117 | $json = json_decode($result,true);
2118 | if (!$json || !empty($json['errcode'])) {
2119 | $this->errCode = $json['errcode'];
2120 | $this->errMsg = $json['errmsg'];
2121 | return false;
2122 | }
2123 | return $json['template_id'];
2124 | }
2125 | return false;
2126 | }
2127 |
2128 | /**
2129 | * 发送模板消息
2130 | * @param array $data 消息结构
2131 | * {
2132 | "touser":"OPENID",
2133 | "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
2134 | "url":"http://weixin.qq.com/download",
2135 | "topcolor":"#FF0000",
2136 | "data":{
2137 | "参数名1": {
2138 | "value":"参数",
2139 | "color":"#173177" //参数颜色
2140 | },
2141 | "Date":{
2142 | "value":"06月07日 19时24分",
2143 | "color":"#173177"
2144 | },
2145 | "CardNumber":{
2146 | "value":"0426",
2147 | "color":"#173177"
2148 | },
2149 | "Type":{
2150 | "value":"消费",
2151 | "color":"#173177"
2152 | }
2153 | }
2154 | }
2155 | * @return boolean|array
2156 | */
2157 | public function sendTemplateMessage($data){
2158 | if (!$this->access_token && !$this->checkAuth()) return false;
2159 | $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));
2160 | if($result){
2161 | $json = json_decode($result,true);
2162 | if (!$json || !empty($json['errcode'])) {
2163 | $this->errCode = $json['errcode'];
2164 | $this->errMsg = $json['errmsg'];
2165 | return false;
2166 | }
2167 | return $json;
2168 | }
2169 | return false;
2170 | }
2171 |
2172 | /**
2173 | * 获取多客服会话记录
2174 | * @param array $data 数据结构{"starttime":123456789,"endtime":987654321,"openid":"OPENID","pagesize":10,"pageindex":1,}
2175 | * @return boolean|array
2176 | */
2177 | public function getCustomServiceMessage($data){
2178 | if (!$this->access_token && !$this->checkAuth()) return false;
2179 | $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_RECORD.'access_token='.$this->access_token,self::json_encode($data));
2180 | if ($result)
2181 | {
2182 | $json = json_decode($result,true);
2183 | if (!$json || !empty($json['errcode'])) {
2184 | $this->errCode = $json['errcode'];
2185 | $this->errMsg = $json['errmsg'];
2186 | return false;
2187 | }
2188 | return $json;
2189 | }
2190 | return false;
2191 | }
2192 |
2193 | /**
2194 | * 转发多客服消息
2195 | * Example: $obj->transfer_customer_service($customer_account)->reply();
2196 | * @param string $customer_account 转发到指定客服帐号:test1@test
2197 | */
2198 | public function transfer_customer_service($customer_account = '')
2199 | {
2200 | $msg = array(
2201 | 'ToUserName' => $this->getRevFrom(),
2202 | 'FromUserName'=>$this->getRevTo(),
2203 | 'CreateTime'=>time(),
2204 | 'MsgType'=>'transfer_customer_service',
2205 | );
2206 | if (!$customer_account) {
2207 | $msg['TransInfo'] = array('KfAccount'=>$customer_account);
2208 | }
2209 | $this->Message($msg);
2210 | return $this;
2211 | }
2212 |
2213 | /**
2214 | * 获取多客服客服基本信息
2215 | *
2216 | * @return boolean|array
2217 | */
2218 | public function getCustomServiceKFlist(){
2219 | if (!$this->access_token && !$this->checkAuth()) return false;
2220 | $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_KFLIST.'access_token='.$this->access_token);
2221 | if ($result)
2222 | {
2223 | $json = json_decode($result,true);
2224 | if (!$json || !empty($json['errcode'])) {
2225 | $this->errCode = $json['errcode'];
2226 | $this->errMsg = $json['errmsg'];
2227 | return false;
2228 | }
2229 | return $json;
2230 | }
2231 | return false;
2232 | }
2233 |
2234 | /**
2235 | * 获取多客服在线客服接待信息
2236 | *
2237 | * @return boolean|array {
2238 | "kf_online_list": [
2239 | {
2240 | "kf_account": "test1@test", //客服账号@微信别名
2241 | "status": 1, //客服在线状态 1:pc在线,2:手机在线,若pc和手机同时在线则为 1+2=3
2242 | "kf_id": "1001", //客服工号
2243 | "auto_accept": 0, //客服设置的最大自动接入数
2244 | "accepted_case": 1 //客服当前正在接待的会话数
2245 | }
2246 | ]
2247 | }
2248 | */
2249 | public function getCustomServiceOnlineKFlist(){
2250 | if (!$this->access_token && !$this->checkAuth()) return false;
2251 | $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_ONLINEKFLIST.'access_token='.$this->access_token);
2252 | if ($result)
2253 | {
2254 | $json = json_decode($result,true);
2255 | if (!$json || !empty($json['errcode'])) {
2256 | $this->errCode = $json['errcode'];
2257 | $this->errMsg = $json['errmsg'];
2258 | return false;
2259 | }
2260 | return $json;
2261 | }
2262 | return false;
2263 | }
2264 |
2265 | /**
2266 | * 创建指定多客服会话
2267 | * @tutorial 当用户已被其他客服接待或指定客服不在线则会失败
2268 | * @param string $openid //用户openid
2269 | * @param string $kf_account //客服账号
2270 | * @param string $text //附加信息,文本会展示在客服人员的多客服客户端,可为空
2271 | * @return boolean | array //成功返回json数组
2272 | * {
2273 | * "errcode": 0,
2274 | * "errmsg": "ok",
2275 | * }
2276 | */
2277 | public function createKFSession($openid,$kf_account,$text=''){
2278 | $data=array(
2279 | "openid" =>$openid,
2280 | "nickname" => $kf_account
2281 | );
2282 | if ($text) $data["text"] = $text;
2283 | if (!$this->access_token && !$this->checkAuth()) return false;
2284 | $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SEESSION_CREATE.'access_token='.$this->access_token,self::json_encode($data));
2285 | if ($result)
2286 | {
2287 | $json = json_decode($result,true);
2288 | if (!$json || !empty($json['errcode'])) {
2289 | $this->errCode = $json['errcode'];
2290 | $this->errMsg = $json['errmsg'];
2291 | return false;
2292 | }
2293 | return $json;
2294 | }
2295 | return false;
2296 | }
2297 |
2298 | /**
2299 | * 关闭指定多客服会话
2300 | * @tutorial 当用户被其他客服接待时则会失败
2301 | * @param string $openid //用户openid
2302 | * @param string $kf_account //客服账号
2303 | * @param string $text //附加信息,文本会展示在客服人员的多客服客户端,可为空
2304 | * @return boolean | array //成功返回json数组
2305 | * {
2306 | * "errcode": 0,
2307 | * "errmsg": "ok",
2308 | * }
2309 | */
2310 | public function closeKFSession($openid,$kf_account,$text=''){
2311 | $data=array(
2312 | "openid" =>$openid,
2313 | "nickname" => $kf_account
2314 | );
2315 | if ($text) $data["text"] = $text;
2316 | if (!$this->access_token && !$this->checkAuth()) return false;
2317 | $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SEESSION_CLOSE .'access_token='.$this->access_token,self::json_encode($data));
2318 | if ($result)
2319 | {
2320 | $json = json_decode($result,true);
2321 | if (!$json || !empty($json['errcode'])) {
2322 | $this->errCode = $json['errcode'];
2323 | $this->errMsg = $json['errmsg'];
2324 | return false;
2325 | }
2326 | return $json;
2327 | }
2328 | return false;
2329 | }
2330 |
2331 | /**
2332 | * 获取用户会话状态
2333 | * @param string $openid //用户openid
2334 | * @return boolean | array //成功返回json数组
2335 | * {
2336 | * "errcode" : 0,
2337 | * "errmsg" : "ok",
2338 | * "kf_account" : "test1@test", //正在接待的客服
2339 | * "createtime": 123456789, //会话接入时间
2340 | * }
2341 | */
2342 | public function getKFSession($openid){
2343 | if (!$this->access_token && !$this->checkAuth()) return false;
2344 | $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SEESSION_GET .'access_token='.$this->access_token.'&openid='.$openid);
2345 | if ($result)
2346 | {
2347 | $json = json_decode($result,true);
2348 | if (!$json || !empty($json['errcode'])) {
2349 | $this->errCode = $json['errcode'];
2350 | $this->errMsg = $json['errmsg'];
2351 | return false;
2352 | }
2353 | return $json;
2354 | }
2355 | return false;
2356 | }
2357 |
2358 | /**
2359 | * 获取指定客服的会话列表
2360 | * @param string $openid //用户openid
2361 | * @return boolean | array //成功返回json数组
2362 | * array(
2363 | * 'sessionlist' => array (
2364 | * array (
2365 | * 'openid'=>'OPENID', //客户 openid
2366 | * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳
2367 | * ),
2368 | * array (
2369 | * 'openid'=>'OPENID', //客户 openid
2370 | * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳
2371 | * ),
2372 | * )
2373 | * )
2374 | */
2375 | public function getKFSessionlist($kf_account){
2376 | if (!$this->access_token && !$this->checkAuth()) return false;
2377 | $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SEESSION_GET_LIST .'access_token='.$this->access_token.'&kf_account='.$kf_account);
2378 | if ($result)
2379 | {
2380 | $json = json_decode($result,true);
2381 | if (!$json || !empty($json['errcode'])) {
2382 | $this->errCode = $json['errcode'];
2383 | $this->errMsg = $json['errmsg'];
2384 | return false;
2385 | }
2386 | return $json;
2387 | }
2388 | return false;
2389 | }
2390 |
2391 | /**
2392 | * 获取未接入会话列表
2393 | * @param string $openid //用户openid
2394 | * @return boolean | array //成功返回json数组
2395 | * array (
2396 | * 'count' => 150 , //未接入会话数量
2397 | * 'waitcaselist' => array (
2398 | * array (
2399 | * 'openid'=>'OPENID', //客户 openid
2400 | * 'kf_account ' =>'', //指定接待的客服,为空则未指定
2401 | * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳
2402 | * ),
2403 | * array (
2404 | * 'openid'=>'OPENID', //客户 openid
2405 | * 'kf_account ' =>'', //指定接待的客服,为空则未指定
2406 | * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳
2407 | * )
2408 | * )
2409 | * )
2410 | */
2411 | public function getKFSessionWait(){
2412 | if (!$this->access_token && !$this->checkAuth()) return false;
2413 | $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SEESSION_GET_WAIT .'access_token='.$this->access_token);
2414 | if ($result)
2415 | {
2416 | $json = json_decode($result,true);
2417 | if (!$json || !empty($json['errcode'])) {
2418 | $this->errCode = $json['errcode'];
2419 | $this->errMsg = $json['errmsg'];
2420 | return false;
2421 | }
2422 | return $json;
2423 | }
2424 | return false;
2425 | }
2426 |
2427 | /**
2428 | * 添加客服账号
2429 | *
2430 | * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符
2431 | * @param string $nickname //客服昵称,最长6个汉字或12个英文字符
2432 | * @param string $password //客服账号明文登录密码,会自动加密
2433 | * @return boolean|array
2434 | * 成功返回结果
2435 | * {
2436 | * "errcode": 0,
2437 | * "errmsg": "ok",
2438 | * }
2439 | */
2440 | public function addKFAccount($account,$nickname,$password){
2441 | $data=array(
2442 | "kf_account" =>$account,
2443 | "nickname" => $nickname,
2444 | "password" => md5($password)
2445 | );
2446 | if (!$this->access_token && !$this->checkAuth()) return false;
2447 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_ADD_URL.'access_token='.$this->access_token,self::json_encode($data));
2448 | if ($result)
2449 | {
2450 | $json = json_decode($result,true);
2451 | if (!$json || !empty($json['errcode'])) {
2452 | $this->errCode = $json['errcode'];
2453 | $this->errMsg = $json['errmsg'];
2454 | return false;
2455 | }
2456 | return $json;
2457 | }
2458 | return false;
2459 | }
2460 |
2461 | /**
2462 | * 修改客服账号信息
2463 | *
2464 | * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符
2465 | * @param string $nickname //客服昵称,最长6个汉字或12个英文字符
2466 | * @param string $password //客服账号明文登录密码,会自动加密
2467 | * @return boolean|array
2468 | * 成功返回结果
2469 | * {
2470 | * "errcode": 0,
2471 | * "errmsg": "ok",
2472 | * }
2473 | */
2474 | public function updateKFAccount($account,$nickname,$password){
2475 | $data=array(
2476 | "kf_account" =>$account,
2477 | "nickname" => $nickname,
2478 | "password" => md5($password)
2479 | );
2480 | if (!$this->access_token && !$this->checkAuth()) return false;
2481 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
2482 | if ($result)
2483 | {
2484 | $json = json_decode($result,true);
2485 | if (!$json || !empty($json['errcode'])) {
2486 | $this->errCode = $json['errcode'];
2487 | $this->errMsg = $json['errmsg'];
2488 | return false;
2489 | }
2490 | return $json;
2491 | }
2492 | return false;
2493 | }
2494 |
2495 | /**
2496 | * 删除客服账号
2497 | *
2498 | * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符
2499 | * @return boolean|array
2500 | * 成功返回结果
2501 | * {
2502 | * "errcode": 0,
2503 | * "errmsg": "ok",
2504 | * }
2505 | */
2506 | public function deleteKFAccount($account){
2507 | if (!$this->access_token && !$this->checkAuth()) return false;
2508 | $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_DEL_URL.'access_token='.$this->access_token.'&kf_account='.$account);
2509 | if ($result)
2510 | {
2511 | $json = json_decode($result,true);
2512 | if (!$json || !empty($json['errcode'])) {
2513 | $this->errCode = $json['errcode'];
2514 | $this->errMsg = $json['errmsg'];
2515 | return false;
2516 | }
2517 | return $json;
2518 | }
2519 | return false;
2520 | }
2521 |
2522 | /**
2523 | * 上传客服头像
2524 | *
2525 | * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符
2526 | * @param string $imgfile //头像文件完整路径,如:'D:\user.jpg'。头像文件必须JPG格式,像素建议640*640
2527 | * @return boolean|array
2528 | * 成功返回结果
2529 | * {
2530 | * "errcode": 0,
2531 | * "errmsg": "ok",
2532 | * }
2533 | */
2534 | public function setKFHeadImg($account,$imgfile){
2535 | if (!$this->access_token && !$this->checkAuth()) return false;
2536 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_UPLOAD_HEADIMG_URL.'access_token='.$this->access_token.'&kf_account='.$account,array('media'=>'@'.$imgfile),true);
2537 | if ($result)
2538 | {
2539 | $json = json_decode($result,true);
2540 | if (!$json || !empty($json['errcode'])) {
2541 | $this->errCode = $json['errcode'];
2542 | $this->errMsg = $json['errmsg'];
2543 | return false;
2544 | }
2545 | return $json;
2546 | }
2547 | return false;
2548 | }
2549 |
2550 | /**
2551 | * 语义理解接口
2552 | * @param String $uid 用户唯一id(非开发者id),用户区分公众号下的不同用户(建议填入用户openid)
2553 | * @param String $query 输入文本串
2554 | * @param String $category 需要使用的服务类型,多个用“,”隔开,不能为空
2555 | * @param Float $latitude 纬度坐标,与经度同时传入;与城市二选一传入
2556 | * @param Float $longitude 经度坐标,与纬度同时传入;与城市二选一传入
2557 | * @param String $city 城市名称,与经纬度二选一传入
2558 | * @param String $region 区域名称,在城市存在的情况下可省略;与经纬度二选一传入
2559 | * @return boolean|array
2560 | */
2561 | public function querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region=""){
2562 | if (!$this->access_token && !$this->checkAuth()) return false;
2563 | $data=array(
2564 | 'query' => $query,
2565 | 'category' => $category,
2566 | 'appid' => $this->appid,
2567 | 'uid' => ''
2568 | );
2569 | //地理坐标或城市名称二选一
2570 | if ($latitude) {
2571 | $data['latitude'] = $latitude;
2572 | $data['longitude'] = $longitude;
2573 | } elseif ($city) {
2574 | $data['city'] = $city;
2575 | } elseif ($region) {
2576 | $data['region'] = $region;
2577 | }
2578 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::SEMANTIC_API_URL.'access_token='.$this->access_token,self::json_encode($data));
2579 | if ($result)
2580 | {
2581 | $json = json_decode($result,true);
2582 | if (!$json || !empty($json['errcode'])) {
2583 | $this->errCode = $json['errcode'];
2584 | $this->errMsg = $json['errmsg'];
2585 | return false;
2586 | }
2587 | return $json;
2588 | }
2589 | return false;
2590 | }
2591 |
2592 | /**
2593 | * 创建卡券
2594 | * @param Array $data 卡券数据
2595 | * @return array|boolean 返回数组中card_id为卡券ID
2596 | */
2597 | public function createCard($data) {
2598 | if (!$this->access_token && !$this->checkAuth()) return false;
2599 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CREATE . 'access_token=' . $this->access_token, self::json_encode($data));
2600 | if ($result) {
2601 | $json = json_decode($result, true);
2602 | if (!$json || !empty($json['errcode'])) {
2603 | $this->errCode = $json['errcode'];
2604 | $this->errMsg = $json['errmsg'];
2605 | return false;
2606 | }
2607 | return $json;
2608 | }
2609 | return false;
2610 | }
2611 |
2612 | /**
2613 | * 更改卡券信息
2614 | * 调用该接口更新信息后会重新送审,卡券状态变更为待审核。已被用户领取的卡券会实时更新票面信息。
2615 | * @param string $data
2616 | * @return boolean
2617 | */
2618 | public function updateCard($data) {
2619 | if (!$this->access_token && !$this->checkAuth()) return false;
2620 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));
2621 | if ($result) {
2622 | $json = json_decode($result, true);
2623 | if (!$json || !empty($json['errcode'])) {
2624 | $this->errCode = $json['errcode'];
2625 | $this->errMsg = $json['errmsg'];
2626 | return false;
2627 | }
2628 | return true;
2629 | }
2630 | return false;
2631 | }
2632 |
2633 | /**
2634 | * 删除卡券
2635 | * 允许商户删除任意一类卡券。删除卡券后,该卡券对应已生成的领取用二维码、添加到卡包 JS API 均会失效。
2636 | * 注意:删除卡券不能删除已被用户领取,保存在微信客户端中的卡券,已领取的卡券依旧有效。
2637 | * @param string $card_id 卡券ID
2638 | * @return boolean
2639 | */
2640 | public function delCard($card_id) {
2641 | $data = array(
2642 | 'card_id' => $card_id,
2643 | );
2644 | if (!$this->access_token && !$this->checkAuth()) return false;
2645 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_DELETE . 'access_token=' . $this->access_token, self::json_encode($data));
2646 | if ($result) {
2647 | $json = json_decode($result, true);
2648 | if (!$json || !empty($json['errcode'])) {
2649 | $this->errCode = $json['errcode'];
2650 | $this->errMsg = $json['errmsg'];
2651 | return false;
2652 | }
2653 | return true;
2654 | }
2655 | return false;
2656 | }
2657 |
2658 | /**
2659 | * 查询卡券详情
2660 | * @param string $card_id
2661 | * @return boolean|array 返回数组信息比较复杂,请参看卡券接口文档
2662 | */
2663 | public function getCardInfo($card_id) {
2664 | $data = array(
2665 | 'card_id' => $card_id,
2666 | );
2667 | if (!$this->access_token && !$this->checkAuth()) return false;
2668 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_GET . 'access_token=' . $this->access_token, self::json_encode($data));
2669 | if ($result) {
2670 | $json = json_decode($result, true);
2671 | if (!$json || !empty($json['errcode'])) {
2672 | $this->errCode = $json['errcode'];
2673 | $this->errMsg = $json['errmsg'];
2674 | return false;
2675 | }
2676 | return $json;
2677 | }
2678 | return false;
2679 | }
2680 |
2681 | /**
2682 | * 获取颜色列表
2683 | * 获得卡券的最新颜色列表,用于创建卡券
2684 | * @return boolean|array 返回数组请参看 微信卡券接口文档 的json格式
2685 | */
2686 | public function getCardColors() {
2687 | if (!$this->access_token && !$this->checkAuth()) return false;
2688 | $result = $this->http_get(self::API_BASE_URL_PREFIX . self::CARD_GETCOLORS . 'access_token=' . $this->access_token);
2689 | if ($result) {
2690 | $json = json_decode($result, true);
2691 | if (!$json || !empty($json['errcode'])) {
2692 | $this->errCode = $json['errcode'];
2693 | $this->errMsg = $json['errmsg'];
2694 | return false;
2695 | }
2696 | return $json;
2697 | }
2698 | return false;
2699 | }
2700 |
2701 | /**
2702 | * 拉取门店列表
2703 | * 获取在公众平台上申请创建的门店列表
2704 | * @param int $offset 开始拉取的偏移,默认为0从头开始
2705 | * @param int $count 拉取的数量,默认为0拉取全部
2706 | * @return boolean|array 返回数组请参看 微信卡券接口文档 的json格式
2707 | */
2708 | public function getCardLocations($offset=0,$count=0) {
2709 | $data=array(
2710 | 'offset'=>$offset,
2711 | 'count'=>$count
2712 | );
2713 | if (!$this->access_token && !$this->checkAuth()) return false;
2714 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data));
2715 | if ($result) {
2716 | $json = json_decode($result, true);
2717 | if (!$json || !empty($json['errcode'])) {
2718 | $this->errCode = $json['errcode'];
2719 | $this->errMsg = $json['errmsg'];
2720 | return false;
2721 | }
2722 | return $json;
2723 | }
2724 | return false;
2725 | }
2726 |
2727 | /**
2728 | * 批量导入门店信息
2729 | * @tutorial 返回插入的门店id列表,以逗号分隔。如果有插入失败的,则为-1,请自行核查是哪个插入失败
2730 | * @param array $data 数组形式的json数据,由于内容较多,具体内容格式请查看 微信卡券接口文档
2731 | * @return boolean|string 成功返回插入的门店id列表
2732 | */
2733 | public function addCardLocations($data) {
2734 | if (!$this->access_token && !$this->checkAuth()) return false;
2735 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHADD . 'access_token=' . $this->access_token, self::json_encode($data));
2736 | if ($result) {
2737 | $json = json_decode($result, true);
2738 | if (!$json || !empty($json['errcode'])) {
2739 | $this->errCode = $json['errcode'];
2740 | $this->errMsg = $json['errmsg'];
2741 | return false;
2742 | }
2743 | return $json;
2744 | }
2745 | return false;
2746 | }
2747 |
2748 | /**
2749 | * 生成卡券二维码
2750 | * 成功则直接返回ticket值,可以用 getQRUrl($ticket) 换取二维码url
2751 | *
2752 | * @param string $cardid 卡券ID 必须
2753 | * @param string $code 指定卡券 code 码,只能被领一次。use_custom_code 字段为 true 的卡券必须填写,非自定义 code 不必填写。
2754 | * @param string $openid 指定领取者的 openid,只有该用户能领取。bind_openid 字段为 true 的卡券必须填写,非自定义 openid 不必填写。
2755 | * @param int $expire_seconds 指定二维码的有效时间,范围是 60 ~ 1800 秒。不填默认为永久有效。
2756 | * @param boolean $is_unique_code 指定下发二维码,生成的二维码随机分配一个 code,领取后不可再次扫描。填写 true 或 false。默认 false。
2757 | * @param string $balance 红包余额,以分为单位。红包类型必填(LUCKY_MONEY),其他卡券类型不填。
2758 | * @return boolean|string
2759 | */
2760 | public function createCardQrcode($card_id,$code='',$openid='',$expire_seconds=0,$is_unique_code=false,$balance='') {
2761 | $card = array(
2762 | 'card_id' => $card_id
2763 | );
2764 | if ($code)
2765 | $card['code'] = $code;
2766 | if ($openid)
2767 | $card['openid'] = $openid;
2768 | if ($expire_seconds)
2769 | $card['expire_seconds'] = $expire_seconds;
2770 | if ($is_unique_code)
2771 | $card['is_unique_code'] = $is_unique_code;
2772 | if ($balance)
2773 | $card['balance'] = $balance;
2774 | $data = array(
2775 | 'action_name' => "QR_CARD",
2776 | 'action_info' => array('card' => $card)
2777 | );
2778 | if (!$this->access_token && !$this->checkAuth()) return false;
2779 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_QRCODE_CREATE . 'access_token=' . $this->access_token, self::json_encode($data));
2780 | if ($result) {
2781 | $json = json_decode($result, true);
2782 | if (!$json || !empty($json['errcode'])) {
2783 | $this->errCode = $json['errcode'];
2784 | $this->errMsg = $json['errmsg'];
2785 | return false;
2786 | }
2787 | return $json;
2788 | }
2789 | return false;
2790 | }
2791 |
2792 | /**
2793 | * 消耗 code
2794 | * 自定义 code(use_custom_code 为 true)的优惠券,在 code 被核销时,必须调用此接口。
2795 | *
2796 | * @param string $code 要消耗的序列号
2797 | * @param string $card_id 要消耗序列号所述的 card_id,创建卡券时use_custom_code 填写 true 时必填。
2798 | * @return boolean|array
2799 | * {
2800 | * "errcode":0,
2801 | * "errmsg":"ok",
2802 | * "card":{"card_id":"pFS7Fjg8kV1IdDz01r4SQwMkuCKc"},
2803 | * "openid":"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA"
2804 | * }
2805 | */
2806 | public function consumeCardCode($code,$card_id='') {
2807 | $data = array('code' => $code);
2808 | if ($card_id)
2809 | $data['card_id'] = $card_id;
2810 | if (!$this->access_token && !$this->checkAuth()) return false;
2811 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_CONSUME . 'access_token=' . $this->access_token, self::json_encode($data));
2812 | if ($result) {
2813 | $json = json_decode($result, true);
2814 | if (!$json || !empty($json['errcode'])) {
2815 | $this->errCode = $json['errcode'];
2816 | $this->errMsg = $json['errmsg'];
2817 | return false;
2818 | }
2819 | return $json;
2820 | }
2821 | return false;
2822 | }
2823 |
2824 | /**
2825 | * code 解码
2826 | * @param string $encrypt_code 通过 choose_card_info 获取的加密字符串
2827 | * @return boolean|array
2828 | * {
2829 | * "errcode":0,
2830 | * "errmsg":"ok",
2831 | * "code":"751234212312"
2832 | * }
2833 | */
2834 | public function decryptCardCode($encrypt_code) {
2835 | $data = array(
2836 | 'encrypt_code' => $encrypt_code,
2837 | );
2838 | if (!$this->access_token && !$this->checkAuth()) return false;
2839 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_DECRYPT . 'access_token=' . $this->access_token, self::json_encode($data));
2840 | if ($result) {
2841 | $json = json_decode($result, true);
2842 | if (!$json || !empty($json['errcode'])) {
2843 | $this->errCode = $json['errcode'];
2844 | $this->errMsg = $json['errmsg'];
2845 | return false;
2846 | }
2847 | return $json;
2848 | }
2849 | return false;
2850 | }
2851 |
2852 | /**
2853 | * 查询 code 的有效性(非自定义 code)
2854 | * @param string $code
2855 | * @return boolean|array
2856 | * {
2857 | * "errcode":0,
2858 | * "errmsg":"ok",
2859 | * "openid":"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA", //用户 openid
2860 | * "card":{
2861 | * "card_id":"pFS7Fjg8kV1IdDz01r4SQwMkuCKc",
2862 | * "begin_time": 1404205036, //起始使用时间
2863 | * "end_time": 1404205036, //结束时间
2864 | * }
2865 | * }
2866 | */
2867 | public function checkCardCode($code) {
2868 | $data = array(
2869 | 'code' => $code,
2870 | );
2871 | if (!$this->access_token && !$this->checkAuth()) return false;
2872 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_GET . 'access_token=' . $this->access_token, self::json_encode($data));
2873 | if ($result) {
2874 | $json = json_decode($result, true);
2875 | if (!$json || !empty($json['errcode'])) {
2876 | $this->errCode = $json['errcode'];
2877 | $this->errMsg = $json['errmsg'];
2878 | return false;
2879 | }
2880 | return $json;
2881 | }
2882 | return false;
2883 | }
2884 |
2885 | /**
2886 | * 批量查询卡列表
2887 | * @param $offset 开始拉取的偏移,默认为0从头开始
2888 | * @param $count 需要查询的卡片的数量(数量最大50,默认50)
2889 | * @return boolean|array
2890 | * {
2891 | * "errcode":0,
2892 | * "errmsg":"ok",
2893 | * "card_id_list":["ph_gmt7cUVrlRk8swPwx7aDyF-pg"], //卡 id 列表
2894 | * "total_num":1 //该商户名下 card_id 总数
2895 | * }
2896 | */
2897 | public function getCardIdList($offset=0,$count=50) {
2898 | if ($count>50)
2899 | $count = 50;
2900 | $data = array(
2901 | 'offset' => $offset,
2902 | 'count' => $count,
2903 | );
2904 | if (!$this->access_token && !$this->checkAuth()) return false;
2905 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data));
2906 | if ($result) {
2907 | $json = json_decode($result, true);
2908 | if (!$json || !empty($json['errcode'])) {
2909 | $this->errCode = $json['errcode'];
2910 | $this->errMsg = $json['errmsg'];
2911 | return false;
2912 | }
2913 | return $json;
2914 | }
2915 | return false;
2916 | }
2917 |
2918 | /**
2919 | * 更改 code
2920 | * 为确保转赠后的安全性,微信允许自定义code的商户对已下发的code进行更改。
2921 | * 注:为避免用户疑惑,建议仅在发生转赠行为后(发生转赠后,微信会通过事件推送的方式告知商户被转赠的卡券code)对用户的code进行更改。
2922 | * @param string $code 卡券的 code 编码
2923 | * @param string $card_id 卡券 ID
2924 | * @param string $new_code 新的卡券 code 编码
2925 | * @return boolean
2926 | */
2927 | public function updateCardCode($code,$card_id,$new_code) {
2928 | $data = array(
2929 | 'code' => $code,
2930 | 'card_id' => $card_id,
2931 | 'new_code' => $new_code,
2932 | );
2933 | if (!$this->access_token && !$this->checkAuth()) return false;
2934 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));
2935 | if ($result) {
2936 | $json = json_decode($result, true);
2937 | if (!$json || !empty($json['errcode'])) {
2938 | $this->errCode = $json['errcode'];
2939 | $this->errMsg = $json['errmsg'];
2940 | return false;
2941 | }
2942 | return true;
2943 | }
2944 | return false;
2945 | }
2946 |
2947 | /**
2948 | * 设置卡券失效
2949 | * 设置卡券失效的操作不可逆
2950 | * @param string $code 需要设置为失效的 code
2951 | * @param string $card_id 自定义 code 的卡券必填。非自定义 code 的卡券不填。
2952 | * @return boolean
2953 | */
2954 | public function unavailableCardCode($code,$card_id='') {
2955 | $data = array(
2956 | 'code' => $code,
2957 | );
2958 | if ($card_id)
2959 | $data['card_id'] = $card_id;
2960 | if (!$this->access_token && !$this->checkAuth()) return false;
2961 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UNAVAILABLE . 'access_token=' . $this->access_token, self::json_encode($data));
2962 | if ($result) {
2963 | $json = json_decode($result, true);
2964 | if (!$json || !empty($json['errcode'])) {
2965 | $this->errCode = $json['errcode'];
2966 | $this->errMsg = $json['errmsg'];
2967 | return false;
2968 | }
2969 | return true;
2970 | }
2971 | return false;
2972 | }
2973 |
2974 | /**
2975 | * 库存修改
2976 | * @param string $data
2977 | * @return boolean
2978 | */
2979 | public function modifyCardStock($data) {
2980 | if (!$this->access_token && !$this->checkAuth()) return false;
2981 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MODIFY_STOCK . 'access_token=' . $this->access_token, self::json_encode($data));
2982 | if ($result) {
2983 | $json = json_decode($result, true);
2984 | if (!$json || !empty($json['errcode'])) {
2985 | $this->errCode = $json['errcode'];
2986 | $this->errMsg = $json['errmsg'];
2987 | return false;
2988 | }
2989 | return true;
2990 | }
2991 | return false;
2992 | }
2993 |
2994 | /**
2995 | * 激活/绑定会员卡
2996 | * @param string $data 具体结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节
2997 | * @return boolean
2998 | */
2999 | public function activateMemberCard($data) {
3000 | if (!$this->access_token && !$this->checkAuth()) return false;
3001 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_ACTIVATE . 'access_token=' . $this->access_token, self::json_encode($data));
3002 | if ($result) {
3003 | $json = json_decode($result, true);
3004 | if (!$json || !empty($json['errcode'])) {
3005 | $this->errCode = $json['errcode'];
3006 | $this->errMsg = $json['errmsg'];
3007 | return false;
3008 | }
3009 | return true;
3010 | }
3011 | return false;
3012 | }
3013 |
3014 | /**
3015 | * 会员卡交易
3016 | * 会员卡交易后每次积分及余额变更需通过接口通知微信,便于后续消息通知及其他扩展功能。
3017 | * @param string $data 具体结构请参看卡券开发文档(6.1.2 会员卡交易)章节
3018 | * @return boolean|array
3019 | */
3020 | public function updateMemberCard($data) {
3021 | if (!$this->access_token && !$this->checkAuth()) return false;
3022 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_UPDATEUSER . 'access_token=' . $this->access_token, self::json_encode($data));
3023 | if ($result) {
3024 | $json = json_decode($result, true);
3025 | if (!$json || !empty($json['errcode'])) {
3026 | $this->errCode = $json['errcode'];
3027 | $this->errMsg = $json['errmsg'];
3028 | return false;
3029 | }
3030 | return $json;
3031 | }
3032 | return false;
3033 | }
3034 |
3035 | /**
3036 | * 更新红包金额
3037 | * @param string $code 红包的序列号
3038 | * @param $balance 红包余额
3039 | * @param string $card_id 自定义 code 的卡券必填。非自定义 code 可不填。
3040 | * @return boolean|array
3041 | */
3042 | public function updateLuckyMoney($code,$balance,$card_id='') {
3043 | $data = array(
3044 | 'code' => $code,
3045 | 'balance' => $balance
3046 | );
3047 | if ($card_id)
3048 | $data['card_id'] = $card_id;
3049 | if (!$this->access_token && !$this->checkAuth()) return false;
3050 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LUCKYMONEY_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));
3051 | if ($result) {
3052 | $json = json_decode($result, true);
3053 | if (!$json || !empty($json['errcode'])) {
3054 | $this->errCode = $json['errcode'];
3055 | $this->errMsg = $json['errmsg'];
3056 | return false;
3057 | }
3058 | return true;
3059 | }
3060 | return false;
3061 | }
3062 |
3063 | /**
3064 | * 设置卡券测试白名单
3065 | * @param string $openid 测试的 openid 列表
3066 | * @param string $user 测试的微信号列表
3067 | * @return boolean
3068 | */
3069 | public function setCardTestWhiteList($openid=array(),$user=array()) {
3070 | $data = array();
3071 | if (count($openid) > 0)
3072 | $data['openid'] = $openid;
3073 | if (count($user) > 0)
3074 | $data['username'] = $user;
3075 | if (!$this->access_token && !$this->checkAuth()) return false;
3076 | $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_TESTWHILELIST_SET . 'access_token=' . $this->access_token, self::json_encode($data));
3077 | if ($result) {
3078 | $json = json_decode($result, true);
3079 | if (!$json || !empty($json['errcode'])) {
3080 | $this->errCode = $json['errcode'];
3081 | $this->errMsg = $json['errmsg'];
3082 | return false;
3083 | }
3084 | return true;
3085 | }
3086 | return false;
3087 | }
3088 |
3089 | }
3090 | /**
3091 | * PKCS7Encoder class
3092 | *
3093 | * 提供基于PKCS7算法的加解密接口.
3094 | */
3095 | class PKCS7Encoder
3096 | {
3097 | public static $block_size = 32;
3098 |
3099 | /**
3100 | * 对需要加密的明文进行填充补位
3101 | * @param $text 需要进行填充补位操作的明文
3102 | * @return 补齐明文字符串
3103 | */
3104 | function encode($text)
3105 | {
3106 | $block_size = PKCS7Encoder::$block_size;
3107 | $text_length = strlen($text);
3108 | //计算需要填充的位数
3109 | $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
3110 | if ($amount_to_pad == 0) {
3111 | $amount_to_pad = PKCS7Encoder::block_size;
3112 | }
3113 | //获得补位所用的字符
3114 | $pad_chr = chr($amount_to_pad);
3115 | $tmp = "";
3116 | for ($index = 0; $index < $amount_to_pad; $index++) {
3117 | $tmp .= $pad_chr;
3118 | }
3119 | return $text . $tmp;
3120 | }
3121 |
3122 | /**
3123 | * 对解密后的明文进行补位删除
3124 | * @param decrypted 解密后的明文
3125 | * @return 删除填充补位后的明文
3126 | */
3127 | function decode($text)
3128 | {
3129 |
3130 | $pad = ord(substr($text, -1));
3131 | if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {
3132 | $pad = 0;
3133 | }
3134 | return substr($text, 0, (strlen($text) - $pad));
3135 | }
3136 |
3137 | }
3138 |
3139 | /**
3140 | * Prpcrypt class
3141 | *
3142 | * 提供接收和推送给公众平台消息的加解密接口.
3143 | */
3144 | class Prpcrypt
3145 | {
3146 | public $key;
3147 |
3148 | function Prpcrypt($k)
3149 | {
3150 | $this->key = base64_decode($k . "=");
3151 | }
3152 |
3153 | /**
3154 | * 对明文进行加密
3155 | * @param string $text 需要加密的明文
3156 | * @return string 加密后的密文
3157 | */
3158 | public function encrypt($text, $appid)
3159 | {
3160 |
3161 | try {
3162 | //获得16位随机字符串,填充到明文之前
3163 | $random = $this->getRandomStr();//"aaaabbbbccccdddd";
3164 | $text = $random . pack("N", strlen($text)) . $text . $appid;
3165 | // 网络字节序
3166 | $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
3167 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
3168 | $iv = substr($this->key, 0, 16);
3169 | //使用自定义的填充方式对明文进行补位填充
3170 | $pkc_encoder = new PKCS7Encoder;
3171 | $text = $pkc_encoder->encode($text);
3172 | mcrypt_generic_init($module, $this->key, $iv);
3173 | //加密
3174 | $encrypted = mcrypt_generic($module, $text);
3175 | mcrypt_generic_deinit($module);
3176 | mcrypt_module_close($module);
3177 |
3178 | // print(base64_encode($encrypted));
3179 | //使用BASE64对加密后的字符串进行编码
3180 | return array(ErrorCode::$OK, base64_encode($encrypted));
3181 | } catch (Exception $e) {
3182 | //print $e;
3183 | return array(ErrorCode::$EncryptAESError, null);
3184 | }
3185 | }
3186 |
3187 | /**
3188 | * 对密文进行解密
3189 | * @param string $encrypted 需要解密的密文
3190 | * @return string 解密得到的明文
3191 | */
3192 | public function decrypt($encrypted, $appid)
3193 | {
3194 |
3195 | try {
3196 | //使用BASE64对需要解密的字符串进行解码
3197 | $ciphertext_dec = base64_decode($encrypted);
3198 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
3199 | $iv = substr($this->key, 0, 16);
3200 | mcrypt_generic_init($module, $this->key, $iv);
3201 | //解密
3202 | $decrypted = mdecrypt_generic($module, $ciphertext_dec);
3203 | mcrypt_generic_deinit($module);
3204 | mcrypt_module_close($module);
3205 | } catch (Exception $e) {
3206 | return array(ErrorCode::$DecryptAESError, null);
3207 | }
3208 |
3209 |
3210 | try {
3211 | //去除补位字符
3212 | $pkc_encoder = new PKCS7Encoder;
3213 | $result = $pkc_encoder->decode($decrypted);
3214 | //去除16位随机字符串,网络字节序和AppId
3215 | if (strlen($result) < 16)
3216 | return "";
3217 | $content = substr($result, 16, strlen($result));
3218 | $len_list = unpack("N", substr($content, 0, 4));
3219 | $xml_len = $len_list[1];
3220 | $xml_content = substr($content, 4, $xml_len);
3221 | $from_appid = substr($content, $xml_len + 4);
3222 | if (!$appid)
3223 | $appid = $from_appid;
3224 | //如果传入的appid是空的,则认为是订阅号,使用数据中提取出来的appid
3225 | } catch (Exception $e) {
3226 | //print $e;
3227 | return array(ErrorCode::$IllegalBuffer, null);
3228 | }
3229 | if ($from_appid != $appid)
3230 | return array(ErrorCode::$ValidateAppidError, null);
3231 | //不注释上边两行,避免传入appid是错误的情况
3232 | return array(0, $xml_content, $from_appid); //增加appid,为了解决后面加密回复消息的时候没有appid的订阅号会无法回复
3233 |
3234 | }
3235 |
3236 |
3237 | /**
3238 | * 随机生成16位字符串
3239 | * @return string 生成的字符串
3240 | */
3241 | function getRandomStr()
3242 | {
3243 |
3244 | $str = "";
3245 | $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
3246 | $max = strlen($str_pol) - 1;
3247 | for ($i = 0; $i < 16; $i++) {
3248 | $str .= $str_pol[mt_rand(0, $max)];
3249 | }
3250 | return $str;
3251 | }
3252 |
3253 | }
3254 |
3255 | /**
3256 | * error code
3257 | * 仅用作类内部使用,不用于官方API接口的errCode码
3258 | */
3259 | class ErrorCode
3260 | {
3261 | public static $OK = 0;
3262 | public static $ValidateSignatureError = 40001;
3263 | public static $ParseXmlError = 40002;
3264 | public static $ComputeSignatureError = 40003;
3265 | public static $IllegalAesKey = 40004;
3266 | public static $ValidateAppidError = 40005;
3267 | public static $EncryptAESError = 40006;
3268 | public static $DecryptAESError = 40007;
3269 | public static $IllegalBuffer = 40008;
3270 | public static $EncodeBase64Error = 40009;
3271 | public static $DecodeBase64Error = 40010;
3272 | public static $GenReturnXmlError = 40011;
3273 | public static $errCode=array(
3274 | '0' => '处理成功',
3275 | '40001' => '校验签名失败',
3276 | '40002' => '解析xml失败',
3277 | '40003' => '计算签名失败',
3278 | '40004' => '不合法的AESKey',
3279 | '40005' => '校验AppID失败',
3280 | '40006' => 'AES加密失败',
3281 | '40007' => 'AES解密失败',
3282 | '40008' => '公众平台发送的xml不合法',
3283 | '40009' => 'Base64编码失败',
3284 | '40010' => 'Base64解码失败',
3285 | '40011' => '公众帐号生成回包xml失败'
3286 | );
3287 | public static function getErrText($err) {
3288 | if (isset(self::$errCode[$err])) {
3289 | return self::$errCode[$err];
3290 | }else {
3291 | return false;
3292 | };
3293 | }
3294 | }
3295 |
--------------------------------------------------------------------------------
/src/Atan/Wechat/WechatServiceProvider.php:
--------------------------------------------------------------------------------
1 | package('atan/wechat');
22 | }
23 |
24 | /**
25 | * Register the service provider.
26 | *
27 | * @return void
28 | */
29 | public function register()
30 | {
31 | $this->app->bind('wechat', 'Atan\Wechat\Wechat');
32 | }
33 |
34 | /**
35 | * Get the services provided by the provider.
36 | *
37 | * @return array
38 | */
39 | public function provides()
40 | {
41 | return array('wechat');
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/config/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanshiqi/laravel-wechat/f1755966efe8c3347c090ea245dd06f7961e177b/src/config/.gitkeep
--------------------------------------------------------------------------------
/src/config/wechat.php:
--------------------------------------------------------------------------------
1 | array(
5 | 'token' => 'token',
6 | 'encodingaeskey' => 'encodingaeskey',
7 | 'appid' => 'appid',
8 | 'appsecret' => 'appsecret',
9 | 'debug' => false,
10 | 'logcallback' => false,
11 | )
12 | );
13 |
--------------------------------------------------------------------------------
/tests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanshiqi/laravel-wechat/f1755966efe8c3347c090ea245dd06f7961e177b/tests/.gitkeep
--------------------------------------------------------------------------------