Repository: dodgepudding/wechat-php-sdk Branch: master Commit: c18ee0176392 Files: 44 Total size: 508.9 KB Directory structure: gitextract_4iqk6cac/ ├── .gitignore ├── Codeigniter/ │ └── CI_Wechat.php ├── README.md ├── Thinkphp/ │ ├── EasyWechat.class.php │ ├── JsSdkPay.class.php │ ├── TPWechat.class.php │ └── Wxauth.class.php ├── composer.json ├── demo.php ├── errCode.php ├── old_version/ │ ├── Thinkphp/ │ │ ├── Snoopy.class.php │ │ ├── Wechatauth.class.php │ │ ├── Wechatext.class.php │ │ └── Wechatpay.class.php │ ├── snoopy.class.php │ ├── test/ │ │ ├── test2.php │ │ ├── test3.php │ │ └── weshare.html │ ├── wechat.js │ ├── wechatauth.class.php │ ├── wechatext.class.php │ └── wechatpay.class.php ├── qyerrCode.php ├── qywechat.class.php ├── test/ │ ├── auth.php │ ├── getQRCode_test.php │ ├── jsapi/ │ │ ├── jsapi-demo-6.1.js │ │ ├── jsapi_demo.php │ │ └── style.css │ ├── merchanttest.php │ ├── qydemo.php │ ├── test-upload-shake-around-media.php │ └── test1.php ├── wechat.class.php └── wiki/ ├── API接口错误码.md ├── Home.md ├── README.md ├── 为开发框架进行适配.md ├── 企业号API类库.md ├── 内嵌JS.md ├── 官方API类库.md ├── 授权登陆类库.md ├── 旧版微信支付V2接口类库.md └── 非官方扩展类库.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /nbproject/private/ .buildpath /pages/ /my/ ================================================ FILE: Codeigniter/CI_Wechat.php ================================================ load->library('CI_Wechat'); * $this->ci_wechat->valid(); * ... * */ require_once(dirname(__FILE__) . '/wechat-php-sdk/wechat.class.php'); class CI_Wechat extends Wechat { protected $_CI; public function __construct() { $this->_CI =& get_instance(); $this->_CI->config->load('wechat'); $options = $this->_CI->config->item('wechat'); $this->_CI->load->driver('cache', array('adapter' => 'apc', 'backup' => 'file')); parent::__construct($options); } /** * 重载设置缓存 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename, $value, $expired) { return $this->_CI->cache->save($cachename, $value, $expired); } /** * 重载获取缓存 * @param string $cachename * @return mixed */ protected function getCache($cachename) { return $this->_CI->cache->get($cachename); } /** * 重载清除缓存 * @param string $cachename * @return boolean */ protected function removeCache($cachename) { return $this->_CI->cache->delete($cachename); } } /* End of file CI_Wechat.php */ /* Location: ./application/libraries/CI_Wechat.php */ ================================================ FILE: README.md ================================================ wechat-php-sdk ============== 微信公众平台php开发包,细化各项接口操作,支持链式调用,欢迎Fork此项目 weixin developer SDK. 项目地址:**https://github.com/dodgepudding/wechat-php-sdk** 项目blog:**http://binsee.github.io/wechat-php-sdk** ## 使用详解 使用前需先打开微信帐号的开发模式,详细步骤请查看微信公众平台接口使用说明: 微信公众平台: http://mp.weixin.qq.com/wiki/ 微信企业平台: http://qydev.weixin.qq.com/wiki/ 微信支付接入文档: https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN 微信多客服:http://dkf.qq.com ## 目录 > **[wechat.class.php 官方API类库](#user-content-1-wechatclassphp-官方api类库)** > **[qywechat.class.php 企业号API类库](#user-content-6-qywechatclassphp-企业号api类库)** > **[errCode.php|qyerrCode.php 全局返回码类](#user-content-5-errcodephp-全局返回码类)** > **[old_version/wechatpay.class.php 旧版微信支付V2接口类库](#user-content-7-wechatpayclassphp-旧版微信支付V2接口类库)** > ~~**[old_version/wechatext.class.php 非官方扩展API(停止维护)](#user-content-2-wechatextclassphp-非官方扩展api)**~~ > ~~**[old_version/wechatauth.class.php 授权登陆(停止维护)](#user-content-3-wechatauthclassphp-授权登陆)**~~ > ~~**[old_version/wechat.js 内嵌JS(已废弃)](#user-content-4-wechatjs-内嵌js)**~~ > **[为开发框架进行适配](#user-content-为开发框架进行适配)** > **[调用示例](#user-content-调用示例)** ---------- ## 1. wechat.class.php 官方API类库 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; ### 主要功能 - 接入验证 **(初级权限)** - 自动回复(文本、图片、语音、视频、音乐、图文) **(初级权限)** - 菜单操作(查询、创建、删除) **(菜单权限)** - 客服消息(文本、图片、语音、视频、音乐、图文) **(认证权限)** - 二维码(创建临时、永久二维码,获取二维码URL) **(服务号、认证权限)** - 长链接转短链接接口 **(服务号、认证权限)** - 分组操作(查询、创建、修改、移动用户到分组) **(认证权限)** - 网页授权(基本授权,用户信息授权) **(服务号、认证权限)** - 用户信息(查询用户基本信息、获取关注者列表) **(认证权限)** - 多客服功能(客服管理、获取客服记录、客服会话管理) **(认证权限)** - 媒体文件(上传、获取) **(认证权限)** - 高级群发 **(认证权限)** - 模板消息(设置所属行业、添加模板、发送模板消息) **(服务号、认证权限)** - 卡券管理(创建、修改、删除、发放、门店管理等) **(认证权限)** - 语义理解 **(服务号、认证权限)** - 获取微信服务器IP列表 **(初级权限)** - 微信JSAPI授权(获取ticket、获取签名) **(初级权限)** - 数据统计(用户、图文、消息、接口分析数据) **(认证权限)** > 备注: > 初级权限:基本权限,任何正常的公众号都有此权限 > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 > 支付权限:仅认证后的服务号可以申请此权限 ### 初始化动作 ```php $options = array( 'token'=>'tokenaccesskey', //填写你设定的key 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ### 被动接口方法: * valid() 验证连接,被动接口处于加密模式时必须调用 * * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 * getRevData() 返回微信服务器发来的信息(数组) * getRevFrom() 返回消息发送者的userid * getRevTo() 返回消息接收者的id(即公众号id) * getRevType() 返回接收消息的类型 * getRevID() 返回消息id * getRevCtime() 返回消息发送时间 * getRevContent() 返回消息内容正文或语音识别结果(文本型) * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} * getRevLink() 接收消息链接(链接型信息) 返回数组{'url'=>'','title'=>'','description'=>''} * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} * getRevTicket() 返回接收TICKET(扫描带参数二维码,关注或SCAN事件) 返回二维码的ticket值 * getRevSceneId() 返回二维码的场景值(扫描带参数二维码的关注事件) 返回二维码的参数值 * getRevTplMsgID() 返回主动推送的消息ID(群发或模板消息事件) 返回MsgID值 * getRevStatus() 返回模板消息发送状态(模板消息事件) 返回文本:success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败(非用户拒绝)) * getRevResult() 返回群发或模板消息发送结果(群发或模板消息事件) 返回数组,内容依事件类型而不同,参考开发文档中群发、模板消息推送事件 * getRevKFCreate() 返回多客服-接入会话的客服账号(多客服-接入会话事件) 返回文本型 * getRevKFClose() 返回多客服-处理会话的客服账号(多客服-接入会话事件) 返回文本型 * getRevKFSwitch() 返回多客服-转接会话信息(多客服-转接会话事件) 返回数组 {'FromKfAccount' => '','ToKfAccount' => ''} * getRevCardPass() 返回卡券-审核通过的卡券ID(卡券-卡券审核事件) 返回文本型 * getRevCardGet() 返回卡券-用户领取卡券的相关信息(卡券-领取卡券事件) 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''} * getRevCardDel() 返回卡券-用户删除卡券的相关信息(卡券-删除卡券事件) 返回数组{'CardId' => '','UserCardCode' => ''} * * text($text) 设置文本型消息,参数:文本内容 * image($mediaid) 设置图片型消息,参数:图片的media_id * voice($mediaid) 设置语音型消息,参数:语音的media_id * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 * music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐,参数:音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) * transfer_customer_service($customer_account = '') 转接多客服,如不指定客服可不提供参数,参数:指定客服的账号 * reply() 将以上已经设置好的消息,回复给微信服务器 ### 预定义常量列表: ```php ////消息类型,使用实例调用getRevType()方法取得 const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; ////事件类型,使用实例调用getRevEvent()方法取得 const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_SCAN = 'SCAN'; //扫描带参数二维码 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 const EVENT_KF_SEESION_CREATE = 'kfcreatesession'; //多客服 - 接入会话 const EVENT_KF_SEESION_CLOSE = 'kfclosesession'; //多客服 - 关闭会话 const EVENT_KF_SEESION_SWITCH = 'kfswitchsession'; //多客服 - 转接会话 const EVENT_CARD_PASS = 'card_pass_check'; //卡券 - 审核通过 const EVENT_CARD_NOTPASS = 'card_not_pass_check'; //卡券 - 审核未通过 const EVENT_CARD_USER_GET = 'user_get_card'; //卡券 - 用户领取卡券 const EVENT_CARD_USER_DEL = 'user_del_card'; //卡券 - 用户删除卡券 ``` ### 主动接口方法: * checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌 * resetAuth($appid='') 删除验证数据 * resetJsTicket($appid='') 删除JSAPI授权TICKET * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 * createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)** * getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') * getMenu() 获取菜单 * deleteMenu() 删除菜单 * uploadMedia($data, $type) 上传临时素材,有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) * getMedia($media_id,$is_video=false) 获取临时素材(含接收到的音频、视频媒体文件) * uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材,可以在公众平台官网素材管理模块中看到 * uploadForeverArticles($data) 上传永久图文素材 * updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用) * getForeverMedia($media_id,$is_video=false) 获取永久素材 * delForeverMedia($media_id) 删除永久素材 * getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用) * getForeverCount() 获取永久素材总数 * uploadMpVideo($data) 上传视频素材,当需要群发视频时,必须使用此方法得到的MediaID,否则无法显示 * uploadArticles($data) 上传图文消息素材 * sendMassMessage($data) 高级群发消息 * sendGroupMassMessage($data) 高级群发消息(全体或分组群发) * deleteMassMessage($msg_id) 删除群发图文消息 * previewMassMessage($data) 预览群发消息 * queryMassMessage($msg_id) 查询群发消息发送状态 * getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 * getQRUrl($ticket) 获取二维码图片地址 * getShortUrl($long_url) 长链接转短链接接口 * getUserList($next_openid) 批量获取关注用户列表 * getUserInfo($openid) 获取关注者详细信息 * updateUserRemark($openid,$remark) 设置用户备注名 * getGroup() 获取用户分组列表 * getUserGroup($openid) 获取用户所在分组 * createGroup($name) 新增自定分组 * updateGroup($groupid,$name) 更改分组名称 * updateGroupMembers($groupid,$openid) 移动用户分组 * batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 * sendCustomMessage($data) 发送客服消息 * getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址 * getOauthAccessToken() 通过回调的code获取网页授权access_token * getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期 * getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料 * getOauthAuth($access_token,$openid) 检验授权凭证access_token是否有效 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * setTMIndustry($id1,$id2='') 模板消息,设置所属行业 * addTemplateMessage($tpl_id) 模板消息,添加消息模板 * sendTemplateMessage($data) 发送模板消息 * * 多客服接口: * getCustomServiceMessage($data) 获取多客服会话记录 * transfer_customer_service($customer_account) 转发多客服消息 * getCustomServiceKFlist() 获取多客服客服基本信息 * getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息 * createKFSession($openid,$kf_account,$text='') 创建指定多客服会话 * closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话 * getKFSession($openid) 获取用户会话状态 * getKFSessionlist($kf_account) 获取指定客服的会话列表 * getKFSessionWait() 获取未接入会话列表 * addKFAccount($account,$nickname,$password) 添加客服账号 * updateKFAccount($account,$nickname,$password) 修改客服账号信息 * deleteKFAccount($account) 删除客服账号 * setKFHeadImg($account,$imgfile) 上传客服头像 * * querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region="") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)** * getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义 > 获取统计数据方法 参数定义 > | 数据分类 | $type值(字符串) | 数据子分类 | $subtype值(字符串) | 时间跨度(天) | | --------- | :-------: | --------- | :------: | ----: | | 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 | | 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 | | 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 | | 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 | | 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 | | 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 | | 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 | | 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 | | 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 | | 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 | | 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 | | 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 | | 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 | | 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 | 需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”(比如最大时间跨度为1时,`begin_date`和`end_date`的差值只能为0,才能小于1) * * 卡券接口: * createCard($data) 创建卡券 * updateCard($data) 修改卡券 * delCard($card_id) 删除卡券 * getCardInfo($card_id) 查询卡券详情 * getCardColors() 获取颜色列表 * getCardLocations() 拉取门店列表 * addCardLocations($data) 批量导入门店信息 * createCardQrcode($card_id) 生成卡券二维码 * consumeCardCode($code) 消耗 code * decryptCardCode($encrypt_code) code 解码 * checkCardCode($code) 获取 code 的有效性 * getCardIdList($data) 批量查询卡列表 * updateCardCode($code,$card_id,$new_code) 更改 code * unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)** * modifyCardStock($data) 库存修改 * activateMemberCard($data) 激活/绑定会员卡,参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节 * updateMemberCard($data) 会员卡交易,参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节 * updateLuckyMoney($code,$balance,$card_id='') 更新红包金额 * setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单 * * 摇一摇周边接口: * applyShakeAroundDevice($data) 申请设备ID * updateShakeAroundDevice($data) 编辑设备的备注信息 * searchShakeAroundDevice($data) 查询设备列表 * bindLocationShakeAroundDevice($device_id,$poi_id,$uuid='',$major=0,$minor=0) 配置设备与门店的关联关系 * bindPageShakeAroundDevice($device_id,$page_ids=array(),$bind=1,$append=1,$uuid='',$major=0,$minor=0) 配置设备与页面的关联关系 * uploadShakeAroundMedia($data) 上传在摇一摇页面展示的图片素材 * addShakeAroundPage($title,$description,$icon_url,$page_url,$comment='') 新增摇一摇出来的页面信息 * updateShakeAroundPage($page_id,$title,$description,$icon_url,$page_url,$comment='') 编辑摇一摇出来的页面信息 * searchShakeAroundPage($page_ids=array(),$begin=0,$count=1) 查询摇一摇已有的页面 * deleteShakeAroundPage($page_ids=array()) 删除摇一摇已有的页面,必须是未与设备关联的页面 * getShakeInfoShakeAroundUser($ticket) 获取摇周边的设备及用户信息 * deviceShakeAroundStatistics($device_id,$begin_date,$end_date,$uuid='',$major=0,$minor=0) 以设备为维度的数据统计接口 * pageShakeAroundStatistics($page_id,$begin_date,$end_date) 以页面为维度的数据统计接口 ## ~~2. wechatext.class.php 非官方扩展API~~ **此扩展类库已经不再更新,原因是官方对公众号开放了众多接口,此类库继续维护的意义不大** 非官方扩展API,需要配置公众平台账户和密码,能实现对已关注用户的点对点微信,此方式不保证长期有效。 类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取. 调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决. 程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持. ### 类主要方法: * send($id,$content) 向某用户id发送微信文字信息 * sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid * getUserList($page,$pagesize,$groupid) 获取用户信息 * getGroupList($page,$pagesize) 获取群组信息 * getNewsList($page,$pagesize) 获取图文信息列表 * uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图 * getFileList($type,$page,$pagesize) 获取素材库文件列表 * sendImage($id,$fid) 发送图片消息 * sendAudio($id,$fid) 发送音频消息 * sendVideo($id,$fid) 发送视频消息 * getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取 * getNewMsgNum($lastid) 获取从$lastid算起新消息的数目 * getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据 * getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数 * 消息返回结构: {"id":"消息id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容"} * getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据 * getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据 ## ~~3. wechatauth.class.php 授权登陆~~ **此扩展类库已经不再更新,原因是官方开放平台对网站应用开放的有授权登陆接口,更标准,更好用。请查看:[微信开放平台](http://open.weixin.qq.com)** 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子. ### 类主要方法: * get_login_code() 获取登陆授权码, 通过授权码才能获取二维码 * get_code_image($code='') 将上面获取的授权码转换为图片二维码 * verify_code() 鉴定是否登陆成功,返回200为最终授权成功. * get_login_info() 鉴定成功后调用此方法即可获取用户基本信息 * get_avatar($url) 获取用户头像图片数据 * logout() 注销登陆 ## ~~4. wechat.js 内嵌JS~~ **此JS脚本已经废弃不再更新,原因是官方在微信6.0.2版本开放了全新的JSAPI接口,更全面好用。请查看:[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)** ### 微信内嵌网页特殊功能js调用: * WeixinJS.hideOptionMenu() 隐藏右上角按钮 * WeixinJS.showOptionMenu() 显示右上角按钮 * WeixinJS.hideToolbar() 隐藏工具栏 * WeixinJS.showToolbar() 显示工具栏 * WeixinJS.getNetworkType() 获取网络状态 * WeixinJS.closeWindow() 关闭窗口 * WeixinJS.scanQRCode() 扫描二维码 * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 * WeixinJS.sendEmail(title,content) 发送邮件 * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 * WeixinJS.addContact(username) 添加微信账号 * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 * 通过定义全局变量dataForWeixin配置触发分享的内容: ```javascript var dataForWeixin={ appId:"", MsgImg:"消息图片路径", TLImg:"时间线图路径", url:"分享url路径", title:"标题", desc:"描述", fakeid:"", callback:function(){} }; ``` ## 5. errCode.php 全局返回码类 当调用API接口失败时,可以用此类来获取失败原因的中文说明。 注意:微信公众号引用`errCode.php`,企业号引用`qyerrCode.php`。 ### 使用方法: ```php include "errCode.php"; //或 qyerrCode.php $ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到 if ($ret) echo $ret; else echo "未找到对应的内容"; ``` ## 6. qywechat.class.php 企业号API类库 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; ### 主要功能 - 接入验证 - 自动回复(文本、图片、语音、视频、音乐、图文) - 菜单操作(查询、创建、删除) - 部门管理(创建、更新、删除、获取部门列表) - 成员管理(创建、更新、删除、获取成员信息,获取部门成员列表) - 标签管理(创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表) - 媒体文件管理(上传、获取) - 二次验证 - OAuth2(生成授权url、获取成员信息) - 获取企业微信服务器IP列表 - 微信JSAPI授权(获取ticket、获取签名) ### 初始化动作 ```php $options = array( 'token'=>'tokenaccesskey', //填写应用接口的Token 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 'agentid'=>'1', //应用的id 'debug'=>false, //调试开关 '_logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ### 被动接口方法: * valid() 验证连接,被动接口必须调用 * * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 * getRevData() 返回微信服务器发来的信息(数组) * getRevPostXml() 返回微信服务器发来的原始加密xml信息 * getRevFrom() 返回消息发送者的userid * getRevTo() 返回消息接收者的id(即公众号id,一般与等同appid一致) * getRevAgentID() 返回接收消息的应用id * getRevType() 返回接收消息的类型 * getRevID() 返回消息id * getRevCtime() 返回消息发送事件 * getRevContent() 返回消息内容正文(文本型消息) * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} * * text($text) 设置文本型消息,参数:文本内容 * image($mediaid) 设置图片型消息,参数:图片的media_id * voice($mediaid) 设置语音型消息,参数:语音的media_id * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 * image($mediaid) 设置图片型消息,参数:图片的media_id * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) * reply() 将已经设置好的消息,回复给微信服务器 ### 预定义常量列表: ```php ////消息类型,使用实例调用getRevType()方法取得 const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; //暂不支持 const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; //暂不支持 const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; ////事件类型,使用实例调用getRevEvent()方法取得 const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_ENTER_AGENT = 'enter_agent'; //用户进入应用 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 ``` ### 主动接口方法: * checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token` * resetAuth($appid='') 清除记录的ACCESS_TOKEN * resetJsTicket($appid='') 删除JSAPI授权TICKET * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id * getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id * deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id * uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) * getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id * getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') * createDepartment($data) 创建部门,参数: array("name"=>"邮箱产品组","parentid"=>"1","order" => "1") * updateDepartment($data) 更新部门,参数: array("id"=>"1","name"=>"邮箱产品组","parentid"=>"1","order" => "1") * deleteDepartment($id) 删除部门,参数:要删除的部门id * moveDepartment($data) 移动部门,参数:array("department_id" => "5","to_parentid" => "2","to_position" => "1") * getDepartment() 获取部门列表,返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列 * createUser($data) 创建成员,参数请看php文件内方法说明 * updateUser($data) 更新成员,参数请看php文件内方法说明 * deleteUser($userid) 删除成员,参数:员工UserID * deleteUsers($userids) 批量删除成员,参数:员工UserID数组 * getUserInfo($userid) 获取成员信息,参数:员工UserID * getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员,参数:部门id,是否递归获取子部门,获取类型。 > 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 * getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情,参数同上 * getUserId($code,$agentid) 根据code获取员工UserID与手机设备号,参数:Oauth2.0或者二次验证返回的code值,跳转链接时所在的企业应用ID * sendInvite($userid,$invite_tips='') 邀请成员关注 * createTag($data) 创建标签,参数:array("tagname" => "UI") * updateTag($data) 更新标签,参数:array("tagid" => "1","tagname" => "UI") * deleteTag($tagid) 删除标签,参数:标签TagID * getTag($tagid) 获取标签成员,参数:标签TagID * addTagUser($data) 增加标签成员,参数请看php文件内方法说明 * delTagUser($data) 删除标签成员,参数请看php文件内方法说明 * getTagList() 获取标签列表,返回标签数组 * sendMessage($data) 主动发送信息接口,参数请看php文件内方法说明 * authSucc($userid) 二次验证,参数: 员工UserID * getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url ## 7. wechatpay.class.php 旧版微信支付V2接口类库 旧版微信支付类库(微信支付V2),已移动至old_version目录下。 自2014年8月开始申请到的微信支付都是V3接口,据官方说V2的会陆续升级为V3接口,但时间及升级渠道未确认。 ### 主要功能 - 获取access_token **(初级权限)** - 调用地址组件 **(支付权限)** - 生成订单签名数据 **(支付权限)** - 订单成功回调 **(支付权限)** - 发货通知 **(支付权限)** - 支付订单查询 **(支付权限)** > 备注: > 初级权限:基本权限,任何正常的公众号都有此权限 > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 > 支付权限:仅认证后的服务号可以申请此权限 ### 初始化动作 ```php $options = array( 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 'partnerid'=>'88888888', //财付通商户身份标识,支付权限专用,没有可不填 'partnerkey'=>'', //财付通商户权限密钥Key,支付权限专用 'paysignkey'=>'' //商户签名密钥Key,支付权限专用 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ### 主动接口方法: * checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取,或手动指定access_token * resetAuth($appid='') 删除验证数据 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * createNativeUrl($productid) 生成原生支付url * 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字符串 * getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法 * checkOrderSignature($orderxml='') 回调通知签名验证 * sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知 * getPayOrder($out_trade_no) 查询订单信息 * setUserToken($user_token) 设置用户授权密钥 * getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名 ## 为开发框架进行适配 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 由于微信api需要缓存access_token与jsapi_ticket,而在不同框架下的缓存方式不同,所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。 需要各位在使用不同框架时再进行修改,但确实很麻烦,因为对结构进行了修改。 >取消了原先同步维护的Thinkphp版本,为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`,以及修改`log`方法可以重载。 >分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。 在不同的开发框架下使用Wechat类库,请继承Wechat类,根据需要实现这4个方法。 可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。 欢迎提交其他框架的适配文件到项目库来。 为Thinkphp进行适配的示例如下: ```php /** * 微信公众平台PHP-SDK, ThinkPHP实例 * @author dodgepudding@gmail.com * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * usage: * $options = array( * 'token'=>'tokenaccesskey', //填写你设定的key * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 * ); * $weObj = new TPWechat($options); * $weObj->valid(); * ... * */ class TPWechat extends Wechat { /** * log overwrite * @see Wechat::log() */ protected function log($log){ if ($this->debug) { if (function_exists($this->logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->logcallback,$log); }elseif (class_exists('Log')) { Log::write('wechat:'.$log, Log::DEBUG); } } return false; } /** * 重载设置缓存 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename,$value,$expired){ return S($cachename,$value,$expired); } /** * 重载获取缓存 * @param string $cachename * @return mixed */ protected function getCache($cachename){ return S($cachename); } /** * 重载清除缓存 * @param string $cachename * @return boolean */ protected function removeCache($cachename){ return S($cachename,null); } } ``` # 调用示例 ---------- ## 官方Wechat调用示例: ```php //test1.php include "wechat.class.php"; $options = array( 'token'=>'tokenaccesskey', //填写你设定的key 'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey,如接口为明文模式可忽略 ); $weObj = new Wechat($options); $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ``` ## 企业号API类库调用示例: 可参考**test**目录下的**qydemo.php** ```php include "wechat.class.php"; $options = array( 'token'=>'9Ixxxxxxx', //填写应用接口的Token 'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 'appid'=>'wxa07979baxxxxxxxx', //填写高级调用功能的appid ); $weObj = new Wechat($options); $weObj->valid(); //注意, 企业号与普通公众号不同,必须打开验证,不要注释掉 $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ``` ## 扩展包Wechatext调用示例: ```php // old_version/test/test2.php include "wechatext.class.php"; function logdebug($text){ file_put_contents('./data/log.txt',$text."\n",FILE_APPEND); }; $options = array( 'account'=>'demo@domain.com', 'password'=>'demo', 'datapath'=>'./data/cookie_', 'debug'=>true, 'logcallback'=>'logdebug' ); $wechat = new Wechatext($options); if ($wechat->checkValid()) { // 获取用户信息 $data = $wechat->getInfo('3974255'); var_dump($data); // 获取最新一条消息 $topmsg = $wechat->getTopMsg(); var_dump($topmsg); // 主动回复消息 if ($topmsg && $topmsg['has_reply']==0) $wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); } ``` ## 微信二维码Wechatauth登陆示例: ```php // old_version/test/test3.php include "../wechatauth.class.php"; session_start(); $sid = session_id(); $options = array( 'account'=>$sid, 'datapath'=>'../data/cookiecode_', ); $wechat = new Wechatauth($options); if (isset($_POST['code'])) { $logincode = $_POST['code']; $vres = $wechat->set_login_code($logincode)->verify_code(); if ($vres===false) { $result = array('status'=>0); } else { $result = array('status'=>$vres); if ($vres==200) { $result['info'] = $wechat->get_login_info(); $result['cookie'] = $wechat->get_login_cookie(true); } } die(json_encode($result)); } $logincode = $wechat->get_login_code(); //获取授权码 $qrimg = $wechat->get_code_image(); //待输出的二维码图片 ``` HTML部分请看old_version/test/test3.php, 主要是定时ajax查询是否已经授权成功 ## 新版微信JSAPI调用DEMO: 请看test/jsapi目录 License ------- This is licensed under the GNU LGPL, version 2.1 or later. For details, see: http://creativecommons.org/licenses/LGPL/2.1/ ================================================ FILE: Thinkphp/EasyWechat.class.php ================================================ 'tokenaccesskey', //填写你设定的key * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'cachedir'=>'./cache/', //填写缓存目录,默认为当前运行目录的子目录cache下 * 'logfile'=>'run.log' //填写日志输出文件,可选项。如果没有提供logcallback回调,且设置了输出文件则将日志输出至此文件,如果省略则不输出 * ); * $weObj = new EasyWechat($options); * $weObj->valid(); * ... * */ class EasyWechat extends Wechat { private $cachedir = ''; private $logfile = ''; public function __construct($options) { $this->cachedir = isset($options['cachedir']) ? dirname($options['cachedir'].'/.cache') . '/' : 'cache/'; $this->logfile = isset($options['logfile']) ? $options['logfile'] : ''; if ($this->cachedir) $this->checkDir($this->cachedir); parent::__construct($options); } /** * log overwrite * @param string|array $log */ protected function log($log){ if (is_array($log)) $log = print_r($log,true); if ($this->debug) { if (function_exists($this->logcallback)) { return call_user_func($this->logcallback,$log); }elseif ($this->logfile) { return file_put_contents($this->logfile, $log."\n", FILE_APPEND) > 0 ? true : false; } } return false; } /** * 重载设置缓存 * @param string $cachename * @param mixed $value * @param int $expired 缓存秒数,如果为0则为长期缓存 * @return boolean */ protected function setCache($cachename,$value,$expired=0){ $file = $this->cachedir . $cachename . '.cache'; $data = array( 'value' => $value, 'expired' => $expired ? time() + $expired : 0 ); return file_put_contents( $file, serialize($data) ) ? true : false; } /** * 重载获取缓存 * @param string $cachename * @return mixed */ protected function getCache($cachename){ $file = $this->cachedir . $cachename . '.cache'; if (!is_file($file)) { return false; } $data = unserialize(file_get_contents( $file )); if (!is_array($data) || !isset($data['value']) || (!empty($data['value']) && $data['expired']cachedir . $cachename . '.cache'; if ( is_file($file) ) { @unlink($file); } return true; } private function checkDir($dir, $mode=0777) { if (!$dir) return false; if(!is_dir($dir)) { if (!file_exists($dir) && @mkdir($dir, $mode, true)) return true; return false; } return true; } } ================================================ FILE: Thinkphp/JsSdkPay.class.php ================================================ appId = $options['appid']; $this->appSecret = $options['appsecret']; $this->weObj = new TPWechat($options); } //微信支付相关方法 /** * 作用:格式化参数,签名过程需要使用 */ function formatBizQueryParaMap($paraMap, $urlencode) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if($urlencode) { $v = urlencode($v); } //$buff .= strtolower($k) . "=" . $v . "&"; $buff .= $k . "=" . $v . "&"; } $reqPar = ""; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff)-1); } return $reqPar; } /** * 作用:设置jsapi的参数 */ public function getParameters() { $jsApiObj["appId"] = $this->appId; //请求生成支付签名时需要,js调起支付参数中不需要 $timeStamp = time(); $jsApiObj["timeStamp"] = "$timeStamp"; //用大写的timeStamp参数请求生成支付签名 $jsParamObj["timestamp"] = $timeStamp; //用小写的timestamp参数生成js支付参数,还要注意数据类型,坑! $jsParamObj["nonceStr"] = $jsApiObj["nonceStr"] = $this->weObj->generateNonceStr(); $jsParamObj["package"] = $jsApiObj["package"] = "prepay_id=$this->prepay_id"; $jsParamObj["signType"] = $jsApiObj["signType"] = "MD5"; $jsParamObj["paySign"] = $jsApiObj["paySign"] = $this->getSign($jsApiObj); $jsParam = json_encode($jsParamObj); return $jsParam; } /** * 获取prepay_id */ function getPrepayId() { $result = $this->xmlToArray($this->postXml()); $prepay_id = $result["prepay_id"]; return $prepay_id; } /** * 作用:将xml转为array */ public function xmlToArray($xml) { //将XML转为array $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $array_data; } /** * 作用:post请求xml */ function postXml() { $xml = $this->createXml(); return $this->postXmlCurl($xml,"https://api.mch.weixin.qq.com/pay/unifiedorder",$this->CURL_TIMEOUT); } /** * 作用:以post方式提交xml到对应的接口url */ public function postXmlCurl($xml,$url,$second=30) { //初始化curl $ch = curl_init(); //设置超时 curl_setopt($ch,CURLOP_TIMEOUT, $this->CURL_TIMEOUT); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); //curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); curl_close($ch); //返回结果 if($data) { curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "curl出错,错误码:$error"."
"; echo "错误原因查询
"; curl_close($ch); return false; } } /** * 作用:设置标配的请求参数,生成签名,生成接口参数xml */ function createXml() { $this->parameters["appid"] = $this->appId;//公众账号ID $this->parameters["mch_id"] = $this->MCHID;//商户号 $this->parameters["nonce_str"] = $this->weObj->generateNonceStr();//随机字符串 $this->parameters["sign"] = $this->getSign($this->parameters);//签名 return $this->arrayToXml($this->parameters); } /** * 作用:array转xml */ function arrayToXml($arr) { $xml = ""; foreach ($arr as $key=>$val) { if (is_numeric($val)) { $xml.="<".$key.">".$val.""; } else $xml.="<".$key.">"; } $xml.=""; return $xml; } /** * 作用:生成签名 */ public function getSign($Obj) { foreach ($Obj as $k => $v) { $Parameters[$k] = $v; } //签名步骤一:按字典序排序参数 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //echo '【string1】'.$String.'
'; //签名步骤二:在string后加入KEY $String = $String."&key=".$this->KEY; //echo "【string2】".$String."
"; //签名步骤三:MD5加密 $String = md5($String); //echo "【string3】 ".$String."
"; //签名步骤四:所有字符转为大写 $result_ = strtoupper($String); //echo "【result】 ".$result_."
"; return $result_; } } ================================================ FILE: Thinkphp/TPWechat.class.php ================================================ 'tokenaccesskey', //填写你设定的key * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 * ); * $weObj = new TPWechat($options); * $weObj->valid(); * ... * */ class TPWechat extends Wechat { /** * log overwrite * @see Wechat::log() */ protected function log($log){ if ($this->debug) { if (function_exists($this->logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->logcallback,$log); }elseif (class_exists('Log')) { Log::write('wechat:'.$log, Log::DEBUG); return true; } } return false; } /** * 重载设置缓存 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename,$value,$expired){ return S($cachename,$value,$expired); } /** * 重载获取缓存 * @param string $cachename * @return mixed */ protected function getCache($cachename){ return S($cachename); } /** * 重载清除缓存 * @param string $cachename * @return boolean */ protected function removeCache($cachename){ return S($cachename,null); } } ================================================ FILE: Thinkphp/Wxauth.class.php ================================================ options = $options; $this->wxoauth(); } public function wxoauth(){ $scope = 'snsapi_base'; $code = isset($_GET['code'])?$_GET['code']:''; $token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0; if(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600) { if (!$this->wxuser) { $this->wxuser = $_SESSION['wxuser']; } $this->open_id = $_SESSION['open_id']; return $this->open_id; } else { $options = array( 'token'=>$this->options["token"], //填写你设定的key 'encodingaeskey'=>$this->options["encodingaeskey"], //填写加密用的EncodingAESKey 'appid'=>$this->options["appid"], //填写高级调用功能的app id 'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥 ); $we_obj = new TPWechat($options); if ($code) { $json = $we_obj->getOauthAccessToken(); if (!$json) { unset($_SESSION['wx_redirect']); die('获取用户授权失败,请重新确认'); } $_SESSION['open_id'] = $this->open_id = $json["openid"]; $access_token = $json['access_token']; $_SESSION['user_token'] = $access_token; $_SESSION['token_time'] = time(); $userinfo = $we_obj->getUserInfo($this->open_id); if ($userinfo && !empty($userinfo['nickname'])) { $this->wxuser = array( 'open_id'=>$this->open_id, 'nickname'=>$userinfo['nickname'], 'sex'=>intval($userinfo['sex']), 'location'=>$userinfo['province'].'-'.$userinfo['city'], 'avatar'=>$userinfo['headimgurl'] ); } elseif (strstr($json['scope'],'snsapi_userinfo')!==false) { $userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id); if ($userinfo && !empty($userinfo['nickname'])) { $this->wxuser = array( 'open_id'=>$this->open_id, 'nickname'=>$userinfo['nickname'], 'sex'=>intval($userinfo['sex']), 'location'=>$userinfo['province'].'-'.$userinfo['city'], 'avatar'=>$userinfo['headimgurl'] ); } else { return $this->open_id; } } if ($this->wxuser) { $_SESSION['wxuser'] = $this->wxuser; $_SESSION['open_id'] = $json["openid"]; unset($_SESSION['wx_redirect']); return $this->open_id; } $scope = 'snsapi_userinfo'; } if ($scope=='snsapi_base') { $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; $_SESSION['wx_redirect'] = $url; } else { $url = $_SESSION['wx_redirect']; } if (!$url) { unset($_SESSION['wx_redirect']); die('获取用户授权失败'); } $oauth_url = $we_obj->getOauthRedirect($url,"wxbase",$scope); redirect ( $oauth_url ); } } } //$options = array( // 'token'=>'uctoo', //填写你设定的key // 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 // 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 //); //$auth = new Wxauth($options); //var_dump($auth->wxuser); ================================================ FILE: composer.json ================================================ { "name" : "dodgepudding/wechat-php-sdk", "description" : "the wechat api library without framework dependency", "version" : "1.1", "require" : { "php" : ">=5.1.0" }, "authors" : [ { "name" : "dodgepudding", "email" : "dodgepudding@gmail.com" }, { "name" : "binsee", "email" : "binsee@163.com" } ], "keywords" : [ "wechat", "weixin", "thinkphp", "sample" ], "support" : { "issues" : "https://github.com/dodgepudding/wechat-php-sdk/issues", "wiki" : "https://github.com/dodgepudding/wechat-php-sdk/wiki", "source" : "https://github.com/dodgepudding/wechat-php-sdk" }, "autoload" : { "psr-4" : { "dodgepudding\\wechat\\sdk\\" : "" } }, "type" : "library", "license" : "LGPL" } ================================================ FILE: demo.php ================================================ 'tokenaccesskey', //填写你设定的key 'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey,如接口为明文模式可忽略 ); $weObj = new Wechat($options); $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ================================================ FILE: errCode.php ================================================ * @link https://github.com/binsee/wechat-php-sdk * @version 1.0 * usage: * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 * if ($ret) * echo $ret; * else * echo "未找到对应的内容"; */ class ErrCode { public static $errCode=array( '-1'=>'系统繁忙', '0'=>'请求成功', '40001'=>'获取access_token时AppSecret错误,或者access_token无效', '40002'=>'不合法的凭证类型', '40003'=>'不合法的OpenID', '40004'=>'不合法的媒体文件类型', '40005'=>'不合法的文件类型', '40006'=>'不合法的文件大小', '40007'=>'不合法的媒体文件id', '40008'=>'不合法的消息类型', '40009'=>'不合法的图片文件大小', '40010'=>'不合法的语音文件大小', '40011'=>'不合法的视频文件大小', '40012'=>'不合法的缩略图文件大小', '40013'=>'不合法的APPID', '40014'=>'不合法的access_token', '40015'=>'不合法的菜单类型', '40016'=>'不合法的按钮个数', '40017'=>'不合法的按钮类型', '40018'=>'不合法的按钮名字长度', '40019'=>'不合法的按钮KEY长度', '40020'=>'不合法的按钮URL长度', '40021'=>'不合法的菜单版本号', '40022'=>'不合法的子菜单级数', '40023'=>'不合法的子菜单按钮个数', '40024'=>'不合法的子菜单按钮类型', '40025'=>'不合法的子菜单按钮名字长度', '40026'=>'不合法的子菜单按钮KEY长度', '40027'=>'不合法的子菜单按钮URL长度', '40028'=>'不合法的自定义菜单使用用户', '40029'=>'不合法的oauth_code', '40030'=>'不合法的refresh_token', '40031'=>'不合法的openid列表', '40032'=>'不合法的openid列表长度', '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', '40035'=>'不合法的参数', '40038'=>'不合法的请求格式', '40039'=>'不合法的URL长度', '40050'=>'不合法的分组id', '40051'=>'分组名字不合法', '40099'=>'该 code 已被核销', '41001'=>'缺少access_token参数', '41002'=>'缺少appid参数', '41003'=>'缺少refresh_token参数', '41004'=>'缺少secret参数', '41005'=>'缺少多媒体文件数据', '41006'=>'缺少media_id参数', '41007'=>'缺少子菜单数据', '41008'=>'缺少oauth code', '41009'=>'缺少openid', '42001'=>'access_token超时', '42002'=>'refresh_token超时', '42003'=>'oauth_code超时', '42005'=>'调用接口频率超过上限', '43001'=>'需要GET请求', '43002'=>'需要POST请求', '43003'=>'需要HTTPS请求', '43004'=>'需要接收者关注', '43005'=>'需要好友关系', '44001'=>'多媒体文件为空', '44002'=>'POST的数据包为空', '44003'=>'图文消息内容为空', '44004'=>'文本消息内容为空', '45001'=>'多媒体文件大小超过限制', '45002'=>'消息内容超过限制', '45003'=>'标题字段超过限制', '45004'=>'描述字段超过限制', '45005'=>'链接字段超过限制', '45006'=>'图片链接字段超过限制', '45007'=>'语音播放时间超过限制', '45008'=>'图文消息超过限制', '45009'=>'接口调用超过限制', '45010'=>'创建菜单个数超过限制', '45015'=>'回复时间超过限制', '45016'=>'系统分组,不允许修改', '45017'=>'分组名字过长', '45018'=>'分组数量超过上限', '45024'=>'账号数量超过上限', '46001'=>'不存在媒体数据', '46002'=>'不存在的菜单版本', '46003'=>'不存在的菜单数据', '46004'=>'不存在的用户', '47001'=>'解析JSON/XML内容错误', '48001'=>'api功能未授权', '50001'=>'用户未授权该api', '61450'=>'系统错误', '61451'=>'参数错误', '61452'=>'无效客服账号', '61453'=>'账号已存在', '61454'=>'客服帐号名长度超过限制(仅允许10个英文字符,不包括@及@后的公众号的微信号)', '61455'=>'客服账号名包含非法字符(英文+数字)', '61456'=>'客服账号个数超过限制(10个客服账号)', '61457'=>'无效头像文件类型', '61458'=>'客户正在被其他客服接待', '61459'=>'客服不在线', '61500'=>'日期格式错误', '61501'=>'日期范围错误', '7000000'=>'请求正常,无语义结果', '7000001'=>'缺失请求参数', '7000002'=>'signature 参数无效', '7000003'=>'地理位置相关配置 1 无效', '7000004'=>'地理位置相关配置 2 无效', '7000005'=>'请求地理位置信息失败', '7000006'=>'地理位置结果解析失败', '7000007'=>'内部初始化失败', '7000008'=>'非法 appid(获取密钥失败)', '7000009'=>'请求语义服务失败', '7000010'=>'非法 post 请求', '7000011'=>'post 请求 json 字段无效', '7000030'=>'查询 query 太短', '7000031'=>'查询 query 太长', '7000032'=>'城市、经纬度信息缺失', '7000033'=>'query 请求语义处理失败', '7000034'=>'获取天气信息失败', '7000035'=>'获取股票信息失败', '7000036'=>'utf8 编码转换失败', ); public static function getErrText($err) { if (isset(self::$errCode[$err])) { return self::$errCode[$err]; }else { return false; }; } } ?> ================================================ FILE: old_version/Thinkphp/Snoopy.class.php ================================================ Copyright (c): 1999-2008 New Digital Group, all rights reserved Version: 1.2.4 * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You may contact the author of Snoopy by e-mail at: monte@ohrt.com The latest version of Snoopy can be obtained from: http://snoopy.sourceforge.net/ *************************************************/ class Snoopy { /**** Public variables ****/ /* user definable vars */ var $host = "www.php.net"; // host name we are connecting to var $port = 80; // port we are connecting to var $proxy_host = ""; // proxy host to use var $proxy_port = ""; // proxy port to use var $proxy_user = ""; // proxy user to use var $proxy_pass = ""; // proxy password to use var $agent = "Mozilla/5.0"; // agent we masquerade as var $referer = ""; // referer info to pass var $cookies = array(); // array of cookies to pass // $cookies["username"]="joe"; var $rawheaders = array(); // array of raw headers to send // $rawheaders["Content-type"]="text/html"; var $maxredirs = 5; // http redirection depth maximum. 0 = disallow var $lastredirectaddr = ""; // contains address of last redirected address var $offsiteok = true; // allows redirection off-site var $maxframes = 0; // frame content depth maximum. 0 = disallow var $expandlinks = true; // expand links to fully qualified URLs. // this only applies to fetchlinks() // submitlinks(), and submittext() var $passcookies = true; // pass set cookies back through redirects // NOTE: this currently does not respect // dates, domains or paths. var $user = ""; // user for http authentication var $pass = ""; // password for http authentication // http accept types var $accept = "application/json, text/javascript, */*; q=0.01"; var $results = ""; // where the content is put var $error = ""; // error messages sent here var $response_code = ""; // response code returned from server var $headers = array(); // headers returned from server sent here var $maxlength = 500000; // max return data length (body) var $read_timeout = 0; // timeout on read operations, in seconds // supported only since PHP 4 Beta 4 // set to 0 to disallow timeouts var $timed_out = false; // if a read operation timed out var $status = 0; // http request status var $temp_dir = "/tmp"; // temporary directory that the webserver // has permission to write to. // under Windows, this should be C:\temp var $curl_path = "/usr/local/bin/curl"; // Snoopy will use cURL for fetching // SSL content if a full system path to // the cURL binary is supplied here. // set to false if you do not have // cURL installed. See http://curl.haxx.se // for details on installing cURL. // Snoopy does *not* use the cURL // library functions built into php, // as these functions are not stable // as of this Snoopy release. /**** Private variables ****/ var $_maxlinelen = 4096; // max line length (headers) var $_httpmethod = "GET"; // default http request method var $_httpversion = "HTTP/1.0"; // default http request version var $_submit_method = "POST"; // default submit method var $_submit_type = "application/x-www-form-urlencoded"; // default submit type var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type var $_redirectaddr = false; // will be set if page fetched is a redirect var $_redirectdepth = 0; // increments on an http redirect var $_frameurls = array(); // frame src urls var $_framedepth = 0; // increments on frame depth var $_isproxy = false; // set if using a proxy server var $_fp_timeout = 30; // timeout for socket connection /*======================================================================*\ Function: fetch Purpose: fetch the contents of a web page (and possibly other protocols in the future like ftp, nntp, gopher, etc.) Input: $URI the location of the page to fetch Output: $this->results the output text from the fetch \*======================================================================*/ function fetch($URI) { //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); $URI_PARTS = parse_url($URI); if (!empty($URI_PARTS["user"])) $this->user = $URI_PARTS["user"]; if (!empty($URI_PARTS["pass"])) $this->pass = $URI_PARTS["pass"]; if (empty($URI_PARTS["query"])) $URI_PARTS["query"] = ''; if (empty($URI_PARTS["path"])) $URI_PARTS["path"] = ''; switch(strtolower($URI_PARTS["scheme"])) { case "http": $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_connect($fp)) { if($this->_isproxy) { // using proxy, send entire URI $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httprequest($path, $fp, $URI, $this->_httpmethod); } $this->_disconnect($fp); if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; $this->fetch($this->_redirectaddr); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } } else { return false; } return true; break; case "https": if (!function_exists('curl_init')) { if(!$this->curl_path) return false; if(function_exists("is_executable")) if (!is_executable($this->curl_path)) return false; } $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_isproxy) { // using proxy, send entire URI $this->_httpsrequest($URI,$URI,$this->_httpmethod); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httpsrequest($path, $URI, $this->_httpmethod); } if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^https://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; $this->fetch($this->_redirectaddr); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } return true; break; default: // not a valid protocol $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; return false; break; } return true; } /*======================================================================*\ Function: submit Purpose: submit an http form Input: $URI the location to post the data $formvars the formvars to use. format: $formvars["var"] = "val"; $formfiles an array of files to submit format: $formfiles["var"] = "/dir/filename.ext"; Output: $this->results the text output from the post \*======================================================================*/ function submit($URI, $formvars="", $formfiles="") { unset($postdata); $postdata = $this->_prepare_post_body($formvars, $formfiles); $URI_PARTS = parse_url($URI); if (!empty($URI_PARTS["user"])) $this->user = $URI_PARTS["user"]; if (!empty($URI_PARTS["pass"])) $this->pass = $URI_PARTS["pass"]; if (empty($URI_PARTS["query"])) $URI_PARTS["query"] = ''; if (empty($URI_PARTS["path"])) $URI_PARTS["path"] = ''; switch(strtolower($URI_PARTS["scheme"])) { case "http": $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_connect($fp)) { if($this->_isproxy) { // using proxy, send entire URI $this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata); } $this->_disconnect($fp); if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; if( strpos( $this->_redirectaddr, "?" ) > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get else $this->submit($this->_redirectaddr,$formvars, $formfiles); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } } else { return false; } return true; break; case "https": if (!function_exists('curl_init')) { if(!$this->curl_path) return false; if(function_exists("is_executable")) if (!is_executable($this->curl_path)) return false; } $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_isproxy) { // using proxy, send entire URI $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata); } if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^https://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; if( strpos( $this->_redirectaddr, "?" ) > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get else $this->submit($this->_redirectaddr,$formvars, $formfiles); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } return true; break; default: // not a valid protocol $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; return false; break; } return true; } /*======================================================================*\ Function: fetchlinks Purpose: fetch the links from a web page Input: $URI where you are fetching from Output: $this->results an array of the URLs \*======================================================================*/ function fetchlinks($URI) { if ($this->fetch($URI)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_striplinks($this->results[$x]); } else $this->results = $this->_striplinks($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results, $URI); return true; } else return false; } /*======================================================================*\ Function: fetchform Purpose: fetch the form elements from a web page Input: $URI where you are fetching from Output: $this->results the resulting html form \*======================================================================*/ function fetchform($URI) { if ($this->fetch($URI)) { if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_stripform($this->results[$x]); } else $this->results = $this->_stripform($this->results); return true; } else return false; } /*======================================================================*\ Function: fetchtext Purpose: fetch the text from a web page, stripping the links Input: $URI where you are fetching from Output: $this->results the text from the web page \*======================================================================*/ function fetchtext($URI) { if($this->fetch($URI)) { if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_striptext($this->results[$x]); } else $this->results = $this->_striptext($this->results); return true; } else return false; } /*======================================================================*\ Function: submitlinks Purpose: grab links from a form submission Input: $URI where you are submitting from Output: $this->results an array of the links from the post \*======================================================================*/ function submitlinks($URI, $formvars="", $formfiles="") { if($this->submit($URI,$formvars, $formfiles)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) { $this->results[$x] = $this->_striplinks($this->results[$x]); if($this->expandlinks) $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); } } else { $this->results = $this->_striplinks($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results,$URI); } return true; } else return false; } /*======================================================================*\ Function: submittext Purpose: grab text from a form submission Input: $URI where you are submitting from Output: $this->results the text from the web page \*======================================================================*/ function submittext($URI, $formvars = "", $formfiles = "") { if($this->submit($URI,$formvars, $formfiles)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) { $this->results[$x] = $this->_striptext($this->results[$x]); if($this->expandlinks) $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); } } else { $this->results = $this->_striptext($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results,$URI); } return true; } else return false; } /*======================================================================*\ Function: set_submit_multipart Purpose: Set the form submission content type to multipart/form-data \*======================================================================*/ function set_submit_multipart() { $this->_submit_type = "multipart/form-data"; } /*======================================================================*\ Function: set_submit_normal Purpose: Set the form submission content type to application/x-www-form-urlencoded \*======================================================================*/ function set_submit_normal() { $this->_submit_type = "application/x-www-form-urlencoded"; } /*======================================================================*\ Private functions \*======================================================================*/ /*======================================================================*\ Function: _striplinks Purpose: strip the hyperlinks from an html document Input: $document document to strip. Output: $match an array of the links \*======================================================================*/ function _striplinks($document) { preg_match_all("'<\s*a\s.*?href\s*=\s* # find ]+)) # if quote found, match up to next matching # quote, otherwise match up to next space 'isx",$document,$links); // catenate the non-empty matches from the conditional subpattern while(list($key,$val) = each($links[2])) { if(!empty($val)) $match[] = $val; } while(list($key,$val) = each($links[3])) { if(!empty($val)) $match[] = $val; } // return the links return $match; } /*======================================================================*\ Function: _stripform Purpose: strip the form elements from an html document Input: $document document to strip. Output: $match an array of the links \*======================================================================*/ function _stripform($document) { preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); // catenate the matches $match = implode("\r\n",$elements[0]); // return the links return $match; } /*======================================================================*\ Function: _striptext Purpose: strip the text from an html document Input: $document document to strip. Output: $text the resulting text \*======================================================================*/ function _striptext($document) { // I didn't use preg eval (//e) since that is only available in PHP 4.0. // so, list your entities one by one here. I included some of the // more common ones. $search = array("']*?>.*?'si", // strip out javascript "'<[\/\!]*?[^<>]*?>'si", // strip out html tags "'([\r\n])[\s]+'", // strip out white space "'&(quot|#34|#034|#x22);'i", // replace html entities "'&(amp|#38|#038|#x26);'i", // added hexadecimal values "'&(lt|#60|#060|#x3c);'i", "'&(gt|#62|#062|#x3e);'i", "'&(nbsp|#160|#xa0);'i", "'&(iexcl|#161);'i", "'&(cent|#162);'i", "'&(pound|#163);'i", "'&(copy|#169);'i", "'&(reg|#174);'i", "'&(deg|#176);'i", "'&(#39|#039|#x27);'", "'&(euro|#8364);'i", // europe "'&a(uml|UML);'", // german "'&o(uml|UML);'", "'&u(uml|UML);'", "'&A(uml|UML);'", "'&O(uml|UML);'", "'&U(uml|UML);'", "'ß'i", ); $replace = array( "", "", "\\1", "\"", "&", "<", ">", " ", chr(161), chr(162), chr(163), chr(169), chr(174), chr(176), chr(39), chr(128), "�", "�", "�", "�", "�", "�", "�", ); $text = preg_replace($search,$replace,$document); return $text; } /*======================================================================*\ Function: _expandlinks Purpose: expand each link into a fully qualified URL Input: $links the links to qualify $URI the full URI to get the base from Output: $expandedLinks the expanded links \*======================================================================*/ function _expandlinks($links,$URI) { preg_match("/^[^\?]+/",$URI,$match); $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); $match = preg_replace("|/$|","",$match); $match_part = parse_url($match); $match_root = $match_part["scheme"]."://".$match_part["host"]; $search = array( "|^http://".preg_quote($this->host)."|i", "|^(\/)|i", "|^(?!http://)(?!mailto:)|i", "|/\./|", "|/[^\/]+/\.\./|" ); $replace = array( "", $match_root."/", $match."/", "/", "/" ); $expandedLinks = preg_replace($search,$replace,$links); return $expandedLinks; } /*======================================================================*\ Function: _httprequest Purpose: go get the http data from the server Input: $url the url to fetch $fp the current open file pointer $URI the full URI $body body contents to send if any (POST) Output: \*======================================================================*/ function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") { $cookie_headers = ''; if($this->passcookies && $this->_redirectaddr) $this->setcookies(); $URI_PARTS = parse_url($URI); if(empty($url)) $url = "/"; $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; if(!empty($this->agent)) $headers .= "User-Agent: ".$this->agent."\r\n"; if(!empty($this->host) && !isset($this->rawheaders['Host'])) { $headers .= "Host: ".$this->host; if(!empty($this->port) && $this->port!=80) $headers .= ":".$this->port; $headers .= "\r\n"; } if(!empty($this->accept)) $headers .= "Accept: ".$this->accept."\r\n"; if(!empty($this->referer)) $headers .= "Referer: ".$this->referer."\r\n"; if(!empty($this->cookies)) { if(!is_array($this->cookies)) $this->cookies = (array)$this->cookies; reset($this->cookies); if ( count($this->cookies) > 0 ) { $cookie_headers .= 'Cookie: '; foreach ( $this->cookies as $cookieKey => $cookieVal ) { $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; } $headers .= substr($cookie_headers,0,-2) . "\r\n"; } } if(!empty($this->rawheaders)) { if(!is_array($this->rawheaders)) $this->rawheaders = (array)$this->rawheaders; while(list($headerKey,$headerVal) = each($this->rawheaders)) $headers .= $headerKey.": ".$headerVal."\r\n"; } if(!empty($content_type)) { $headers .= "Content-type: $content_type"; if ($content_type == "multipart/form-data") $headers .= "; boundary=".$this->_mime_boundary; $headers .= "\r\n"; } if(!empty($body)) $headers .= "Content-length: ".strlen($body)."\r\n"; if(!empty($this->user) || !empty($this->pass)) $headers .= "Authorization: Basic ".base64_encode($this->user.":".$this->pass)."\r\n"; //add proxy auth headers if(!empty($this->proxy_user)) $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass)."\r\n"; $headers .= "\r\n"; // set the read timeout if needed if ($this->read_timeout > 0) socket_set_timeout($fp, $this->read_timeout); $this->timed_out = false; fwrite($fp,$headers.$body,strlen($headers.$body)); $this->_redirectaddr = false; unset($this->headers); while($currentHeader = fgets($fp,$this->_maxlinelen)) { if ($this->read_timeout > 0 && $this->_check_timeout($fp)) { $this->status=-100; return false; } if($currentHeader == "\r\n") break; // if a header begins with Location: or URI:, set the redirect if(preg_match("/^(Location:|URI:)/i",$currentHeader)) { // get URL portion of the redirect preg_match("/^(Location:|URI:)[ ]+(.*)/i",chop($currentHeader),$matches); // look for :// in the Location header to see if hostname is included if (!empty($matches)) { if(!preg_match("|\:\/\/|",$matches[2])) { // no host in the path, so prepend $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; // eliminate double slash if(!preg_match("|^/|",$matches[2])) $this->_redirectaddr .= "/".$matches[2]; else $this->_redirectaddr .= $matches[2]; } else $this->_redirectaddr = $matches[2]; } } if(preg_match("|^HTTP/|",$currentHeader)) { if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) { $this->status= $status[1]; } $this->response_code = $currentHeader; } $this->headers[] = $currentHeader; } $results = ''; do { $_data = fread($fp, $this->maxlength); if (strlen($_data) == 0) { break; } $results .= $_data; } while(true); if ($this->read_timeout > 0 && $this->_check_timeout($fp)) { $this->status=-100; return false; } // check if there is a a redirect meta tag if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) { $this->_redirectaddr = $this->_expandlinks($match[1],$URI); } // have we hit our frame depth and is there frame src to fetch? if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) { $this->results[] = $results; for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); } // have we already fetched framed content? elseif(is_array($this->results)) $this->results[] = $results; // no framed content else $this->results = $results; return true; } /*======================================================================*\ Function: _httpsrequest Purpose: go get the https data from the server using curl Input: $url the url to fetch $URI the full URI $body body contents to send if any (POST) Output: \*======================================================================*/ function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") { if($this->passcookies && $this->_redirectaddr) $this->setcookies(); $headers = array(); $URI_PARTS = parse_url($URI); if(empty($url)) $url = "/"; // GET ... header not needed for curl //$headers[] = $http_method." ".$url." ".$this->_httpversion; if(!empty($this->agent)) $headers[] = "User-Agent: ".$this->agent; if(!empty($this->host)) if(!empty($this->port) && $this->port!=80) $headers[] = "Host: ".$this->host.":".$this->port; else $headers[] = "Host: ".$this->host; if(!empty($this->accept)) $headers[] = "Accept: ".$this->accept; if(!empty($this->referer)) $headers[] = "Referer: ".$this->referer; if(!empty($this->cookies)) { if(!is_array($this->cookies)) $this->cookies = (array)$this->cookies; reset($this->cookies); if ( count($this->cookies) > 0 ) { $cookie_str = 'Cookie: '; foreach ( $this->cookies as $cookieKey => $cookieVal ) { $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; } $headers[] = substr($cookie_str,0,-2); } } if(!empty($this->rawheaders)) { if(!is_array($this->rawheaders)) $this->rawheaders = (array)$this->rawheaders; while(list($headerKey,$headerVal) = each($this->rawheaders)) $headers[] = $headerKey.": ".$headerVal; } if(!empty($content_type)) { if ($content_type == "multipart/form-data") $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; else $headers[] = "Content-type: $content_type"; } if(!empty($body)) $headers[] = "Content-length: ".strlen($body); if(!empty($this->user) || !empty($this->pass)) $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); if (function_exists('curl_init')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $URI); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_TIMEOUT, $this->read_timeout); if(!empty($body)) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); } $data = curl_exec($ch); if ($data === false) { $this->error = "Error: Curl error ".curl_error($ch); return false; } $parts = explode("\r\n\r\n",$data,2); $result_headers = explode("\r\n",$parts[0]); $results = $parts[1]; unset($parts); } else { for($curr_header = 0; $curr_header < count($headers); $curr_header++) { $safer_header = strtr( $headers[$curr_header], "\"", " " ); $cmdline_params .= " -H \"".$safer_header."\""; } if(!empty($body)) $cmdline_params .= " -d \"$body\""; if($this->read_timeout > 0) $cmdline_params .= " -m ".$this->read_timeout; $headerfile = tempnam($temp_dir, "sno"); exec($this->curl_path." -k -D \"$headerfile\"".$cmdline_params." \"".escapeshellcmd($URI)."\"",$results,$return); if($return) { $this->error = "Error: cURL could not retrieve the document, error $return."; return false; } $results = implode("\r\n",$results); $result_headers = file("$headerfile"); } $this->_redirectaddr = false; unset($this->headers); for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) { // if a header begins with Location: or URI:, set the redirect if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) { // get URL portion of the redirect preg_match("/^(Location: |URI:)\s+(.*)/",chop($result_headers[$currentHeader]),$matches); // look for :// in the Location header to see if hostname is included if (!empty($matches)) { if(!preg_match("|\:\/\/|",$matches[2])) { // no host in the path, so prepend $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host; // eliminate double slash if(!preg_match("|^/|",$matches[2])) $this->_redirectaddr .= "/".$matches[2]; else $this->_redirectaddr .= $matches[2]; } else $this->_redirectaddr = $matches[2]; } } if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) $this->response_code = $result_headers[$currentHeader]; $this->headers[] = $result_headers[$currentHeader]; } // check if there is a a redirect meta tag if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) { $this->_redirectaddr = $this->_expandlinks($match[1],$URI); } // have we hit our frame depth and is there frame src to fetch? if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) { $this->results[] = $results; for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); } // have we already fetched framed content? elseif(is_array($this->results)) $this->results[] = $results; // no framed content else $this->results = $results; if (isset($headerfile) && file_exists($headerfile)) unlink($headerfile); return true; } /*======================================================================*\ Function: setcookies() Purpose: set cookies for a redirection \*======================================================================*/ function setcookies() { for($x=0; $xheaders); $x++) { if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match)) $this->cookies[$match[1]] = urldecode($match[2]); } } /*======================================================================*\ Function: _check_timeout Purpose: checks whether timeout has occurred Input: $fp file pointer \*======================================================================*/ function _check_timeout($fp) { if ($this->read_timeout > 0) { $fp_status = socket_get_status($fp); if ($fp_status["timed_out"]) { $this->timed_out = true; return true; } } return false; } /*======================================================================*\ Function: _connect Purpose: make a socket connection Input: $fp file pointer \*======================================================================*/ function _connect(&$fp) { if(!empty($this->proxy_host) && !empty($this->proxy_port)) { $this->_isproxy = true; $host = $this->proxy_host; $port = $this->proxy_port; } else { $host = $this->host; $port = $this->port; } $this->status = 0; if($fp = fsockopen( $host, $port, $errno, $errstr, $this->_fp_timeout )) { // socket connection succeeded return true; } else { // socket connection failed $this->status = $errno; switch($errno) { case -3: $this->error="socket creation failed (-3)"; case -4: $this->error="dns lookup failure (-4)"; case -5: $this->error="connection refused or timed out (-5)"; default: $this->error="connection failed (".$errno.")"; } return false; } } /*======================================================================*\ Function: _disconnect Purpose: disconnect a socket connection Input: $fp file pointer \*======================================================================*/ function _disconnect($fp) { return(fclose($fp)); } /*======================================================================*\ Function: _prepare_post_body Purpose: Prepare post body according to encoding type Input: $formvars - form variables $formfiles - form upload files Output: post body \*======================================================================*/ function _prepare_post_body($formvars, $formfiles) { settype($formvars, "array"); settype($formfiles, "array"); $postdata = ''; if (count($formvars) == 0 && count($formfiles) == 0) return; if (is_string($formvars)) return $formvars; if((count($formvars) == 1) && isset($formvars[0])) return $formvars[0]; switch ($this->_submit_type) { case "application/x-www-form-urlencoded": reset($formvars); while(list($key,$val) = each($formvars)) { if (is_array($val) || is_object($val)) { while (list($cur_key, $cur_val) = each($val)) { $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; } } else $postdata .= urlencode($key)."=".urlencode($val)."&"; } break; case "multipart/form-data": $this->_mime_boundary = "--------".md5(uniqid(microtime())); reset($formvars); while(list($key,$val) = each($formvars)) { if (is_array($val) || is_object($val)) { while (list($cur_key, $cur_val) = each($val)) { $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; $postdata .= "$cur_val\r\n"; } } else { $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; $postdata .= "$val\r\n"; } } reset($formfiles); while (list($field_name, $file_names) = each($formfiles)) { settype($file_names, "array"); while (list(, $file_name) = each($file_names)) { $file_content = file_get_contents($file_name); if (!$file_content) continue; $base_name = basename($file_name); $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\nContent-Type: image/jpeg\r\n\r\n"; $postdata .= "$file_content\r\n"; } } $postdata .= "--".$this->_mime_boundary."--\r\n"; break; } return $postdata; } } ================================================ FILE: old_version/Thinkphp/Wechatauth.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.1 * */ include "Snoopy.class.php"; class Wechatauth { private $cookie; private $skey; private $_cookiename; private $_cookieexpired = 3600; private $_account = 'test'; private $_datapath = './data/cookie_'; private $debug; private $_logcallback; public $login_user; //当前登陆用户, 调用get_login_info后获取 public function __construct($options) { $this->_account = isset($options['account'])?$options['account']:''; $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; $this->_cookiename = $this->_datapath.$this->_account; $this->getCookie($this->_cookiename); } /** * 把cookie写入缓存 * @param string $filename 缓存文件名 * @param string $content 文件内容 * @return bool */ public function saveCookie($filename,$content){ return S($filename,$content,$this->_cookieexpired); } /** * 读取cookie缓存内容 * @param string $filename 缓存文件名 * @return string cookie */ public function getCookie($filename){ $data = S($filename); if ($data) $this->cookie = $data; return $this->cookie; } /* * 删除cookie */ public function deleteCookie($filename) { $this->cookie = ''; S($filename,null); return true; } private function log($log){ if ($this->debug && function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); } } /** * 获取登陆二维码对应的授权码 */ public function get_login_code(){ if ($this->_logincode) return $this->_logincode; $t = time().strval(mt_rand(100,999)); $codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t; $send_snoopy = new Snoopy; $send_snoopy->fetch($codeurl); $result = $send_snoopy->results; if ($result) { preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches); if(count($matches)>1) { $this->_logincode = $matches[1]; $_SESSION['login_step'] = 0; return $this->_logincode; } } return $result; } /** * 通过授权码获取对应的二维码图片地址 * @param string $code * @return string image url */ public function get_code_image($code=''){ if ($code=='') $code = $this->_logincode; if (!$code) return false; return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx'; } /** * 设置二维码对应的授权码 * @param string $code * @return class $this */ public function set_login_code($code) { $this->_logincode = $code; return $this; } /** * 二维码登陆验证 * * @return status: * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired * 201: just scaned but not confirm * 200: confirm then you can get user info */ public function verify_code() { if (!$this->_logincode) return false; $t = time().strval(mt_rand(100,999)); $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t; $send_snoopy = new Snoopy; $send_snoopy->referer = "https://wx.qq.com/"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('step1:'.$result); if ($result) { preg_match("/window\.code=(\d+)/",$result,$matches); if(count($matches)>1) { $status = intval($matches[1]); if ($status==201) $_SESSION['login_step'] = 1; if ($status==200) { preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches); preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl); $this->log('step2:'.print_r($matches,true)); if (count($matcheurl)>1) { $ticket = $matches[1]; $scan = $matches[2]; //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new'; $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old'; $urlpart = parse_url($loginurl); $send_snoopy = new Snoopy; $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat"; $send_snoopy->fetch($loginurl); $result = $send_snoopy->results; $xml = simplexml_load_string($result); if ($xml->ret=="0") $this->skey = $xml->skey; foreach ($send_snoopy->headers as $key => $value) { $value = trim($value); if(strpos($value,'Set-Cookie: ') !== false){ $tmp = str_replace("Set-Cookie: ","",$value); $tmparray = explode(';', $tmp); $item = trim($tmparray[0]); $cookie.=$item.';'; } } $cookie .="Domain=.qq.com;"; $this->cookie = $cookie; $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result); $this->saveCookie($this->_cookiename,$this->cookie); } } return $status; } } return false; } /** * 获取登陆的cookie * * @param bool $is_array 是否以数值方式返回,默认否,返回字符串 * @return string|array */ public function get_login_cookie($is_array = false){ if (!$is_array) return $this->cookie; $c_arr = explode(';',$this->cookie); $cookie = array(); foreach($c_arr as $item) { $kitem = explode('=',trim($item)); if (count($kitem)>1) { $key = trim($kitem[0]); $val = trim($kitem[1]); if (!empty($val)) $cookie[$key] = $val; } } return $cookie; } /** * 授权登陆后获取用户登陆信息 */ public function get_login_info(){ if (!$this->cookie) return false; $t = time().strval(mt_rand(100,999)); $send_snoopy = new Snoopy; $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $citems = $this->get_login_cookie(true); $post = array( "BaseRequest"=>array( array( "Uin"=>$citems['wxuin'], "Sid"=>$citems['wxsid'], "Skey"=>$this->skey, "DeviceID"=>'' ) ) ); $send_snoopy->submit($submit,json_encode($post)); $this->log('login_info:'.$send_snoopy->results); $result = json_decode($send_snoopy->results,true); if ($result['BaseResponse']['Ret']<0) return false; $this->_login_user = $result['User']; return $result; } /** * 获取头像 * @param string $url 传入从用户信息接口获取到的头像地址 */ public function get_avatar($url) { if (!$this->cookie) return false; if (strpos($url, 'http')===false) { $url = 'http://wx2.qq.com'.$url; } $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $send_snoopy->fetch($url); $result = $send_snoopy->results; if ($result) return $result; else return false; } /** * 登出当前登陆用户 */ public function logout(){ if (!$this->cookie) return false; preg_match("/wxuin=(\w+);/",$this->cookie,$matches); if (count($matches)>1) $uid = $matches[1]; preg_match("/wxsid=(\w+);/",$this->cookie,$matches); if (count($matches)>1) $sid = $matches[1]; $this->log('logout: uid='.$uid.';sid='.$sid); $send_snoopy = new Snoopy; $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1'; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid)); $this->deleteCookie($this->_cookiename); return true; } } ================================================ FILE: old_version/Thinkphp/Wechatext.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * */ include "Snoopy.class.php"; class Wechatext { private $cookie; private $_cookiename; private $_cookieexpired = 3600; private $_account; private $_password; private $_datapath = './data/cookie_'; private $debug; private $_logcallback; private $_token; public function __construct($options) { $this->_account = isset($options['account'])?$options['account']:''; $this->_password = isset($options['password'])?$options['password']:''; $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; $this->_cookiename = $this->_datapath.$this->_account; $this->cookie = $this->getCookie($this->_cookiename); } /** * 主动发消息 * @param string $id 用户的uid(即FakeId) * @param string $content 发送的内容 */ public function send($id,$content) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = 1; $post['token'] = $this->_token; $post['content'] = $content; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 群发功能 纯文本 * @param string $content * @return string */ public function mass($content) { $send_snoopy = new Snoopy; $post = array(); $post['type'] = 1; $post['token'] = $this->_token; $post['content'] = $content; $post['ajax'] = 1; $post['city']=''; $post['country']=''; $post['f']='json'; $post['groupid']='-1'; $post['imgcode']=''; $post['lang']='zh_CN'; $post['province']=''; $post['random']= rand(0, 1); $post['sex']=0; $post['synctxnews']=0; $post['synctxweibo']=0; $post['t']='ajax-response'; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 群发功能 图文素材 * @param int $appmsgid 图文素材ID * @return string */ function massNews($appmsgid){ $send_snoopy = new Snoopy; $post = array(); $post['type'] = 10; $post['token'] = $this->_token; $post['appmsgid'] = $appmsgid; $post['ajax'] = 1; $post['city']=''; $post['country']=''; $post['f']='json'; $post['groupid']='-1'; $post['imgcode']=''; $post['lang']='zh_CN'; $post['province']=''; $post['random']= rand(0, 1); $post['sex']=0; $post['synctxnews']=0; $post['synctxweibo']=0; $post['t']='ajax-response'; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 获取用户列表列表 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @param $groupid 分组id * @return array ({contacts:[{id:12345667,nick_name:"昵称",remark_name:"备注名",group_id:0},{}....]}) */ function getUserList($page=0,$pagesize=10,$groupid=0){ $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=0&lang=zh_CN&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=$groupid&lang=zh_CN&f=json&token=".$this->_token; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('userlist:'.$result); $json = json_decode($result,true); if (isset($json['contact_list'])) { $json = json_decode($json['contact_list'],true); if (isset($json['contacts'])) return $json['contacts']; } return false; } /** * 获取分组列表 * */ function getGroupList(){ $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&f=json&token=".$this->_token; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('userlist:'.$result); $json = json_decode($result,true); if (isset($json['group_list'])){ $json = json_decode($json['group_list'],true); if (isset($json['groups'])) return $json['groups']; } return false; } /** * 获取图文信息列表 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @return array */ public function getNewsList($page,$pagesize=10) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $type=10; $begin = $page*$pagesize; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/appmsg?token=".$this->_token."&lang=zh_CN&type=$type&action=list&begin=$begin&count=$pagesize&f=json&random=0.".$t; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('newslist:'.$result); $json = json_decode($result,true); if (isset($json['app_msg_info'])) { return $json['app_msg_info']; } return false; } /** * 获取与指定用户的对话内容 * @param $fakeid * @return array */ public function getDialogMsg($fakeid) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=".$fakeid."&token=".$this->_token."&lang=zh_CN&f=json&random=".$t; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('DialogMsg:'.$result); $json = json_decode($result,true); if (isset($json['page_info'])) { return $json['page_info']; } return false; } /** * 发送图文信息,必须从图文库里选取消息ID发送 * @param string $id 用户的uid(即FakeId) * @param string $msgid 图文消息id */ public function sendNews($id,$msgid) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = 10; $post['token'] = $this->_token; $post['fid'] = $msgid; $post['appmsgid'] = $msgid; $post['error'] = 'false'; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 上传附件(图片/音频/视频) * @param string $filepath 本地文件地址 * @param int $type 文件类型: 2:图片 3:音频 4:视频 */ public function uploadFile($filepath,$type=2) { $send_snoopy = new Snoopy; $send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1"; $t = time().strval(mt_rand(100,999)); $post = array('formId'=>''); $postfile = array('uploadfile'=>$filepath); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->set_submit_multipart(); $submit = "http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=".$this->_token."&t=iframe-uploadfile&lang=zh_CN&formId= file_from_".$t; $send_snoopy->submit($submit,$post,$postfile); $tmp = $send_snoopy->results; $this->log('upload:'.$tmp); preg_match("/formId,.*?\'(\d+)\'/",$tmp,$matches); if (isset($matches[1])) { return $matches[1]; } return false; } /** * 创建图文消息 * @param array $title 标题 * @param array $summary 摘要 * @param array $content 内容 * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取) * @param array $srcurl 原文链接 * @return json */ public function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') { $send_snoopy = new Snoopy; $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&token='.$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->set_submit_normal(); $post = array( 'token'=>$this->_token, 'type'=>10, 'lang'=>'zh_CN', 'sub'=>'create', 'ajax'=>1, 'AppMsgId'=>'', 'error'=>'false', ); if (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid)) { $i = 0; foreach($title as $v) { $post['title'.$i] = $title[$i]; $post['author'.$i] = $author[$i]; $post['digest'.$i] = $summary[$i]; $post['content'.$i] = $content[$i]; $post['fileid'.$i] = $photoid[$i]; if ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i]; $i++; } } $post['count'] = $i; $post['token'] = $this->_token; $send_snoopy->submit($submit,$post); $tmp = $send_snoopy->results; $this->log('step2:'.$tmp); $json = json_decode($tmp,true); return $json; } /** * 发送媒体文件 * @param $id 用户的uid(即FakeId) * @param $fid 文件id * @param $type 文件类型 */ public function sendFile($id,$fid,$type) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = $type; $post['token'] = $this->_token; $post['fid'] = $fid; $post['fileid'] = $fid; $post['error'] = 'false'; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log('sendfile:'.$result); $json = json_decode($result,true); if ($json && $json['ret']==0) return true; else return false; } /** * 获取素材库文件列表 * @param $type 文件类型: 2:图片 3:音频 4:视频 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @return array */ public function getFileList($type,$page,$pagesize=10) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $begin = $page*$pagesize; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/filepage?token=".$this->_token."&lang=zh_CN&type=$type&random=0.".$t."&begin=$begin&count=$pagesize&f=json"; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('filelist:'.$result); $json = json_decode($result,true); if (isset($json['page_info'])) return $json['page_info']; else return false; } /** * 发送图文信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 文件id */ public function sendImage($id,$fid) { return $this->sendFile($id,$fid,2); } /** * 发送语音信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 语音文件id */ public function sendAudio($id,$fid) { return $this->sendFile($id,$fid,3); } /** * 发送视频信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 视频文件id */ public function sendVideo($id,$fid) { return $this->sendFile($id,$fid,4); } /** * 发送预览图文消息 * @param string $account 账户名称(user_name) * @param string $title 标题 * @param string $summary 摘要 * @param string $content 内容 * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取) * @param string $srcurl 原文链接 * @return json */ public function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') { $send_snoopy = new Snoopy; $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview"; $send_snoopy->set_submit_normal(); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN'; $post = array( 'AppMsgId'=>'', 'ajax'=>1, 'content0'=>$content, 'count'=>1, 'digest0'=>$summary, 'error'=>'false', 'fileid0'=>$photoid, 'preusername'=>$account, 'sourceurl0'=>$srcurl, 'title0'=>$title, ); $post['token'] = $this->_token; $send_snoopy->submit($submit,$post); $tmp = $send_snoopy->results; $this->log('sendpreview:'.$tmp); $json = json_decode($tmp,true); return $json; } /** * 获取用户的信息 * @param string $id 用户的uid(即FakeId) * @return array {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]} */ public function getInfo($id) { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/getcontactinfo"; $post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id); $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); $result = json_decode($send_snoopy->results,true); if(isset($result['contact_info'])){ return $result['contact_info']; } return false; } /** * 获得头像数据 * * @param FakeId $fakeid * @return JPG二进制数据 */ public function getHeadImg($fakeid){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=".$this->_token."&lang=zh_CN"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('Head image:'.$fakeid.'; length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 获取消息更新数目 * @param int $lastid 最近获取的消息ID,为0时获取总消息数目 * @return int 数目 */ public function getNewMsgNum($lastid=0){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=".$lastid; $post = array('ajax'=>1,'token'=>$this->_token); $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); $result = json_decode($send_snoopy->results,1); if(!$result){ return false; } return intval($result['newTotalMsgCount']); } /** * 获取最新一条消息 * @return array {"id":"最新一条id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容","playLength":"0","length":"0","source":"","starred":"0","status":"4"} */ public function getTopMsg(){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=".$this->_token; $send_snoopy->fetch($submit); $this->log($send_snoopy->results); $result = $send_snoopy->results; $json = json_decode($result,true); if (isset($json['msg_items'])) { $json = json_decode($json['msg_items'],true); if(isset($json['msg_item'])) return array_shift($json['msg_item']); } return false; } /** * 获取新消息 * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取 * @param $offset lastid起算第一条的偏移量 * @param $perpage 每页获取多少条 * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内) * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0 * @param $star 是否星标组信息 * @return array[] 同getTopMsg()返回的字段结构相同 */ public function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=".$this->_token; $lastid = $lastid===0 ? '':$lastid; $addstar = $star?'&action=star':''; $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&lang=zh_CN{$addstar}&count=$perpage&timeline=$today&day=$day&frommsgid=$lastid&offset=$offset&token=".$this->_token; $send_snoopy->fetch($submit); $this->log($send_snoopy->results); $result = $send_snoopy->results; $json = json_decode($result,true); if (isset($json['msg_items'])) { $json = json_decode($json['msg_items'],true); if(isset($json['msg_item'])) return $json['msg_item']; } return false; } /** * 获取图片消息 * @param int $msgid 消息id * @param string $mode 图片尺寸(large/small) * @return jpg二进制文件 */ public function getMsgImage($msgid,$mode='large'){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/getimgdata?token=".$this->_token."&msgid=$msgid&mode=$mode&source=&fileId=0"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('msg image:'.$msgid.';length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 获取语音消息 * @param int $msgid 消息id * @return mp3二进制文件 */ public function getMsgVoice($msgid){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=".$this->_token."&msgid=$msgid&fileId=0"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('msg voice:'.$msgid.';length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 开启开发者模式 */ public function openDevModel() { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; $post['flag']=1; $post['type']=2; $post['token']=$this->_token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if(!$result){ return false; } return true; } /** * 关闭编辑模式 */ public function closeEditModel() { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; $post['flag']=0; $post['type']=1; $post['token']=$this->_token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if(!$result){ return false; } return true; } /** * 配置接口信息 * @param string $url 接口回调URL * @param string $token 接口Token */ public function setUrlToken($url, $token) { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/interface&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=".$this->_token; $post['url'] = $url; $post['callback_token'] = $token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if ($json && $json['ret']==0) return true; return false; } /** * 快速设置接口 * @param string $url 接口回调URL * @param string $token 接口Token */ public function quickSetInterface($url, $token) { if ($this->closeEditModel() && $this->openDevModel() && $this->setUrlToken($url, $token)) return true; return false; } /** * 模拟登录获取cookie * @return [type] [description] */ public function login(){ $snoopy = new Snoopy; $submit = "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"; $post["username"] = $this->_account; $post["pwd"] = md5($this->_password); $post["f"] = "json"; $post["imgcode"] = ""; $snoopy->referer = "https://mp.weixin.qq.com/"; $snoopy->submit($submit,$post); $cookie = ''; $this->log($snoopy->results); $result = json_decode($snoopy->results,true); if (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) { return false; } foreach ($snoopy->headers as $key => $value) { $value = trim($value); if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $value,$match)) $cookie .=$match[1].'='.$match[2].'; '; } preg_match("/token=(\d+)/i",$result['redirect_url'],$matches); if($matches){ $this->_token = $matches[1]; $this->log('token:'.$this->_token); } $cookies='{"cookie":"'.$cookie.'","token":"'.$this->_token.'"}'; $this->saveCookie($this->_cookiename,$cookies); return $cookie; } /** * 把cookie写入缓存 * @param string $filename 缓存文件名 * @param string $content 文件内容 * @return bool */ public function saveCookie($filename,$content){ return S($filename,$content,$this->_cookieexpired); } /** * 读取cookie缓存内容 * @param string $filename 缓存文件名 * @return string cookie */ public function getCookie($filename){ $data = S($filename); if($data){ $login=json_decode($data,true); $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $login['cookie']; $send_snoopy->maxredirs = 0; $url = "https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=".$login['token']; $send_snoopy->fetch($url); $header = $send_snoopy->headers; $this->log('header:'.print_r($send_snoopy->headers,true)); if( strstr($header[3], 'EXPIRED')){ return $this->login(); }else{ $this->_token =$login['token']; return $login['cookie']; } }else{ return $this->login(); } } /** * 验证cookie的有效性 * @return bool */ public function checkValid() { if (!$this->cookie || !$this->_token) return false; $send_snoopy = new Snoopy; $post = array('ajax'=>1,'token'=>$this->_token); $submit = "https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; if(json_decode($result,1)){ return true; }else{ return false; } } private function log($log){ if ($this->debug ) { if (function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); }elseif (class_exists('Log')) { Log::write('wechat:'.$log, Log::DEBUG); } } return false; } } ================================================ FILE: old_version/Thinkphp/Wechatpay.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * 参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN * usage: * $options = array( * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 * 'partnerid'=>'88888888', //财付通商户身份标识 * 'partnerkey'=>'', //财付通商户权限密钥Key * 'paysignkey'=>'' //商户签名密钥Key * ); * $payObj = new Wechatpay($options); * $package = $payObj->createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type,$bank_type,$input_charset,$time_start,$time_expire,$transport_fee,$product_fee,$goods_tag,$attach); * */ class Wechatpay { const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin'; const AUTH_URL = '/token?grant_type=client_credential&'; const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀 const PAY_DELIVERNOTIFY = '/pay/delivernotify?'; const PAY_ORDERQUERY = '/pay/orderquery?'; private $appid; private $appsecret; private $access_token; private $user_token; private $partnerid; private $partnerkey; private $paysignkey; public $debug = false; public $errCode = 40001; public $errMsg = "no access"; private $_logcallback; public function __construct($options) { $this->appid = isset($options['appid'])?$options['appid']:''; $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; $this->partnerid = isset($options['partnerid'])?$options['partnerid']:''; $this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:''; $this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:''; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; } private function log($log){ if ($this->debug ) { if (function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); }elseif (class_exists('Log')) { Log::write('wechat:'.$log, Log::DEBUG); } } return false; } /** * GET 请求 * @param string $url */ private function http_get($url){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * POST 请求 * @param string $url * @param array $param * @param boolean $post_file 是否文件上传 * @return string content */ private function http_post($url,$param,$post_file=false){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (is_string($param) || $post_file) { $strPOST = $param; } else { $aPOST = array(); foreach($param as $key=>$val){ $aPOST[] = $key."=".urlencode($val); } $strPOST = join("&", $aPOST); } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($oCurl, CURLOPT_POST,true); curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * 获取access_token * @param string $appid 如在类初始化时已提供,则可为空 * @param string $appsecret 如在类初始化时已提供,则可为空 * @param string $token 手动指定access_token,非必要情况不建议用 */ public function checkAuth($appid='',$appsecret='',$token=''){ if (!$appid || !$appsecret) { $appid = $this->appid; $appsecret = $this->appsecret; } $authname = 'wechat_access_token'.$appid; if ($token) { //手动指定token,优先使用 $this->access_token=$token; return $this->access_token; } if ($rs = S($authname)) { $this->access_token = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->access_token = $json['access_token']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; S($authname,$this->access_token,$expire); return $this->access_token; } return false; } /** * 删除验证数据 * @param string $appid */ public function resetAuth($appid=''){ if (!$appid) $appid = $this->appid; $this->access_token = ''; $authname = 'wechat_access_token'.$appid; S($authname,null); return true; } /** * 微信api不支持中文转义的json结构 * @param array $arr */ static function json_encode($arr) { $parts = array (); $is_list = false; //Find out if the given array is a numerical array $keys = array_keys ( $arr ); $max_length = count ( $arr ) - 1; if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1 $is_list = true; for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position if ($i != $keys [$i]) { //A key fails at position check. $is_list = false; //It is an associative array. break; } } } foreach ( $arr as $key => $value ) { if (is_array ( $value )) { //Custom handling for arrays if ($is_list) $parts [] = self::json_encode ( $value ); /* :RECURSION: */ else $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */ } else { $str = ''; if (! $is_list) $str = '"' . $key . '":'; //Custom handling for multiple data types if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000) $str .= $value; //Numbers elseif ($value === false) $str .= 'false'; //The booleans elseif ($value === true) $str .= 'true'; else $str .= '"' . addslashes ( $value ) . '"'; //All other things // :TODO: Is there any more datatype we should be in the lookout for? (Object?) $parts [] = $str; } } $json = implode ( ',', $parts ); if ($is_list) return '[' . $json . ']'; //Return numerical JSON return '{' . $json . '}'; //Return associative JSON } /** * 获取签名 * @param array $arrdata 签名数组 * @param string $method 签名方法 * @return boolean|string 签名值 */ public function getSignature($arrdata,$method="sha1") { if (!function_exists($method)) return false; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $paySign = $method($paramstring); return $paySign; } /** * 生成随机字串 * @param number $length 长度,默认为16,最长为32字节 * @return string */ public function generateNonceStr($length=16){ // 密码字符集,可任意添加你需要的字符 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for($i = 0; $i < $length; $i++) { $str .= $chars[mt_rand(0, strlen($chars) - 1)]; } return $str; } /** * 生成原生支付url * @param number $productid 商品编号,最长为32字节 * @return string */ public function createNativeUrl($productid){ $nativeObj["appid"] = $this->appid; $nativeObj["appkey"] = $this->paysignkey; $nativeObj["productid"] = urlencode($productid); $nativeObj["timestamp"] = time(); $nativeObj["noncestr"] = $this->generateNonceStr(); $nativeObj["sign"] = $this->getSignature($nativeObj); unset($nativeObj["appkey"]); $bizString = ""; foreach($nativeObj as $key => $value) { if(strlen($bizString) == 0) $bizString .= $key . "=" . $value; else $bizString .= "&" . $key . "=" . $value; } return "weixin://wxpay/bizpayurl?".$bizString; //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX×tamp=XXXXXX&noncestr=XXXXXX } /** * 生成订单package字符串 * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一 * @param string $body 必填,商品描述,128 字节以下 * @param int $total_fee 必填,订单总金额,单位为分 * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内 * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内 * @param int $fee_type 必填,现金支付币种,默认1:人民币 * @param string $bank_type 必填,银行通道类型,默认WX * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss * @param string $time_expire 交易结束时间,也是订单失效时间 * @param int $transport_fee 物流费用,单位为分 * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee * @param string $goods_tag 商品标记,优惠券时可能用到 * @param string $attach 附加数据,notify接口原样返回 * @return string */ public function 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=""){ $arrdata = array("bank_type" => $bank_type, "body" => $body, "partner" => $this->partnerid, "out_trade_no" => $out_trade_no, "total_fee" => $total_fee, "fee_type" => $fee_type, "notify_url" => $notify_url, "spbill_create_ip" => $spbill_create_ip, "input_charset" => $input_charset); if ($time_start) $arrdata['time_start'] = $time_start; if ($time_expire) $arrdata['time_expire'] = $time_expire; if ($transport_fee) $arrdata['transport_fee'] = $transport_fee; if ($product_fee) $arrdata['product_fee'] = $product_fee; if ($goods_tag) $arrdata['goods_tag'] = $goods_tag; if ($attach) $arrdata['attach'] = $attach; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $stringSignTemp = $paramstring . "&key=" . $this->partnerkey; $signValue = strtoupper(md5($stringSignTemp)); $package = http_build_query($arrdata) . "&sign=" . $signValue; return $package; } /** * 支付签名(paySign)生成方法 * @param string $package 订单详情字串 * @param string $timeStamp 当前时间戳(需与JS输出的一致) * @param string $nonceStr 随机串(需与JS输出的一致) * @return string 返回签名字串 */ public function getPaySign($package, $timeStamp, $nonceStr){ $arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey); $paySign = $this->getSignature($arrdata); return $paySign; } /** * 回调通知签名验证 * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取 * @return boolean */ public function checkOrderSignature($orderxml=''){ if (!$orderxml) { $postStr = file_get_contents("php://input"); if (!empty($postStr)) { $orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); } else return false; } $arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']); $paySign = $this->getSignature($arrdata); if ($paySign!=$orderxml['AppSignature']) return false; return true; } /** * 发货通知 * @param string $openid 用户open_id * @param string $transid 交易单号 * @param string $out_trade_no 第三方订单号 * @param int $status 0:发货失败;1:已发货 * @param string $msg 失败原因 * @return boolean|array */ public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){ if (!$this->access_token && !$this->checkAuth()) return false; $postdata = array( "appid"=>$this->appid, "appkey"=>$this->paysignkey, "openid"=>$openid, "transid"=>strval($transid), "out_trade_no"=>strval($out_trade_no), "deliver_timestamp"=>strval(time()), "deliver_status"=>strval($status), "deliver_msg"=>$msg, ); $postdata['app_signature'] = $this->getSignature($postdata); $postdata['sign_method'] = 'sha1'; unset($postdata['appkey']); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 查询订单信息 * @param string $out_trade_no 订单号 * @return boolean|array */ public function getPayOrder($out_trade_no) { if (!$this->access_token && !$this->checkAuth()) return false; $sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}")); $postdata = array( "appid"=>$this->appid, "appkey"=>$this->paysignkey, "package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign", "timestamp"=>strval(time()), ); $postdata['app_signature'] = $this->getSignature($postdata); $postdata['sign_method'] = 'sha1'; unset($postdata['appkey']); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg'].json_encode($postdata); return false; } return $json["order_info"]; } return false; } /** * 设置用户授权密钥 * @param string $user_token * @return string */ public function setUserToken($user_token) { return $this->user_token = $user_token; } /** * 获取收货地址JS的签名 * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用 * @param string $appId * @param string $url * @param int $timeStamp * @param string $nonceStr * @param string $user_token * @return Ambigous */ public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){ if (!$user_token) $user_token = $this->user_token; if (!$user_token) { $this->errMsg = 'no user access token found!'; return false; } $url = htmlspecialchars_decode($url); $arrdata = array( 'appid'=>$this->appid, 'url'=>$url, 'timestamp'=>strval($timeStamp), 'noncestr'=>$nonceStr, 'accesstoken'=>$user_token ); return $this->getSignature($arrdata); } } ================================================ FILE: old_version/snoopy.class.php ================================================ Copyright (c): 1999-2008 New Digital Group, all rights reserved Version: 1.2.4 * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You may contact the author of Snoopy by e-mail at: monte@ohrt.com The latest version of Snoopy can be obtained from: http://snoopy.sourceforge.net/ *************************************************/ class Snoopy { /**** Public variables ****/ /* user definable vars */ var $host = "www.php.net"; // host name we are connecting to var $port = 80; // port we are connecting to var $proxy_host = ""; // proxy host to use var $proxy_port = ""; // proxy port to use var $proxy_user = ""; // proxy user to use var $proxy_pass = ""; // proxy password to use var $agent = "Mozilla/5.0"; // agent we masquerade as var $referer = ""; // referer info to pass var $cookies = array(); // array of cookies to pass // $cookies["username"]="joe"; var $rawheaders = array(); // array of raw headers to send // $rawheaders["Content-type"]="text/html"; var $maxredirs = 5; // http redirection depth maximum. 0 = disallow var $lastredirectaddr = ""; // contains address of last redirected address var $offsiteok = true; // allows redirection off-site var $maxframes = 0; // frame content depth maximum. 0 = disallow var $expandlinks = true; // expand links to fully qualified URLs. // this only applies to fetchlinks() // submitlinks(), and submittext() var $passcookies = true; // pass set cookies back through redirects // NOTE: this currently does not respect // dates, domains or paths. var $user = ""; // user for http authentication var $pass = ""; // password for http authentication // http accept types var $accept = "application/json, text/javascript, */*; q=0.01"; var $results = ""; // where the content is put var $error = ""; // error messages sent here var $response_code = ""; // response code returned from server var $headers = array(); // headers returned from server sent here var $maxlength = 500000; // max return data length (body) var $read_timeout = 0; // timeout on read operations, in seconds // supported only since PHP 4 Beta 4 // set to 0 to disallow timeouts var $timed_out = false; // if a read operation timed out var $status = 0; // http request status var $temp_dir = "/tmp"; // temporary directory that the webserver // has permission to write to. // under Windows, this should be C:\temp var $curl_path = "/usr/local/bin/curl"; // Snoopy will use cURL for fetching // SSL content if a full system path to // the cURL binary is supplied here. // set to false if you do not have // cURL installed. See http://curl.haxx.se // for details on installing cURL. // Snoopy does *not* use the cURL // library functions built into php, // as these functions are not stable // as of this Snoopy release. /**** Private variables ****/ var $_maxlinelen = 4096; // max line length (headers) var $_httpmethod = "GET"; // default http request method var $_httpversion = "HTTP/1.0"; // default http request version var $_submit_method = "POST"; // default submit method var $_submit_type = "application/x-www-form-urlencoded"; // default submit type var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type var $_redirectaddr = false; // will be set if page fetched is a redirect var $_redirectdepth = 0; // increments on an http redirect var $_frameurls = array(); // frame src urls var $_framedepth = 0; // increments on frame depth var $_isproxy = false; // set if using a proxy server var $_fp_timeout = 30; // timeout for socket connection /*======================================================================*\ Function: fetch Purpose: fetch the contents of a web page (and possibly other protocols in the future like ftp, nntp, gopher, etc.) Input: $URI the location of the page to fetch Output: $this->results the output text from the fetch \*======================================================================*/ function fetch($URI) { //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); $URI_PARTS = parse_url($URI); if (!empty($URI_PARTS["user"])) $this->user = $URI_PARTS["user"]; if (!empty($URI_PARTS["pass"])) $this->pass = $URI_PARTS["pass"]; if (empty($URI_PARTS["query"])) $URI_PARTS["query"] = ''; if (empty($URI_PARTS["path"])) $URI_PARTS["path"] = ''; switch(strtolower($URI_PARTS["scheme"])) { case "http": $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_connect($fp)) { if($this->_isproxy) { // using proxy, send entire URI $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httprequest($path, $fp, $URI, $this->_httpmethod); } $this->_disconnect($fp); if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; $this->fetch($this->_redirectaddr); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } } else { return false; } return true; break; case "https": if (!function_exists('curl_init')) { if(!$this->curl_path) return false; if(function_exists("is_executable")) if (!is_executable($this->curl_path)) return false; } $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_isproxy) { // using proxy, send entire URI $this->_httpsrequest($URI,$URI,$this->_httpmethod); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httpsrequest($path, $URI, $this->_httpmethod); } if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^https://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; $this->fetch($this->_redirectaddr); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } return true; break; default: // not a valid protocol $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; return false; break; } return true; } /*======================================================================*\ Function: submit Purpose: submit an http form Input: $URI the location to post the data $formvars the formvars to use. format: $formvars["var"] = "val"; $formfiles an array of files to submit format: $formfiles["var"] = "/dir/filename.ext"; Output: $this->results the text output from the post \*======================================================================*/ function submit($URI, $formvars="", $formfiles="") { unset($postdata); $postdata = $this->_prepare_post_body($formvars, $formfiles); $URI_PARTS = parse_url($URI); if (!empty($URI_PARTS["user"])) $this->user = $URI_PARTS["user"]; if (!empty($URI_PARTS["pass"])) $this->pass = $URI_PARTS["pass"]; if (empty($URI_PARTS["query"])) $URI_PARTS["query"] = ''; if (empty($URI_PARTS["path"])) $URI_PARTS["path"] = ''; switch(strtolower($URI_PARTS["scheme"])) { case "http": $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_connect($fp)) { if($this->_isproxy) { // using proxy, send entire URI $this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata); } $this->_disconnect($fp); if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; if( strpos( $this->_redirectaddr, "?" ) > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get else $this->submit($this->_redirectaddr,$formvars, $formfiles); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } } else { return false; } return true; break; case "https": if (!function_exists('curl_init')) { if(!$this->curl_path) return false; if(function_exists("is_executable")) if (!is_executable($this->curl_path)) return false; } $this->host = $URI_PARTS["host"]; if(!empty($URI_PARTS["port"])) $this->port = $URI_PARTS["port"]; if($this->_isproxy) { // using proxy, send entire URI $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata); } else { $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); // no proxy, send only the path $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata); } if($this->_redirectaddr) { /* url was redirected, check if we've hit the max depth */ if($this->maxredirs > $this->_redirectdepth) { if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); // only follow redirect if it's on this site, or offsiteok is true if(preg_match("|^https://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) { /* follow the redirect */ $this->_redirectdepth++; $this->lastredirectaddr=$this->_redirectaddr; if( strpos( $this->_redirectaddr, "?" ) > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get else $this->submit($this->_redirectaddr,$formvars, $formfiles); } } } if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) { $frameurls = $this->_frameurls; $this->_frameurls = array(); while(list(,$frameurl) = each($frameurls)) { if($this->_framedepth < $this->maxframes) { $this->fetch($frameurl); $this->_framedepth++; } else break; } } return true; break; default: // not a valid protocol $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; return false; break; } return true; } /*======================================================================*\ Function: fetchlinks Purpose: fetch the links from a web page Input: $URI where you are fetching from Output: $this->results an array of the URLs \*======================================================================*/ function fetchlinks($URI) { if ($this->fetch($URI)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_striplinks($this->results[$x]); } else $this->results = $this->_striplinks($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results, $URI); return true; } else return false; } /*======================================================================*\ Function: fetchform Purpose: fetch the form elements from a web page Input: $URI where you are fetching from Output: $this->results the resulting html form \*======================================================================*/ function fetchform($URI) { if ($this->fetch($URI)) { if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_stripform($this->results[$x]); } else $this->results = $this->_stripform($this->results); return true; } else return false; } /*======================================================================*\ Function: fetchtext Purpose: fetch the text from a web page, stripping the links Input: $URI where you are fetching from Output: $this->results the text from the web page \*======================================================================*/ function fetchtext($URI) { if($this->fetch($URI)) { if(is_array($this->results)) { for($x=0;$xresults);$x++) $this->results[$x] = $this->_striptext($this->results[$x]); } else $this->results = $this->_striptext($this->results); return true; } else return false; } /*======================================================================*\ Function: submitlinks Purpose: grab links from a form submission Input: $URI where you are submitting from Output: $this->results an array of the links from the post \*======================================================================*/ function submitlinks($URI, $formvars="", $formfiles="") { if($this->submit($URI,$formvars, $formfiles)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) { $this->results[$x] = $this->_striplinks($this->results[$x]); if($this->expandlinks) $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); } } else { $this->results = $this->_striplinks($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results,$URI); } return true; } else return false; } /*======================================================================*\ Function: submittext Purpose: grab text from a form submission Input: $URI where you are submitting from Output: $this->results the text from the web page \*======================================================================*/ function submittext($URI, $formvars = "", $formfiles = "") { if($this->submit($URI,$formvars, $formfiles)) { if($this->lastredirectaddr) $URI = $this->lastredirectaddr; if(is_array($this->results)) { for($x=0;$xresults);$x++) { $this->results[$x] = $this->_striptext($this->results[$x]); if($this->expandlinks) $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); } } else { $this->results = $this->_striptext($this->results); if($this->expandlinks) $this->results = $this->_expandlinks($this->results,$URI); } return true; } else return false; } /*======================================================================*\ Function: set_submit_multipart Purpose: Set the form submission content type to multipart/form-data \*======================================================================*/ function set_submit_multipart() { $this->_submit_type = "multipart/form-data"; } /*======================================================================*\ Function: set_submit_normal Purpose: Set the form submission content type to application/x-www-form-urlencoded \*======================================================================*/ function set_submit_normal() { $this->_submit_type = "application/x-www-form-urlencoded"; } /*======================================================================*\ Private functions \*======================================================================*/ /*======================================================================*\ Function: _striplinks Purpose: strip the hyperlinks from an html document Input: $document document to strip. Output: $match an array of the links \*======================================================================*/ function _striplinks($document) { preg_match_all("'<\s*a\s.*?href\s*=\s* # find ]+)) # if quote found, match up to next matching # quote, otherwise match up to next space 'isx",$document,$links); // catenate the non-empty matches from the conditional subpattern while(list($key,$val) = each($links[2])) { if(!empty($val)) $match[] = $val; } while(list($key,$val) = each($links[3])) { if(!empty($val)) $match[] = $val; } // return the links return $match; } /*======================================================================*\ Function: _stripform Purpose: strip the form elements from an html document Input: $document document to strip. Output: $match an array of the links \*======================================================================*/ function _stripform($document) { preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); // catenate the matches $match = implode("\r\n",$elements[0]); // return the links return $match; } /*======================================================================*\ Function: _striptext Purpose: strip the text from an html document Input: $document document to strip. Output: $text the resulting text \*======================================================================*/ function _striptext($document) { // I didn't use preg eval (//e) since that is only available in PHP 4.0. // so, list your entities one by one here. I included some of the // more common ones. $search = array("']*?>.*?'si", // strip out javascript "'<[\/\!]*?[^<>]*?>'si", // strip out html tags "'([\r\n])[\s]+'", // strip out white space "'&(quot|#34|#034|#x22);'i", // replace html entities "'&(amp|#38|#038|#x26);'i", // added hexadecimal values "'&(lt|#60|#060|#x3c);'i", "'&(gt|#62|#062|#x3e);'i", "'&(nbsp|#160|#xa0);'i", "'&(iexcl|#161);'i", "'&(cent|#162);'i", "'&(pound|#163);'i", "'&(copy|#169);'i", "'&(reg|#174);'i", "'&(deg|#176);'i", "'&(#39|#039|#x27);'", "'&(euro|#8364);'i", // europe "'&a(uml|UML);'", // german "'&o(uml|UML);'", "'&u(uml|UML);'", "'&A(uml|UML);'", "'&O(uml|UML);'", "'&U(uml|UML);'", "'ß'i", ); $replace = array( "", "", "\\1", "\"", "&", "<", ">", " ", chr(161), chr(162), chr(163), chr(169), chr(174), chr(176), chr(39), chr(128), "�", "�", "�", "�", "�", "�", "�", ); $text = preg_replace($search,$replace,$document); return $text; } /*======================================================================*\ Function: _expandlinks Purpose: expand each link into a fully qualified URL Input: $links the links to qualify $URI the full URI to get the base from Output: $expandedLinks the expanded links \*======================================================================*/ function _expandlinks($links,$URI) { preg_match("/^[^\?]+/",$URI,$match); $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); $match = preg_replace("|/$|","",$match); $match_part = parse_url($match); $match_root = $match_part["scheme"]."://".$match_part["host"]; $search = array( "|^http://".preg_quote($this->host)."|i", "|^(\/)|i", "|^(?!http://)(?!mailto:)|i", "|/\./|", "|/[^\/]+/\.\./|" ); $replace = array( "", $match_root."/", $match."/", "/", "/" ); $expandedLinks = preg_replace($search,$replace,$links); return $expandedLinks; } /*======================================================================*\ Function: _httprequest Purpose: go get the http data from the server Input: $url the url to fetch $fp the current open file pointer $URI the full URI $body body contents to send if any (POST) Output: \*======================================================================*/ function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") { $cookie_headers = ''; if($this->passcookies && $this->_redirectaddr) $this->setcookies(); $URI_PARTS = parse_url($URI); if(empty($url)) $url = "/"; $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; if(!empty($this->agent)) $headers .= "User-Agent: ".$this->agent."\r\n"; if(!empty($this->host) && !isset($this->rawheaders['Host'])) { $headers .= "Host: ".$this->host; if(!empty($this->port) && $this->port!=80) $headers .= ":".$this->port; $headers .= "\r\n"; } if(!empty($this->accept)) $headers .= "Accept: ".$this->accept."\r\n"; if(!empty($this->referer)) $headers .= "Referer: ".$this->referer."\r\n"; if(!empty($this->cookies)) { if(!is_array($this->cookies)) $this->cookies = (array)$this->cookies; reset($this->cookies); if ( count($this->cookies) > 0 ) { $cookie_headers .= 'Cookie: '; foreach ( $this->cookies as $cookieKey => $cookieVal ) { $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; } $headers .= substr($cookie_headers,0,-2) . "\r\n"; } } if(!empty($this->rawheaders)) { if(!is_array($this->rawheaders)) $this->rawheaders = (array)$this->rawheaders; while(list($headerKey,$headerVal) = each($this->rawheaders)) $headers .= $headerKey.": ".$headerVal."\r\n"; } if(!empty($content_type)) { $headers .= "Content-type: $content_type"; if ($content_type == "multipart/form-data") $headers .= "; boundary=".$this->_mime_boundary; $headers .= "\r\n"; } if(!empty($body)) $headers .= "Content-length: ".strlen($body)."\r\n"; if(!empty($this->user) || !empty($this->pass)) $headers .= "Authorization: Basic ".base64_encode($this->user.":".$this->pass)."\r\n"; //add proxy auth headers if(!empty($this->proxy_user)) $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass)."\r\n"; $headers .= "\r\n"; // set the read timeout if needed if ($this->read_timeout > 0) socket_set_timeout($fp, $this->read_timeout); $this->timed_out = false; fwrite($fp,$headers.$body,strlen($headers.$body)); $this->_redirectaddr = false; unset($this->headers); while($currentHeader = fgets($fp,$this->_maxlinelen)) { if ($this->read_timeout > 0 && $this->_check_timeout($fp)) { $this->status=-100; return false; } if($currentHeader == "\r\n") break; // if a header begins with Location: or URI:, set the redirect if(preg_match("/^(Location:|URI:)/i",$currentHeader)) { // get URL portion of the redirect preg_match("/^(Location:|URI:)[ ]+(.*)/i",chop($currentHeader),$matches); // look for :// in the Location header to see if hostname is included if (!empty($matches)) { if(!preg_match("|\:\/\/|",$matches[2])) { // no host in the path, so prepend $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; // eliminate double slash if(!preg_match("|^/|",$matches[2])) $this->_redirectaddr .= "/".$matches[2]; else $this->_redirectaddr .= $matches[2]; } else $this->_redirectaddr = $matches[2]; } } if(preg_match("|^HTTP/|",$currentHeader)) { if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) { $this->status= $status[1]; } $this->response_code = $currentHeader; } $this->headers[] = $currentHeader; } $results = ''; do { $_data = fread($fp, $this->maxlength); if (strlen($_data) == 0) { break; } $results .= $_data; } while(true); if ($this->read_timeout > 0 && $this->_check_timeout($fp)) { $this->status=-100; return false; } // check if there is a a redirect meta tag if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) { $this->_redirectaddr = $this->_expandlinks($match[1],$URI); } // have we hit our frame depth and is there frame src to fetch? if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) { $this->results[] = $results; for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); } // have we already fetched framed content? elseif(is_array($this->results)) $this->results[] = $results; // no framed content else $this->results = $results; return true; } /*======================================================================*\ Function: _httpsrequest Purpose: go get the https data from the server using curl Input: $url the url to fetch $URI the full URI $body body contents to send if any (POST) Output: \*======================================================================*/ function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") { if($this->passcookies && $this->_redirectaddr) $this->setcookies(); $headers = array(); $URI_PARTS = parse_url($URI); if(empty($url)) $url = "/"; // GET ... header not needed for curl //$headers[] = $http_method." ".$url." ".$this->_httpversion; if(!empty($this->agent)) $headers[] = "User-Agent: ".$this->agent; if(!empty($this->host)) if(!empty($this->port) && $this->port!=80) $headers[] = "Host: ".$this->host.":".$this->port; else $headers[] = "Host: ".$this->host; if(!empty($this->accept)) $headers[] = "Accept: ".$this->accept; if(!empty($this->referer)) $headers[] = "Referer: ".$this->referer; if(!empty($this->cookies)) { if(!is_array($this->cookies)) $this->cookies = (array)$this->cookies; reset($this->cookies); if ( count($this->cookies) > 0 ) { $cookie_str = 'Cookie: '; foreach ( $this->cookies as $cookieKey => $cookieVal ) { $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; } $headers[] = substr($cookie_str,0,-2); } } if(!empty($this->rawheaders)) { if(!is_array($this->rawheaders)) $this->rawheaders = (array)$this->rawheaders; while(list($headerKey,$headerVal) = each($this->rawheaders)) $headers[] = $headerKey.": ".$headerVal; } if(!empty($content_type)) { if ($content_type == "multipart/form-data") $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; else $headers[] = "Content-type: $content_type"; } if(!empty($body)) $headers[] = "Content-length: ".strlen($body); if(!empty($this->user) || !empty($this->pass)) $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); if (function_exists('curl_init')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $URI); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_SSLVERSION,3); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_TIMEOUT, $this->read_timeout); if(!empty($body)) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); } $data = curl_exec($ch); if ($data === false) { $this->error = "Error: Curl error ".curl_error($ch); return false; } $parts = explode("\r\n\r\n",$data,2); $result_headers = explode("\r\n",$parts[0]); $results = $parts[1]; unset($parts); } else { for($curr_header = 0; $curr_header < count($headers); $curr_header++) { $safer_header = strtr( $headers[$curr_header], "\"", " " ); $cmdline_params .= " -H \"".$safer_header."\""; } if(!empty($body)) $cmdline_params .= " -d \"$body\""; if($this->read_timeout > 0) $cmdline_params .= " -m ".$this->read_timeout; $headerfile = tempnam($temp_dir, "sno"); exec($this->curl_path." -k -D \"$headerfile\"".$cmdline_params." \"".escapeshellcmd($URI)."\"",$results,$return); if($return) { $this->error = "Error: cURL could not retrieve the document, error $return."; return false; } $results = implode("\r\n",$results); $result_headers = file("$headerfile"); } $this->_redirectaddr = false; unset($this->headers); for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) { // if a header begins with Location: or URI:, set the redirect if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) { // get URL portion of the redirect preg_match("/^(Location: |URI:)\s+(.*)/",chop($result_headers[$currentHeader]),$matches); // look for :// in the Location header to see if hostname is included if (!empty($matches)) { if(!preg_match("|\:\/\/|",$matches[2])) { // no host in the path, so prepend $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host; // eliminate double slash if(!preg_match("|^/|",$matches[2])) $this->_redirectaddr .= "/".$matches[2]; else $this->_redirectaddr .= $matches[2]; } else $this->_redirectaddr = $matches[2]; } } if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) $this->response_code = $result_headers[$currentHeader]; $this->headers[] = $result_headers[$currentHeader]; } // check if there is a a redirect meta tag if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) { $this->_redirectaddr = $this->_expandlinks($match[1],$URI); } // have we hit our frame depth and is there frame src to fetch? if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) { $this->results[] = $results; for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); } // have we already fetched framed content? elseif(is_array($this->results)) $this->results[] = $results; // no framed content else $this->results = $results; if (isset($headerfile) && file_exists($headerfile)) unlink($headerfile); return true; } /*======================================================================*\ Function: setcookies() Purpose: set cookies for a redirection \*======================================================================*/ function setcookies() { for($x=0; $xheaders); $x++) { if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match)) $this->cookies[$match[1]] = urldecode($match[2]); } } /*======================================================================*\ Function: _check_timeout Purpose: checks whether timeout has occurred Input: $fp file pointer \*======================================================================*/ function _check_timeout($fp) { if ($this->read_timeout > 0) { $fp_status = socket_get_status($fp); if ($fp_status["timed_out"]) { $this->timed_out = true; return true; } } return false; } /*======================================================================*\ Function: _connect Purpose: make a socket connection Input: $fp file pointer \*======================================================================*/ function _connect(&$fp) { if(!empty($this->proxy_host) && !empty($this->proxy_port)) { $this->_isproxy = true; $host = $this->proxy_host; $port = $this->proxy_port; } else { $host = $this->host; $port = $this->port; } $this->status = 0; if($fp = fsockopen( $host, $port, $errno, $errstr, $this->_fp_timeout )) { // socket connection succeeded return true; } else { // socket connection failed $this->status = $errno; switch($errno) { case -3: $this->error="socket creation failed (-3)"; case -4: $this->error="dns lookup failure (-4)"; case -5: $this->error="connection refused or timed out (-5)"; default: $this->error="connection failed (".$errno.")"; } return false; } } /*======================================================================*\ Function: _disconnect Purpose: disconnect a socket connection Input: $fp file pointer \*======================================================================*/ function _disconnect($fp) { return(fclose($fp)); } /*======================================================================*\ Function: _prepare_post_body Purpose: Prepare post body according to encoding type Input: $formvars - form variables $formfiles - form upload files Output: post body \*======================================================================*/ function _prepare_post_body($formvars, $formfiles) { settype($formvars, "array"); settype($formfiles, "array"); $postdata = ''; if (count($formvars) == 0 && count($formfiles) == 0) return; if (is_string($formvars)) return $formvars; if((count($formvars) == 1) && isset($formvars[0])) return $formvars[0]; switch ($this->_submit_type) { case "application/x-www-form-urlencoded": reset($formvars); while(list($key,$val) = each($formvars)) { if (is_array($val) || is_object($val)) { while (list($cur_key, $cur_val) = each($val)) { $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; } } else $postdata .= urlencode($key)."=".urlencode($val)."&"; } break; case "multipart/form-data": $this->_mime_boundary = "--------".md5(uniqid(microtime())); reset($formvars); while(list($key,$val) = each($formvars)) { if (is_array($val) || is_object($val)) { while (list($cur_key, $cur_val) = each($val)) { $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; $postdata .= "$cur_val\r\n"; } } else { $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; $postdata .= "$val\r\n"; } } reset($formfiles); while (list($field_name, $file_names) = each($formfiles)) { settype($file_names, "array"); while (list(, $file_name) = each($file_names)) { $file_content = file_get_contents($file_name); if (!$file_content) continue; $base_name = basename($file_name); $postdata .= "--".$this->_mime_boundary."\r\n"; $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\nContent-Type: image/jpeg\r\n\r\n"; $postdata .= "$file_content\r\n"; } } $postdata .= "--".$this->_mime_boundary."--\r\n"; break; } return $postdata; } } ================================================ FILE: old_version/test/test2.php ================================================ 'demo@domain.com', 'password'=>'demo', 'datapath'=>'../data/cookie_', 'debug'=>true, 'logcallback'=>'logdebug' ); $wechat = new Wechatext($options); if ($wechat->checkValid()) { //获取分组列表 $grouplist = $wechat->getGroupList(); var_dump($grouplist); //获取用户列表 $userlist = $wechat->getUserlist(0,10); var_dump($userlist); $user = $userlist[0]; // 获取用户信息 $userdata = $wechat->getInfo($user['id']); var_dump($userdata); // 获取已保存的图文消息 $newslist = $wechat->getNewsList(0,10); var_dump($newslist); //获取用户最新消息 $topmsg = $wechat->getTopMsg(); var_dump($topmsg); $msglist = $wechat->getMsg(); var_dump($msglist); // 主动回复消息 if ($topmsg && $topmsg['has_reply']==0){ $wechat->send($user['id'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); $content = '这是一条Wechatext发出的测试微信'; $imgdata = file_get_contents('http://github.global.ssl.fastly.net/images/modules/dashboard/bootcamp/octocat_fork.png'); $img = '../data/send.png'; file_put_contents($img,$imgdata); //上传图片 $fileid = $wechat->uploadFile($img); echo 'fileid:'.$fileid; //if ($fileid) $re = $wechat->sendImage($user['id'],$fileid); //发送图文信息 $re = $wechat->sendPreview($userdata['user_name'],$content,$content,$content,$fileid,'http://github.com/dodgepudding/wechat-php-sdk'); var_dump($re); //发送视频 //$re = $wechat->sendVideo($user['id'],$fileid); $re = $wechat->getFileList(2,0,10); var_dump($re); } else { echo 'no top msg'; } } else { echo "login error"; } ================================================ FILE: old_version/test/test3.php ================================================ $sid, 'datapath'=>'../log/cookiecode_', 'debug'=>true, 'logcallback'=>'logdebug' ); $wechat = new Wechatauth($options); if (isset($_POST['code'])) { $logincode = $_POST['code']; $vres = $wechat->set_login_code($logincode)->verify_code(); if ($vres===false) { $result = array('status'=>0); } else { $result = array('status'=>$vres); if ($vres==200) { $result['info'] = $wechat->get_login_info(); $result['cookie'] = $wechat->get_login_cookie(true); } } die(json_encode($result)); } $logincode = $wechat->get_login_code(); $qrimg = $wechat->get_code_image(); ?> 微信二维码登陆接口

微信里扫描以下二维码实现登陆

================================================ FILE: old_version/test/weshare.html ================================================ weshare

这里演示了微信分享前调和回调方法,点击微信右上角分享相关的功能,即可看到各类返回的alert信息

================================================ FILE: old_version/wechat.js ================================================ /** * 微信网页端调用JS(官方于微信6.0.2版本发布新版JSAPI接口,此接口文件废弃) * @author dodge * @contact dodgepudding@gmail.com * @link http://blog.4wer.com/wechat-timeline-share * @version 1.1 * * 自定义分享使用: * WeixinJS.hideOptionMenu() 隐藏右上角按钮 * WeixinJS.showOptionMenu() 显示右上角按钮 * WeixinJS.hideToolbar() 隐藏工具栏 * WeixinJS.showToolbar() 显示工具栏 * WeixinJS.getNetworkType() 获取网络状态 * WeixinJS.closeWindow() 关闭窗口 * WeixinJS.scanQRCode() 扫描二维码 * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 * WeixinJS.sendEmail(title,content) 发送邮件 * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 * WeixinJS.addContact(username) 添加微信账号 * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 * 自定义分享内容数据格式: * var dataForWeixin={ appId:"", MsgImg:"消息图片路径", TLImg:"时间线图路径", url:"分享url路径", title:"标题", desc:"描述", fakeid:"", prepare:function(argv){ if (typeof argv.shareTo!='undefined') switch(argv.shareTo) { case 'friend': //发送给朋友 alert(argv.scene); //friend break; case 'timeline': //发送给朋友 break; case 'weibo': //发送到微博 alert(argv.url); break; case 'favorite': //收藏 alert(argv.scene);//favorite break; case 'connector': //分享到第三方应用 alert(argv.scene);//connector break; default: } }, callback:function(res){ //发送给好友或应用 if (res.err_msg=='send_app_msg:confirm') { //todo:func1(); alert(res.err_desc); } if (res.err_msg=='send_app_msg:cancel') { //todo:func2(); alert(res.err_desc); } //分享到朋友圈 if (res.err_msg=='share_timeline:ok') { //todo:func1(); alert(res.err_desc); } if (res.err_msg=='share_timeline:cancel') { //todo:func1(); alert(res.err_desc); } //分享到微博 if (res.err_msg=='share_weibo:confirm') { //todo:func1(); alert(res.err_desc); } if (res.err_msg=='share_weibo:cancel') { //todo:func1(); alert(res.err_desc); } //收藏或分享到应用 if (res.err_msg=='send_app_msg:ok') { //todo:func1(); alert(res.err_desc); } } }; */ WeixinJS = typeof WeixinJS!='undefined' || {}; //隐藏右上角按钮 WeixinJS.hideOptionMenu = function() { document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideOptionMenu'); }); }; //显示右上角按钮 WeixinJS.showOptionMenu = function() { document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showOptionMenu'); }); }; //隐藏底部导航栏 WeixinJS.hideToolbar = function() { document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideToolbar'); }); }; //显示底部导航栏 WeixinJS.showToolbar = function() { document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showToolbar'); }); }; //网页获取用户网络状态 netType={"network_type:wifi":"wifi网络","network_type:edge":"非wifi,包含3G/2G","network_type:fail":"网络断开连接","network_type:wwan":"2g或者3g"}; WeixinJS.getNetworkType = function(callback) { document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getNetworkType',{}, function(res){ //result: network_type:wifi,network_type:edge,network_type:fail,network_type:wwan //netType[e.err_msg] callback(res.err_msg); }); }); }; //关闭窗口 WeixinJS.closeWindow = function() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("closeWindow", {}); }; //扫描二维码 WeixinJS.scanQRCode = function() { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("scanQRCode", {}); }; //使用浏览器打开网址 WeixinJS.openUrlByExtBrowser=function(url){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openUrlByExtBrowser",{"url" : url}); }; //跳转到指定公众账号页面 WeixinJS.jumpToBizProfile=function(username){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("jumpToBizProfile",{"tousername" : username}); }; //发送邮件 WeixinJS.sendEmail=function(title,content){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("sendEmail",{ "title" : title, "content" : content }); }; //查看地图 WeixinJS.openProductView=function(latitude,longitude,name,address,scale,infoUrl){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openProductView",{ "latitude" : latitude, //纬度 "longitude" : longitude, //经度 "name" : name, //名称 "address" : address, //地址 "scale" : scale, //地图缩放级别 "infoUrl" : infoUrl, //查看位置界面底部的超链接 }); }; //添加微信账号 WeixinJS.addContact=function weixinAddContact(username){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("addContact", { "webtype": "1", "username": username }, function(e) { WeixinJSBridge.log(e.err_msg); //e.err_msg:add_contact:added 已经添加 //e.err_msg:add_contact:cancel 取消添加 //e.err_msg:add_contact:ok 添加成功 if(e.err_msg == 'add_contact:added' || e.err_msg == 'add_contact:ok'){ //关注成功,或者已经关注过 } }); }; /** * 调出微信内图片预览scrollview * @param array urls 图片url数组 * @param string current 当前图片url */ WeixinJS.imagePreview = function(urls,current) { if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("imagePreview", { current: current, urls: urls }); }; //微信JsApi支付接口 WeixinJS.payCallback = function(appId,package,timeStamp,nonceStr,signType,paySign,callback){ if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : appId.toString(), "timeStamp" : timeStamp.toString(), "nonceStr" : nonceStr.toString(), "package" : package.toString(), "signType" : signType.toString(), "paySign" : paySign.toString() },function(res){ // res.err_msg == "get_brand_wcpay_request:ok" return true; // res.err_msg == "get_brand_wcpay_request:cancel" return false; callback(res); }); }; //编辑收货地址 WeixinJS.editAddress = function(appId,addrSign,timeStamp,nonceStr,callback){ var postdata = { "appId" : appId.toString(), "scope" : "jsapi_address", "signType" : "sha1", "addrSign" : addrSign.toString(), "timeStamp" : timeStamp.toString(), "nonceStr" : nonceStr.toString() }; if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('editAddress',postdata, function(res){ //return res.proviceFirstStageName,res.addressCitySecondStageName,res.addressCountiesThirdStageName,res.addressDetailInfo,res.userName,res.addressPostalCode,res.telNumber //error return res.err_msg callback(res); }); }; (function(){ if (typeof dataForWeixin=="undefined") return; var onBridgeReady=function(){ WeixinJSBridge.on('menu:share:appmessage', function(argv){ (dataForWeixin.prepare)(argv); WeixinJSBridge.invoke('sendAppMessage',{ "appid":dataForWeixin.appId, "img_url":dataForWeixin.MsgImg, "img_width":"120", "img_height":"120", "link":dataForWeixin.url, "desc":dataForWeixin.desc, "title":dataForWeixin.title }, function(res){(dataForWeixin.callback)(res);}); }); WeixinJSBridge.on('menu:share:timeline', function(argv){ (dataForWeixin.prepare)(argv); WeixinJSBridge.invoke('shareTimeline',{ "img_url":dataForWeixin.TLImg, "img_width":"120", "img_height":"120", "link":dataForWeixin.url, "desc":dataForWeixin.desc, "title":dataForWeixin.title }, function(res){(dataForWeixin.callback)(res);}); }); WeixinJSBridge.on('menu:share:weibo', function(argv){ (dataForWeixin.prepare)(argv); WeixinJSBridge.invoke('shareWeibo',{ "content":dataForWeixin.title, "url":dataForWeixin.url }, function(res){(dataForWeixin.callback)(res);}); }); WeixinJSBridge.on('menu:share:facebook', function(argv){ (dataForWeixin.prepare)(argv); WeixinJSBridge.invoke('shareFB',{ "img_url":dataForWeixin.TLImg, "img_width":"120", "img_height":"120", "link":dataForWeixin.url, "desc":dataForWeixin.desc, "title":dataForWeixin.title }, function(res){(dataForWeixin.callback)(res);}); }); }; if(document.addEventListener){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if(document.attachEvent){ document.attachEvent('WeixinJSBridgeReady' , onBridgeReady); document.attachEvent('onWeixinJSBridgeReady' , onBridgeReady); } })(); ================================================ FILE: old_version/wechatauth.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.1 * */ include "snoopy.class.php"; class Wechatauth { private $cookie; private $skey; private $_cookiename; private $_cookieexpired = 3600; private $_account = 'test'; private $_datapath = './data/cookie_'; private $debug; private $_logcallback; public $login_user; //当前登陆用户, 调用get_login_info后获取 public function __construct($options) { $this->_account = isset($options['account'])?$options['account']:''; $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; $this->_cookiename = $this->_datapath.$this->_account; $this->getCookie($this->_cookiename); } /** * 把cookie写入缓存 * @param string $filename 缓存文件名 * @param string $content 文件内容 * @return bool */ public function saveCookie($filename,$content){ return file_put_contents($filename,$content); } /** * 读取cookie缓存内容 * @param string $filename 缓存文件名 * @return string cookie */ public function getCookie($filename){ if (file_exists($filename)) { $mtime = filemtime($filename); if ($mtime_cookieexpired) return false; $data = file_get_contents($filename); if ($data) $this->cookie = $data; } return $this->cookie; } /* * 删除cookie */ public function deleteCookie($filename) { $this->cookie = ''; @unlink($filename); return true; } private function log($log){ if ($this->debug && function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); } } /** * 获取登陆二维码对应的授权码 */ public function get_login_code(){ if ($this->_logincode) return $this->_logincode; $t = time().strval(mt_rand(100,999)); $codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t; $send_snoopy = new Snoopy; $send_snoopy->fetch($codeurl); $result = $send_snoopy->results; if ($result) { preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches); if(count($matches)>1) { $this->_logincode = $matches[1]; $_SESSION['login_step'] = 0; return $this->_logincode; } } return $result; } /** * 通过授权码获取对应的二维码图片地址 * @param string $code * @return string image url */ public function get_code_image($code=''){ if ($code=='') $code = $this->_logincode; if (!$code) return false; return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx'; } /** * 设置二维码对应的授权码 * @param string $code * @return class $this */ public function set_login_code($code) { $this->_logincode = $code; return $this; } /** * 二维码登陆验证 * * @return status: * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired * 201: just scaned but not confirm * 200: confirm then you can get user info */ public function verify_code() { if (!$this->_logincode) return false; $t = time().strval(mt_rand(100,999)); $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t; $send_snoopy = new Snoopy; $send_snoopy->referer = "https://wx.qq.com/"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('step1:'.$result); if ($result) { preg_match("/window\.code=(\d+)/",$result,$matches); if(count($matches)>1) { $status = intval($matches[1]); if ($status==201) $_SESSION['login_step'] = 1; if ($status==200) { preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches); preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl); $this->log('step2:'.print_r($matches,true)); if (count($matcheurl)>1) { $ticket = $matches[1]; $scan = $matches[2]; //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new'; $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old'; $urlpart = parse_url($loginurl); $send_snoopy = new Snoopy; $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat"; $send_snoopy->fetch($loginurl); $result = $send_snoopy->results; $xml = simplexml_load_string($result); if ($xml->ret=="0") $this->skey = $xml->skey; foreach ($send_snoopy->headers as $key => $value) { $value = trim($value); if(strpos($value,'Set-Cookie: ') !== false){ $tmp = str_replace("Set-Cookie: ","",$value); $tmparray = explode(';', $tmp); $item = trim($tmparray[0]); $cookie.=$item.';'; } } $cookie .="Domain=.qq.com;"; $this->cookie = $cookie; $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result); $this->saveCookie($this->_cookiename,$this->cookie); } } return $status; } } return false; } /** * 获取登陆的cookie * * @param bool $is_array 是否以数值方式返回,默认否,返回字符串 * @return string|array */ public function get_login_cookie($is_array = false){ if (!$is_array) return $this->cookie; $c_arr = explode(';',$this->cookie); $cookie = array(); foreach($c_arr as $item) { $kitem = explode('=',trim($item)); if (count($kitem)>1) { $key = trim($kitem[0]); $val = trim($kitem[1]); if (!empty($val)) $cookie[$key] = $val; } } return $cookie; } /** * 授权登陆后获取用户登陆信息 */ public function get_login_info(){ if (!$this->cookie) return false; $t = time().strval(mt_rand(100,999)); $send_snoopy = new Snoopy; $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $citems = $this->get_login_cookie(true); $post = array( "BaseRequest"=>array( array( "Uin"=>$citems['wxuin'], "Sid"=>$citems['wxsid'], "Skey"=>$this->skey, "DeviceID"=>'' ) ) ); $send_snoopy->submit($submit,json_encode($post)); $this->log('login_info:'.$send_snoopy->results); $result = json_decode($send_snoopy->results,true); if ($result['BaseResponse']['Ret']<0) return false; $this->_login_user = $result['User']; return $result; } /** * 获取头像 * @param string $url 传入从用户信息接口获取到的头像地址 */ public function get_avatar($url) { if (!$this->cookie) return false; if (strpos($url, 'http')===false) { $url = 'http://wx2.qq.com'.$url; } $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $send_snoopy->fetch($url); $result = $send_snoopy->results; if ($result) return $result; else return false; } /** * 登出当前登陆用户 */ public function logout(){ if (!$this->cookie) return false; preg_match("/wxuin=(\w+);/",$this->cookie,$matches); if (count($matches)>1) $uid = $matches[1]; preg_match("/wxsid=(\w+);/",$this->cookie,$matches); if (count($matches)>1) $sid = $matches[1]; $this->log('logout: uid='.$uid.';sid='.$sid); $send_snoopy = new Snoopy; $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1'; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://wx2.qq.com/"; $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid)); $this->deleteCookie($this->_cookiename); return true; } } ================================================ FILE: old_version/wechatext.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * */ include "snoopy.class.php"; class Wechatext { private $cookie; private $_cookiename; private $_cookieexpired = 3600; private $_account; private $_password; private $_datapath = './data/cookie_'; private $debug; private $_logcallback; private $_token; public function __construct($options) { $this->_account = isset($options['account'])?$options['account']:''; $this->_password = isset($options['password'])?$options['password']:''; $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; $this->_cookiename = $this->_datapath.$this->_account; $this->cookie = $this->getCookie($this->_cookiename); } /** * 主动发消息 * @param string $id 用户的uid(即FakeId) * @param string $content 发送的内容 */ public function send($id,$content) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = 1; $post['token'] = $this->_token; $post['content'] = $content; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 群发功能 纯文本 * @param string $content * @return string */ public function mass($content) { $send_snoopy = new Snoopy; $post = array(); $post['type'] = 1; $post['token'] = $this->_token; $post['content'] = $content; $post['ajax'] = 1; $post['city']=''; $post['country']=''; $post['f']='json'; $post['groupid']='-1'; $post['imgcode']=''; $post['lang']='zh_CN'; $post['province']=''; $post['random']= rand(0, 1); $post['sex']=0; $post['synctxnews']=0; $post['synctxweibo']=0; $post['t']='ajax-response'; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 群发功能 图文素材 * @param int $appmsgid 图文素材ID * @return string */ function massNews($appmsgid){ $send_snoopy = new Snoopy; $post = array(); $post['type'] = 10; $post['token'] = $this->_token; $post['appmsgid'] = $appmsgid; $post['ajax'] = 1; $post['city']=''; $post['country']=''; $post['f']='json'; $post['groupid']='-1'; $post['imgcode']=''; $post['lang']='zh_CN'; $post['province']=''; $post['random']= rand(0, 1); $post['sex']=0; $post['synctxnews']=0; $post['synctxweibo']=0; $post['t']='ajax-response'; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 获取用户列表列表 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @param $groupid 分组id * @return array ({contacts:[{id:12345667,nick_name:"昵称",remark_name:"备注名",group_id:0},{}....]}) */ function getUserList($page=0,$pagesize=10,$groupid=0){ $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=0&lang=zh_CN&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=".$pagesize."&pageidx=".$page."&type=0&groupid=$groupid&lang=zh_CN&f=json&token=".$this->_token; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('userlist:'.$result); $json = json_decode($result,true); if (isset($json['contact_list'])) { $json = json_decode($json['contact_list'],true); if (isset($json['contacts'])) return $json['contacts']; } return false; } /** * 获取分组列表 * */ function getGroupList(){ $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&f=json&token=".$this->_token; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('userlist:'.$result); $json = json_decode($result,true); if (isset($json['group_list'])){ $json = json_decode($json['group_list'],true); if (isset($json['groups'])) return $json['groups']; } return false; } /** * 获取图文信息列表 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @return array */ public function getNewsList($page,$pagesize=10) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $type=10; $begin = $page*$pagesize; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/appmsg?token=".$this->_token."&lang=zh_CN&type=$type&action=list&begin=$begin&count=$pagesize&f=json&random=0.".$t; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('newslist:'.$result); $json = json_decode($result,true); if (isset($json['app_msg_info'])) { return $json['app_msg_info']; } return false; } /** * 获取与指定用户的对话内容 * @param $fakeid * @return array */ public function getDialogMsg($fakeid) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=".$fakeid."&token=".$this->_token."&lang=zh_CN&f=json&random=".$t; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('DialogMsg:'.$result); $json = json_decode($result,true); if (isset($json['page_info'])) { return $json['page_info']; } return false; } /** * 发送图文信息,必须从图文库里选取消息ID发送 * @param string $id 用户的uid(即FakeId) * @param string $msgid 图文消息id */ public function sendNews($id,$msgid) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = 10; $post['token'] = $this->_token; $post['fid'] = $msgid; $post['appmsgid'] = $msgid; $post['error'] = 'false'; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); return $send_snoopy->results; } /** * 上传附件(图片/音频/视频) * @param string $filepath 本地文件地址 * @param int $type 文件类型: 2:图片 3:音频 4:视频 */ public function uploadFile($filepath,$type=2) { $send_snoopy = new Snoopy; $send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1"; $t = time().strval(mt_rand(100,999)); $post = array('formId'=>''); $postfile = array('uploadfile'=>$filepath); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->set_submit_multipart(); $submit = "http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=".$this->_token."&t=iframe-uploadfile&lang=zh_CN&formId= file_from_".$t; $send_snoopy->submit($submit,$post,$postfile); $tmp = $send_snoopy->results; $this->log('upload:'.$tmp); preg_match("/formId,.*?\'(\d+)\'/",$tmp,$matches); if (isset($matches[1])) { return $matches[1]; } return false; } /** * 创建图文消息 * @param array $title 标题 * @param array $summary 摘要 * @param array $content 内容 * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取) * @param array $srcurl 原文链接 * @return json */ public function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') { $send_snoopy = new Snoopy; $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&token='.$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=".$this->_token; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->set_submit_normal(); $post = array( 'token'=>$this->_token, 'type'=>10, 'lang'=>'zh_CN', 'sub'=>'create', 'ajax'=>1, 'AppMsgId'=>'', 'error'=>'false', ); if (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid)) { $i = 0; foreach($title as $v) { $post['title'.$i] = $title[$i]; $post['author'.$i] = $author[$i]; $post['digest'.$i] = $summary[$i]; $post['content'.$i] = $content[$i]; $post['fileid'.$i] = $photoid[$i]; if ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i]; $i++; } } $post['count'] = $i; $post['token'] = $this->_token; $send_snoopy->submit($submit,$post); $tmp = $send_snoopy->results; $this->log('step2:'.$tmp); $json = json_decode($tmp,true); return $json; } /** * 发送媒体文件 * @param $id 用户的uid(即FakeId) * @param $fid 文件id * @param $type 文件类型 */ public function sendFile($id,$fid,$type) { $send_snoopy = new Snoopy; $post = array(); $post['tofakeid'] = $id; $post['type'] = $type; $post['token'] = $this->_token; $post['fid'] = $fid; $post['fileid'] = $fid; $post['error'] = 'false'; $post['ajax'] = 1; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log('sendfile:'.$result); $json = json_decode($result,true); if ($json && $json['ret']==0) return true; else return false; } /** * 获取素材库文件列表 * @param $type 文件类型: 2:图片 3:音频 4:视频 * @param $page 页码(从0开始) * @param $pagesize 每页大小 * @return array */ public function getFileList($type,$page,$pagesize=10) { $send_snoopy = new Snoopy; $t = time().strval(mt_rand(100,999)); $begin = $page*$pagesize; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $submit = "https://mp.weixin.qq.com/cgi-bin/filepage?token=".$this->_token."&lang=zh_CN&type=$type&random=0.".$t."&begin=$begin&count=$pagesize&f=json"; $send_snoopy->fetch($submit); $result = $send_snoopy->results; $this->log('filelist:'.$result); $json = json_decode($result,true); if (isset($json['page_info'])) return $json['page_info']; else return false; } /** * 发送图文信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 文件id */ public function sendImage($id,$fid) { return $this->sendFile($id,$fid,2); } /** * 发送语音信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 语音文件id */ public function sendAudio($id,$fid) { return $this->sendFile($id,$fid,3); } /** * 发送视频信息,必须从库里选取文件ID发送 * @param string $id 用户的uid(即FakeId) * @param string $fid 视频文件id */ public function sendVideo($id,$fid) { return $this->sendFile($id,$fid,4); } /** * 发送预览图文消息 * @param string $account 账户名称(user_name) * @param string $title 标题 * @param string $summary 摘要 * @param string $content 内容 * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取) * @param string $srcurl 原文链接 * @return json */ public function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') { $send_snoopy = new Snoopy; $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview"; $send_snoopy->set_submit_normal(); $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN'; $post = array( 'AppMsgId'=>'', 'ajax'=>1, 'content0'=>$content, 'count'=>1, 'digest0'=>$summary, 'error'=>'false', 'fileid0'=>$photoid, 'preusername'=>$account, 'sourceurl0'=>$srcurl, 'title0'=>$title, ); $post['token'] = $this->_token; $send_snoopy->submit($submit,$post); $tmp = $send_snoopy->results; $this->log('sendpreview:'.$tmp); $json = json_decode($tmp,true); return $json; } /** * 获取用户的信息 * @param string $id 用户的uid(即FakeId) * @return array {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]} */ public function getInfo($id) { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $t = time().strval(mt_rand(100,999)); $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/getcontactinfo"; $post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id); $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); $result = json_decode($send_snoopy->results,true); if(isset($result['contact_info'])){ return $result['contact_info']; } return false; } /** * 获得头像数据 * * @param FakeId $fakeid * @return JPG二进制数据 */ public function getHeadImg($fakeid){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=".$this->_token."&lang=zh_CN"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('Head image:'.$fakeid.'; length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 获取消息更新数目 * @param int $lastid 最近获取的消息ID,为0时获取总消息数目 * @return int 数目 */ public function getNewMsgNum($lastid=0){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=".$lastid; $post = array('ajax'=>1,'token'=>$this->_token); $send_snoopy->submit($submit,$post); $this->log($send_snoopy->results); $result = json_decode($send_snoopy->results,1); if(!$result){ return false; } return intval($result['newTotalMsgCount']); } /** * 获取最新一条消息 * @return array {"id":"最新一条id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容","playLength":"0","length":"0","source":"","starred":"0","status":"4"} */ public function getTopMsg(){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=".$this->_token; $send_snoopy->fetch($submit); $this->log($send_snoopy->results); $result = $send_snoopy->results; $json = json_decode($result,true); if (isset($json['msg_items'])) { $json = json_decode($json['msg_items'],true); if(isset($json['msg_item'])) return array_shift($json['msg_item']); } return false; } /** * 获取新消息 * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取 * @param $offset lastid起算第一条的偏移量 * @param $perpage 每页获取多少条 * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内) * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0 * @param $star 是否星标组信息 * @return array[] 同getTopMsg()返回的字段结构相同 */ public function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=".$this->_token; $lastid = $lastid===0 ? '':$lastid; $addstar = $star?'&action=star':''; $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&lang=zh_CN{$addstar}&count=$perpage&timeline=$today&day=$day&frommsgid=$lastid&offset=$offset&token=".$this->_token; $send_snoopy->fetch($submit); $this->log($send_snoopy->results); $result = $send_snoopy->results; $json = json_decode($result,true); if (isset($json['msg_items'])) { $json = json_decode($json['msg_items'],true); if(isset($json['msg_item'])) return $json['msg_item']; } return false; } /** * 获取图片消息 * @param int $msgid 消息id * @param string $mode 图片尺寸(large/small) * @return jpg二进制文件 */ public function getMsgImage($msgid,$mode='large'){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/getimgdata?token=".$this->_token."&msgid=$msgid&mode=$mode&source=&fileId=0"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('msg image:'.$msgid.';length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 获取语音消息 * @param int $msgid 消息id * @return mp3二进制文件 */ public function getMsgVoice($msgid){ $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=".$this->_token."&msgid=$msgid&fileId=0"; $send_snoopy->fetch($url); $result = $send_snoopy->results; $this->log('msg voice:'.$msgid.';length:'.strlen($result)); if(!$result){ return false; } return $result; } /** * 开启开发者模式 */ public function openDevModel() { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; $post['flag']=1; $post['type']=2; $post['token']=$this->_token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if(!$result){ return false; } return true; } /** * 关闭编辑模式 */ public function closeEditModel() { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; $post['flag']=0; $post['type']=1; $post['token']=$this->_token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if(!$result){ return false; } return true; } /** * 配置接口信息 * @param string $url 接口回调URL * @param string $token 接口Token */ public function setUrlToken($url, $token) { $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/interface&lang=zh_CN&token=".$this->_token; $submit = "https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=".$this->_token; $post['url'] = $url; $post['callback_token'] = $token; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; $this->log($send_snoopy->results); $json = json_decode($result,true); if ($json && $json['ret']==0) return true; return false; } /** * 快速设置接口 * @param string $url 接口回调URL * @param string $token 接口Token */ public function quickSetInterface($url, $token) { if ($this->closeEditModel() && $this->openDevModel() && $this->setUrlToken($url, $token)) return true; return false; } /** * 获取公众账号基本信息 * @param [string] $dir [指定相对于网站根目录的下载路径,因为需要下载二维码和用户头像] * @return [array] [公众账号信息,其中包含:nickname,avatar,type,qrcode,appid,appsecret] */ public function getCommonInfo($dir) { $userInfo = array(); $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=".$this->_token; $send_snoopy->fetch($url); $result = $send_snoopy->results; // 分析首页内容,获取nickname,avatar,usertype preg_match_all('/class=\"nickname\">(.*)<\/a>/', $result, $matches1); preg_match_all('/(.*)<\/label>/', $result, $matches3); $userInfo["nickname"] = $nickname = $matches1[1][0]; if(strpos($nickname, '<') !== false) { $userInfo["nickname"] = $nickname = substr($nickname, 0, strpos($nickname, '<')); } $userInfo["avatar"] = $avatar = $matches2[1][0]; if( ! empty($matches3[1][0])) { $userInfo["type"] = $usertype = $matches3[1][0]; } else { $userInfo["type"] = $usertype = "订阅号"; } $this->log('Analysis account info:'. "\nNickname:". $nickname. "\nAvatar:". $avatar. "\nUsertype:". $usertype); // 分析设置页面,获取二维码 $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; $url = "https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=".$this->_token; $send_snoopy->fetch($url); $result = $send_snoopy->results; // $this->log("QRCODE contents:". $result); preg_match_all('/rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=".$this->_token; $url = "https://mp.weixin.qq.com". $avatar; $send_snoopy->fetch($url); $result = $send_snoopy->results; $userInfo["avatar"] = $this->downloadImage($result, $dir. DIRECTORY_SEPARATOR. 'avatars'); // downloads the qrcode $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=".$this->_token; $url = "https://mp.weixin.qq.com". $qrcode; $send_snoopy->fetch($url); $result = $send_snoopy->results; $userInfo["qrcode"] = $this->downloadImage($result, $dir. DIRECTORY_SEPARATOR. 'qrcodes'); // 获取appid和appsecret $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=".$this->_token; $url = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; $send_snoopy->fetch($url); $result = $send_snoopy->results; preg_match_all('/name:\"AppId\",value:\"(.*)\"/', $result, $matches_id); preg_match_all('/name:\"AppSecret\",value:\"(.*)\"/', $result, $matches_secret); $userInfo["appid"] = $AppId = $matches_id[1][0]; $userInfo["appsecret"] = $AppSecret = $matches_secret[1][0]; if(! empty($userInfo)){ return $userInfo; } return false; } /** * 下载图片资源 * @param [string] $from [资源链接] * @param [string] $dir [相对于网站根目录] * @return [string] [返回相对地址] */ public function downloadImage($from, $dir) { $random_name = str_replace('.', '', microtime(true)). rand(2, 10000); if( ! is_dir($dir)) { mkdir($dir, 0755, true); } $savefile = preg_replace('/[\\\\\/]+/', DIRECTORY_SEPARATOR, $dir. '/'. $random_name); file_put_contents($savefile, $from); $filesize = filesize($savefile); $avatar_type = $this->checkImgType($savefile); if ($filesize <= 0 || $avatar_type=='unknown') { unlink($savefile); } exec("mv $savefile ". $savefile. '.'. $avatar_type); return $dir. '/'. $random_name. '.'. $avatar_type; } /** * 检测图片类型 * @param [string] $imgName [文件路径] * @return [string] [文件类型] */ public function checkImgType($imgName){ $file = fopen($imgName, "rb"); $bin = fread($file, 2); $strInfo = @unpack("C2chars", $bin); $typeCode = intval($strInfo['chars1'].$strInfo['chars2']); switch($typeCode) { case '255216': return 'jpg'; break; case '7173': return 'gif'; break; case '13780': return 'png'; break; case '6677': return 'bmp'; break; default: return 'unknown'; break; } } /** * 模拟登录获取cookie * @return [type] [description] */ public function login(){ $snoopy = new Snoopy; $submit = "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"; $post["username"] = $this->_account; $post["pwd"] = md5($this->_password); $post["f"] = "json"; $post["imgcode"] = ""; $snoopy->referer = "https://mp.weixin.qq.com/"; $snoopy->submit($submit,$post); $cookie = ''; $this->log($snoopy->results); $result = json_decode($snoopy->results,true); if (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) { return false; } foreach ($snoopy->headers as $key => $value) { $value = trim($value); if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $value,$match)) $cookie .=$match[1].'='.$match[2].'; '; } preg_match("/token=(\d+)/i",$result['redirect_url'],$matches); if($matches){ $this->_token = $matches[1]; $this->log('token:'.$this->_token); } $cookies='{"cookie":"'.$cookie.'","token":"'.$this->_token.'"}'; $this->saveCookie($this->_cookiename,$cookies); return $cookie; } /** * 把cookie写入缓存 * @param string $filename 缓存文件名 * @param string $content 文件内容 * @return bool */ public function saveCookie($filename,$content){ return file_put_contents($filename,$content); } /** * 读取cookie缓存内容 * @param string $filename 缓存文件名 * @return string cookie */ public function getCookie($filename){ if (file_exists($filename)) { $mtime = filemtime($filename); if ($mtime_cookieexpired) $data = ''; else $data = file_get_contents($filename); } else $data = ''; if($data){ $login=json_decode($data,true); $send_snoopy = new Snoopy; $send_snoopy->rawheaders['Cookie']= $login['cookie']; $send_snoopy->maxredirs = 0; $url = "https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=".$login['token']; $send_snoopy->fetch($url); $header = $send_snoopy->headers; $this->log('header:'.print_r($send_snoopy->headers,true)); if( strstr($header[3], 'EXPIRED')){ return $this->login(); }else{ $this->_token =$login['token']; $this->log('token:'.$this->_token); return $login['cookie']; } }else{ return $this->login(); } } /** * 验证cookie的有效性 * @return bool */ public function checkValid() { if (!$this->cookie || !$this->_token) return false; $send_snoopy = new Snoopy; $post = array('ajax'=>1,'token'=>$this->_token); $submit = "https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN"; $send_snoopy->rawheaders['Cookie']= $this->cookie; $send_snoopy->submit($submit,$post); $result = $send_snoopy->results; if(json_decode($result,1)){ return true; }else{ return false; } } private function log($log){ if ($this->debug && function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); } } } ================================================ FILE: old_version/wechatpay.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * 参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN * usage: * $options = array( * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 * 'partnerid'=>'88888888', //财付通商户身份标识 * 'partnerkey'=>'', //财付通商户权限密钥Key * 'paysignkey'=>'' //商户签名密钥Key * ); * $payObj = new Wechatpay($options); * $package = $payObj->createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type,$bank_type,$input_charset,$time_start,$time_expire,$transport_fee,$product_fee,$goods_tag,$attach); * */ class Wechatpay { const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin'; const AUTH_URL = '/token?grant_type=client_credential&'; const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀 const PAY_DELIVERNOTIFY = '/pay/delivernotify?'; const PAY_ORDERQUERY = '/pay/orderquery?'; private $appid; private $appsecret; private $access_token; private $user_token; private $partnerid; private $partnerkey; private $paysignkey; public $debug = false; public $errCode = 40001; public $errMsg = "no access"; private $_logcallback; public function __construct($options) { $this->appid = isset($options['appid'])?$options['appid']:''; $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; $this->partnerid = isset($options['partnerid'])?$options['partnerid']:''; $this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:''; $this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:''; $this->debug = isset($options['debug'])?$options['debug']:false; $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; } private function log($log){ if ($this->debug && function_exists($this->_logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->_logcallback,$log); } } /** * GET 请求 * @param string $url */ private function http_get($url){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * POST 请求 * @param string $url * @param array $param * @param boolean $post_file 是否文件上传 * @return string content */ private function http_post($url,$param,$post_file=false){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (is_string($param) || $post_file) { $strPOST = $param; } else { $aPOST = array(); foreach($param as $key=>$val){ $aPOST[] = $key."=".urlencode($val); } $strPOST = join("&", $aPOST); } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($oCurl, CURLOPT_POST,true); curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * 获取access_token * @param string $appid 如在类初始化时已提供,则可为空 * @param string $appsecret 如在类初始化时已提供,则可为空 * @param string $token 手动指定access_token,非必要情况不建议用 */ public function checkAuth($appid='',$appsecret='',$token=''){ if (!$appid || !$appsecret) { $appid = $this->appid; $appsecret = $this->appsecret; } if ($token) { //手动指定token,优先使用 $this->access_token=$token; return $this->access_token; } //TODO: get the cache access_token $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->access_token = $json['access_token']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; //TODO: cache access_token return $this->access_token; } return false; } /** * 删除验证数据 * @param string $appid */ public function resetAuth($appid=''){ if (!$appid) $appid = $this->appid; $this->access_token = ''; //TODO: remove cache return true; } /** * 微信api不支持中文转义的json结构 * @param array $arr */ static function json_encode($arr) { $parts = array (); $is_list = false; //Find out if the given array is a numerical array $keys = array_keys ( $arr ); $max_length = count ( $arr ) - 1; if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1 $is_list = true; for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position if ($i != $keys [$i]) { //A key fails at position check. $is_list = false; //It is an associative array. break; } } } foreach ( $arr as $key => $value ) { if (is_array ( $value )) { //Custom handling for arrays if ($is_list) $parts [] = self::json_encode ( $value ); /* :RECURSION: */ else $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */ } else { $str = ''; if (! $is_list) $str = '"' . $key . '":'; //Custom handling for multiple data types if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000) $str .= $value; //Numbers elseif ($value === false) $str .= 'false'; //The booleans elseif ($value === true) $str .= 'true'; else $str .= '"' . addslashes ( $value ) . '"'; //All other things // :TODO: Is there any more datatype we should be in the lookout for? (Object?) $parts [] = $str; } } $json = implode ( ',', $parts ); if ($is_list) return '[' . $json . ']'; //Return numerical JSON return '{' . $json . '}'; //Return associative JSON } /** * 获取签名 * @param array $arrdata 签名数组 * @param string $method 签名方法 * @return boolean|string 签名值 */ public function getSignature($arrdata,$method="sha1") { if (!function_exists($method)) return false; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $paySign = $method($paramstring); return $paySign; } /** * 生成随机字串 * @param number $length 长度,默认为16,最长为32字节 * @return string */ public function generateNonceStr($length=16){ // 密码字符集,可任意添加你需要的字符 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for($i = 0; $i < $length; $i++) { $str .= $chars[mt_rand(0, strlen($chars) - 1)]; } return $str; } /** * 生成原生支付url * @param number $productid 商品编号,最长为32字节 * @return string */ public function createNativeUrl($productid){ $nativeObj["appid"] = $this->appid; $nativeObj["appkey"] = $this->paysignkey; $nativeObj["productid"] = urlencode($productid); $nativeObj["timestamp"] = time(); $nativeObj["noncestr"] = $this->generateNonceStr(); $nativeObj["sign"] = $this->getSignature($nativeObj); unset($nativeObj["appkey"]); $bizString = ""; foreach($nativeObj as $key => $value) { if(strlen($bizString) == 0) $bizString .= $key . "=" . $value; else $bizString .= "&" . $key . "=" . $value; } return "weixin://wxpay/bizpayurl?".$bizString; //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX×tamp=XXXXXX&noncestr=XXXXXX } /** * 生成订单package字符串 * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一 * @param string $body 必填,商品描述,128 字节以下 * @param int $total_fee 必填,订单总金额,单位为分 * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内 * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内 * @param int $fee_type 必填,现金支付币种,默认1:人民币 * @param string $bank_type 必填,银行通道类型,默认WX * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss * @param string $time_expire 交易结束时间,也是订单失效时间 * @param int $transport_fee 物流费用,单位为分 * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee * @param string $goods_tag 商品标记,优惠券时可能用到 * @param string $attach 附加数据,notify接口原样返回 * @return string */ public function 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=""){ $arrdata = array("bank_type" => $bank_type, "body" => $body, "partner" => $this->partnerid, "out_trade_no" => $out_trade_no, "total_fee" => $total_fee, "fee_type" => $fee_type, "notify_url" => $notify_url, "spbill_create_ip" => $spbill_create_ip, "input_charset" => $input_charset); if ($time_start) $arrdata['time_start'] = $time_start; if ($time_expire) $arrdata['time_expire'] = $time_expire; if ($transport_fee) $arrdata['transport_fee'] = $transport_fee; if ($product_fee) $arrdata['product_fee'] = $product_fee; if ($goods_tag) $arrdata['goods_tag'] = $goods_tag; if ($attach) $arrdata['attach'] = $attach; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $stringSignTemp = $paramstring . "&key=" . $this->partnerkey; $signValue = strtoupper(md5($stringSignTemp)); $package = http_build_query($arrdata) . "&sign=" . $signValue; return $package; } /** * 支付签名(paySign)生成方法 * @param string $package 订单详情字串 * @param string $timeStamp 当前时间戳(需与JS输出的一致) * @param string $nonceStr 随机串(需与JS输出的一致) * @return string 返回签名字串 */ public function getPaySign($package, $timeStamp, $nonceStr){ $arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey); $paySign = $this->getSignature($arrdata); return $paySign; } /** * 回调通知签名验证 * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取 * @return boolean */ public function checkOrderSignature($orderxml=''){ if (!$orderxml) { $postStr = file_get_contents("php://input"); if (!empty($postStr)) { $orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); } else return false; } $arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']); $paySign = $this->getSignature($arrdata); if ($paySign!=$orderxml['AppSignature']) return false; return true; } /** * 发货通知 * @param string $openid 用户open_id * @param string $transid 交易单号 * @param string $out_trade_no 第三方订单号 * @param int $status 0:发货失败;1:已发货 * @param string $msg 失败原因 * @return boolean|array */ public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){ if (!$this->access_token && !$this->checkAuth()) return false; $postdata = array( "appid"=>$this->appid, "appkey"=>$this->paysignkey, "openid"=>$openid, "transid"=>strval($transid), "out_trade_no"=>strval($out_trade_no), "deliver_timestamp"=>strval(time()), "deliver_status"=>strval($status), "deliver_msg"=>$msg, ); $postdata['app_signature'] = $this->getSignature($postdata); $postdata['sign_method'] = 'sha1'; unset($postdata['appkey']); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 查询订单信息 * @param string $out_trade_no 订单号 * @return boolean|array */ public function getPayOrder($out_trade_no) { if (!$this->access_token && !$this->checkAuth()) return false; $sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}")); $postdata = array( "appid"=>$this->appid, "appkey"=>$this->paysignkey, "package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign", "timestamp"=>strval(time()), ); $postdata['app_signature'] = $this->getSignature($postdata); $postdata['sign_method'] = 'sha1'; unset($postdata['appkey']); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg'].json_encode($postdata); return false; } return $json["order_info"]; } return false; } /** * 设置用户授权密钥 * @param string $user_token * @return string */ public function setUserToken($user_token) { return $this->user_token = $user_token; } /** * 获取收货地址JS的签名 * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用 * @param string $appId * @param string $url * @param int $timeStamp * @param string $nonceStr * @param string $user_token * @return Ambigous */ public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){ if (!$user_token) $user_token = $this->user_token; if (!$user_token) { $this->errMsg = 'no user access token found!'; return false; } $url = htmlspecialchars_decode($url); $arrdata = array( 'appid'=>$this->appid, 'url'=>$url, 'timestamp'=>strval($timeStamp), 'noncestr'=>$nonceStr, 'accesstoken'=>$user_token ); return $this->getSignature($arrdata); } } ================================================ FILE: qyerrCode.php ================================================ * @link https://github.com/binsee/wechat-php-sdk * @version 1.0 * usage: * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 * if ($ret) * echo $ret; * else * echo "未找到对应的内容"; */ class ErrCode { public static $errCode=array( '-1'=>'系统繁忙', '0'=>'请求成功', '40001'=>'获取access_token时AppSecret错误,或者access_token无效', '40002'=>'不合法的凭证类型', '40003'=>'不合法的UserID', '40004'=>'不合法的媒体文件类型', '40005'=>'不合法的文件类型', '40006'=>'不合法的文件大小', '40007'=>'不合法的媒体文件id', '40008'=>'不合法的消息类型', '40013'=>'不合法的corpid', '40014'=>'不合法的access_token', '40015'=>'不合法的菜单类型', '40016'=>'不合法的按钮个数', '40017'=>'不合法的按钮类型', '40018'=>'不合法的按钮名字长度', '40019'=>'不合法的按钮KEY长度', '40020'=>'不合法的按钮URL长度', '40021'=>'不合法的菜单版本号', '40022'=>'不合法的子菜单级数', '40023'=>'不合法的子菜单按钮个数', '40024'=>'不合法的子菜单按钮类型', '40025'=>'不合法的子菜单按钮名字长度', '40026'=>'不合法的子菜单按钮KEY长度', '40027'=>'不合法的子菜单按钮URL长度', '40028'=>'不合法的自定义菜单使用员工', '40029'=>'不合法的oauth_code', '40031'=>'不合法的UserID列表', '40032'=>'不合法的UserID列表长度', '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', '40035'=>'不合法的参数', '40038'=>'不合法的请求格式', '40039'=>'不合法的URL长度', '40040'=>'不合法的插件token', '40041'=>'不合法的插件id', '40042'=>'不合法的插件会话', '40048'=>'url中包含不合法domain', '40054'=>'不合法的子菜单url域名', '40055'=>'不合法的按钮url域名', '40056'=>'不合法的agentid', '40057'=>'不合法的callbackurl', '40058'=>'不合法的红包参数', '40059'=>'不合法的上报地理位置标志位', '40060'=>'设置上报地理位置标志位时没有设置callbackurl', '40061'=>'设置应用头像失败', '40062'=>'不合法的应用模式', '40063'=>'红包参数为空', '40064'=>'管理组名字已存在', '40065'=>'不合法的管理组名字长度', '40066'=>'不合法的部门列表', '40067'=>'标题长度不合法', '40068'=>'不合法的标签ID', '40069'=>'不合法的标签ID列表', '40070'=>'列表中所有标签(用户)ID都不合法', '40071'=>'不合法的标签名字,标签名字已经存在', '40072'=>'不合法的标签名字长度', '40073'=>'不合法的openid', '40074'=>'news消息不支持指定为高保密消息', '41001'=>'缺少access_token参数', '41002'=>'缺少corpid参数', '41003'=>'缺少refresh_token参数', '41004'=>'缺少secret参数', '41005'=>'缺少多媒体文件数据', '41006'=>'缺少media_id参数', '41007'=>'缺少子菜单数据', '41008'=>'缺少oauth code', '41009'=>'缺少UserID', '41010'=>'缺少url', '41011'=>'缺少agentid', '41012'=>'缺少应用头像mediaid', '41013'=>'缺少应用名字', '41014'=>'缺少应用描述', '41015'=>'缺少Content', '41016'=>'缺少标题', '41017'=>'缺少标签ID', '41018'=>'缺少标签名字', '42001'=>'access_token超时', '42002'=>'refresh_token超时', '42003'=>'oauth_code超时', '42004'=>'插件token超时', '43001'=>'需要GET请求', '43002'=>'需要POST请求', '43003'=>'需要HTTPS', '43004'=>'需要接收者关注', '43005'=>'需要好友关系', '43006'=>'需要订阅', '43007'=>'需要授权', '43008'=>'需要支付授权', '43009'=>'需要员工已关注', '43010'=>'需要处于企业模式', '43011'=>'需要企业授权', '44001'=>'多媒体文件为空', '44002'=>'POST的数据包为空', '44003'=>'图文消息内容为空', '44004'=>'文本消息内容为空', '45001'=>'多媒体文件大小超过限制', '45002'=>'消息内容超过限制', '45003'=>'标题字段超过限制', '45004'=>'描述字段超过限制', '45005'=>'链接字段超过限制', '45006'=>'图片链接字段超过限制', '45007'=>'语音播放时间超过限制', '45008'=>'图文消息超过限制', '45009'=>'接口调用超过限制', '45010'=>'创建菜单个数超过限制', '45015'=>'回复时间超过限制', '45016'=>'系统分组,不允许修改', '45017'=>'分组名字过长', '45018'=>'分组数量超过上限', '46001'=>'不存在媒体数据', '46002'=>'不存在的菜单版本', '46003'=>'不存在的菜单数据', '46004'=>'不存在的员工', '47001'=>'解析JSON/XML内容错误', '48002'=>'Api禁用', '50001'=>'redirect_uri未授权', '50002'=>'员工不在权限范围', '50003'=>'应用已停用', '50004'=>'员工状态不正确(未关注状态)', '50005'=>'企业已禁用', '60001'=>'部门长度不符合限制', '60002'=>'部门层级深度超过限制', '60003'=>'部门不存在', '60004'=>'父亲部门不存在', '60005'=>'不允许删除有成员的部门', '60006'=>'不允许删除有子部门的部门', '60007'=>'不允许删除根部门', '60008'=>'部门名称已存在', '60009'=>'部门名称含有非法字符', '60010'=>'部门存在循环关系', '60011'=>'管理员权限不足,(user/department/agent)无权限', '60012'=>'不允许删除默认应用', '60013'=>'不允许关闭应用', '60014'=>'不允许开启应用', '60015'=>'不允许修改默认应用可见范围', '60016'=>'不允许删除存在成员的标签', '60017'=>'不允许设置企业', '60102'=>'UserID已存在', '60103'=>'手机号码不合法', '60104'=>'手机号码已存在', '60105'=>'邮箱不合法', '60106'=>'邮箱已存在', '60107'=>'微信号不合法', '60108'=>'微信号已存在', '60109'=>'QQ号已存在', '60110'=>'部门个数超出限制', '60111'=>'UserID不存在', '60112'=>'成员姓名不合法', '60113'=>'身份认证信息(微信号/手机/邮箱)不能同时为空', '60114'=>'性别不合法', '60119'=>'用户已关注', '60120'=>'用户已禁用', '60121'=>'找不到该用户', '60023'=>'应用已授权予第三方,不允许通过分级管理员修改菜单', '80001'=>'可信域名没有IPC备案,后续将不能在该域名下正常使用jssdk', ); public static function getErrText($err) { if (isset(self::$errCode[$err])) { return self::$errCode[$err]; }else { return false; }; } } ?> ================================================ FILE: qywechat.class.php ================================================ * @link https://github.com/binsee/wechat-php-sdk * @version 1.0 * usage: * $options = array( * 'token'=>'tokenaccesskey', //填写应用接口的Token * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 * 'agentid'=>'1', //应用的id * 'debug'=>false, //调试开关 * 'logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数 * ); * */ class Wechat { const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; //暂不支持 const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; //暂不支持 const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_ENTER_AGENT = 'enter_agent'; //用户进入应用 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 const API_URL_PREFIX = 'https://qyapi.weixin.qq.com/cgi-bin'; const USER_CREATE_URL = '/user/create?'; const USER_UPDATE_URL = '/user/update?'; const USER_DELETE_URL = '/user/delete?'; const USER_BATCHDELETE_URL = '/user/batchdelete?'; const USER_GET_URL = '/user/get?'; const USER_LIST_URL = '/user/simplelist?'; const USER_LIST_INFO_URL = '/user/list?'; const USER_GETINFO_URL = '/user/getuserinfo?'; const USER_INVITE_URL = '/invite/send?'; const DEPARTMENT_CREATE_URL = '/department/create?'; const DEPARTMENT_UPDATE_URL = '/department/update?'; const DEPARTMENT_DELETE_URL = '/department/delete?'; const DEPARTMENT_MOVE_URL = '/department/move?'; const DEPARTMENT_LIST_URL = '/department/list?'; const TAG_CREATE_URL = '/tag/create?'; const TAG_UPDATE_URL = '/tag/update?'; const TAG_DELETE_URL = '/tag/delete?'; const TAG_GET_URL = '/tag/get?'; const TAG_ADDUSER_URL = '/tag/addtagusers?'; const TAG_DELUSER_URL = '/tag/deltagusers?'; const TAG_LIST_URL = '/tag/list?'; const MEDIA_UPLOAD_URL = '/media/upload?'; const MEDIA_GET_URL = '/media/get?'; const AUTHSUCC_URL = '/user/authsucc?'; const MASS_SEND_URL = '/message/send?'; const MENU_CREATE_URL = '/menu/create?'; const MENU_GET_URL = '/menu/get?'; const MENU_DELETE_URL = '/menu/delete?'; const TOKEN_GET_URL = '/gettoken?'; const TICKET_GET_URL = '/get_jsapi_ticket?'; const CALLBACKSERVER_GET_URL = '/getcallbackip?'; const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2'; const OAUTH_AUTHORIZE_URL = '/authorize?'; private $token; private $encodingAesKey; private $appid; //也就是企业号的CorpID private $appsecret; private $access_token; private $agentid; //应用id AgentID private $postxml; private $agentidxml; //接收的应用id AgentID private $_msg; private $_receive; private $_sendmsg; //主动发送消息的内容 private $_text_filter = true; public $debug = false; public $errCode = 40001; public $errMsg = "no access"; public $logcallback; public function __construct($options) { $this->token = isset($options['token'])?$options['token']:''; $this->encodingAesKey = isset($options['encodingaeskey'])?$options['encodingaeskey']:''; $this->appid = isset($options['appid'])?$options['appid']:''; $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; $this->agentid = isset($options['agentid'])?$options['agentid']:''; $this->debug = isset($options['debug'])?$options['debug']:false; $this->logcallback = isset($options['logcallback'])?$options['logcallback']:false; } protected function log($log){ if ($this->debug && function_exists($this->logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->logcallback,$log); } } /** * 数据XML编码 * @param mixed $data 数据 * @return string */ public static function data_to_xml($data) { $xml = ''; foreach ($data as $key => $val) { is_numeric($key) && $key = "item id=\"$key\""; $xml .= "<$key>"; $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val); list($key, ) = explode(' ', $key); $xml .= ""; } return $xml; } public static function xmlSafeStr($str) { return ''; } /** * XML编码 * @param mixed $data 数据 * @param string $root 根节点名 * @param string $item 数字索引的子节点名 * @param string $attr 根节点属性 * @param string $id 数字索引子节点key转换的属性名 * @param string $encoding 数据编码 * @return string */ public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') { if(is_array($attr)){ $_attr = array(); foreach ($attr as $key => $value) { $_attr[] = "{$key}=\"{$value}\""; } $attr = implode(' ', $_attr); } $attr = trim($attr); $attr = empty($attr) ? '' : " {$attr}"; $xml = "<{$root}{$attr}>"; $xml .= self::data_to_xml($data, $item, $id); $xml .= ""; return $xml; } /** * 微信api不支持中文转义的json结构 * @param array $arr */ static function json_encode($arr) { $parts = array (); $is_list = false; //Find out if the given array is a numerical array $keys = array_keys ( $arr ); $max_length = count ( $arr ) - 1; if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1 $is_list = true; for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position if ($i != $keys [$i]) { //A key fails at position check. $is_list = false; //It is an associative array. break; } } } foreach ( $arr as $key => $value ) { if (is_array ( $value )) { //Custom handling for arrays if ($is_list) $parts [] = self::json_encode ( $value ); /* :RECURSION: */ else $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */ } else { $str = ''; if (! $is_list) $str = '"' . $key . '":'; //Custom handling for multiple data types if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000) $str .= $value; //Numbers elseif ($value === false) $str .= 'false'; //The booleans elseif ($value === true) $str .= 'true'; else $str .= '"' .addcslashes($value, "\\\"\n\r\t/"). '"'; //All other things // :TODO: Is there any more datatype we should be in the lookout for? (Object?) $parts [] = $str; } } $json = implode ( ',', $parts ); if ($is_list) return '[' . $json . ']'; //Return numerical JSON return '{' . $json . '}'; //Return associative JSON } /** * 过滤文字回复\r\n换行符 * @param string $text * @return string|mixed */ private function _auto_text_filter($text) { if (!$this->_text_filter) return $text; return str_replace("\r\n", "\n", $text); } /** * GET 请求 * @param string $url */ private function http_get($url){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * POST 请求 * @param string $url * @param array $param * @param boolean $post_file 是否文件上传 * @return string content */ private function http_post($url,$param,$post_file=false){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (is_string($param) || $post_file) { $strPOST = $param; } else { $aPOST = array(); foreach($param as $key=>$val){ $aPOST[] = $key."=".urlencode($val); } $strPOST = join("&", $aPOST); } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($oCurl, CURLOPT_POST,true); if(PHP_VERSION_ID >= 50500){ curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, FALSE); } curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * For weixin server validation */ private function checkSignature($str) { $signature = isset($_GET["msg_signature"])?$_GET["msg_signature"]:''; $timestamp = isset($_GET["timestamp"])?$_GET["timestamp"]:''; $nonce = isset($_GET["nonce"])?$_GET["nonce"]:''; $tmpArr = array($str,$this->token, $timestamp, $nonce);//比普通公众平台多了一个加密的密文 sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $shaStr = sha1($tmpStr); if( $shaStr == $signature ){ return true; }else{ return false; } } /** * 微信验证,包括post来的xml解密 * @param bool $return 是否返回 */ public function valid($return=false) { $encryptStr=""; if ($_SERVER['REQUEST_METHOD'] == "POST") { $postStr = file_get_contents("php://input"); $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $this->log($postStr); if (isset($array['Encrypt'])) { $encryptStr = $array['Encrypt']; $this->agentidxml = isset($array['AgentID']) ? $array['AgentID']: ''; } } else { $encryptStr = isset($_GET["echostr"]) ? $_GET["echostr"]: ''; } if ($encryptStr) { $ret=$this->checkSignature($encryptStr); } if (!isset($ret) || !$ret) { if (!$return) { die('no access'); } else { return false; } } $pc = new Prpcrypt($this->encodingAesKey); $array = $pc->decrypt($encryptStr,$this->appid); if (!isset($array[0]) || ($array[0] != 0)) { if (!$return) { die('解密失败!'); } else { return false; } } if ($_SERVER['REQUEST_METHOD'] == "POST") { $this->postxml = $array[1]; //$this->log($array[1]); return ($this->postxml!=""); } else { $echoStr = $array[1]; if ($return) { return $echoStr; } else { die($echoStr); } } return false; } /** * 获取微信服务器发来的信息 */ public function getRev() { if ($this->_receive) return $this; $postStr = $this->postxml; $this->log($postStr); if (!empty($postStr)) { $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); if (!isset($this->_receive['AgentID'])) { $this->_receive['AgentID']=$this->agentidxml; //当前接收消息的应用id } } return $this; } /** * 获取微信服务器发来的信息 */ public function getRevData() { return $this->_receive; } /** * 获取微信服务器发来的原始加密信息 */ public function getRevPostXml() { return $this->postxml; } /** * 获取消息发送者 */ public function getRevFrom() { if (isset($this->_receive['FromUserName'])) return $this->_receive['FromUserName']; else return false; } /** * 获取消息接受者 */ public function getRevTo() { if (isset($this->_receive['ToUserName'])) return $this->_receive['ToUserName']; else return false; } /** * 获取接收消息的应用id */ public function getRevAgentID() { if (isset($this->_receive['AgentID'])) return $this->_receive['AgentID']; else return false; } /** * 获取接收消息的类型 */ public function getRevType() { if (isset($this->_receive['MsgType'])) return $this->_receive['MsgType']; else return false; } /** * 获取消息ID */ public function getRevID() { if (isset($this->_receive['MsgId'])) return $this->_receive['MsgId']; else return false; } /** * 获取消息发送时间 */ public function getRevCtime() { if (isset($this->_receive['CreateTime'])) return $this->_receive['CreateTime']; else return false; } /** * 获取接收消息内容正文 */ public function getRevContent(){ if (isset($this->_receive['Content'])) return $this->_receive['Content']; else return false; } /** * 获取接收消息图片 */ public function getRevPic(){ if (isset($this->_receive['PicUrl'])) return array( 'mediaid'=>$this->_receive['MediaId'], 'picurl'=>(string)$this->_receive['PicUrl'], //防止picurl为空导致解析出错 ); else return false; } /** * 获取接收地理位置 */ public function getRevGeo(){ if (isset($this->_receive['Location_X'])){ return array( 'x'=>$this->_receive['Location_X'], 'y'=>$this->_receive['Location_Y'], 'scale'=>(string)$this->_receive['Scale'], 'label'=>(string)$this->_receive['Label'] ); } else return false; } /** * 获取上报地理位置事件 */ public function getRevEventGeo(){ if (isset($this->_receive['Latitude'])){ return array( 'x'=>$this->_receive['Latitude'], 'y'=>$this->_receive['Longitude'], 'precision'=>$this->_receive['Precision'], ); } else return false; } /** * 获取接收事件推送 */ public function getRevEvent(){ if (isset($this->_receive['Event'])){ $array['event'] = $this->_receive['Event']; } if (isset($this->_receive['EventKey']) && !empty($this->_receive['EventKey'])){ $array['key'] = $this->_receive['EventKey']; } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的扫码推事件信息 * * 事件类型为以下两种时则调用此方法有效 * Event 事件类型,scancode_push * Event 事件类型,scancode_waitmsg * * @return: array | false * array ( * 'ScanType'=>'qrcode', * 'ScanResult'=>'123123' * ) */ public function getRevScanInfo(){ if (isset($this->_receive['ScanCodeInfo'])){ if (!is_array($this->_receive['SendPicsInfo'])) { $array=(array)$this->_receive['ScanCodeInfo']; $this->_receive['ScanCodeInfo']=$array; }else { $array=$this->_receive['ScanCodeInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的图片发送事件信息 * * 事件类型为以下三种时则调用此方法有效 * Event 事件类型,pic_sysphoto 弹出系统拍照发图的事件推送 * Event 事件类型,pic_photo_or_album 弹出拍照或者相册发图的事件推送 * Event 事件类型,pic_weixin 弹出微信相册发图器的事件推送 * * @return: array | false * array ( * 'Count' => '2', * 'PicList' =>array ( * 'item' =>array ( * 0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'), * 1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'), * ), * ), * ) * */ public function getRevSendPicsInfo(){ if (isset($this->_receive['SendPicsInfo'])){ if (!is_array($this->_receive['SendPicsInfo'])) { $array=(array)$this->_receive['SendPicsInfo']; if (isset($array['PicList'])){ $array['PicList']=(array)$array['PicList']; $item=$array['PicList']['item']; $array['PicList']['item']=array(); foreach ( $item as $key => $value ){ $array['PicList']['item'][$key]=(array)$value; } } $this->_receive['SendPicsInfo']=$array; } else { $array=$this->_receive['SendPicsInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的地理位置选择器事件推送 * * 事件类型为以下时则可以调用此方法有效 * Event 事件类型,location_select 弹出系统拍照发图的事件推送 * * @return: array | false * array ( * 'Location_X' => '33.731655000061', * 'Location_Y' => '113.29955200008047', * 'Scale' => '16', * 'Label' => '某某市某某区某某路', * 'Poiname' => '', * ) * */ public function getRevSendGeoInfo(){ if (isset($this->_receive['SendLocationInfo'])){ if (!is_array($this->_receive['SendLocationInfo'])) { $array=(array)$this->_receive['SendLocationInfo']; if (empty($array['Poiname'])) { $array['Poiname']=""; } if (empty($array['Label'])) { $array['Label']=""; } $this->_receive['SendLocationInfo']=$array; } else { $array=$this->_receive['SendLocationInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取接收语音推送 */ public function getRevVoice(){ if (isset($this->_receive['MediaId'])){ return array( 'mediaid'=>$this->_receive['MediaId'], 'format'=>$this->_receive['Format'], ); } else return false; } /** * 获取接收视频推送 */ public function getRevVideo(){ if (isset($this->_receive['MediaId'])){ return array( 'mediaid'=>$this->_receive['MediaId'], 'thumbmediaid'=>$this->_receive['ThumbMediaId'] ); } else return false; } /** * 设置回复消息 * Example: $obj->text('hello')->reply(); * @param string $text */ public function text($text='') { $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_TEXT, 'Content'=>$this->_auto_text_filter($text), 'CreateTime'=>time(), ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->image('media_id')->reply(); * @param string $mediaid */ public function image($mediaid='') { $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_IMAGE, 'Image'=>array('MediaId'=>$mediaid), 'CreateTime'=>time(), ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->voice('media_id')->reply(); * @param string $mediaid */ public function voice($mediaid='') { $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_IMAGE, 'Voice'=>array('MediaId'=>$mediaid), 'CreateTime'=>time(), ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->video('media_id','title','description')->reply(); * @param string $mediaid */ public function video($mediaid='',$title='',$description='') { $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_IMAGE, 'Video'=>array( 'MediaId'=>$mediaid, 'Title'=>$title, 'Description'=>$description ), 'CreateTime'=>time(), ); $this->Message($msg); return $this; } /** * 设置回复图文 * @param array $newsData * 数组结构: * array( * "0"=>array( * 'Title'=>'msg title', * 'Description'=>'summary text', * 'PicUrl'=>'http://www.domain.com/1.jpg', * 'Url'=>'http://www.domain.com/1.html' * ), * "1"=>.... * ) */ public function news($newsData=array()) { $count = count($newsData); $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_NEWS, 'CreateTime'=>time(), 'ArticleCount'=>$count, 'Articles'=>$newsData, ); $this->Message($msg); return $this; } /** * 设置发送消息 * @param array $msg 消息数组 * @param bool $append 是否在原消息数组追加 */ public function Message($msg = '',$append = false){ if (is_null($msg)) { $this->_msg =array(); }elseif (is_array($msg)) { if ($append) $this->_msg = array_merge($this->_msg,$msg); else $this->_msg = $msg; return $this->_msg; } else { return $this->_msg; } } /** * * 回复微信服务器, 此函数支持链式操作 * Example: $this->text('msg tips')->reply(); * @param string $msg 要发送的信息, 默认取$this->_msg * @param bool $return 是否返回信息而不抛出到浏览器 默认:否 */ public function reply($msg=array(),$return = false) { if (empty($msg)) $msg = $this->_msg; $xmldata= $this->xml_encode($msg); $this->log($xmldata); $pc = new Prpcrypt($this->encodingAesKey); $array = $pc->encrypt($xmldata, $this->appid); $ret = $array[0]; if ($ret != 0) { $this->log('encrypt err!'); return false; } $timestamp = time(); $nonce = rand(77,999)*rand(605,888)*rand(11,99); $encrypt = $array[1]; $tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文 sort($tmpArr, SORT_STRING); $signature = implode($tmpArr); $signature = sha1($signature); $smsg = $this->generate($encrypt, $signature, $timestamp, $nonce); $this->log($smsg); if ($return) return $smsg; elseif ($smsg){ echo $smsg; return true; }else return false; } private function generate($encrypt, $signature, $timestamp, $nonce) { //格式化加密信息 $format = " %s "; return sprintf($format, $encrypt, $signature, $timestamp, $nonce); } /** * 设置缓存,按需重载 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename,$value,$expired){ //TODO: set cache implementation return false; } /** * 获取缓存,按需重载 * @param string $cachename * @return mixed */ protected function getCache($cachename){ //TODO: get cache implementation return false; } /** * 清除缓存,按需重载 * @param string $cachename * @return boolean */ protected function removeCache($cachename){ //TODO: remove cache implementation return false; } /** * 通用auth验证方法 * @param string $appid * @param string $appsecret * @param string $token 手动指定access_token,非必要情况不建议用 */ public function checkAuth($appid='',$appsecret='',$token=''){ if (!$appid || !$appsecret) { $appid = $this->appid; $appsecret = $this->appsecret; } if ($token) { //手动指定token,优先使用 $this->access_token=$token; return $this->access_token; } $authname = 'qywechat_access_token'.$appid; if ($rs = $this->getCache($authname)) { $this->access_token = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::TOKEN_GET_URL.'corpid='.$appid.'&corpsecret='.$appsecret); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->access_token = $json['access_token']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; $this->setCache($authname,$this->access_token,$expire); return $this->access_token; } return false; } /** * 删除验证数据 * @param string $appid */ public function resetAuth($appid=''){ if (!$appid) $appid = $this->appid; $this->access_token = ''; $authname = 'qywechat_access_token'.$appid; $this->removeCache($authname); return true; } /** * 删除JSAPI授权TICKET * @param string $appid 用于多个appid时使用 */ public function resetJsTicket($appid=''){ if (!$appid) $appid = $this->appid; $this->jsapi_ticket = ''; $authname = 'qywechat_jsapi_ticket'.$appid; $this->removeCache($authname); return true; } /** * 获取JSAPI授权TICKET * @param string $appid 用于多个appid时使用,可空 * @param string $jsapi_ticket 手动指定jsapi_ticket,非必要情况不建议用 */ public function getJsTicket($appid='',$jsapi_ticket=''){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$appid) $appid = $this->appid; if ($jsapi_ticket) { //手动指定token,优先使用 $this->jsapi_ticket = $jsapi_ticket; return $this->jsapi_ticket; } $authname = 'qywechat_jsapi_ticket'.$appid; if ($rs = $this->getCache($authname)) { $this->jsapi_ticket = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::TICKET_GET_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->jsapi_ticket = $json['ticket']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; $this->setCache($authname, $this->jsapi_ticket, $expire); return $this->jsapi_ticket; } return false; } /** * 获取JsApi使用签名 * @param string $url 网页的URL,自动处理#及其后面部分 * @param string $timestamp 当前时间戳 (为空则自动生成) * @param string $noncestr 随机串 (为空则自动生成) * @param string $appid 用于多个appid时使用,可空 * @return array|bool 返回签名字串 */ public function getJsSign($url, $timestamp=0, $noncestr='', $appid=''){ if (!$this->jsapi_ticket && !$this->getJsTicket($appid) || !$url) return false; if (!$timestamp) $timestamp = time(); if (!$noncestr) $noncestr = $this->generateNonceStr(); $ret = strpos($url,'#'); if ($ret) $url = substr($url,0,$ret); $url = trim($url); if (empty($url)) return false; $arrdata = array("timestamp" => $timestamp, "noncestr" => $noncestr, "url" => $url, "jsapi_ticket" => $this->jsapi_ticket); $sign = $this->getSignature($arrdata); if (!$sign) return false; $signPackage = array( "appid" => $this->appid, "noncestr" => $noncestr, "timestamp" => $timestamp, "url" => $url, "signature" => $sign ); return $signPackage; } /** * 获取签名 * @param array $arrdata 签名数组 * @param string $method 签名方法 * @return boolean|string 签名值 */ public function getSignature($arrdata,$method="sha1") { if (!function_exists($method)) return false; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $Sign = $method($paramstring); return $Sign; } /** * 生成随机字串 * @param number $length 长度,默认为16,最长为32字节 * @return string */ public function generateNonceStr($length=16){ // 密码字符集,可任意添加你需要的字符 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for($i = 0; $i < $length; $i++) { $str .= $chars[mt_rand(0, strlen($chars) - 1)]; } return $str; } /** * 创建菜单 * @param array $data 菜单数组数据 * example: * array ( * 'button' => array ( * 0 => array ( * 'name' => '扫码', * 'sub_button' => array ( * 0 => array ( * 'type' => 'scancode_waitmsg', * 'name' => '扫码带提示', * 'key' => 'rselfmenu_0_0', * ), * 1 => array ( * 'type' => 'scancode_push', * 'name' => '扫码推事件', * 'key' => 'rselfmenu_0_1', * ), * ), * ), * 1 => array ( * 'name' => '发图', * 'sub_button' => array ( * 0 => array ( * 'type' => 'pic_sysphoto', * 'name' => '系统拍照发图', * 'key' => 'rselfmenu_1_0', * ), * 1 => array ( * 'type' => 'pic_photo_or_album', * 'name' => '拍照或者相册发图', * 'key' => 'rselfmenu_1_1', * ) * ), * ), * 2 => array ( * 'type' => 'location_select', * 'name' => '发送位置', * 'key' => 'rselfmenu_2_0' * ), * ), * ) * type可以选择为以下几种,会收到相应类型的事件推送。请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本, * 和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。 * 1、click:点击推事件 * 2、view:跳转URL * 3、scancode_push:扫码推事件 * 4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框 * 5、pic_sysphoto:弹出系统拍照发图 * 6、pic_photo_or_album:弹出拍照或者相册发图 * 7、pic_weixin:弹出微信相册发图器 * 8、location_select:弹出地理位置选择器 */ public function createMenu($data,$agentid=''){ if ($agentid=='') { $agentid=$this->agentid; } if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token.'&agentid='.$agentid,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 获取菜单 * @return array('menu'=>array(....s)) */ public function getMenu($agentid=''){ if ($agentid=='') { $agentid=$this->agentid; } if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token.'&agentid='.$agentid); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除菜单 * @return boolean */ public function deleteMenu($agentid=''){ if ($agentid=='') { $agentid=$this->agentid; } if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token.'&agentid='.$agentid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 上传多媒体文件 (只有三天的有效期,过期自动被删除) * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义 * @param array $data {"media":'@Path\filename.jpg'} * @param type 媒体文件类型:图片(image)、语音(voice)、视频(video),普通文件(file) * @return boolean|array * { * "type": "image", * "media_id": "0000001", * "created_at": "1380000000" * } */ public function uploadMedia($data, $type){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 根据媒体文件ID获取媒体文件 * @param string $media_id 媒体文件id * @return raw data */ public function getMedia($media_id){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $result; } return false; } /** * 获取企业微信服务器IP地址列表 * @return array('127.0.0.1','127.0.0.1') */ public function getServerIp(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['ip_list']; } return false; } /** * 创建部门 * @param array $data 结构体为: * array ( * "name" => "邮箱产品组", //部门名称 * "parentid" => "1" //父部门id * "order" => "1", //(非必须)在父部门中的次序。从1开始,数字越大排序越靠后 * ) * @return boolean|array * 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "created", //对返回码的文本描述内容 * "id": 2 //创建的部门id。 * } */ public function createDepartment($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更新部门 * @param array $data 结构体为: * array( * "id" => "1" //(必须)部门id * "name" => "邮箱产品组", //(非必须)部门名称 * "parentid" => "1", //(非必须)父亲部门id。根部门id为1 * "order" => "1", //(非必须)在父部门中的次序。从1开始,数字越大排序越靠后 * ) * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "updated" //对返回码的文本描述内容 * } */ public function updateDepartment($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除部门 * @param $id * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "deleted" //对返回码的文本描述内容 * } */ public function deleteDepartment($id){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_DELETE_URL.'access_token='.$this->access_token.'&id='.$id); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 移动部门 * @param $data * array( * "department_id" => "5", //所要移动的部门 * "to_parentid" => "2", //想移动到的父部门节点,根部门为1 * "to_position" => "1" //(非必须)想移动到的父部门下的位置,1表示最上方,往后位置为2,3,4,以此类推,默认为1 * ) * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "ok" //对返回码的文本描述内容 * } */ public function moveDepartment($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_MOVE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取部门列表 * @return boolean|array 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * "department": [ //部门列表数据。以部门的order字段从小到大排列 * { * "id": 1, * "name": "广州研发中心", * "parentid": 0, * "order": 40 * }, * { * "id": 2 * "name": "邮箱产品部", * "parentid": 1, * "order": 40 * } * ] * } */ public function getDepartment(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_LIST_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 创建成员 * @param array $data 结构体为: * array( * "userid" => "zhangsan", * "name" => "张三", * "department" => [1, 2], * "position" => "产品经理", * "mobile" => "15913215421", * "gender" => 1, //性别。gender=0表示男,=1表示女 * "tel" => "62394", * "email" => "zhangsan@gzdev.com", * "weixinid" => "zhangsan4dev" * ) * @return boolean|array * 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "created", //对返回码的文本描述内容 * } */ public function createUser($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::USER_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更新成员 * @param array $data 结构体为: * array( * "userid" => "zhangsan", * "name" => "张三", * "department" => [1, 2], * "position" => "产品经理", * "mobile" => "15913215421", * "gender" => 1, //性别。gender=0表示男,=1表示女 * "tel" => "62394", * "email" => "zhangsan@gzdev.com", * "weixinid" => "zhangsan4dev" * ) * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "updated" //对返回码的文本描述内容 * } */ public function updateUser($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除成员 * @param $userid 员工UserID。对应管理端的帐号 * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "deleted" //对返回码的文本描述内容 * } */ public function deleteUser($userid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_DELETE_URL.'access_token='.$this->access_token.'&userid='.$userid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 批量删除成员 * @param array $userid 员工UserID数组。对应管理端的帐号 * { * 'userid1', * 'userid2', * 'userid3', * } * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "deleted" //对返回码的文本描述内容 * } */ public function deleteUsers($userids){ if (!$userids) return false; $data = array('useridlist'=>$userids); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::USER_BATCHDELETE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取成员信息 * @param $userid 员工UserID。对应管理端的帐号 * @return boolean|array 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * "userid": "zhangsan", * "name": "李四", * "department": [1, 2], * "position": "后台工程师", * "mobile": "15913215421", * "gender": 1, //性别。gender=0表示男,=1表示女 * "tel": "62394", * "email": "zhangsan@gzdev.com", * "weixinid": "lisifordev", //微信号 * "avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3W..../0", //头像url。注:如果要获取小图将url最后的"/0"改成"/64"即可 * "status": 1 //关注状态: 1=已关注,2=已冻结,4=未关注 * "extattr": {"attrs":[{"name":"爱好","value":"旅游"},{"name":"卡号","value":"1234567234"}]} * } */ public function getUserInfo($userid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&userid='.$userid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取部门成员 * @param $department_id 部门id * @param $fetch_child 1/0:是否递归获取子部门下面的成员 * @param $status 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 * @return boolean|array 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * "userlist": [ * { * "userid": "zhangsan", * "name": "李四" * } * ] * } */ public function getUserList($department_id,$fetch_child=0,$status=0){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_LIST_URL.'access_token='.$this->access_token .'&department_id='.$department_id.'&fetch_child='.$fetch_child.'&status='.$status); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取部门成员详情 * @param $department_id 部门id * @param $fetch_child 1/0:是否递归获取子部门下面的成员 * @param $status 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 * @return boolean|array 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * "userlist": [ * { * "userid": "zhangsan", * "name": "李四", * "department": [1, 2], * "position": "后台工程师", * "mobile": "15913215421", * "gender": 1, //性别。gender=0表示男,=1表示女 * "tel": "62394", * "email": "zhangsan@gzdev.com", * "weixinid": "lisifordev", //微信号 * "avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3W..../0", //头像url。注:如果要获取小图将url最后的"/0"改成"/64"即可 * "status": 1 //关注状态: 1=已关注,2=已冻结,4=未关注 * "extattr": {"attrs":[{"name":"爱好","value":"旅游"},{"name":"卡号","value":"1234567234"}]} * } * ] * } */ public function getUserListInfo($department_id,$fetch_child=0,$status=0){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_LIST_INFO_URL.'access_token='.$this->access_token .'&department_id='.$department_id.'&fetch_child='.$fetch_child.'&status='.$status); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 根据code获取成员信息 * 通过Oauth2.0或者设置了二次验证时获取的code,用于换取成员的UserId和DeviceId * * @param $code Oauth2.0或者二次验证时返回的code值 * @param $agentid 跳转链接时所在的企业应用ID,未填则默认为当前配置的应用id * @return boolean|array 成功返回数组 * array( * 'UserId' => 'USERID', //员工UserID * 'DeviceId' => 'DEVICEID' //手机设备号(由微信在安装时随机生成) * ) */ public function getUserId($code,$agentid=0){ if (!$agentid) $agentid=$this->agentid; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_GETINFO_URL.'access_token='.$this->access_token.'&code='.$code.'&agentid='.$agentid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 邀请成员关注 * 向未关注企业号的成员发送关注邀请。认证号优先判断顺序weixinid>手机号>邮箱绑定>邮件;非认证号只能邮件邀请 * * @param $userid 用户的userid * @param $invite_tips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 * @return boolean|array 成功返回数组 * array( * 'errcode' => 0, * 'errmsg' => 'ok', * 'type' => 1 //邀请方式 1.微信邀请 2.邮件邀请 * ) */ public function sendInvite($userid,$invite_tips=''){ $data = array( 'userid' => $userid ); if (!$invite_tips) { $data['invite_tips'] = $invite_tips; } if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::USER_INVITE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 创建标签 * @param array $data 结构体为: * array( * "tagname" => "UI" * ) * @return boolean|array * 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "created", //对返回码的文本描述内容 * "tagid": "1" * } */ public function createTag($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TAG_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更新标签 * @param array $data 结构体为: * array( * "tagid" => "1", * "tagname" => "UI design" * ) * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "updated" //对返回码的文本描述内容 * } */ public function updateTag($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TAG_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除标签 * @param $tagid 标签TagID * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "deleted" //对返回码的文本描述内容 * } */ public function deleteTag($tagid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::TAG_DELETE_URL.'access_token='.$this->access_token.'&tagid='.$tagid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取标签成员 * @param $tagid 标签TagID * @return boolean|array 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * "userlist": [ * { * "userid": "zhangsan", * "name": "李四" * } * ] * } */ public function getTag($tagid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::TAG_GET_URL.'access_token='.$this->access_token.'&tagid='.$tagid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 增加标签成员 * @param array $data 结构体为: * array ( * "tagid" => "1", * "userlist" => array( //企业员工ID列表 * "user1", * "user2" * ) * ) * @return boolean|array * 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "ok", //对返回码的文本描述内容 * "invalidlist":"usr1|usr2|usr" //若部分userid非法,则会有此段。不在权限内的员工ID列表,以“|”分隔 * } */ public function addTagUser($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TAG_ADDUSER_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除标签成员 * @param array $data 结构体为: * array ( * "tagid" => "1", * "userlist" => array( //企业员工ID列表 * "user1", * "user2" * ) * ) * @return boolean|array * 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "deleted", //对返回码的文本描述内容 * "invalidlist":"usr1|usr2|usr" //若部分userid非法,则会有此段。不在权限内的员工ID列表,以“|”分隔 * } */ public function delTagUser($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TAG_DELUSER_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取标签列表 * @return boolean|array 成功返回数组结果,这里附上json样例 * { * "errcode": 0, * "errmsg": "ok", * "taglist":[ * {"tagid":1,"tagname":"a"}, * {"tagid":2,"tagname":"b"} * ] * } */ public function getTagList(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::TAG_LIST_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 主动发送信息接口 * @param array $data 结构体为: * array( * "touser" => "UserID1|UserID2|UserID3", * "toparty" => "PartyID1|PartyID2 ", * "totag" => "TagID1|TagID2 ", * "safe":"0" //是否为保密消息,对于news无效 * "agentid" => "001", //应用id * "msgtype" => "text", //根据信息类型,选择下面对应的信息结构体 * * "text" => array( * "content" => "Holiday Request For Pony(http://xxxxx)" * ), * * "image" => array( * "media_id" => "MEDIA_ID" * ), * * "voice" => array( * "media_id" => "MEDIA_ID" * ), * * " video" => array( * "media_id" => "MEDIA_ID", * "title" => "Title", * "description" => "Description" * ), * * "file" => array( * "media_id" => "MEDIA_ID" * ), * * "news" => array( //不支持保密 * "articles" => array( //articles 图文消息,一个图文消息支持1到10个图文 * array( * "title" => "Title", //标题 * "description" => "Description", //描述 * "url" => "URL", //点击后跳转的链接。可根据url里面带的code参数校验员工的真实身份。 * "picurl" => "PIC_URL", //图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图640*320, * //小图80*80。如不填,在客户端不显示图片 * ), * ) * ), * * "mpnews" => array( * "articles" => array( //articles 图文消息,一个图文消息支持1到10个图文 * array( * "title" => "Title", //图文消息的标题 * "thumb_media_id" => "id", //图文消息缩略图的media_id * "author" => "Author", //图文消息的作者(可空) * "content_source_url" => "URL", //图文消息点击“阅读原文”之后的页面链接(可空) * "content" => "Content" //图文消息的内容,支持html标签 * "digest" => "Digest description", //图文消息的描述 * "show_cover_pic" => "0" //是否显示封面,1为显示,0为不显示(可空) * ), * ) * ) * ) * 请查看官方开发文档中的 发送消息 -> 消息类型及数据格式 * * @return boolean|array * 如果对应用或收件人、部门、标签任何一个无权限,则本次发送失败; * 如果收件人、部门或标签不存在,发送仍然执行,但返回无效的部分。 * { * "errcode": 0, * "errmsg": "ok", * "invaliduser": "UserID1", * "invalidparty":"PartyID1", * "invalidtag":"TagID1" * } */ public function sendMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 二次验证 * 企业在开启二次验证时,必须填写企业二次验证页面的url。 * 当员工绑定通讯录中的帐号后,会收到一条图文消息, * 引导员工到企业的验证页面验证身份,企业在员工验证成功后, * 调用如下接口即可让员工关注成功。 * * @param $userid * @return boolean|array 成功返回结果 * { * "errcode": 0, //返回码 * "errmsg": "ok" //对返回码的文本描述内容 * } */ public function authSucc($userid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::AUTHSUCC_URL.'access_token='.$this->access_token.'&userid='.$userid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode']) || $json['errcode']!=0) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * oauth 授权跳转接口 * @param string $callback 回调URI * @param string $state 重定向后会带上state参数,企业可以填写a-zA-Z0-9的参数值 * @return string */ public function getOauthRedirect($callback,$state='STATE',$scope='snsapi_base'){ return self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; } } /** * PKCS7Encoder class * * 提供基于PKCS7算法的加解密接口. */ class PKCS7Encoder { public static $block_size = 32; /** * 对需要加密的明文进行填充补位 * @param $text 需要进行填充补位操作的明文 * @return 补齐明文字符串 */ function encode($text) { $block_size = PKCS7Encoder::$block_size; $text_length = strlen($text); //计算需要填充的位数 $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size); if ($amount_to_pad == 0) { $amount_to_pad = PKCS7Encoder::block_size; } //获得补位所用的字符 $pad_chr = chr($amount_to_pad); $tmp = ""; for ($index = 0; $index < $amount_to_pad; $index++) { $tmp .= $pad_chr; } return $text . $tmp; } /** * 对解密后的明文进行补位删除 * @param decrypted 解密后的明文 * @return 删除填充补位后的明文 */ function decode($text) { $pad = ord(substr($text, -1)); if ($pad < 1 || $pad > PKCS7Encoder::$block_size) { $pad = 0; } return substr($text, 0, (strlen($text) - $pad)); } } /** * Prpcrypt class * * 提供接收和推送给公众平台消息的加解密接口. */ class Prpcrypt { public $key; function __construct($k) { $this->key = base64_decode($k . "="); } /** * 兼容老版本php构造函数,不能在 __construct() 方法前边,否则报错 */ function Prpcrypt($k) { $this->key = base64_decode($k . "="); } /** * 对明文进行加密 * @param string $text 需要加密的明文 * @return string 加密后的密文 */ public function encrypt($text, $appid) { try { //获得16位随机字符串,填充到明文之前 $random = $this->getRandomStr();//"aaaabbbbccccdddd"; $text = $random . pack("N", strlen($text)) . $text . $appid; // 网络字节序 $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); $iv = substr($this->key, 0, 16); //使用自定义的填充方式对明文进行补位填充 $pkc_encoder = new PKCS7Encoder; $text = $pkc_encoder->encode($text); mcrypt_generic_init($module, $this->key, $iv); //加密 $encrypted = mcrypt_generic($module, $text); mcrypt_generic_deinit($module); mcrypt_module_close($module); // print(base64_encode($encrypted)); //使用BASE64对加密后的字符串进行编码 return array(ErrorCode::$OK, base64_encode($encrypted)); } catch (Exception $e) { //print $e; return array(ErrorCode::$EncryptAESError, null); } } /** * 对密文进行解密 * @param string $encrypted 需要解密的密文 * @return string 解密得到的明文 */ public function decrypt($encrypted, $appid) { try { //使用BASE64对需要解密的字符串进行解码 $ciphertext_dec = base64_decode($encrypted); $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); $iv = substr($this->key, 0, 16); mcrypt_generic_init($module, $this->key, $iv); //解密 $decrypted = mdecrypt_generic($module, $ciphertext_dec); mcrypt_generic_deinit($module); mcrypt_module_close($module); } catch (Exception $e) { return array(ErrorCode::$DecryptAESError, null); } try { //去除补位字符 $pkc_encoder = new PKCS7Encoder; $result = $pkc_encoder->decode($decrypted); //去除16位随机字符串,网络字节序和AppId if (strlen($result) < 16) return ""; $content = substr($result, 16, strlen($result)); $len_list = unpack("N", substr($content, 0, 4)); $xml_len = $len_list[1]; $xml_content = substr($content, 4, $xml_len); $from_appid = substr($content, $xml_len + 4); } catch (Exception $e) { //print $e; return array(ErrorCode::$IllegalBuffer, null); } if ($from_appid != $appid) return array(ErrorCode::$ValidateAppidError, null); return array(0, $xml_content); } /** * 随机生成16位字符串 * @return string 生成的字符串 */ function getRandomStr() { $str = ""; $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($str_pol) - 1; for ($i = 0; $i < 16; $i++) { $str .= $str_pol[mt_rand(0, $max)]; } return $str; } } /** * error code * 仅用作类内部使用,不用于官方API接口的errCode码 */ class ErrorCode { public static $OK = 0; public static $ValidateSignatureError = 40001; public static $ParseXmlError = 40002; public static $ComputeSignatureError = 40003; public static $IllegalAesKey = 40004; public static $ValidateAppidError = 40005; public static $EncryptAESError = 40006; public static $DecryptAESError = 40007; public static $IllegalBuffer = 40008; public static $EncodeBase64Error = 40009; public static $DecodeBase64Error = 40010; public static $GenReturnXmlError = 40011; public static $errCode=array( '0'=>'无问题', '40001'=>'签名验证错误', '40002'=>'xml解析失败', '40003'=>'sha加密生成签名失败', '40004'=>'encodingAesKey 非法', '40005'=>'appid 校验错误', '40006'=>'aes 加密失败', '40007'=>'aes 解密失败', '40008'=>'解密后得到的buffer非法', '40009'=>'base64加密失败', '40010'=>'base64解密失败', '40011'=>'生成xml失败', ); public static function getErrText($err) { if (isset(self::$errCode[$err])) { return self::$errCode[$err]; }else { return false; }; } } ================================================ FILE: test/auth.php ================================================ options = $options; $this->wxoauth(); session_start(); } public function wxoauth(){ $scope = 'snsapi_base'; $code = isset($_GET['code'])?$_GET['code']:''; $token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0; if(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600) { if (!$this->wxuser) { $this->wxuser = $_SESSION['wxuser']; } $this->open_id = $_SESSION['open_id']; return $this->open_id; } else { $options = array( 'token'=>$this->options["token"], //填写你设定的key 'appid'=>$this->options["appid"], //填写高级调用功能的app id 'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥 ); $we_obj = new Wechat($options); if ($code) { $json = $we_obj->getOauthAccessToken(); if (!$json) { unset($_SESSION['wx_redirect']); die('获取用户授权失败,请重新确认'); } $_SESSION['open_id'] = $this->open_id = $json["openid"]; $access_token = $json['access_token']; $_SESSION['user_token'] = $access_token; $_SESSION['token_time'] = time(); $userinfo = $we_obj->getUserInfo($this->open_id); if ($userinfo && !empty($userinfo['nickname'])) { $this->wxuser = array( 'open_id'=>$this->open_id, 'nickname'=>$userinfo['nickname'], 'sex'=>intval($userinfo['sex']), 'location'=>$userinfo['province'].'-'.$userinfo['city'], 'avatar'=>$userinfo['headimgurl'] ); } elseif (strstr($json['scope'],'snsapi_userinfo')!==false) { $userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id); if ($userinfo && !empty($userinfo['nickname'])) { $this->wxuser = array( 'open_id'=>$this->open_id, 'nickname'=>$userinfo['nickname'], 'sex'=>intval($userinfo['sex']), 'location'=>$userinfo['province'].'-'.$userinfo['city'], 'avatar'=>$userinfo['headimgurl'] ); } else { return $this->open_id; } } if ($this->wxuser) { $_SESSION['wxuser'] = $this->wxuser; $_SESSION['open_id'] = $json["openid"]; unset($_SESSION['wx_redirect']); return $this->open_id; } $scope = 'snsapi_userinfo'; } if ($scope=='snsapi_base') { $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; $_SESSION['wx_redirect'] = $url; } else { $url = $_SESSION['wx_redirect']; } if (!$url) { unset($_SESSION['wx_redirect']); die('获取用户授权失败'); } $oauth_url = $we_obj->getOauthRedirect($url,"wxbase",$scope); header('Location: ' . $oauth_url); } } } $options = array( 'token'=>'tokenaccesskey', //填写你设定的key 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 ); $auth = new wxauth($options); var_dump($auth->wxuser); ================================================ FILE: test/getQRCode_test.php ================================================ 'tokenaccesskey', //填写你设定的key 'debug'=>true, 'logcallback'=>'logdebug' ); $weObj = new Wechat($options); // check null $scene_id $qrcode = $weObj->getQRCode(); if ($qrcode != false) { echo "test failed.\n"; die(); } // check bad $type $qrcode = $weObj->getQRCode(123, -1); if ($qrcode != false) { echo "test failed.\n"; die();} // check bad $type $qrcode = $weObj->getQRCode(123, 5); if ($qrcode != false) { echo "test failed.\n"; die();} // check bad $scene_id $qrcode = $weObj->getQRCode('ad', 0); if ($qrcode != false) { echo "test failed.\n"; die();} // check bad $scene_id $qrcode = $weObj->getQRCode('ad', 1); if ($qrcode != false) { echo "test failed.\n"; die();} // check bad $scene_id $qrcode = $weObj->getQRCode(123, 2); if ($qrcode != false) { echo "test failed.\n"; die();} echo "test passed.\n"; ================================================ FILE: test/jsapi/jsapi-demo-6.1.js ================================================ wx.ready(function () { alert("启动jsapi!"); // 1 判断当前版本是否支持指定 JS 接口,支持批量判断 document.querySelector('#checkJsApi').onclick = function () { wx.checkJsApi({ jsApiList: [ 'checkJsApi', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'translateVoice', 'startRecord', 'stopRecord', 'onRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard' ], success: function (res) { alert("检测通过:" +JSON.stringify(res)); }, fail: function(res) { alert("检测失败:" +JSON.stringify(res)); }, complete: function(res) { alert("检测结束"); } }); }; // 2. 分享接口 // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口 document.querySelector('#onMenuShareAppMessage').onclick = function () { wx.onMenuShareAppMessage({ title: 'wechat-php-sdk博客', desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', link: 'http://binsee.github.io/wechat-php-sdk/', imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', trigger: function (res) { alert("点击分享:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, success: function (res) { alert("分享成功:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, cancel: function (res) { alert("取消分享:" +JSON.stringify(res)); // 用户取消分享后执行的回调函数 }, fail:function (res) { alert("分享失败:" +JSON.stringify(res)); } }); alert('已注册获取“发送给朋友”状态事件'); }; // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口 document.querySelector('#onMenuShareTimeline').onclick = function () { wx.onMenuShareTimeline({ title: 'wechat-php-sdk博客', desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', link: 'http://binsee.github.io/wechat-php-sdk/', imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', trigger: function (res) { alert("点击分享:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, success: function (res) { alert("分享成功:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, cancel: function (res) { alert("取消分享:" +JSON.stringify(res)); // 用户取消分享后执行的回调函数 }, fail:function (res) { alert("分享失败:" +JSON.stringify(res)); } }); alert('已注册获取“分享到朋友圈”状态事件'); }; // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口 document.querySelector('#onMenuShareQQ').onclick = function () { wx.onMenuShareQQ({ title: 'wechat-php-sdk博客', desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', link: 'http://binsee.github.io/wechat-php-sdk/', imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', trigger: function (res) { alert("点击分享:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, success: function (res) { alert("分享成功:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, cancel: function (res) { alert("取消分享:" +JSON.stringify(res)); // 用户取消分享后执行的回调函数 }, fail:function (res) { alert("分享失败:" +JSON.stringify(res)); } }); alert('已注册获取“分享到 QQ”状态事件'); }; // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口 document.querySelector('#onMenuShareWeibo').onclick = function () { wx.onMenuShareWeibo({ title: 'wechat-php-sdk博客', desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', link: 'http://binsee.github.io/wechat-php-sdk/', imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', trigger: function (res) { alert("点击分享:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, success: function (res) { alert("分享成功:" +JSON.stringify(res)); // 用户确认分享后执行的回调函数 }, cancel: function (res) { alert("取消分享:" +JSON.stringify(res)); // 用户取消分享后执行的回调函数 }, fail:function (res) { alert("分享失败:" +JSON.stringify(res)); } }); alert('已注册获取“分享到微博”状态事件'); }; // 3 智能接口 var voice = { localId: '', serverId: '' }; // 3.1 识别音频并返回识别结果 document.querySelector('#translateVoice').onclick = function () { if (voice.localId == '') { alert('请先使用 startRecord 接口录制一段声音'); return; } wx.translateVoice({ localId: voice.localId, complete: function (res) { if (res.hasOwnProperty('translateResult')) { alert('识别结果:' + res.translateResult); } else { alert('无法识别'); } } }); }; // 4 音频接口 // 4.2 开始录音 document.querySelector('#startRecord').onclick = function () { wx.startRecord({ cancel: function () { alert('用户拒绝授权录音'); } }); }; // 4.3 停止录音 document.querySelector('#stopRecord').onclick = function () { wx.stopRecord({ success: function (res) { voice.localId = res.localId; }, fail: function (res) { alert(JSON.stringify(res)); } }); }; // 4.4 监听录音自动停止 wx.onVoiceRecordEnd({ complete: function (res) { voice.localId = res.localId; alert('录音时间已超过一分钟'); } }); // 4.5 播放音频 document.querySelector('#playVoice').onclick = function () { if (voice.localId == '') { alert('请先使用 startRecord 接口录制一段声音'); return; } wx.playVoice({ localId: voice.localId }); }; // 4.6 暂停播放音频 document.querySelector('#pauseVoice').onclick = function () { wx.pauseVoice({ localId: voice.localId }); }; // 4.7 停止播放音频 document.querySelector('#stopVoice').onclick = function () { wx.stopVoice({ localId: voice.localId }); }; // 4.8 监听录音播放停止 wx.onVoicePlayEnd({ complete: function (res) { alert('录音(' + res.localId + ')播放结束'); } }); // 4.8 上传语音 document.querySelector('#uploadVoice').onclick = function () { if (voice.localId == '') { alert('请先使用 startRecord 接口录制一段声音'); return; } wx.uploadVoice({ localId: voice.localId, success: function (res) { alert('上传语音成功,serverId 为' + res.serverId); voice.serverId = res.serverId; alert("上传语音信息:" + JSON.stringify(res)); } }); }; // 4.9 下载语音 document.querySelector('#downloadVoice').onclick = function () { if (voice.serverId == '') { alert('请先使用 uploadVoice 上传声音'); return; } wx.downloadVoice({ serverId: voice.serverId, success: function (res) { alert('下载语音成功,localId 为' + res.localId); voice.localId = res.localId; alert("下载语音信息:" + JSON.stringify(res)); } }); }; // 5 图片接口 // 5.1 拍照、本地选图 var images = { localId: [], serverId: [] }; document.querySelector('#chooseImage').onclick = function () { wx.chooseImage({ success: function (res) { images.localId = res.localIds; alert('已选择 ' + res.localIds.length + ' 张图片'); } }); }; // 5.2 图片预览 document.querySelector('#previewImage').onclick = function () { wx.previewImage({ current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', urls: [ 'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg', 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', 'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg' ] }); }; // 5.3 上传图片 document.querySelector('#uploadImage').onclick = function () { if (images.localId.length == 0) { alert('请先使用 chooseImage 接口选择图片'); return; } var i = 0, length = images.localId.length; images.serverId = []; function upload() { wx.uploadImage({ localId: images.localId[i], success: function (res) { i++; alert('已上传:' + i + '/' + length); alert("上传图片信息:" + JSON.stringify(res)); images.serverId.push(res.serverId); if (i < length) { upload(); } }, fail: function (res) { alert(JSON.stringify(res)); } }); } upload(); }; // 5.4 下载图片 document.querySelector('#downloadImage').onclick = function () { if (images.serverId.length === 0) { alert('请先使用 uploadImage 上传图片'); return; } var i = 0, length = images.serverId.length; images.localId = []; function download() { wx.downloadImage({ serverId: images.serverId[i], success: function (res) { i++; alert('已下载:' + i + '/' + length); alert("下载图片信息:" + JSON.stringify(res)); images.localId.push(res.localId); if (i < length) { download(); } } }); } download(); }; // 6 设备信息接口 // 6.1 获取当前网络状态 document.querySelector('#getNetworkType').onclick = function () { wx.getNetworkType({ success: function (res) { alert(res.networkType); }, fail: function (res) { alert(JSON.stringify(res)); } }); }; // 7 地理位置接口 // 7.1 查看地理位置 document.querySelector('#openLocation').onclick = function () { wx.openLocation({ latitude: 23.099994, longitude: 113.324520, name: 'TIT 创意园', address: '广州市海珠区新港中路 397 号', scale: 14, infoUrl: 'http://weixin.qq.com' }); }; // 7.2 获取当前地理位置 document.querySelector('#getLocation').onclick = function () { wx.getLocation({ success: function (res) { alert(JSON.stringify(res)); }, cancel: function (res) { alert('用户拒绝授权获取地理位置'); } }); }; // 8 界面操作接口 // 8.1 隐藏右上角菜单 document.querySelector('#hideOptionMenu').onclick = function () { wx.hideOptionMenu(); }; // 8.2 显示右上角菜单 document.querySelector('#showOptionMenu').onclick = function () { wx.showOptionMenu(); }; // 8.3 批量隐藏菜单项 document.querySelector('#hideMenuItems').onclick = function () { wx.hideMenuItems({ menuList: [ 'menuItem:readMode', // 阅读模式 'menuItem:share:timeline', // 分享到朋友圈 'menuItem:copyUrl' // 复制链接 ], success: function (res) { alert('已隐藏“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); }, fail: function (res) { alert(JSON.stringify(res)); } }); }; // 8.4 批量显示菜单项 document.querySelector('#showMenuItems').onclick = function () { wx.showMenuItems({ menuList: [ 'menuItem:readMode', // 阅读模式 'menuItem:share:timeline', // 分享到朋友圈 'menuItem:copyUrl' // 复制链接 ], success: function (res) { alert('已显示“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); }, fail: function (res) { alert(JSON.stringify(res)); } }); }; // 8.5 隐藏所有非基本菜单项 document.querySelector('#hideAllNonBaseMenuItem').onclick = function () { wx.hideAllNonBaseMenuItem({ success: function () { alert('已隐藏所有非基本菜单项'); } }); }; // 8.6 显示所有被隐藏的非基本菜单项 document.querySelector('#showAllNonBaseMenuItem').onclick = function () { wx.showAllNonBaseMenuItem({ success: function () { alert('已显示所有非基本菜单项'); } }); }; // 8.7 关闭当前窗口 document.querySelector('#closeWindow').onclick = function () { wx.closeWindow(); }; // 9 微信原生接口 // 9.1.1 扫描二维码并返回结果 document.querySelector('#scanQRCode0').onclick = function () { wx.scanQRCode({ desc: 'scanQRCode desc' }); }; // 9.1.2 扫描二维码并返回结果 document.querySelector('#scanQRCode1').onclick = function () { wx.scanQRCode({ needResult: 1, desc: 'scanQRCode desc', success: function (res) { alert(JSON.stringify(res)); } }); }; // 10 微信支付接口 // 10.1 发起一个支付请求 document.querySelector('#chooseWXPay').onclick = function () { wx.chooseWXPay({ timestamp: 1414723227, nonceStr: 'noncestr', package: 'addition=action_id%3dgaby1234%26limit_pay%3d&bank_type=WX&body=innertest&fee_type=1&input_charset=GBK¬ify_url=http%3A%2F%2F120.204.206.246%2Fcgi-bin%2Fmmsupport-bin%2Fnotifypay&out_trade_no=1414723227818375338&partner=1900000109&spbill_create_ip=127.0.0.1&total_fee=1&sign=432B647FE95C7BF73BCD177CEECBEF8D', paySign: 'bd5b1933cda6e9548862944836a9b52e8c9a2b69' }); }; // 11.3 跳转微信商品页 document.querySelector('#openProductSpecificView').onclick = function () { wx.openProductSpecificView({ productId: 'pDF3iY0ptap-mIIPYnsM5n8VtCR0' }); }; // 12 微信卡券接口 // 12.1 添加卡券 document.querySelector('#addCard').onclick = function () { wx.addCard({ cardList: [ { cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' }, { cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' } ], success: function (res) { alert('已添加卡券:' + JSON.stringify(res.cardList)); } }); }; // 12.2 选择卡券 document.querySelector('#chooseCard').onclick = function () { wx.chooseCard({ cardSign: '97e9c5e58aab3bdf6fd6150e599d7e5806e5cb91', timestamp: 1417504553, nonceStr: 'k0hGdSXKZEj3Min5', success: function (res) { alert('已选择卡券:' + JSON.stringify(res.cardList)); } }); }; // 12.3 查看卡券 document.querySelector('#openCard').onclick = function () { alert('您没有该公众号的卡券无法打开卡券。'); wx.openCard({ cardList: [ ] }); }; }); wx.error(function (res) { alert(res.errMsg); }); ================================================ FILE: test/jsapi/jsapi_demo.php ================================================ 'xxxxxxxxxxxxxxxxxxxxxxxxxx',//填写高级调用功能的密钥 'appid'=>'wxxxxxxxxxxxxxx' //填写高级调用功能的appid ); $we = new Wechat($opt); $auth = $we->checkAuth(); $js_ticket = $we->getJsTicket(); if (!$js_ticket) { echo "获取js_ticket失败!
"; echo '错误码:'.$we->errCode; echo ' 错误原因:'.ErrCode::getErrText($weObj->errCode); exit; } $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; $js_sign = $we->getJsSign($url); ?> JS-SDK测试页
判断当前客户端是否支持指定JS接口 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 获取“分享给朋友”按钮点击状态及自定义分享内容接口 获取“分享到QQ”按钮点击状态及自定义分享内容接口 获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 拍照或从手机相册中选图接口 预览图片接口 上传图片接口 下载图片接口 开始录音接口 停止录音接口 播放语音接口 暂停播放接口 停止播放接口 上传语音接口 下载语音接口 识别音频并返回识别结果接口 获取网络状态接口 使用微信内置地图查看位置接口 获取地理位置接口 隐藏右上角菜单接口 显示右上角菜单接口 关闭当前网页窗口接口 批量隐藏功能按钮接口 批量显示功能按钮接口 隐藏所有非基础按钮接口 显示所有功能按钮接口 调起微信扫一扫接口 跳转微信商品页接口 批量添加卡券接口 调起适用于门店的卡券列表并获取用户选择列表 查看微信卡包中的卡券接口 发起一个微信支付请求
================================================ FILE: test/jsapi/style.css ================================================ html { -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; -webkit-user-select: none; user-select: none; } body { line-height: 1.6; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; background-color: #f1f0f6; } * { margin: 0; padding: 0; } button { font-family: inherit; font-size: 100%; margin: 0; *font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } ul, ol { padding-left: 0; list-style-type: none; } a { text-decoration: none; } .label_box { background-color: #ffffff; } .label_item { padding-left: 15px; } .label_inner { padding-top: 10px; padding-bottom: 10px; min-height: 24px; position: relative; } .label_inner:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); top: auto; bottom: -2px; } .lbox_close { position: relative; } .lbox_close:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); } .lbox_close:after { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); top: auto; bottom: -2px; } .lbox_close .label_item:last-child .label_inner:before { display: none; } .btn { display: block; margin-left: auto; margin-right: auto; padding-left: 14px; padding-right: 14px; font-size: 18px; text-align: center; text-decoration: none; overflow: visible; /*.btn_h(@btnHeight);*/ height: 42px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; color: #ffffff; line-height: 42px; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } .btn.btn_inline { display: inline-block; } .btn_primary { background-color: #04be02; } .btn_primary:not(.btn_disabled):visited { color: #ffffff; } .btn_primary:not(.btn_disabled):active { color: rgba(255, 255, 255, 0.9); background-color: #039702; } button.btn { width: 100%; border: 0; outline: 0; -webkit-appearance: none; } button.btn:focus { outline: 0; } .wxapi_container { font-size: 16px; } h1 { font-size: 14px; font-weight: 400; line-height: 2em; padding-left: 15px; color: #8d8c92; } .desc { font-size: 14px; font-weight: 400; line-height: 2em; color: #8d8c92; } .wxapi_index_item a { display: block; color: #3e3e3e; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .wxapi_form { background-color: #ffffff; padding: 0 15px; margin-top: 30px; padding-bottom: 15px; } h3 { padding-top: 16px; margin-top: 25px; font-size: 16px; font-weight: 400; color: #3e3e3e; position: relative; } h3:first-child { padding-top: 15px; } h3:before { content: " "; position: absolute; left: 0; top: 0; width: 200%; height: 1px; border-top: 1px solid #ededed; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scale(0.5); transform: scale(0.5); } .btn { margin-bottom: 15px; } ================================================ FILE: test/merchanttest.php ================================================ 'XXXXXX', //填写你设定的key 'encodingaeskey'=>'XXXXXX', //填写加密用的EncodingAESKey,如接口为明文模式可忽略 'appid'=>'XXXXXX', //填写高级调用功能的app id 'appsecret'=>'XXXXXX' //填写高级调用功能的密钥 ); $weObj = new Wechat($options); $order = $weObj->getOrderByID("13791169361138306965"); var_dump($order); $order = $weObj->getOrderByFilter(); var_dump($order); ================================================ FILE: test/qydemo.php ================================================ '9xxxxxxxxxxxx', //填写应用接口的Token 'encodingaeskey'=>'d4oxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 'appid'=>'wxa0xxxxxxxxxx', //填写高级调用功能的appid 'debug'=>true, 'logcallback'=>'logg' ); logg("GET参数为:\n".var_export($_GET,true)); $weObj = new Wechat($options); $ret=$weObj->valid(); if (!$ret) { logg("验证失败!"); var_dump($ret); exit; } $f = $weObj->getRev()->getRevFrom(); $t = $weObj->getRevType(); $d = $weObj->getRevData(); $weObj->text("你好!来自星星的:".$f."\n你发送的".$t."类型信息:\n原始信息如下:\n".var_export($d,true))->reply(); logg("-----------------------------------------"); ?> ================================================ FILE: test/test-upload-shake-around-media.php ================================================ '', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret' => '', //填写高级调用功能的密钥 ); $weObj = new Wechat($options); $weObj->uploadShakeAroundMedia(array('media'=>'@')); ================================================ FILE: test/test1.php ================================================ 'tokenaccesskey', //填写你设定的key 'debug'=>true, 'logcallback'=>'logdebug' ); $weObj = new Wechat($options); $weObj->valid(); $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ================================================ FILE: wechat.class.php ================================================ * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * usage: * $options = array( * 'token'=>'tokenaccesskey', //填写你设定的key * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 * ); * $weObj = new Wechat($options); * $weObj->valid(); * $type = $weObj->getRev()->getRevType(); * switch($type) { * case Wechat::MSGTYPE_TEXT: * $weObj->text("hello, I'm wechat")->reply(); * exit; * break; * case Wechat::MSGTYPE_EVENT: * .... * break; * case Wechat::MSGTYPE_IMAGE: * ... * break; * default: * $weObj->text("help info")->reply(); * } * * //获取菜单操作: * $menu = $weObj->getMenu(); * //设置菜单 * $newmenu = array( * "button"=> * array( * array('type'=>'click','name'=>'最新消息','key'=>'MENU_KEY_NEWS'), * array('type'=>'view','name'=>'我要搜索','url'=>'http://www.baidu.com'), * ) * ); * $result = $weObj->createMenu($newmenu); */ class Wechat { const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; const MSGTYPE_SHORTVIDEO = 'shortvideo'; const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_SCAN = 'SCAN'; //扫描带参数二维码 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 const EVENT_KF_SEESION_CREATE = 'kfcreatesession'; //多客服 - 接入会话 const EVENT_KF_SEESION_CLOSE = 'kfclosesession'; //多客服 - 关闭会话 const EVENT_KF_SEESION_SWITCH = 'kfswitchsession'; //多客服 - 转接会话 const EVENT_CARD_PASS = 'card_pass_check'; //卡券 - 审核通过 const EVENT_CARD_NOTPASS = 'card_not_pass_check'; //卡券 - 审核未通过 const EVENT_CARD_USER_GET = 'user_get_card'; //卡券 - 用户领取卡券 const EVENT_CARD_USER_DEL = 'user_del_card'; //卡券 - 用户删除卡券 const EVENT_MERCHANT_ORDER = 'merchant_order'; //微信小店 - 订单付款通知 const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin'; const AUTH_URL = '/token?grant_type=client_credential&'; const MENU_CREATE_URL = '/menu/create?'; const MENU_GET_URL = '/menu/get?'; const MENU_DELETE_URL = '/menu/delete?'; const MENU_ADDCONDITIONAL_URL = '/menu/addconditional?'; const MENU_DELCONDITIONAL_URL = '/menu/delconditional?'; const MENU_TRYMATCH_URL = '/menu/trymatch?'; const GET_TICKET_URL = '/ticket/getticket?'; const CALLBACKSERVER_GET_URL = '/getcallbackip?'; const QRCODE_CREATE_URL='/qrcode/create?'; const QR_SCENE = 0; const QR_LIMIT_SCENE = 1; const QRCODE_IMG_URL='https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket='; const SHORT_URL='/shorturl?'; const USER_GET_URL='/user/get?'; const USER_INFO_URL='/user/info?'; const USERS_INFO_URL='/user/info/batchget?'; const USER_UPDATEREMARK_URL='/user/info/updateremark?'; const GROUP_GET_URL='/groups/get?'; const USER_GROUP_URL='/groups/getid?'; const GROUP_CREATE_URL='/groups/create?'; const GROUP_UPDATE_URL='/groups/update?'; const GROUP_MEMBER_UPDATE_URL='/groups/members/update?'; const GROUP_MEMBER_BATCHUPDATE_URL='/groups/members/batchupdate?'; const CUSTOM_SEND_URL='/message/custom/send?'; const MEDIA_UPLOADNEWS_URL = '/media/uploadnews?'; const MASS_SEND_URL = '/message/mass/send?'; const TEMPLATE_SET_INDUSTRY_URL = '/template/api_set_industry?'; const TEMPLATE_ADD_TPL_URL = '/template/api_add_template?'; const TEMPLATE_SEND_URL = '/message/template/send?'; const MASS_SEND_GROUP_URL = '/message/mass/sendall?'; const MASS_DELETE_URL = '/message/mass/delete?'; const MASS_PREVIEW_URL = '/message/mass/preview?'; const MASS_QUERY_URL = '/message/mass/get?'; const UPLOAD_MEDIA_URL = 'http://file.api.weixin.qq.com/cgi-bin'; const MEDIA_UPLOAD_URL = '/media/upload?'; const MEDIA_UPLOADIMG_URL = '/media/uploadimg?';//图片上传接口 const MEDIA_GET_URL = '/media/get?'; const MEDIA_VIDEO_UPLOAD = '/media/uploadvideo?'; const MEDIA_FOREVER_UPLOAD_URL = '/material/add_material?'; const MEDIA_FOREVER_NEWS_UPLOAD_URL = '/material/add_news?'; const MEDIA_FOREVER_NEWS_UPDATE_URL = '/material/update_news?'; const MEDIA_FOREVER_GET_URL = '/material/get_material?'; const MEDIA_FOREVER_DEL_URL = '/material/del_material?'; const MEDIA_FOREVER_COUNT_URL = '/material/get_materialcount?'; const MEDIA_FOREVER_BATCHGET_URL = '/material/batchget_material?'; const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2'; const OAUTH_AUTHORIZE_URL = '/authorize?'; ///多客服相关地址 const CUSTOM_SERVICE_GET_RECORD = '/customservice/getrecord?'; const CUSTOM_SERVICE_GET_KFLIST = '/customservice/getkflist?'; const CUSTOM_SERVICE_GET_ONLINEKFLIST = '/customservice/getonlinekflist?'; const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀 const OAUTH_TOKEN_URL = '/sns/oauth2/access_token?'; const OAUTH_REFRESH_URL = '/sns/oauth2/refresh_token?'; const OAUTH_USERINFO_URL = '/sns/userinfo?'; const OAUTH_AUTH_URL = '/sns/auth?'; ///多客服相关地址 const CUSTOM_SESSION_CREATE = '/customservice/kfsession/create?'; const CUSTOM_SESSION_CLOSE = '/customservice/kfsession/close?'; const CUSTOM_SESSION_SWITCH = '/customservice/kfsession/switch?'; const CUSTOM_SESSION_GET = '/customservice/kfsession/getsession?'; const CUSTOM_SESSION_GET_LIST = '/customservice/kfsession/getsessionlist?'; const CUSTOM_SESSION_GET_WAIT = '/customservice/kfsession/getwaitcase?'; const CS_KF_ACCOUNT_ADD_URL = '/customservice/kfaccount/add?'; const CS_KF_ACCOUNT_UPDATE_URL = '/customservice/kfaccount/update?'; const CS_KF_ACCOUNT_DEL_URL = '/customservice/kfaccount/del?'; const CS_KF_ACCOUNT_UPLOAD_HEADIMG_URL = '/customservice/kfaccount/uploadheadimg?'; ///卡券相关地址 const CARD_CREATE = '/card/create?'; const CARD_DELETE = '/card/delete?'; const CARD_UPDATE = '/card/update?'; const CARD_GET = '/card/get?'; const CARD_USER_GETCARDLIST = '/card/user/getcardlist?'; const CARD_BATCHGET = '/card/batchget?'; const CARD_MODIFY_STOCK = '/card/modifystock?'; const CARD_LOCATION_BATCHADD = '/card/location/batchadd?'; const CARD_LOCATION_BATCHGET = '/card/location/batchget?'; const CARD_GETCOLORS = '/card/getcolors?'; const CARD_QRCODE_CREATE = '/card/qrcode/create?'; const CARD_CODE_CONSUME = '/card/code/consume?'; const CARD_CODE_DECRYPT = '/card/code/decrypt?'; const CARD_CODE_GET = '/card/code/get?'; const CARD_CODE_UPDATE = '/card/code/update?'; const CARD_CODE_UNAVAILABLE = '/card/code/unavailable?'; const CARD_TESTWHILELIST_SET = '/card/testwhitelist/set?'; const CARD_MEETINGCARD_UPDATEUSER = '/card/meetingticket/updateuser?'; //更新会议门票 const CARD_MEMBERCARD_ACTIVATE = '/card/membercard/activate?'; //激活会员卡 const CARD_MEMBERCARD_UPDATEUSER = '/card/membercard/updateuser?'; //更新会员卡 const CARD_MOVIETICKET_UPDATEUSER = '/card/movieticket/updateuser?'; //更新电影票(未加方法) const CARD_BOARDINGPASS_CHECKIN = '/card/boardingpass/checkin?'; //飞机票-在线选座(未加方法) const CARD_LUCKYMONEY_UPDATE = '/card/luckymoney/updateuserbalance?'; //更新红包金额 const SEMANTIC_API_URL = '/semantic/semproxy/search?'; //语义理解 ///数据分析接口 static $DATACUBE_URL_ARR = array( //用户分析 'user' => array( 'summary' => '/datacube/getusersummary?', //获取用户增减数据(getusersummary) 'cumulate' => '/datacube/getusercumulate?', //获取累计用户数据(getusercumulate) ), 'article' => array( //图文分析 'summary' => '/datacube/getarticlesummary?', //获取图文群发每日数据(getarticlesummary) 'total' => '/datacube/getarticletotal?', //获取图文群发总数据(getarticletotal) 'read' => '/datacube/getuserread?', //获取图文统计数据(getuserread) 'readhour' => '/datacube/getuserreadhour?', //获取图文统计分时数据(getuserreadhour) 'share' => '/datacube/getusershare?', //获取图文分享转发数据(getusershare) 'sharehour' => '/datacube/getusersharehour?', //获取图文分享转发分时数据(getusersharehour) ), 'upstreammsg' => array( //消息分析 'summary' => '/datacube/getupstreammsg?', //获取消息发送概况数据(getupstreammsg) 'hour' => '/datacube/getupstreammsghour?', //获取消息分送分时数据(getupstreammsghour) 'week' => '/datacube/getupstreammsgweek?', //获取消息发送周数据(getupstreammsgweek) 'month' => '/datacube/getupstreammsgmonth?', //获取消息发送月数据(getupstreammsgmonth) 'dist' => '/datacube/getupstreammsgdist?', //获取消息发送分布数据(getupstreammsgdist) 'distweek' => '/datacube/getupstreammsgdistweek?', //获取消息发送分布周数据(getupstreammsgdistweek) 'distmonth' => '/datacube/getupstreammsgdistmonth?', //获取消息发送分布月数据(getupstreammsgdistmonth) ), 'interface' => array( //接口分析 'summary' => '/datacube/getinterfacesummary?', //获取接口分析数据(getinterfacesummary) 'summaryhour' => '/datacube/getinterfacesummaryhour?', //获取接口分析分时数据(getinterfacesummaryhour) ) ); ///微信摇一摇周边 const SHAKEAROUND_DEVICE_APPLYID = '/shakearound/device/applyid?';//申请设备ID const SHAKEAROUND_DEVICE_UPDATE = '/shakearound/device/update?';//编辑设备信息 const SHAKEAROUND_DEVICE_SEARCH = '/shakearound/device/search?';//查询设备列表 const SHAKEAROUND_DEVICE_BINDLOCATION = '/shakearound/device/bindlocation?';//配置设备与门店ID的关系 const SHAKEAROUND_DEVICE_BINDPAGE = '/shakearound/device/bindpage?';//配置设备与页面的绑定关系 const SHAKEAROUND_MATERIAL_ADD = '/shakearound/material/add?';//上传摇一摇图片素材 const SHAKEAROUND_PAGE_ADD = '/shakearound/page/add?';//增加页面 const SHAKEAROUND_PAGE_UPDATE = '/shakearound/page/update?';//编辑页面 const SHAKEAROUND_PAGE_SEARCH = '/shakearound/page/search?';//查询页面列表 const SHAKEAROUND_PAGE_DELETE = '/shakearound/page/delete?';//删除页面 const SHAKEAROUND_USER_GETSHAKEINFO = '/shakearound/user/getshakeinfo?';//获取摇周边的设备及用户信息 const SHAKEAROUND_STATISTICS_DEVICE = '/shakearound/statistics/device?';//以设备为维度的数据统计接口 const SHAKEAROUND_STATISTICS_PAGE = '/shakearound/statistics/page?';//以页面为维度的数据统计接口 ///微信小店相关接口 const MERCHANT_ORDER_GETBYID = '/merchant/order/getbyid?';//根据订单ID获取订单详情 const MERCHANT_ORDER_GETBYFILTER = '/merchant/order/getbyfilter?';//根据订单状态/创建时间获取订单详情 const MERCHANT_ORDER_SETDELIVERY = '/merchant/order/setdelivery?';//设置订单发货信息 const MERCHANT_ORDER_CLOSE = '/merchant/order/close?';//关闭订单 private $token; private $encodingAesKey; private $encrypt_type; private $appid; private $appsecret; private $access_token; private $jsapi_ticket; private $api_ticket; private $user_token; private $partnerid; private $partnerkey; private $paysignkey; private $postxml; private $_msg; private $_funcflag = false; private $_receive; private $_text_filter = true; public $debug = false; public $errCode = 40001; public $errMsg = "no access"; public $logcallback; public function __construct($options) { $this->token = isset($options['token'])?$options['token']:''; $this->encodingAesKey = isset($options['encodingaeskey'])?$options['encodingaeskey']:''; $this->appid = isset($options['appid'])?$options['appid']:''; $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; $this->debug = isset($options['debug'])?$options['debug']:false; $this->logcallback = isset($options['logcallback'])?$options['logcallback']:false; } /** * For weixin server validation */ private function checkSignature($str='') { $signature = isset($_GET["signature"])?$_GET["signature"]:''; $signature = isset($_GET["msg_signature"])?$_GET["msg_signature"]:$signature; //如果存在加密验证则用加密验证段 $timestamp = isset($_GET["timestamp"])?$_GET["timestamp"]:''; $nonce = isset($_GET["nonce"])?$_GET["nonce"]:''; $token = $this->token; $tmpArr = array($token, $timestamp, $nonce,$str); sort($tmpArr, SORT_STRING); $tmpStr = implode( $tmpArr ); $tmpStr = sha1( $tmpStr ); if( $tmpStr == $signature ){ return true; }else{ return false; } } /** * For weixin server validation * @param bool $return 是否返回 */ public function valid($return=false) { $encryptStr=""; if ($_SERVER['REQUEST_METHOD'] == "POST") { $postStr = file_get_contents("php://input"); $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $this->encrypt_type = isset($_GET["encrypt_type"]) ? $_GET["encrypt_type"]: ''; if ($this->encrypt_type == 'aes') { //aes加密 $this->log($postStr); $encryptStr = $array['Encrypt']; $pc = new Prpcrypt($this->encodingAesKey); $array = $pc->decrypt($encryptStr,$this->appid); if (!isset($array[0]) || ($array[0] != 0)) { if (!$return) { die('decrypt error!'); } else { return false; } } $this->postxml = $array[1]; if (!$this->appid) $this->appid = $array[2];//为了没有appid的订阅号。 } else { $this->postxml = $postStr; } } elseif (isset($_GET["echostr"])) { $echoStr = $_GET["echostr"]; if ($return) { if ($this->checkSignature()) return $echoStr; else return false; } else { if ($this->checkSignature()) die($echoStr); else die('no access'); } } if (!$this->checkSignature($encryptStr)) { if ($return) return false; else die('no access'); } return true; } /** * 设置发送消息 * @param array $msg 消息数组 * @param bool $append 是否在原消息数组追加 */ public function Message($msg = '',$append = false){ if (is_null($msg)) { $this->_msg =array(); }elseif (is_array($msg)) { if ($append) $this->_msg = array_merge($this->_msg,$msg); else $this->_msg = $msg; return $this->_msg; } else { return $this->_msg; } } /** * 设置消息的星标标志,官方已取消对此功能的支持 */ public function setFuncFlag($flag) { $this->_funcflag = $flag; return $this; } /** * 日志记录,可被重载。 * @param mixed $log 输入日志 * @return mixed */ protected function log($log){ if ($this->debug && function_exists($this->logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->logcallback,$log); } } /** * 获取微信服务器发来的信息 */ public function getRev() { if ($this->_receive) return $this; $postStr = !empty($this->postxml)?$this->postxml:file_get_contents("php://input"); //兼顾使用明文又不想调用valid()方法的情况 $this->log($postStr); if (!empty($postStr)) { $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); } return $this; } /** * 获取微信服务器发来的信息 */ public function getRevData() { return $this->_receive; } /** * 获取消息发送者 */ public function getRevFrom() { if (isset($this->_receive['FromUserName'])) return $this->_receive['FromUserName']; else return false; } /** * 获取消息接受者 */ public function getRevTo() { if (isset($this->_receive['ToUserName'])) return $this->_receive['ToUserName']; else return false; } /** * 获取接收消息的类型 */ public function getRevType() { if (isset($this->_receive['MsgType'])) return $this->_receive['MsgType']; else return false; } /** * 获取消息ID */ public function getRevID() { if (isset($this->_receive['MsgId'])) return $this->_receive['MsgId']; else return false; } /** * 获取消息发送时间 */ public function getRevCtime() { if (isset($this->_receive['CreateTime'])) return $this->_receive['CreateTime']; else return false; } /** * 获取接收消息内容正文 */ public function getRevContent(){ if (isset($this->_receive['Content'])) return $this->_receive['Content']; else if (isset($this->_receive['Recognition'])) //获取语音识别文字内容,需申请开通 return $this->_receive['Recognition']; else return false; } /** * 获取接收消息图片 */ public function getRevPic(){ if (isset($this->_receive['PicUrl'])) return array( 'mediaid'=>$this->_receive['MediaId'], 'picurl'=>(string)$this->_receive['PicUrl'], //防止picurl为空导致解析出错 ); else return false; } /** * 获取接收消息链接 */ public function getRevLink(){ if (isset($this->_receive['Url'])){ return array( 'url'=>$this->_receive['Url'], 'title'=>$this->_receive['Title'], 'description'=>$this->_receive['Description'] ); } else return false; } /** * 获取接收地理位置 */ public function getRevGeo(){ if (isset($this->_receive['Location_X'])){ return array( 'x'=>$this->_receive['Location_X'], 'y'=>$this->_receive['Location_Y'], 'scale'=>$this->_receive['Scale'], 'label'=>$this->_receive['Label'] ); } else return false; } /** * 获取上报地理位置事件 */ public function getRevEventGeo(){ if (isset($this->_receive['Latitude'])){ return array( 'x'=>$this->_receive['Latitude'], 'y'=>$this->_receive['Longitude'], 'precision'=>$this->_receive['Precision'], ); } else return false; } /** * 获取接收事件推送 */ public function getRevEvent(){ if (isset($this->_receive['Event'])){ $array['event'] = $this->_receive['Event']; } if (isset($this->_receive['EventKey'])){ $array['key'] = $this->_receive['EventKey']; } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的扫码推事件信息 * * 事件类型为以下两种时则调用此方法有效 * Event 事件类型,scancode_push * Event 事件类型,scancode_waitmsg * * @return: array | false * array ( * 'ScanType'=>'qrcode', * 'ScanResult'=>'123123' * ) */ public function getRevScanInfo(){ if (isset($this->_receive['ScanCodeInfo'])){ if (!is_array($this->_receive['ScanCodeInfo'])) { $array=(array)$this->_receive['ScanCodeInfo']; $this->_receive['ScanCodeInfo']=$array; }else { $array=$this->_receive['ScanCodeInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的图片发送事件信息 * * 事件类型为以下三种时则调用此方法有效 * Event 事件类型,pic_sysphoto 弹出系统拍照发图的事件推送 * Event 事件类型,pic_photo_or_album 弹出拍照或者相册发图的事件推送 * Event 事件类型,pic_weixin 弹出微信相册发图器的事件推送 * * @return: array | false * array ( * 'Count' => '2', * 'PicList' =>array ( * 'item' =>array ( * 0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'), * 1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'), * ), * ), * ) * */ public function getRevSendPicsInfo(){ if (isset($this->_receive['SendPicsInfo'])){ if (!is_array($this->_receive['SendPicsInfo'])) { $array=(array)$this->_receive['SendPicsInfo']; if (isset($array['PicList'])){ $array['PicList']=(array)$array['PicList']; $item=$array['PicList']['item']; $array['PicList']['item']=array(); foreach ( $item as $key => $value ){ $array['PicList']['item'][$key]=(array)$value; } } $this->_receive['SendPicsInfo']=$array; } else { $array=$this->_receive['SendPicsInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取自定义菜单的地理位置选择器事件推送 * * 事件类型为以下时则可以调用此方法有效 * Event 事件类型,location_select 弹出地理位置选择器的事件推送 * * @return: array | false * array ( * 'Location_X' => '33.731655000061', * 'Location_Y' => '113.29955200008047', * 'Scale' => '16', * 'Label' => '某某市某某区某某路', * 'Poiname' => '', * ) * */ public function getRevSendGeoInfo(){ if (isset($this->_receive['SendLocationInfo'])){ if (!is_array($this->_receive['SendLocationInfo'])) { $array=(array)$this->_receive['SendLocationInfo']; if (empty($array['Poiname'])) { $array['Poiname']=""; } if (empty($array['Label'])) { $array['Label']=""; } $this->_receive['SendLocationInfo']=$array; } else { $array=$this->_receive['SendLocationInfo']; } } if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取接收语音推送 */ public function getRevVoice(){ if (isset($this->_receive['MediaId'])){ return array( 'mediaid'=>$this->_receive['MediaId'], 'format'=>$this->_receive['Format'], ); } else return false; } /** * 获取接收视频推送 */ public function getRevVideo(){ if (isset($this->_receive['MediaId'])){ return array( 'mediaid'=>$this->_receive['MediaId'], 'thumbmediaid'=>$this->_receive['ThumbMediaId'] ); } else return false; } /** * 获取接收TICKET */ public function getRevTicket(){ if (isset($this->_receive['Ticket'])){ return $this->_receive['Ticket']; } else return false; } /** * 获取二维码的场景值 */ public function getRevSceneId (){ if (isset($this->_receive['EventKey'])){ return str_replace('qrscene_','',$this->_receive['EventKey']); } else{ return false; } } /** * 获取主动推送的消息ID * 经过验证,这个和普通的消息MsgId不一样 * 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH */ public function getRevTplMsgID(){ if (isset($this->_receive['MsgID'])){ return $this->_receive['MsgID']; } else return false; } /** * 获取模板消息发送状态 */ public function getRevStatus(){ if (isset($this->_receive['Status'])){ return $this->_receive['Status']; } else return false; } /** * 获取群发或模板消息发送结果 * 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH,即高级群发/模板消息 */ public function getRevResult(){ if (isset($this->_receive['Status'])) //发送是否成功,具体的返回值请参考 高级群发/模板消息 的事件推送说明 $array['Status'] = $this->_receive['Status']; if (isset($this->_receive['MsgID'])) //发送的消息id $array['MsgID'] = $this->_receive['MsgID']; //以下仅当群发消息时才会有的事件内容 if (isset($this->_receive['TotalCount'])) //分组或openid列表内粉丝数量 $array['TotalCount'] = $this->_receive['TotalCount']; if (isset($this->_receive['FilterCount'])) //过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数 $array['FilterCount'] = $this->_receive['FilterCount']; if (isset($this->_receive['SentCount'])) //发送成功的粉丝数 $array['SentCount'] = $this->_receive['SentCount']; if (isset($this->_receive['ErrorCount'])) //发送失败的粉丝数 $array['ErrorCount'] = $this->_receive['ErrorCount']; if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取多客服会话状态推送事件 - 接入会话 * 当Event为 kfcreatesession 即接入会话 * @return string | boolean 返回分配到的客服 */ public function getRevKFCreate(){ if (isset($this->_receive['KfAccount'])){ return $this->_receive['KfAccount']; } else return false; } /** * 获取多客服会话状态推送事件 - 关闭会话 * 当Event为 kfclosesession 即关闭会话 * @return string | boolean 返回分配到的客服 */ public function getRevKFClose(){ if (isset($this->_receive['KfAccount'])){ return $this->_receive['KfAccount']; } else return false; } /** * 获取多客服会话状态推送事件 - 转接会话 * 当Event为 kfswitchsession 即转接会话 * @return array | boolean 返回分配到的客服 * { * 'FromKfAccount' => '', //原接入客服 * 'ToKfAccount' => '' //转接到客服 * } */ public function getRevKFSwitch(){ if (isset($this->_receive['FromKfAccount'])) //原接入客服 $array['FromKfAccount'] = $this->_receive['FromKfAccount']; if (isset($this->_receive['ToKfAccount'])) //转接到客服 $array['ToKfAccount'] = $this->_receive['ToKfAccount']; if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取卡券事件推送 - 卡卷审核是否通过 * 当Event为 card_pass_check(审核通过) 或 card_not_pass_check(未通过) * @return string|boolean 返回卡券ID */ public function getRevCardPass(){ if (isset($this->_receive['CardId'])) return $this->_receive['CardId']; else return false; } /** * 获取卡券事件推送 - 领取卡券 * 当Event为 user_get_card(用户领取卡券) * @return array|boolean */ public function getRevCardGet(){ if (isset($this->_receive['CardId'])) //卡券 ID $array['CardId'] = $this->_receive['CardId']; if (isset($this->_receive['IsGiveByFriend'])) //是否为转赠,1 代表是,0 代表否。 $array['IsGiveByFriend'] = $this->_receive['IsGiveByFriend']; $array['OldUserCardCode'] = $this->_receive['OldUserCardCode']; if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。 $array['UserCardCode'] = $this->_receive['UserCardCode']; if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取卡券事件推送 - 删除卡券 * 当Event为 user_del_card(用户删除卡券) * @return array|boolean */ public function getRevCardDel(){ if (isset($this->_receive['CardId'])) //卡券 ID $array['CardId'] = $this->_receive['CardId']; if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。 $array['UserCardCode'] = $this->_receive['UserCardCode']; if (isset($array) && count($array) > 0) { return $array; } else { return false; } } /** * 获取订单ID - 订单付款通知 * 当Event为 merchant_order(订单付款通知) * @return orderId|boolean */ public function getRevOrderId(){ if (isset($this->_receive['OrderId'])) //订单 ID return $this->_receive['OrderId']; else return false; } public static function xmlSafeStr($str) { return ''; } /** * 数据XML编码 * @param mixed $data 数据 * @return string */ public static function data_to_xml($data) { $xml = ''; foreach ($data as $key => $val) { is_numeric($key) && $key = "item id=\"$key\""; $xml .= "<$key>"; $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val); list($key, ) = explode(' ', $key); $xml .= ""; } return $xml; } /** * XML编码 * @param mixed $data 数据 * @param string $root 根节点名 * @param string $item 数字索引的子节点名 * @param string $attr 根节点属性 * @param string $id 数字索引子节点key转换的属性名 * @param string $encoding 数据编码 * @return string */ public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') { if(is_array($attr)){ $_attr = array(); foreach ($attr as $key => $value) { $_attr[] = "{$key}=\"{$value}\""; } $attr = implode(' ', $_attr); } $attr = trim($attr); $attr = empty($attr) ? '' : " {$attr}"; $xml = "<{$root}{$attr}>"; $xml .= self::data_to_xml($data, $item, $id); $xml .= ""; return $xml; } /** * 过滤文字回复\r\n换行符 * @param string $text * @return string|mixed */ private function _auto_text_filter($text) { if (!$this->_text_filter) return $text; return str_replace("\r\n", "\n", $text); } /** * 设置回复消息 * Example: $obj->text('hello')->reply(); * @param string $text */ public function text($text='') { $FuncFlag = $this->_funcflag ? 1 : 0; $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_TEXT, 'Content'=>$this->_auto_text_filter($text), 'CreateTime'=>time(), 'FuncFlag'=>$FuncFlag ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->image('media_id')->reply(); * @param string $mediaid */ public function image($mediaid='') { $FuncFlag = $this->_funcflag ? 1 : 0; $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_IMAGE, 'Image'=>array('MediaId'=>$mediaid), 'CreateTime'=>time(), 'FuncFlag'=>$FuncFlag ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->voice('media_id')->reply(); * @param string $mediaid */ public function voice($mediaid='') { $FuncFlag = $this->_funcflag ? 1 : 0; $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_VOICE, 'Voice'=>array('MediaId'=>$mediaid), 'CreateTime'=>time(), 'FuncFlag'=>$FuncFlag ); $this->Message($msg); return $this; } /** * 设置回复消息 * Example: $obj->video('media_id','title','description')->reply(); * @param string $mediaid */ public function video($mediaid='',$title='',$description='') { $FuncFlag = $this->_funcflag ? 1 : 0; $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_VIDEO, 'Video'=>array( 'MediaId'=>$mediaid, 'Title'=>$title, 'Description'=>$description ), 'CreateTime'=>time(), 'FuncFlag'=>$FuncFlag ); $this->Message($msg); return $this; } /** * 设置回复音乐 * @param string $title * @param string $desc * @param string $musicurl * @param string $hgmusicurl * @param string $thumbmediaid 音乐图片缩略图的媒体id,非必须 */ public function music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') { $FuncFlag = $this->_funcflag ? 1 : 0; $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'CreateTime'=>time(), 'MsgType'=>self::MSGTYPE_MUSIC, 'Music'=>array( 'Title'=>$title, 'Description'=>$desc, 'MusicUrl'=>$musicurl, 'HQMusicUrl'=>$hgmusicurl ), 'FuncFlag'=>$FuncFlag ); if ($thumbmediaid) { $msg['Music']['ThumbMediaId'] = $thumbmediaid; } $this->Message($msg); return $this; } /** * 设置回复图文 * @param array $newsData * 数组结构: * array( * "0"=>array( * 'Title'=>'msg title', * 'Description'=>'summary text', * 'PicUrl'=>'http://www.domain.com/1.jpg', * 'Url'=>'http://www.domain.com/1.html' * ), * "1"=>.... * ) */ public function news($newsData=array()) { $FuncFlag = $this->_funcflag ? 1 : 0; $count = count($newsData); $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'MsgType'=>self::MSGTYPE_NEWS, 'CreateTime'=>time(), 'ArticleCount'=>$count, 'Articles'=>$newsData, 'FuncFlag'=>$FuncFlag ); $this->Message($msg); return $this; } /** * * 回复微信服务器, 此函数支持链式操作 * Example: $this->text('msg tips')->reply(); * @param string $msg 要发送的信息, 默认取$this->_msg * @param bool $return 是否返回信息而不抛出到浏览器 默认:否 */ public function reply($msg=array(),$return = false) { if (empty($msg)) { if (empty($this->_msg)) //防止不先设置回复内容,直接调用reply方法导致异常 return false; $msg = $this->_msg; } $xmldata= $this->xml_encode($msg); $this->log($xmldata); if ($this->encrypt_type == 'aes') { //如果来源消息为加密方式 $pc = new Prpcrypt($this->encodingAesKey); $array = $pc->encrypt($xmldata, $this->appid); $ret = $array[0]; if ($ret != 0) { $this->log('encrypt err!'); return false; } $timestamp = time(); $nonce = rand(77,999)*rand(605,888)*rand(11,99); $encrypt = $array[1]; $tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文 sort($tmpArr, SORT_STRING); $signature = implode($tmpArr); $signature = sha1($signature); $xmldata = $this->generate($encrypt, $signature, $timestamp, $nonce); $this->log($xmldata); } if ($return) return $xmldata; else echo $xmldata; } /** * xml格式加密,仅请求为加密方式时再用 */ private function generate($encrypt, $signature, $timestamp, $nonce) { //格式化加密信息 $format = " %s "; return sprintf($format, $encrypt, $signature, $timestamp, $nonce); } /** * GET 请求 * @param string $url */ private function http_get($url){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * POST 请求 * @param string $url * @param array $param * @param boolean $post_file 是否文件上传 * @return string content */ private function http_post($url,$param,$post_file=false){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) { $is_curlFile = true; } else { $is_curlFile = false; if (defined('CURLOPT_SAFE_UPLOAD')) { curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false); } } if (is_string($param)) { $strPOST = $param; }elseif($post_file) { if($is_curlFile) { foreach ($param as $key => $val) { if (substr($val, 0, 1) == '@') { $param[$key] = new \CURLFile(realpath(substr($val,1))); } } } $strPOST = $param; } else { $aPOST = array(); foreach($param as $key=>$val){ $aPOST[] = $key."=".urlencode($val); } $strPOST = join("&", $aPOST); } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($oCurl, CURLOPT_POST,true); curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } } /** * 设置缓存,按需重载 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename,$value,$expired){ //TODO: set cache implementation return false; } /** * 获取缓存,按需重载 * @param string $cachename * @return mixed */ protected function getCache($cachename){ //TODO: get cache implementation return false; } /** * 清除缓存,按需重载 * @param string $cachename * @return boolean */ protected function removeCache($cachename){ //TODO: remove cache implementation return false; } /** * 获取access_token * @param string $appid 如在类初始化时已提供,则可为空 * @param string $appsecret 如在类初始化时已提供,则可为空 * @param string $token 手动指定access_token,非必要情况不建议用 */ public function checkAuth($appid='',$appsecret='',$token=''){ if (!$appid || !$appsecret) { $appid = $this->appid; $appsecret = $this->appsecret; } if ($token) { //手动指定token,优先使用 $this->access_token=$token; return $this->access_token; } $authname = 'wechat_access_token'.$appid; if ($rs = $this->getCache($authname)) { $this->access_token = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->access_token = $json['access_token']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; $this->setCache($authname,$this->access_token,$expire); return $this->access_token; } return false; } /** * 删除验证数据 * @param string $appid */ public function resetAuth($appid=''){ if (!$appid) $appid = $this->appid; $this->access_token = ''; $authname = 'wechat_access_token'.$appid; $this->removeCache($authname); return true; } /** * 删除JSAPI授权TICKET * @param string $appid 用于多个appid时使用 */ public function resetJsTicket($appid=''){ if (!$appid) $appid = $this->appid; $this->jsapi_ticket = ''; $authname = 'wechat_jsapi_ticket'.$appid; $this->removeCache($authname); return true; } /** * 获取JSAPI授权TICKET * @param string $appid 用于多个appid时使用,可空 * @param string $jsapi_ticket 手动指定jsapi_ticket,非必要情况不建议用 */ public function getJsTicket($appid='',$jsapi_ticket=''){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$appid) $appid = $this->appid; if ($jsapi_ticket) { //手动指定token,优先使用 $this->jsapi_ticket = $jsapi_ticket; return $this->jsapi_ticket; } $authname = 'wechat_jsapi_ticket'.$appid; if ($rs = $this->getCache($authname)) { $this->jsapi_ticket = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=jsapi'); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->jsapi_ticket = $json['ticket']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; $this->setCache($authname,$this->jsapi_ticket,$expire); return $this->jsapi_ticket; } return false; } /** * 获取JsApi使用签名 * @param string $url 网页的URL,自动处理#及其后面部分 * @param string $timestamp 当前时间戳 (为空则自动生成) * @param string $noncestr 随机串 (为空则自动生成) * @param string $appid 用于多个appid时使用,可空 * @return array|bool 返回签名字串 */ public function getJsSign($url, $timestamp=0, $noncestr='', $appid=''){ if (!$this->jsapi_ticket && !$this->getJsTicket($appid) || !$url) return false; if (!$timestamp) $timestamp = time(); if (!$noncestr) $noncestr = $this->generateNonceStr(); $ret = strpos($url,'#'); if ($ret) $url = substr($url,0,$ret); $url = trim($url); if (empty($url)) return false; $arrdata = array("timestamp" => $timestamp, "noncestr" => $noncestr, "url" => $url, "jsapi_ticket" => $this->jsapi_ticket); $sign = $this->getSignature($arrdata); if (!$sign) return false; $signPackage = array( "appId" => $this->appid, "nonceStr" => $noncestr, "timestamp" => $timestamp, "url" => $url, "signature" => $sign ); return $signPackage; } /** * 获取卡券签名cardSign * @param string $card_type 卡券的类型,不可为空,官方jssdk文档说这个值可空,但签名验证工具又必填这个值,官方文档到处是坑, * @param string $card_id 卡券的ID,可空 * @param string $location_id 卡券的适用门店ID,可空 * @param string $timestamp 当前时间戳 (为空则自动生成) * @param string $noncestr 随机串 (为空则自动生成) * @param string $appid 用于多个appid时使用,可空 * @return array|bool 返回签名字串 */ public function getCardSign($card_type='',$card_id='',$code='',$location_id='',$timestamp=0, $noncestr='', $appid=''){ if (!$this->api_ticket && !$this->getJsCardTicket($appid)) return false; if (!$timestamp) $timestamp = time(); if (!$noncestr) $noncestr = $this->generateNonceStr(); $arrdata = array("api_ticket" => $this->api_ticket,"app_id" => $this->appid,"card_id" => $card_id,"code" => $code,"card_type" => $card_type,"location_id" => $location_id,"timestamp" => $timestamp, "noncestr" => $noncestr ); $sign = $this->getTicketSignature($arrdata); if (!$sign) return false; $signPackage = array( "cardType" => $card_type, "cardId" => $card_id, "shopId" => $location_id, //location_id就是shopId "nonceStr" => $noncestr, "timestamp" => $timestamp, "cardSign" => $sign ); return $signPackage; } /** * 微信api不支持中文转义的json结构 * @param array $arr */ static function json_encode($arr) { return json_encode($arr,JSON_UNESCAPED_UNICODE); } /** * 获取签名 * @param array $arrdata 签名数组 * @param string $method 签名方法 * @return boolean|string 签名值 */ public function getSignature($arrdata,$method="sha1") { if (!function_exists($method)) return false; ksort($arrdata); $paramstring = ""; foreach($arrdata as $key => $value) { if(strlen($paramstring) == 0) $paramstring .= $key . "=" . $value; else $paramstring .= "&" . $key . "=" . $value; } $Sign = $method($paramstring); return $Sign; } /** * 获取微信卡券api_ticket * @param string $appid 用于多个appid时使用,可空 * @param string $api_ticket 手动指定api_ticket,非必要情况不建议用 */ public function getJsCardTicket($appid='',$api_ticket=''){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$appid) $appid = $this->appid; if ($api_ticket) { //手动指定token,优先使用 $this->api_ticket = $api_ticket; return $this->api_ticket; } $authname = 'wechat_api_ticket_wxcard'.$appid; if ($rs = $this->getCache($authname)) { $this->api_ticket = $rs; return $rs; } $result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=wx_card'); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->api_ticket = $json['ticket']; $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; $this->setCache($authname,$this->api_ticket,$expire); return $this->api_ticket; } return false; } /** * 获取微信卡券签名 * @param array $arrdata 签名数组 * @param string $method 签名方法 * @return boolean|string 签名值 */ public function getTicketSignature($arrdata,$method="sha1") { if (!function_exists($method)) return false; $newArray = array(); foreach($arrdata as $key => $value) { array_push($newArray,(string)$value); } sort($newArray,SORT_STRING); return $method(implode($newArray)); } /** * 生成随机字串 * @param number $length 长度,默认为16,最长为32字节 * @return string */ public function generateNonceStr($length=16){ // 密码字符集,可任意添加你需要的字符 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for($i = 0; $i < $length; $i++) { $str .= $chars[mt_rand(0, strlen($chars) - 1)]; } return $str; } /** * 获取微信服务器IP地址列表 * @return array('127.0.0.1','127.0.0.1') */ public function getServerIp(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['ip_list']; } return false; } /** * 创建菜单(认证后的订阅号可用) * @param array $data 菜单数组数据 * example: * array ( * 'button' => array ( * 0 => array ( * 'name' => '扫码', * 'sub_button' => array ( * 0 => array ( * 'type' => 'scancode_waitmsg', * 'name' => '扫码带提示', * 'key' => 'rselfmenu_0_0', * ), * 1 => array ( * 'type' => 'scancode_push', * 'name' => '扫码推事件', * 'key' => 'rselfmenu_0_1', * ), * ), * ), * 1 => array ( * 'name' => '发图', * 'sub_button' => array ( * 0 => array ( * 'type' => 'pic_sysphoto', * 'name' => '系统拍照发图', * 'key' => 'rselfmenu_1_0', * ), * 1 => array ( * 'type' => 'pic_photo_or_album', * 'name' => '拍照或者相册发图', * 'key' => 'rselfmenu_1_1', * ) * ), * ), * 2 => array ( * 'type' => 'location_select', * 'name' => '发送位置', * 'key' => 'rselfmenu_2_0' * ), * ), * ) * type可以选择为以下几种,其中5-8除了收到菜单事件以外,还会单独收到对应类型的信息。 * 1、click:点击推事件 * 2、view:跳转URL * 3、scancode_push:扫码推事件 * 4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框 * 5、pic_sysphoto:弹出系统拍照发图 * 6、pic_photo_or_album:弹出拍照或者相册发图 * 7、pic_weixin:弹出微信相册发图器 * 8、location_select:弹出地理位置选择器 */ public function createMenu($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 获取菜单(认证后的订阅号可用) * @return array('menu'=>array(....s)) */ public function getMenu(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除菜单(认证后的订阅号可用) * @return boolean */ public function deleteMenu(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 创建个性化菜单(认证后的订阅号可用) * @param array $data * @return bool * */ public function addconditionalMenu($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MENU_ADDCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 删除个性化菜单(认证后的订阅号可用) * @param $data {"menuid":"208379533"} * * @return bool */ public function delconditionalMenu($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MENU_DELCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 测试个性化菜单匹配结果(认证后的订阅号可用) * @param $data {"user_id":"weixin"} user_id可以是粉丝的OpenID,也可以是粉丝的微信号 * * @return bool|array('button'=>array(....s)) */ public function trymatchMenu($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MENU_TRYMATCH_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传临时素材,有效期为3天(认证后的订阅号可用) * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义 * 注意:临时素材的media_id是可复用的! * @param array $data {"media":'@Path\filename.jpg'} * @param type 类型:图片:image 语音:voice 视频:video 缩略图:thumb * @return boolean|array */ public function uploadMedia($data, $type){ if (!$this->access_token && !$this->checkAuth()) return false; //原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀 $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取临时素材(认证后的订阅号可用) * @param string $media_id 媒体文件id * @param boolean $is_video 是否为视频文件,默认为否 * @return raw data */ public function getMedia($media_id,$is_video=false){ if (!$this->access_token && !$this->checkAuth()) return false; //原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀 //如果要获取的素材是视频文件时,不能使用https协议,必须更换成http协议 $url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX; $result = $this->http_get($url_prefix.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id); if ($result) { if (is_string($result)) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } } return $result; } return false; } /** * 上传图片,本接口所上传的图片不占用公众号的素材库中图片数量的5000个的限制。图片仅支持jpg/png格式,大小必须在1MB以下。 (认证后的订阅号可用) * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义 * @param array $data {"media":'@Path\filename.jpg'} * * @return boolean|array */ public function uploadImg($data){ if (!$this->access_token && !$this->checkAuth()) return false; //原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀 $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOADIMG_URL.'access_token='.$this->access_token,$data,true); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传永久素材(认证后的订阅号可用) * 新增的永久素材也可以在公众平台官网素材管理模块中看到 * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义 * @param array $data {"media":'@Path\filename.jpg'} * @param type 类型:图片:image 语音:voice 视频:video 缩略图:thumb * @param boolean $is_video 是否为视频文件,默认为否 * @param array $video_info 视频信息数组,非视频素材不需要提供 array('title'=>'视频标题','introduction'=>'描述') * @return boolean|array */ public function uploadForeverMedia($data, $type,$is_video=false,$video_info=array()){ if (!$this->access_token && !$this->checkAuth()) return false; //#TODO 暂不确定此接口是否需要让视频文件走http协议 //如果要获取的素材是视频文件时,不能使用https协议,必须更换成http协议 //$url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX; //当上传视频文件时,附加视频文件信息 if ($is_video) $data['description'] = self::json_encode($video_info); $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传永久图文素材(认证后的订阅号可用) * 新增的永久素材也可以在公众平台官网素材管理模块中看到 * @param array $data 消息结构{"articles":[{...}]} * @return boolean|array */ public function uploadForeverArticles($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_NEWS_UPLOAD_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 修改永久图文素材(认证后的订阅号可用) * 永久素材也可以在公众平台官网素材管理模块中看到 * @param string $media_id 图文素材id * @param array $data 消息结构{"articles":[{...}]} * @param int $index 更新的文章在图文素材的位置,第一篇为0,仅多图文使用 * @return boolean|array */ public function updateForeverArticles($media_id,$data,$index=0){ if (!$this->access_token && !$this->checkAuth()) return false; if (!isset($data['media_id'])) $data['media_id'] = $media_id; if (!isset($data['index'])) $data['index'] = $index; $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_NEWS_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取永久素材(认证后的订阅号可用) * 返回图文消息数组或二进制数据,失败返回false * @param string $media_id 媒体文件id * @param boolean $is_video 是否为视频文件,默认为否 * @return boolean|array|raw data */ public function getForeverMedia($media_id,$is_video=false){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array('media_id' => $media_id); //#TODO 暂不确定此接口是否需要让视频文件走http协议 //如果要获取的素材是视频文件时,不能使用https协议,必须更换成http协议 //$url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX; $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_GET_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { if (is_string($result)) { $json = json_decode($result,true); if ($json) { if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } else { return $result; } } return $result; } return false; } /** * 删除永久素材(认证后的订阅号可用) * @param string $media_id 媒体文件id * @return boolean */ public function delForeverMedia($media_id){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array('media_id' => $media_id); $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_DEL_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 获取永久素材列表(认证后的订阅号可用) * @param string $type 素材的类型,图片(image)、视频(video)、语音 (voice)、图文(news) * @param int $offset 全部素材的偏移位置,0表示从第一个素材 * @param int $count 返回素材的数量,取值在1到20之间 * @return boolean|array * 返回数组格式: * array( * 'total_count'=>0, //该类型的素材的总数 * 'item_count'=>0, //本次调用获取的素材的数量 * 'item'=>array() //素材列表数组,内容定义请参考官方文档 * ) */ public function getForeverList($type,$offset,$count){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'type' => $type, 'offset' => $offset, 'count' => $count, ); $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_BATCHGET_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取永久素材总数(认证后的订阅号可用) * @return boolean|array * 返回数组格式: * array( * 'voice_count'=>0, //语音总数量 * 'video_count'=>0, //视频总数量 * 'image_count'=>0, //图片总数量 * 'news_count'=>0 //图文总数量 * ) */ public function getForeverCount(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::MEDIA_FOREVER_COUNT_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传图文消息素材,用于群发(认证后的订阅号可用) * @param array $data 消息结构{"articles":[{...}]} * @return boolean|array */ public function uploadArticles($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOADNEWS_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传视频素材(认证后的订阅号可用) * @param array $data 消息结构 * { * "media_id"=>"", //通过上传媒体接口得到的MediaId * "title"=>"TITLE", //视频标题 * "description"=>"Description" //视频描述 * } * @return boolean|array * { * "type":"video", * "media_id":"mediaid", * "created_at":1398848981 * } */ public function uploadMpVideo($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::UPLOAD_MEDIA_URL.self::MEDIA_VIDEO_UPLOAD.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 高级群发消息, 根据OpenID列表群发图文消息(订阅号不可用) * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成, * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。 * @param array $data 消息结构 * { * "touser"=>array( * "OPENID1", * "OPENID2" * ), * "msgtype"=>"mpvideo", * // 在下面5种类型中选择对应的参数内容 * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId") * // text => array ( "content" => "hello") * } * @return boolean|array */ public function sendMassMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 高级群发消息, 根据群组id群发图文消息(认证后的订阅号可用) * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成, * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。 * @param array $data 消息结构 * { * "filter"=>array( * "is_to_all"=>False, //是否群发给所有用户.True不用分组id,False需填写分组id * "group_id"=>"2" //群发的分组id * ), * "msgtype"=>"mpvideo", * // 在下面5种类型中选择对应的参数内容 * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId") * // text => array ( "content" => "hello") * } * @return boolean|array */ public function sendGroupMassMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 高级群发消息, 删除群发图文消息(认证后的订阅号可用) * @param int $msg_id 消息id * @return boolean|array */ public function deleteMassMessage($msg_id){ if (!$this->access_token && !$this->checkAuth()) return false; $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))); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 高级群发消息, 预览群发消息(认证后的订阅号可用) * 注意:视频需要在调用uploadMedia()方法后,再使用 uploadMpVideo() 方法生成, * 然后获得的 mediaid 才能用于群发,且消息类型为 mpvideo 类型。 * @param array $data 消息结构 * { * "touser"=>"OPENID", * "msgtype"=>"mpvideo", * // 在下面5种类型中选择对应的参数内容 * // mpnews | voice | image | mpvideo => array( "media_id"=>"MediaId") * // text => array ( "content" => "hello") * } * @return boolean|array */ public function previewMassMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::MASS_PREVIEW_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 高级群发消息, 查询群发消息发送状态(认证后的订阅号可用) * @param int $msg_id 消息id * @return boolean|array * { * "msg_id":201053012, //群发消息后返回的消息id * "msg_status":"SEND_SUCCESS" //消息发送后的状态,SENDING表示正在发送 SEND_SUCCESS表示发送成功 * } */ public function queryMassMessage($msg_id){ if (!$this->access_token && !$this->checkAuth()) return false; $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))); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 创建二维码ticket * @param int|string $scene_id 自定义追踪id,临时二维码只能用数值型 * @param int $type 0:临时二维码;1:数值型永久二维码(此时expire参数无效);2:字符串型永久二维码(此时expire参数无效) * @param int $expire 临时二维码有效期,最大为604800秒 * @return array('ticket'=>'qrcode字串','expire_seconds'=>604800,'url'=>'二维码图片解析后的地址') */ public function getQRCode($scene_id,$type=0,$expire=604800){ if (!$this->access_token && !$this->checkAuth()) return false; if (!isset($scene_id)) return false; switch ($type) { case '0': if (!is_numeric($scene_id)) return false; $action_name = 'QR_SCENE'; $action_info = array('scene'=>(array('scene_id'=>$scene_id))); break; case '1': if (!is_numeric($scene_id)) return false; $action_name = 'QR_LIMIT_SCENE'; $action_info = array('scene'=>(array('scene_id'=>$scene_id))); break; case '2': if (!is_string($scene_id)) return false; $action_name = 'QR_LIMIT_STR_SCENE'; $action_info = array('scene'=>(array('scene_str'=>$scene_id))); break; default: return false; } $data = array( 'action_name' => $action_name, 'expire_seconds' => $expire, 'action_info' => $action_info ); if ($type) { unset($data['expire_seconds']); } $result = $this->http_post(self::API_URL_PREFIX.self::QRCODE_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取二维码图片 * @param string $ticket 传入由getQRCode方法生成的ticket参数 * @return string url 返回http地址 */ public function getQRUrl($ticket) { return self::QRCODE_IMG_URL.urlencode($ticket); } /** * 长链接转短链接接口 * @param string $long_url 传入要转换的长url * @return boolean|string url 成功则返回转换后的短url */ public function getShortUrl($long_url){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'action'=>'long2short', 'long_url'=>$long_url ); $result = $this->http_post(self::API_URL_PREFIX.self::SHORT_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['short_url']; } return false; } /** * 获取统计数据 * @param string $type 数据分类(user|article|upstreammsg|interface)分别为(用户分析|图文分析|消息分析|接口分析) * @param string $subtype 数据子分类,参考 DATACUBE_URL_ARR 常量定义部分 或者README.md说明文档 * @param string $begin_date 开始时间 * @param string $end_date 结束时间 * @return boolean|array 成功返回查询结果数组,其定义请看官方文档 */ public function getDatacube($type,$subtype,$begin_date,$end_date=''){ if (!$this->access_token && !$this->checkAuth()) return false; if (!isset(self::$DATACUBE_URL_ARR[$type]) || !isset(self::$DATACUBE_URL_ARR[$type][$subtype])) return false; $data = array( 'begin_date'=>$begin_date, 'end_date'=>$end_date?$end_date:$begin_date ); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::$DATACUBE_URL_ARR[$type][$subtype].'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return isset($json['list'])?$json['list']:$json; } return false; } /** * 批量获取关注用户列表 * @param unknown $next_openid */ public function getUserList($next_openid=''){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&next_openid='.$next_openid); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取关注者详细信息 * @param string $openid * @param string $lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 * @return array {subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]} * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下 */ public function getUserInfo($openid, $lang = 'zh_CN'){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::USER_INFO_URL.'access_token='.$this->access_token.'&openid='.$openid.'&lang='.$lang); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 批量获取关注者详细信息 * @param array $openids user_list{{'openid:xxxxxx'},{},{}} * @return array user_info_list{subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}{}{}... * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下 */ public function getUsersInfo($openids){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::USERS_INFO_URL.'access_token='.$this->access_token,json_encode($openids)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 设置用户备注名 * @param string $openid * @param string $remark 备注名 * @return boolean|array */ public function updateUserRemark($openid,$remark){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'openid'=>$openid, 'remark'=>$remark ); $result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATEREMARK_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取用户分组列表 * @return boolean|array */ public function getGroup(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::GROUP_GET_URL.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (isset($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取用户所在分组 * @param string $openid * @return boolean|int 成功则返回用户分组id */ public function getUserGroup($openid){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'openid'=>$openid ); $result = $this->http_post(self::API_URL_PREFIX.self::USER_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } else if (isset($json['groupid'])) return $json['groupid']; } return false; } /** * 新增自定分组 * @param string $name 分组名称 * @return boolean|array */ public function createGroup($name){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'group'=>array('name'=>$name) ); $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更改分组名称 * @param int $groupid 分组id * @param string $name 分组名称 * @return boolean|array */ public function updateGroup($groupid,$name){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'group'=>array('id'=>$groupid,'name'=>$name) ); $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 移动用户分组 * @param int $groupid 分组id * @param string $openid 用户openid * @return boolean|array */ public function updateGroupMembers($groupid,$openid){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'openid'=>$openid, 'to_groupid'=>$groupid ); $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_MEMBER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 批量移动用户分组 * @param int $groupid 分组id * @param string $openid_list 用户openid数组,一次不能超过50个 * @return boolean|array */ public function batchUpdateGroupMembers($groupid,$openid_list){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'openid_list'=>$openid_list, 'to_groupid'=>$groupid ); $result = $this->http_post(self::API_URL_PREFIX.self::GROUP_MEMBER_BATCHUPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 发送客服消息 * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}} * @return boolean|array */ public function sendCustomMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SEND_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * oauth 授权跳转接口 * @param string $callback 回调URI * @return string */ public function getOauthRedirect($callback,$state='',$scope='snsapi_userinfo'){ return self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; } /** * 通过code获取Access Token * @return array {access_token,expires_in,refresh_token,openid,scope} */ public function getOauthAccessToken(){ $code = isset($_GET['code'])?$_GET['code']:''; if (!$code) return false; $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'); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->user_token = $json['access_token']; return $json; } return false; } /** * 刷新access token并续期 * @param string $refresh_token * @return boolean|mixed */ public function getOauthRefreshToken($refresh_token){ $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_REFRESH_URL.'appid='.$this->appid.'&grant_type=refresh_token&refresh_token='.$refresh_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } $this->user_token = $json['access_token']; return $json; } return false; } /** * 获取授权后的用户资料 * @param string $access_token * @param string $openid * @return array {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]} * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下 */ public function getOauthUserinfo($access_token,$openid){ $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_USERINFO_URL.'access_token='.$access_token.'&openid='.$openid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 检验授权凭证是否有效 * @param string $access_token * @param string $openid * @return boolean 是否有效 */ public function getOauthAuth($access_token,$openid){ $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_AUTH_URL.'access_token='.$access_token.'&openid='.$openid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } else if ($json['errcode']==0) return true; } return false; } /** * 模板消息 设置所属行业 * @param int $id1 公众号模板消息所属行业编号,参看官方开发文档 行业代码 * @param int $id2 同$id1。但如果只有一个行业,此参数可省略 * @return boolean|array */ public function setTMIndustry($id1,$id2=''){ if ($id1) $data['industry_id1'] = $id1; if ($id2) $data['industry_id2'] = $id2; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SET_INDUSTRY_URL.'access_token='.$this->access_token,self::json_encode($data)); if($result){ $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 模板消息 添加消息模板 * 成功返回消息模板的调用id * @param string $tpl_id 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式 * @return boolean|string */ public function addTemplateMessage($tpl_id){ $data = array ('template_id_short' =>$tpl_id); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_ADD_TPL_URL.'access_token='.$this->access_token,self::json_encode($data)); if($result){ $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['template_id']; } return false; } /** * 发送模板消息 * @param array $data 消息结构 * { "touser":"OPENID", "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY", "url":"http://weixin.qq.com/download", "topcolor":"#FF0000", "data":{ "参数名1": { "value":"参数", "color":"#173177" //参数颜色 }, "Date":{ "value":"06月07日 19时24分", "color":"#173177" }, "CardNumber":{ "value":"0426", "color":"#173177" }, "Type":{ "value":"消费", "color":"#173177" } } } * @return boolean|array */ public function sendTemplateMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SEND_URL.'access_token='.$this->access_token,self::json_encode($data)); if($result){ $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取多客服会话记录 * @param array $data 数据结构{"starttime":123456789,"endtime":987654321,"openid":"OPENID","pagesize":10,"pageindex":1,} * @return boolean|array */ public function getCustomServiceMessage($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_RECORD.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 转发多客服消息 * Example: $obj->transfer_customer_service($customer_account)->reply(); * @param string $customer_account 转发到指定客服帐号:test1@test */ public function transfer_customer_service($customer_account = '') { $msg = array( 'ToUserName' => $this->getRevFrom(), 'FromUserName'=>$this->getRevTo(), 'CreateTime'=>time(), 'MsgType'=>'transfer_customer_service', ); if ($customer_account) { $msg['TransInfo'] = array('KfAccount'=>$customer_account); } $this->Message($msg); return $this; } /** * 获取多客服客服基本信息 * * @return boolean|array */ public function getCustomServiceKFlist(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_KFLIST.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取多客服在线客服接待信息 * * @return boolean|array { "kf_online_list": [ { "kf_account": "test1@test", //客服账号@微信别名 "status": 1, //客服在线状态 1:pc在线,2:手机在线,若pc和手机同时在线则为 1+2=3 "kf_id": "1001", //客服工号 "auto_accept": 0, //客服设置的最大自动接入数 "accepted_case": 1 //客服当前正在接待的会话数 } ] } */ public function getCustomServiceOnlineKFlist(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_ONLINEKFLIST.'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 创建指定多客服会话 * @tutorial 当用户已被其他客服接待或指定客服不在线则会失败 * @param string $openid //用户openid * @param string $kf_account //客服账号 * @param string $text //附加信息,文本会展示在客服人员的多客服客户端,可为空 * @return boolean | array //成功返回json数组 * { * "errcode": 0, * "errmsg": "ok", * } */ public function createKFSession($openid,$kf_account,$text=''){ $data=array( "openid" =>$openid, "kf_account" => $kf_account ); if ($text) $data["text"] = $text; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_CREATE.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 关闭指定多客服会话 * @tutorial 当用户被其他客服接待时则会失败 * @param string $openid //用户openid * @param string $kf_account //客服账号 * @param string $text //附加信息,文本会展示在客服人员的多客服客户端,可为空 * @return boolean | array //成功返回json数组 * { * "errcode": 0, * "errmsg": "ok", * } */ public function closeKFSession($openid,$kf_account,$text=''){ $data=array( "openid" =>$openid, "kf_account" => $kf_account ); if ($text) $data["text"] = $text; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_CLOSE .'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取用户会话状态 * @param string $openid //用户openid * @return boolean | array //成功返回json数组 * { * "errcode" : 0, * "errmsg" : "ok", * "kf_account" : "test1@test", //正在接待的客服 * "createtime": 123456789, //会话接入时间 * } */ public function getKFSession($openid){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET .'access_token='.$this->access_token.'&openid='.$openid); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取指定客服的会话列表 * @param string $openid //用户openid * @return boolean | array //成功返回json数组 * array( * 'sessionlist' => array ( * array ( * 'openid'=>'OPENID', //客户 openid * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳 * ), * array ( * 'openid'=>'OPENID', //客户 openid * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳 * ), * ) * ) */ public function getKFSessionlist($kf_account){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET_LIST .'access_token='.$this->access_token.'&kf_account='.$kf_account); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取未接入会话列表 * @param string $openid //用户openid * @return boolean | array //成功返回json数组 * array ( * 'count' => 150 , //未接入会话数量 * 'waitcaselist' => array ( * array ( * 'openid'=>'OPENID', //客户 openid * 'kf_account ' =>'', //指定接待的客服,为空则未指定 * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳 * ), * array ( * 'openid'=>'OPENID', //客户 openid * 'kf_account ' =>'', //指定接待的客服,为空则未指定 * 'createtime'=>123456789, //会话创建时间,UNIX 时间戳 * ) * ) * ) */ public function getKFSessionWait(){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET_WAIT .'access_token='.$this->access_token); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 添加客服账号 * * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符 * @param string $nickname //客服昵称,最长6个汉字或12个英文字符 * @param string $password //客服账号明文登录密码,会自动加密 * @return boolean|array * 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * } */ public function addKFAccount($account,$nickname,$password){ $data=array( "kf_account" =>$account, "nickname" => $nickname, "password" => md5($password) ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_ADD_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 修改客服账号信息 * * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符 * @param string $nickname //客服昵称,最长6个汉字或12个英文字符 * @param string $password //客服账号明文登录密码,会自动加密 * @return boolean|array * 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * } */ public function updateKFAccount($account,$nickname,$password){ $data=array( "kf_account" =>$account, "nickname" => $nickname, "password" => md5($password) ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 删除客服账号 * * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符 * @return boolean|array * 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * } */ public function deleteKFAccount($account){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_DEL_URL.'access_token='.$this->access_token.'&kf_account='.$account); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传客服头像 * * @param string $account //完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符 * @param string $imgfile //头像文件完整路径,如:'D:\user.jpg'。头像文件必须JPG格式,像素建议640*640 * @return boolean|array * 成功返回结果 * { * "errcode": 0, * "errmsg": "ok", * } */ public function setKFHeadImg($account,$imgfile){ if (!$this->access_token && !$this->checkAuth()) return false; $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); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 语义理解接口 * @param String $uid 用户唯一id(非开发者id),用户区分公众号下的不同用户(建议填入用户openid) * @param String $query 输入文本串 * @param String $category 需要使用的服务类型,多个用“,”隔开,不能为空 * @param Float $latitude 纬度坐标,与经度同时传入;与城市二选一传入 * @param Float $longitude 经度坐标,与纬度同时传入;与城市二选一传入 * @param String $city 城市名称,与经纬度二选一传入 * @param String $region 区域名称,在城市存在的情况下可省略;与经纬度二选一传入 * @return boolean|array */ public function querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region=""){ if (!$this->access_token && !$this->checkAuth()) return false; $data=array( 'query' => $query, 'category' => $category, 'appid' => $this->appid, 'uid' => '' ); //地理坐标或城市名称二选一 if ($latitude) { $data['latitude'] = $latitude; $data['longitude'] = $longitude; } elseif ($city) { $data['city'] = $city; } elseif ($region) { $data['region'] = $region; } $result = $this->http_post(self::API_BASE_URL_PREFIX.self::SEMANTIC_API_URL.'access_token='.$this->access_token,self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 创建卡券 * @param Array $data 卡券数据 * @return array|boolean 返回数组中card_id为卡券ID */ public function createCard($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CREATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更改卡券信息 * 调用该接口更新信息后会重新送审,卡券状态变更为待审核。已被用户领取的卡券会实时更新票面信息。 * @param string $data * @return boolean */ public function updateCard($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 删除卡券 * 允许商户删除任意一类卡券。删除卡券后,该卡券对应已生成的领取用二维码、添加到卡包 JS API 均会失效。 * 注意:删除卡券不能删除已被用户领取,保存在微信客户端中的卡券,已领取的卡券依旧有效。 * @param string $card_id 卡券ID * @return boolean */ public function delCard($card_id) { $data = array( 'card_id' => $card_id, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_DELETE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 查询卡券详情 * @param string $card_id * @return boolean|array 返回数组信息比较复杂,请参看卡券接口文档 */ public function getCardInfo($card_id) { $data = array( 'card_id' => $card_id, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_GET . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取用户已领取卡券接口 * @param string $openid * @param string $card_id * @return boolean|array 返回数组信息比较复杂,请参看卡券接口文档 * 成功返回结果 * { * "errcode":0, * "errmsg":"ok", * "card_list": [ * {"code": "xxx1434079154", "card_id": "xxxxxxxxxx"}, * {"code": "xxx1434079155", "card_id": "xxxxxxxxxx"} * ] * } */ public function getUserCardList($openid,$card_id) { $data = array( 'openid' => $openid, 'card_id' => $card_id ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_USER_GETCARDLIST . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 获取颜色列表 * 获得卡券的最新颜色列表,用于创建卡券 * @return boolean|array 返回数组请参看 微信卡券接口文档 的json格式 */ public function getCardColors() { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_get(self::API_BASE_URL_PREFIX . self::CARD_GETCOLORS . 'access_token=' . $this->access_token); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 拉取门店列表 * 获取在公众平台上申请创建的门店列表 * @param int $offset 开始拉取的偏移,默认为0从头开始 * @param int $count 拉取的数量,默认为0拉取全部 * @return boolean|array 返回数组请参看 微信卡券接口文档 的json格式 */ public function getCardLocations($offset=0,$count=0) { $data=array( 'offset'=>$offset, 'count'=>$count ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 批量导入门店信息 * @tutorial 返回插入的门店id列表,以逗号分隔。如果有插入失败的,则为-1,请自行核查是哪个插入失败 * @param array $data 数组形式的json数据,由于内容较多,具体内容格式请查看 微信卡券接口文档 * @return boolean|string 成功返回插入的门店id列表 */ public function addCardLocations($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHADD . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 生成卡券二维码 * 成功则直接返回ticket值,可以用 getQRUrl($ticket) 换取二维码url * * @param string $cardid 卡券ID 必须 * @param string $code 指定卡券 code 码,只能被领一次。use_custom_code 字段为 true 的卡券必须填写,非自定义 code 不必填写。 * @param string $openid 指定领取者的 openid,只有该用户能领取。bind_openid 字段为 true 的卡券必须填写,非自定义 openid 不必填写。 * @param int $expire_seconds 指定二维码的有效时间,范围是 60 ~ 1800 秒。不填默认为永久有效。 * @param boolean $is_unique_code 指定下发二维码,生成的二维码随机分配一个 code,领取后不可再次扫描。填写 true 或 false。默认 false。 * @param string $balance 红包余额,以分为单位。红包类型必填(LUCKY_MONEY),其他卡券类型不填。 * @return boolean|string */ public function createCardQrcode($card_id,$code='',$openid='',$expire_seconds=0,$is_unique_code=false,$balance='') { $card = array( 'card_id' => $card_id ); $data = array( 'action_name' => "QR_CARD" ); if ($code) $card['code'] = $code; if ($openid) $card['openid'] = $openid; if ($is_unique_code) $card['is_unique_code'] = $is_unique_code; if ($balance) $card['balance'] = $balance; if ($expire_seconds) $data['expire_seconds'] = $expire_seconds; $data['action_info'] = array('card' => $card); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_QRCODE_CREATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 消耗 code * 自定义 code(use_custom_code 为 true)的优惠券,在 code 被核销时,必须调用此接口。 * * @param string $code 要消耗的序列号 * @param string $card_id 要消耗序列号所述的 card_id,创建卡券时use_custom_code 填写 true 时必填。 * @return boolean|array * { * "errcode":0, * "errmsg":"ok", * "card":{"card_id":"pFS7Fjg8kV1IdDz01r4SQwMkuCKc"}, * "openid":"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA" * } */ public function consumeCardCode($code,$card_id='') { $data = array('code' => $code); if ($card_id) $data['card_id'] = $card_id; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_CONSUME . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * code 解码 * @param string $encrypt_code 通过 choose_card_info 获取的加密字符串 * @return boolean|array * { * "errcode":0, * "errmsg":"ok", * "code":"751234212312" * } */ public function decryptCardCode($encrypt_code) { $data = array( 'encrypt_code' => $encrypt_code, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_DECRYPT . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 查询 code 的有效性(非自定义 code) * @param string $code * @return boolean|array * { * "errcode":0, * "errmsg":"ok", * "openid":"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA", //用户 openid * "card":{ * "card_id":"pFS7Fjg8kV1IdDz01r4SQwMkuCKc", * "begin_time": 1404205036, //起始使用时间 * "end_time": 1404205036, //结束时间 * } * } */ public function checkCardCode($code) { $data = array( 'code' => $code, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_GET . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 批量查询卡列表 * @param $offset 开始拉取的偏移,默认为0从头开始 * @param $count 需要查询的卡片的数量(数量最大50,默认50) * @return boolean|array * { * "errcode":0, * "errmsg":"ok", * "card_id_list":["ph_gmt7cUVrlRk8swPwx7aDyF-pg"], //卡 id 列表 * "total_num":1 //该商户名下 card_id 总数 * } */ public function getCardIdList($offset=0,$count=50) { if ($count>50) $count = 50; $data = array( 'offset' => $offset, 'count' => $count, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更改 code * 为确保转赠后的安全性,微信允许自定义code的商户对已下发的code进行更改。 * 注:为避免用户疑惑,建议仅在发生转赠行为后(发生转赠后,微信会通过事件推送的方式告知商户被转赠的卡券code)对用户的code进行更改。 * @param string $code 卡券的 code 编码 * @param string $card_id 卡券 ID * @param string $new_code 新的卡券 code 编码 * @return boolean */ public function updateCardCode($code,$card_id,$new_code) { $data = array( 'code' => $code, 'card_id' => $card_id, 'new_code' => $new_code, ); if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 设置卡券失效 * 设置卡券失效的操作不可逆 * @param string $code 需要设置为失效的 code * @param string $card_id 自定义 code 的卡券必填。非自定义 code 的卡券不填。 * @return boolean */ public function unavailableCardCode($code,$card_id='') { $data = array( 'code' => $code, ); if ($card_id) $data['card_id'] = $card_id; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UNAVAILABLE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 库存修改 * @param string $data * @return boolean */ public function modifyCardStock($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MODIFY_STOCK . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 更新门票 * @param string $data * @return boolean */ public function updateMeetingCard($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEETINGCARD_UPDATEUSER . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 激活/绑定会员卡 * @param string $data 具体结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节 * @return boolean */ public function activateMemberCard($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_ACTIVATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 会员卡交易 * 会员卡交易后每次积分及余额变更需通过接口通知微信,便于后续消息通知及其他扩展功能。 * @param string $data 具体结构请参看卡券开发文档(6.1.2 会员卡交易)章节 * @return boolean|array */ public function updateMemberCard($data) { if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_UPDATEUSER . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 更新红包金额 * @param string $code 红包的序列号 * @param $balance 红包余额 * @param string $card_id 自定义 code 的卡券必填。非自定义 code 可不填。 * @return boolean|array */ public function updateLuckyMoney($code,$balance,$card_id='') { $data = array( 'code' => $code, 'balance' => $balance ); if ($card_id) $data['card_id'] = $card_id; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LUCKYMONEY_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 设置卡券测试白名单 * @param string $openid 测试的 openid 列表 * @param string $user 测试的微信号列表 * @return boolean */ public function setCardTestWhiteList($openid=array(),$user=array()) { $data = array(); if (count($openid) > 0) $data['openid'] = $openid; if (count($user) > 0) $data['username'] = $user; if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_TESTWHILELIST_SET . 'access_token=' . $this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 申请设备ID * [applyShakeAroundDevice 申请配置设备所需的UUID、Major、Minor。 * 若激活率小于50%,不能新增设备。单次新增设备超过500 个,需走人工审核流程。 * 审核通过后,可用迒回的批次ID 用“查询设备列表”接口拉取本次申请的设备ID] * @param array $data * array( * "quantity" => 3, //申请的设备ID 的数量,单次新增设备超过500 个,需走人工审核流程(必填) * "apply_reason" => "测试",//申请理由(必填) * "comment" => "测试专用", //备注(非必填) * "poi_id" => 1234 //设备关联的门店ID(非必填) * ) * @return boolean|mixed * { "data": { "apply_id": 123, "device_identifiers":[ { "device_id":10100, "uuid":"FDA50693-A4E2-4FB1-AFCF-C6EB07647825", "major":10001, "minor":10002 } ] }, "errcode": 0, "errmsg": "success." } apply_id:申请的批次ID,可用在“查询设备列表”接口按批次查询本次申请成功的设备ID device_identifiers:指定的设备ID 列表 device_id:设备编号 uuid、major、minor audit_status:审核状态。0:审核未通过、1:审核中、2:审核已通过;审核会在三个工作日内完成 audit_comment:审核备注,包括审核不通过的原因 * @access public * @author polo * @version 2015-3-25 下午1:24:06 * @copyright Show More */ public function applyShakeAroundDevice($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_APPLYID . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 编辑设备信息 * [updateShakeAroundDevice 编辑设备的备注信息。可用设备ID或完整的UUID、Major、Minor指定设备,二者选其一。] * @param array $data * array( * "device_identifier" => array( * "device_id" => 10011, //当提供了device_id则不需要使用uuid、major、minor,反之亦然 * "uuid" => "FDA50693-A4E2-4FB1-AFCF-C6EB07647825", * "major" => 1002, * "minor" => 1223 * ), * "comment" => "测试专用", //备注(非必填) * ) * { "data": { }, "errcode": 0, "errmsg": "success." } * @return boolean * @author binsee * @version 2015-4-20 23:45:00 */ public function updateShakeAroundDevice($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 查询设备列表 * [searchShakeAroundDevice 查询已有的设备ID、UUID、Major、Minor、激活状态、备注信息、关联门店、关联页面等信息。 * 可指定设备ID 或完整的UUID、Major、Minor 查询,也可批量拉取设备信息列表。] * @param array $data * $data 三种格式: * ①查询指定设备时:$data = array( * "device_identifiers" => array( * array( * "device_id" => 10100, * "uuid" => "FDA50693-A4E2-4FB1-AFCF-C6EB07647825", * "major" => 10001, * "minor" => 10002 * ) * ) * ); * device_identifiers:指定的设备 * device_id:设备编号,若填了UUID、major、minor,则可不填设备编号,若二者都填,则以设备编号为优先 * uuid、major、minor:三个信息需填写完整,若填了设备编号,则可不填此信息 * +------------------------------------------------------------------------------------------------------------- * ②需要分页查询或者指定范围内的设备时: $data = array( * "begin" => 0, * "count" => 3 * ); * begin:设备列表的起始索引值 * count:待查询的设备个数 * +------------------------------------------------------------------------------------------------------------- * ③当需要根据批次ID 查询时: $data = array( * "apply_id" => 1231, * "begin" => 0, * "count" => 3 * ); * apply_id:批次ID * +------------------------------------------------------------------------------------------------------------- * @return boolean|mixed *正确迒回JSON 数据示例: *字段说明 { "data": { "devices": [ //指定的设备信息列表 { "comment": "", //设备的备注信息 "device_id": 10097, //设备编号 "major": 10001, "minor": 12102, "page_ids": "15369", //与此设备关联的页面ID 列表,用逗号隔开 "status": 1, //激活状态,0:未激活,1:已激活(但不活跃),2:活跃 "poi_id": 0, //门店ID "uuid": "FDA50693-A4E2-4FB1-AFCF-C6EB07647825" }, { "comment": "", //设备的备注信息 "device_id": 10098, //设备编号 "major": 10001, "minor": 12103, "page_ids": "15368", //与此设备关联的页面ID 列表,用逗号隔开 "status": 1, //激活状态,0:未激活,1:已激活(但不活跃),2:活跃 "poi_id": 0, //门店ID "uuid": "FDA50693-A4E2-4FB1-AFCF-C6EB07647825" } ], "total_count": 151 //商户名下的设备总量 }, "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-3-25 下午1:45:42 * @copyright Show More */ public function searchShakeAroundDevice($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_SEARCH . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [bindLocationShakeAroundDevice 配置设备与门店的关联关系] * @param string $device_id 设备编号,若填了UUID、major、minor,则可不填设备编号,若二者都填,则以设备编号为优先 * @param int $poi_id 待关联的门店ID * @param string $uuid UUID、major、minor,三个信息需填写完整,若填了设备编号,则可不填此信息 * @param int $major * @param int $minor * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { }, "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-4-21 00:14:00 * @copyright Show More */ public function bindLocationShakeAroundDevice($device_id,$poi_id,$uuid='',$major=0,$minor=0){ if (!$this->access_token && !$this->checkAuth()) return false; if(!$device_id){ if(!$uuid || !$major || !$minor){ return false; } $device_identifier = array( 'uuid' => $uuid, 'major' => $major, 'minor' => $minor ); }else{ $device_identifier = array( 'device_id' => $device_id ); } $data = array( 'device_identifier' => $device_identifier, 'poi_id' => $poi_id ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_BINDLOCATION . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; //这个可以更改为返回true } return false; } /** * [bindPageShakeAroundDevice 配置设备与页面的关联关系。 * 支持建立或解除关联关系,也支持新增页面或覆盖页面等操作。 * 配置完成后,在此设备的信号范围内,即可摇出关联的页面信息。 * 若设备配置多个页面,则随机出现页面信息] * @param string $device_id 设备编号,若填了UUID、major、minor,则可不填设备编号,若二者都填,则以设备编号为优先 * @param array $page_ids 待关联的页面列表 * @param number $bind 关联操作标志位, 0 为解除关联关系,1 为建立关联关系 * @param number $append 新增操作标志位, 0 为覆盖,1 为新增 * @param string $uuid UUID、major、minor,三个信息需填写完整,若填了设备编号,则可不填此信息 * @param int $major * @param int $minor * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { }, "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-4-21 00:31:00 * @copyright Show More */ public function bindPageShakeAroundDevice($device_id,$page_ids=array(),$bind=1,$append=1,$uuid='',$major=0,$minor=0){ if (!$this->access_token && !$this->checkAuth()) return false; if(!$device_id){ if(!$uuid || !$major || !$minor){ return false; } $device_identifier = array( 'uuid' => $uuid, 'major' => $major, 'minor' => $minor ); }else{ $device_identifier = array( 'device_id' => $device_id ); } $data = array( 'device_identifier' => $device_identifier, 'page_ids' => $page_ids, 'bind' => $bind, 'append' => $append ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_BINDPAGE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 上传在摇一摇页面展示的图片素材 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义 * @param array $data {"media":'@Path\filename.jpg'} 格式限定为:jpg,jpeg,png,gif,图片大小建议120px*120 px,限制不超过200 px *200 px,图片需为正方形。 * @return boolean|array * { "data": { "pic_url":"http://shp.qpic.cn/wechat_shakearound_pic/0/1428377032e9dd2797018cad79186e03e8c5aec8dc/120" }, "errcode": 0, "errmsg": "success." } } * @author binsee * @version 2015-4-21 00:51:00 */ public function uploadShakeAroundMedia($data){ if (!$this->access_token && !$this->checkAuth()) return false; $result = $this->http_post(self::API_BASE_URL_PREFIX.self::SHAKEAROUND_MATERIAL_ADD.'access_token='.$this->access_token,$data,true); if ($result) { $json = json_decode($result,true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [addShakeAroundPage 增加摇一摇出来的页面信息,包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。] * @param string $title 在摇一摇页面展示的主标题,不超过6 个字 * @param string $description 在摇一摇页面展示的副标题,不超过7 个字 * @param sting $icon_url 在摇一摇页面展示的图片, 格式限定为:jpg,jpeg,png,gif; 建议120*120 , 限制不超过200*200 * @param string $page_url 跳转链接 * @param string $comment 页面的备注信息,不超过15 个字,可不填 * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { "page_id": 28840 //新增页面的页面id } "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-3-25 下午2:57:09 * @copyright Show More */ public function addShakeAroundPage($title,$description,$icon_url,$page_url,$comment=''){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( "title" => $title, "description" => $description, "icon_url" => $icon_url, "page_url" => $page_url, "comment" => $comment ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_ADD . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [updateShakeAroundPage 编辑摇一摇出来的页面信息,包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。] * @param int $page_id * @param string $title 在摇一摇页面展示的主标题,不超过6 个字 * @param string $description 在摇一摇页面展示的副标题,不超过7 个字 * @param sting $icon_url 在摇一摇页面展示的图片, 格式限定为:jpg,jpeg,png,gif; 建议120*120 , 限制不超过200*200 * @param string $page_url 跳转链接 * @param string $comment 页面的备注信息,不超过15 个字,可不填 * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { "page_id": 28840 //编辑页面的页面ID } "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-3-25 下午3:02:51 * @copyright Show More */ public function updateShakeAroundPage($page_id,$title,$description,$icon_url,$page_url,$comment=''){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( "page_id" => $page_id, "title" => $title, "description" => $description, "icon_url" => $icon_url, "page_url" => $page_url, "comment" => $comment ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [searchShakeAroundPage 查询已有的页面,包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。 * 提供两种查询方式,①可指定页面ID 查询,②也可批量拉取页面列表。] * @param array $page_ids * @param int $begin * @param int $count * ①需要查询指定页面时: * { "page_ids":[12345, 23456, 34567] } * +------------------------------------------------------------------------------------------------------------- * ②需要分页查询或者指定范围内的页面时: * { "begin": 0, "count": 3 } * +------------------------------------------------------------------------------------------------------------- * @return boolean|mixed * 正确返回JSON 数据示例: { "data": { "pages": [ { "comment": "just for test", "description": "test", "icon_url": "https://www.baidu.com/img/bd_logo1.png", "page_id": 28840, "page_url": "http://xw.qq.com/testapi1", "title": "测试1" }, { "comment": "just for test", "description": "test", "icon_url": "https://www.baidu.com/img/bd_logo1.png", "page_id": 28842, "page_url": "http://xw.qq.com/testapi2", "title": "测试2" } ], "total_count": 2 }, "errcode": 0, "errmsg": "success." } *字段说明: *total_count 商户名下的页面总数 *page_id 摇周边页面唯一ID *title 在摇一摇页面展示的主标题 *description 在摇一摇页面展示的副标题 *icon_url 在摇一摇页面展示的图片 *page_url 跳转链接 *comment 页面的备注信息 * @access public * @author polo * @version 2015-3-25 下午3:12:17 * @copyright Show More */ public function searchShakeAroundPage($page_ids=array(),$begin=0,$count=1){ if (!$this->access_token && !$this->checkAuth()) return false; if(!empty($page_ids)){ $data = array( 'page_ids' => $page_ids ); }else{ $data = array( 'begin' => $begin, 'count' => $count ); } $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_SEARCH . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [deleteShakeAroundPage 删除已有的页面,包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。 * 只有页面与设备没有关联关系时,才可被删除。] * @param array $page_ids * { "page_ids":[12345,23456,34567] } * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { }, "errcode": 0, "errmsg": "success." } * @access public * @author polo * @version 2015-3-25 下午3:23:00 * @copyright Show More */ public function deleteShakeAroundPage($page_ids=array()){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'page_ids' => $page_ids ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_DELETE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [getShakeInfoShakeAroundUser 获取设备信息,包括UUID、major、minor,以及距离、openID 等信息。] * @param string $ticket 摇周边业务的ticket,可在摇到的URL 中得到,ticket生效时间为30 分钟 * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": { "page_id ": 14211, "beacon_info": { "distance": 55.00620700469034, "major": 10001, "minor": 19007, "uuid": "FDA50693-A4E2-4FB1-AFCF-C6EB07647825" }, "openid": "oVDmXjp7y8aG2AlBuRpMZTb1-cmA" }, "errcode": 0, "errmsg": "success." } * 字段说明: * beacon_info 设备信息,包括UUID、major、minor,以及距离 * UUID、major、minor UUID、major、minor * distance Beacon 信号与手机的距离 * page_id 摇周边页面唯一ID * openid 商户AppID 下用户的唯一标识 * poi_id 门店ID,有的话则返回,没有的话不会在JSON 格式内 * @access public * @author polo * @version 2015-3-25 下午3:28:20 * @copyright Show More */ public function getShakeInfoShakeAroundUser($ticket){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array('ticket' => $ticket); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_USER_GETSHAKEINFO . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [deviceShakeAroundStatistics 以设备为维度的数据统计接口。 * 查询单个设备进行摇周边操作的人数、次数,点击摇周边消息的人数、次数;查询的最长时间跨度为30天。] * @param int $device_id 设备编号,若填了UUID、major、minor,即可不填设备编号,二者选其一 * @param int $begin_date 起始日期时间戳,最长时间跨度为30 天 * @param int $end_date 结束日期时间戳,最长时间跨度为30 天 * @param string $uuid UUID、major、minor,三个信息需填写完成,若填了设备编辑,即可不填此信息,二者选其一 * @param int $major * @param int $minor * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": [ { "click_pv": 0, "click_uv": 0, "ftime": 1425052800, "shake_pv": 0, "shake_uv": 0 }, { "click_pv": 0, "click_uv": 0, "ftime": 1425139200, "shake_pv": 0, "shake_uv": 0 } ], "errcode": 0, "errmsg": "success." } * 字段说明: * ftime 当天0 点对应的时间戳 * click_pv 点击摇周边消息的次数 * click_uv 点击摇周边消息的人数 * shake_pv 摇周边的次数 * shake_uv 摇周边的人数 * @access public * @author polo * @version 2015-4-21 00:39:00 * @copyright Show More */ public function deviceShakeAroundStatistics($device_id,$begin_date,$end_date,$uuid='',$major=0,$minor=0){ if (!$this->access_token && !$this->checkAuth()) return false; if(!$device_id){ if(!$uuid || !$major || !$minor){ return false; } $device_identifier = array( 'uuid' => $uuid, 'major' => $major, 'minor' => $minor ); }else{ $device_identifier = array( 'device_id' => $device_id ); } $data = array( 'device_identifier' => $device_identifier, 'begin_date' => $begin_date, 'end_date' => $end_date ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_STATISTICS_DEVICE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * [pageShakeAroundStatistics 以页面为维度的数据统计接口。 * 查询单个页面通过摇周边摇出来的人数、次数,点击摇周边页面的人数、次数;查询的最长时间跨度为30天。] * @param int $page_id 指定页面的ID * @param int $begin_date 起始日期时间戳,最长时间跨度为30 天 * @param int $end_date 结束日期时间戳,最长时间跨度为30 天 * @return boolean|mixed * 正确返回JSON 数据示例: * { "data": [ { "click_pv": 0, "click_uv": 0, "ftime": 1425052800, "shake_pv": 0, "shake_uv": 0 }, { "click_pv": 0, "click_uv": 0, "ftime": 1425139200, "shake_pv": 0, "shake_uv": 0 } ], "errcode": 0, "errmsg": "success." } * 字段说明: * ftime 当天0 点对应的时间戳 * click_pv 点击摇周边消息的次数 * click_uv 点击摇周边消息的人数 * shake_pv 摇周边的次数 * shake_uv 摇周边的人数 * @author binsee * @version 2015-4-21 00:43:00 */ public function pageShakeAroundStatistics($page_id,$begin_date,$end_date){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array( 'page_id' => $page_id, 'begin_date' => $begin_date, 'end_date' => $end_date ); $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_STATISTICS_DEVICE . 'access_token=' . $this->access_token, self::json_encode($data)); $this->log($result); if ($result) { $json = json_decode($result, true); if (!$json || !empty($json['errcode'])) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json; } return false; } /** * 根据订单ID获取订单详情 * @param string $order_id 订单ID * @return order array|bool */ public function getOrderByID($order_id){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$order_id) return false; $data = array( 'order_id'=>$order_id ); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYID.'access_token='.$this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode']) && $json['errcode']) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['order']; } return false; } /** * 根据订单状态/创建时间获取订单详情 * @param int $status 订单状态(不带该字段-全部状态, 2-待发货, 3-已发货, 5-已完成, 8-维权中, ) * @param int $begintime 订单创建时间起始时间(不带该字段则不按照时间做筛选) * @param int $endtime 订单创建时间终止时间(不带该字段则不按照时间做筛选) * @return order list array|bool */ public function getOrderByFilter($status = null, $begintime = null, $endtime = null){ if (!$this->access_token && !$this->checkAuth()) return false; $data = array(); $valid_status = array(2, 3, 5, 8); if (is_numeric($status) && in_array($status, $valid_status)) { $data['status'] = $status; } if (is_numeric($begintime) && is_numeric($endtime)) { $data['begintime'] = $begintime; $data['endtime'] = $endtime; } $result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYFILTER.'access_token='.$this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode']) && $json['errcode']) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return $json['order_list']; } return false; } /** * 设置订单发货信息 * @param string $order_id 订单 ID * @param int $need_delivery 商品是否需要物流(0-不需要,1-需要) * @param string $delivery_company 物流公司 ID * @param string $delivery_track_no 运单 ID * @param int $is_others 是否为 6.4.5 表之外的其它物流公司(0-否,1-是) * @return bool */ public function setOrderDelivery($order_id, $need_delivery = 0, $delivery_company = null, $delivery_track_no = null, $is_others = 0){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$order_id) return false; $data = array(); $data['order_id'] = $order_id; if ($need_delivery) { $data['delivery_company'] = $delivery_company; $data['delivery_track_no'] = $delivery_track_no; $data['is_others'] = $is_others; } else { $data['need_delivery'] = $need_delivery; } $result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_SETDELIVERY.'access_token='.$this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode']) && $json['errcode']) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } /** * 关闭订单 * @param string $order_id 订单 ID * @return bool */ public function closeOrder($order_id){ if (!$this->access_token && !$this->checkAuth()) return false; if (!$order_id) return false; $data = array( 'order_id'=>$order_id ); $result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_CLOSE.'access_token='.$this->access_token, self::json_encode($data)); if ($result) { $json = json_decode($result,true); if (isset($json['errcode']) && $json['errcode']) { $this->errCode = $json['errcode']; $this->errMsg = $json['errmsg']; return false; } return true; } return false; } private function parseSkuInfo($skuInfo) { $skuInfo = str_replace("\$", "", $skuInfo); $matches = explode(";", $skuInfo); $result = array(); foreach ($matches as $matche) { $arrs = explode(":", $matche); $result[$arrs[0]] = $arrs[1]; } return $result; } /** * 获取订单SkuInfo - 订单付款通知 * 当Event为 merchant_order(订单付款通知) * @return array|boolean */ public function getRevOrderSkuInfo(){ if (isset($this->_receive['SkuInfo'])) //订单 SkuInfo return $this->parseSkuInfo($this->_receive['SkuInfo']); else return false; } } /** * PKCS7Encoder class * * 提供基于PKCS7算法的加解密接口. */ class PKCS7Encoder { public static $block_size = 32; /** * 对需要加密的明文进行填充补位 * @param $text 需要进行填充补位操作的明文 * @return 补齐明文字符串 */ function encode($text) { $block_size = PKCS7Encoder::$block_size; $text_length = strlen($text); //计算需要填充的位数 $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size); if ($amount_to_pad == 0) { $amount_to_pad = PKCS7Encoder::$block_size; } //获得补位所用的字符 $pad_chr = chr($amount_to_pad); $tmp = ""; for ($index = 0; $index < $amount_to_pad; $index++) { $tmp .= $pad_chr; } return $text . $tmp; } /** * 对解密后的明文进行补位删除 * @param decrypted 解密后的明文 * @return 删除填充补位后的明文 */ function decode($text) { $pad = ord(substr($text, -1)); if ($pad < 1 || $pad > PKCS7Encoder::$block_size) { $pad = 0; } return substr($text, 0, (strlen($text) - $pad)); } } /** * Prpcrypt class * * 提供接收和推送给公众平台消息的加解密接口. */ class Prpcrypt { public $key; function __construct($k) { $this->key = base64_decode($k . "="); } /** * 兼容老版本php构造函数,不能在 __construct() 方法前边,否则报错 */ function Prpcrypt($k) { $this->key = base64_decode($k . "="); } /** * 对明文进行加密 * @param string $text 需要加密的明文 * @return string 加密后的密文 */ public function encrypt($text, $appid){ try { //获得16位随机字符串,填充到明文之前 $random = $this->getRandomStr(); $text = $random . pack("N", strlen($text)) . $text . $appid; $iv = substr($this->key, 0, 16); $pkc_encoder = new PKCS7Encoder; $text = $pkc_encoder->encode($text); $encrypted = openssl_encrypt($text,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv); return array(ErrorCode::$OK, $encrypted); } catch (Exception $e) { //print $e; return array(ErrorCode::$EncryptAESError, null); } } /** * 对密文进行解密 * @param string $encrypted 需要解密的密文 * @return string 解密得到的明文 */ public function decrypt($encrypted, $appid){ try { $iv = substr($this->key, 0, 16); $decrypted = openssl_decrypt($encrypted,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv); } catch (Exception $e) { return array(ErrorCode::$DecryptAESError, null); } try { //去除补位字符 $pkc_encoder = new PKCS7Encoder; $result = $pkc_encoder->decode($decrypted); //去除16位随机字符串,网络字节序和AppId if (strlen($result) < 16) return ""; $content = substr($result, 16, strlen($result)); $len_list = unpack("N", substr($content, 0, 4)); $xml_len = $len_list[1]; $xml_content = substr($content, 4, $xml_len); $from_appid = substr($content, $xml_len + 4); if (!$appid) $appid = $from_appid; //如果传入的appid是空的,则认为是订阅号,使用数据中提取出来的appid } catch (Exception $e) { //print $e; return array(ErrorCode::$IllegalBuffer, null); } if ($from_appid != $appid) return array(ErrorCode::$ValidateAppidError, null); //不注释上边两行,避免传入appid是错误的情况 return array(0, $xml_content, $from_appid); //增加appid,为了解决后面加密回复消息的时候没有appid的订阅号会无法回复 } /** * 随机生成16位字符串 * @return string 生成的字符串 */ function getRandomStr() { $str = ""; $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($str_pol) - 1; for ($i = 0; $i < 16; $i++) { $str .= $str_pol[mt_rand(0, $max)]; } return $str; } } /** * error code * 仅用作类内部使用,不用于官方API接口的errCode码 */ class ErrorCode { public static $OK = 0; public static $ValidateSignatureError = 40001; public static $ParseXmlError = 40002; public static $ComputeSignatureError = 40003; public static $IllegalAesKey = 40004; public static $ValidateAppidError = 40005; public static $EncryptAESError = 40006; public static $DecryptAESError = 40007; public static $IllegalBuffer = 40008; public static $EncodeBase64Error = 40009; public static $DecodeBase64Error = 40010; public static $GenReturnXmlError = 40011; public static $errCode=array( '0' => '处理成功', '40001' => '校验签名失败', '40002' => '解析xml失败', '40003' => '计算签名失败', '40004' => '不合法的AESKey', '40005' => '校验AppID失败', '40006' => 'AES加密失败', '40007' => 'AES解密失败', '40008' => '公众平台发送的xml不合法', '40009' => 'Base64编码失败', '40010' => 'Base64解码失败', '40011' => '公众帐号生成回包xml失败' ); public static function getErrText($err) { if (isset(self::$errCode[$err])) { return self::$errCode[$err]; }else { return false; }; } } ================================================ FILE: wiki/API接口错误码.md ================================================ ## errCode.php ### 关于API接口错误码有两个版本: **一个是普通公众号平台的errCode.php; 一个是企业号平台的 qyerrCode.php 用法都是一样的。** 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 使用方法: ```php include "errCode.php"; //或 qyerrCode.php $ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到 if ($ret) echo $ret; else echo "未找到对应的内容"; ``` ================================================ FILE: wiki/Home.md ================================================ # wechat-php-sdk 微信公众平台php开发包,细化各项接口操作,支持链式调用 项目地址:**https://github.com/dodgepudding/wechat-php-sdk** 项目wiki:**http://binsee.github.io/wechat-php-sdk** ---- ## 使用详解 使用前需先打开微信帐号的开发模式,详细步骤请查看微信公众平台接口使用说明: 微信公众平台: http://mp.weixin.qq.com/wiki/ 微信企业平台: http://qydev.weixin.qq.com/wiki/ 微信支付接入文档:https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl 微信多客服:http://dkf.qq.com ## 功能目录 - [[官方API类库]] > wechat.class.php > 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; - [[企业号API类库]] > qywechat.class.php > 微信公众平台企业号PHP-SDK > 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; - [[API接口错误码]] > errCode.php 或 qyerrCode.php > 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 - [[旧版微信支付V2接口类库]] > old_version/wechatpay.class.php > 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 - ~~[[非官方扩展类库]]~~*(停止维护)* > old_version/wechatext.class.php > 非官方扩展API,模拟人工操作微信平台,此方式不保证长期有效。 - ~~[[授权登陆类库]]~~*(停止维护)* > old_version/wechatauth.class.php > 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆 - ~~[[内嵌JS]]~~*(已废弃)* > old_version/wechat.js > 微信内嵌网页功能调用js - [[为开发框架进行适配]] > 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 ================================================ FILE: wiki/README.md ================================================ wiki目录说明 ============== 这个目录是wechat-php-sdk项目的wiki文档 Make By:binsee ##说明 **这里的wiki文档可以让你更好的了解`wechat-php-sdk`项目,更好的使用。 ** **欢迎对wiki文档内容进行补充,把`wechat-php-sdk`项目变得更清晰易懂。** ##为你的github生成wiki **如果你在github上fork了`wechat-php-sdk`项目,而且想为项目生成wiki,可以用这里的文件来生成。** ###使用步骤: 1. 在你的github上,fork或者创建`wechat-php-sdk`项目 2. 激活项目wiki,已激活的请跳过 ``` 进入项目的设置页面: https://github.com/你的用户名/wechat-php-sdk/settings 找到Features一栏,把 Wikis 选项打钩,就可以激活你项目的wiki了 ``` 3. 进入项目的wiki页面: `https://github.com/你的用户名/wechat-php-sdk/wiki` 4. 点绿色的 `Create the first page` 按钮 5. 直接到下方点 `Save Page` 按钮 6. 在右边找到 `Clone this wiki locally` 一栏,复制git地址: `git@github.com:你的用户名/wechat-php-sdk.wiki.git` 7. 在项目的上一层目录执行 `git clone git@github.com:你的用户名/wechat-php-sdk.wiki.git` 8. 进入新出现的 `wechat-php-sdk.wiki` 目录,把wiki目录下的文件都复制过来 > **这里有个高级用法,就是使用连接方式把wiki目录链接过来,而不是复制** > windows下的用法: ``` #如项目目录为:E:\wechat-php-sdk\ #项目wiki目录为:E:\wechat-php-sdk.wiki\ 执行命令: mklink /j E:\wechat-php-sdk.wiki\wiki E:\wechat-php-sdk\wiki ``` > 这样的话,两个目录就会被联接到一起。 > 以后进行更改wiki在哪个目录都行,另一个目录都是同步的。 > 分别在 项目目录 和 项目wiki目录 进行git提交就可以了。 9. 然后直接缓存上传即可。 10. 现在去你github上项目的wiki目录里看一下吧 ##生成page **你也可以将wiki文档,生成为个人站点,会更加直观。** **比如使用 Hexo 或者其他的框架之类。** **这块的话,请自行搜索相关资料。** ================================================ FILE: wiki/为开发框架进行适配.md ================================================ #为开发框架进行适配 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 由于微信api需要缓存access_token与jsapi_ticket,而在不同框架下的缓存方式不同,所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。 需要各位在使用不同框架时再进行修改,但确实很麻烦,因为对结构进行了修改。 >取消了原先同步维护的Thinkphp版本,为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`,以及修改`log`方法可以重载。 >分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。 在不同的开发框架下使用Wechat类库,请继承Wechat类,根据需要实现这4个方法。 可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。 欢迎提交其他框架的适配文件到项目库来。 为Thinkphp进行适配的示例如下: ```php /** * 微信公众平台PHP-SDK, ThinkPHP实例 * @author dodgepudding@gmail.com * @link https://github.com/dodgepudding/wechat-php-sdk * @version 1.2 * usage: * $options = array( * 'token'=>'tokenaccesskey', //填写你设定的key * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 * ); * $weObj = new TPWechat($options); * $weObj->valid(); * ... * */ class TPWechat extends Wechat { /** * log overwrite * @see Wechat::log() */ protected function log($log){ if ($this->debug) { if (function_exists($this->logcallback)) { if (is_array($log)) $log = print_r($log,true); return call_user_func($this->logcallback,$log); }elseif (class_exists('Log')) { Log::write('wechat:'.$log, Log::DEBUG); } } return false; } /** * 重载设置缓存 * @param string $cachename * @param mixed $value * @param int $expired * @return boolean */ protected function setCache($cachename,$value,$expired){ return S($cachename,$value,$expired); } /** * 重载获取缓存 * @param string $cachename * @return mixed */ protected function getCache($cachename){ return S($cachename); } /** * 重载清除缓存 * @param string $cachename * @return boolean */ protected function removeCache($cachename){ return S($cachename,null); } } ``` ================================================ FILE: wiki/企业号API类库.md ================================================ # qywechat.class.php ## 企业号API类库 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; ## 主要功能 - 接入验证 - 自动回复(文本、图片、语音、视频、音乐、图文) - 菜单操作(查询、创建、删除) - 部门管理(创建、更新、删除、获取部门列表) - 成员管理(创建、更新、删除、获取成员信息,获取部门成员列表) - 标签管理(创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表) - 媒体文件管理(上传、获取) - 二次验证 - OAuth2(生成授权url、获取成员信息) - 获取企业微信服务器IP列表 - 微信JSAPI授权(获取ticket、获取签名) ## 初始化动作 ```php $options = array( 'token'=>'tokenaccesskey', //填写应用接口的Token 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 'agentid'=>'1', //应用的id 'debug'=>false, //调试开关 '_logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ## 被动接口方法: * valid() 验证连接,被动接口必须调用 * * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 * getRevData() 返回微信服务器发来的信息(数组) * getRevPostXml() 返回微信服务器发来的原始加密xml信息 * getRevFrom() 返回消息发送者的userid * getRevTo() 返回消息接收者的id(即公众号id,一般与等同appid一致) * getRevAgentID() 返回接收消息的应用id * getRevType() 返回接收消息的类型 * getRevID() 返回消息id * getRevCtime() 返回消息发送事件 * getRevContent() 返回消息内容正文(文本型消息) * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} * * text($text) 设置文本型消息,参数:文本内容 * image($mediaid) 设置图片型消息,参数:图片的media_id * voice($mediaid) 设置语音型消息,参数:语音的media_id * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 * image($mediaid) 设置图片型消息,参数:图片的media_id * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) * reply() 将已经设置好的消息,回复给微信服务器 ### 预定义常量列表: ```php ////消息类型,使用实例调用getRevType()方法取得 const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; //暂不支持 const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; //暂不支持 const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; ////事件类型,使用实例调用getRevEvent()方法取得 const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_ENTER_AGENT = 'enter_agent'; //用户进入应用 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 ``` ## 主动接口方法: * checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token` * resetAuth($appid='') 清除记录的ACCESS_TOKEN * resetJsTicket($appid='') 删除JSAPI授权TICKET * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id * getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id * deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id * uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) * getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id * getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') * createDepartment($data) 创建部门,参数: array("name"=>"邮箱产品组","parentid"=>"1","order" => "1") * updateDepartment($data) 更新部门,参数: array("id"=>"1","name"=>"邮箱产品组","parentid"=>"1","order" => "1") * deleteDepartment($id) 删除部门,参数:要删除的部门id * moveDepartment($data) 移动部门,参数:array("department_id" => "5","to_parentid" => "2","to_position" => "1") * getDepartment() 获取部门列表,返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列 * createUser($data) 创建成员,参数请看php文件内方法说明 * updateUser($data) 更新成员,参数请看php文件内方法说明 * deleteUser($userid) 删除成员,参数:员工UserID * deleteUsers($userids) 批量删除成员,参数:员工UserID数组 * getUserInfo($userid) 获取成员信息,参数:员工UserID * getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员,参数:部门id,是否递归获取子部门,获取类型。 > 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 * getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情,参数同上 * getUserId($code,$agentid) 根据code获取员工UserID与手机设备号,参数:Oauth2.0或者二次验证返回的code值,跳转链接时所在的企业应用ID * sendInvite($userid,$invite_tips='') 邀请成员关注 * createTag($data) 创建标签,参数:array("tagname" => "UI") * updateTag($data) 更新标签,参数:array("tagid" => "1","tagname" => "UI") * deleteTag($tagid) 删除标签,参数:标签TagID * getTag($tagid) 获取标签成员,参数:标签TagID * addTagUser($data) 增加标签成员,参数请看php文件内方法说明 * delTagUser($data) 删除标签成员,参数请看php文件内方法说明 * getTagList() 获取标签列表,返回标签数组 * sendMessage($data) 主动发送信息接口,参数请看php文件内方法说明 * authSucc($userid) 二次验证,参数: 员工UserID * getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url ## 企业号API类库调用示例: -------- 可参考**test**目录下的**qydemo.php** ```php include "wechat.class.php"; $options = array( 'token'=>'9Ixxxxxxx', //填写应用接口的Token 'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 'appid'=>'wxa07979baxxxxxxxx', //填写高级调用功能的appid ); $weObj = new Wechat($options); $weObj->valid(); //注意, 企业号与普通公众号不同,必须打开验证,不要注释掉 $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ``` ================================================ FILE: wiki/内嵌JS.md ================================================ #wechat.js **此JS脚本已经废弃不再更新,原因是官方在微信6.0.2版本开放了全新的JSAPI接口,更全面好用。请查看:[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)** ##微信内嵌网页特殊功能js调用: * WeixinJS.hideOptionMenu() 隐藏右上角按钮 * WeixinJS.showOptionMenu() 显示右上角按钮 * WeixinJS.hideToolbar() 隐藏工具栏 * WeixinJS.showToolbar() 显示工具栏 * WeixinJS.getNetworkType() 获取网络状态 * WeixinJS.closeWindow() 关闭窗口 * WeixinJS.scanQRCode() 扫描二维码 * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 * WeixinJS.sendEmail(title,content) 发送邮件 * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 * WeixinJS.addContact(username) 添加微信账号 * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 * 通过定义全局变量dataForWeixin配置触发分享的内容: ```javascript var dataForWeixin={ appId:"", MsgImg:"消息图片路径", TLImg:"时间线图路径", url:"分享url路径", title:"标题", desc:"描述", fakeid:"", callback:function(){} }; ``` 可以参考weshare.html及wechat.js的备注进行使用 ================================================ FILE: wiki/官方API类库.md ================================================ # wechat.class.php 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; ## 主要功能 - 接入验证 **(初级权限)** - 自动回复(文本、图片、语音、视频、音乐、图文) **(初级权限)** - 菜单操作(查询、创建、删除) **(菜单权限)** - 客服消息(文本、图片、语音、视频、音乐、图文) **(认证权限)** - 二维码(创建临时、永久二维码,获取二维码URL) **(服务号、认证权限)** - 长链接转短链接接口 **(服务号、认证权限)** - 分组操作(查询、创建、修改、移动用户到分组) **(认证权限)** - 网页授权(基本授权,用户信息授权) **(服务号、认证权限)** - 用户信息(查询用户基本信息、获取关注者列表) **(认证权限)** - 多客服功能(客服管理、获取客服记录、客服会话管理) **(认证权限)** - 媒体文件(上传、获取) **(认证权限)** - 高级群发 **(认证权限)** - 模板消息(设置所属行业、添加模板、发送模板消息) **(服务号、认证权限)** - 卡券管理(创建、修改、删除、发放、门店管理等) **(认证权限)** - 语义理解 **(服务号、认证权限)** - 获取微信服务器IP列表 **(初级权限)** - 微信JSAPI授权(获取ticket、获取签名) **(初级权限)** - 数据统计(用户、图文、消息、接口分析数据) **(认证权限)** > 备注: > 初级权限:基本权限,任何正常的公众号都有此权限 > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 > 支付权限:仅认证后的服务号可以申请此权限 ## 初始化动作 ```php $options = array( 'token'=>'tokenaccesskey', //填写你设定的key 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ### 被动接口方法: * valid() 验证连接,被动接口处于加密模式时必须调用 * * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 * getRevData() 返回微信服务器发来的信息(数组) * getRevFrom() 返回消息发送者的userid * getRevTo() 返回消息接收者的id(即公众号id) * getRevType() 返回接收消息的类型 * getRevID() 返回消息id * getRevCtime() 返回消息发送时间 * getRevContent() 返回消息内容正文或语音识别结果(文本型) * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} * getRevLink() 接收消息链接(链接型信息) 返回数组{'url'=>'','title'=>'','description'=>''} * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} * getRevTicket() 返回接收TICKET(扫描带参数二维码,关注或SCAN事件) 返回二维码的ticket值 * getRevSceneId() 返回二维码的场景值(扫描带参数二维码的关注事件) 返回二维码的参数值 * getRevTplMsgID() 返回主动推送的消息ID(群发或模板消息事件) 返回MsgID值 * getRevStatus() 返回模板消息发送状态(模板消息事件) 返回文本:success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败(非用户拒绝)) * getRevResult() 返回群发或模板消息发送结果(群发或模板消息事件) 返回数组,内容依事件类型而不同,参考开发文档中群发、模板消息推送事件 * getRevKFCreate() 返回多客服-接入会话的客服账号(多客服-接入会话事件) 返回文本型 * getRevKFClose() 返回多客服-处理会话的客服账号(多客服-接入会话事件) 返回文本型 * getRevKFSwitch() 返回多客服-转接会话信息(多客服-转接会话事件) 返回数组 {'FromKfAccount' => '','ToKfAccount' => ''} * getRevCardPass() 返回卡券-审核通过的卡券ID(卡券-卡券审核事件) 返回文本型 * getRevCardGet() 返回卡券-用户领取卡券的相关信息(卡券-领取卡券事件) 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''} * getRevCardDel() 返回卡券-用户删除卡券的相关信息(卡券-删除卡券事件) 返回数组{'CardId' => '','UserCardCode' => ''} * * text($text) 设置文本型消息,参数:文本内容 * image($mediaid) 设置图片型消息,参数:图片的media_id * voice($mediaid) 设置语音型消息,参数:语音的media_id * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 * music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐,参数:音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 * image($mediaid) 设置图片型消息,参数:图片的media_id * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) * transfer_customer_service($customer_account = '') 转接多客服,如不指定客服可不提供参数,参数:指定客服的账号 * reply() 将以上已经设置好的消息,回复给微信服务器 ### 预定义常量列表: ```php ////消息类型,使用实例调用getRevType()方法取得 const MSGTYPE_TEXT = 'text'; const MSGTYPE_IMAGE = 'image'; const MSGTYPE_LOCATION = 'location'; const MSGTYPE_LINK = 'link'; const MSGTYPE_EVENT = 'event'; const MSGTYPE_MUSIC = 'music'; const MSGTYPE_NEWS = 'news'; const MSGTYPE_VOICE = 'voice'; const MSGTYPE_VIDEO = 'video'; ////事件类型,使用实例调用getRevEvent()方法取得 const EVENT_SUBSCRIBE = 'subscribe'; //订阅 const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 const EVENT_SCAN = 'SCAN'; //扫描带参数二维码 const EVENT_LOCATION = 'LOCATION'; //上报地理位置 const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 const EVENT_KF_SEESION_CREATE = 'kfcreatesession'; //多客服 - 接入会话 const EVENT_KF_SEESION_CLOSE = 'kfclosesession'; //多客服 - 关闭会话 const EVENT_KF_SEESION_SWITCH = 'kfswitchsession'; //多客服 - 转接会话 const EVENT_CARD_PASS = 'card_pass_check'; //卡券 - 审核通过 const EVENT_CARD_NOTPASS = 'card_not_pass_check'; //卡券 - 审核未通过 const EVENT_CARD_USER_GET = 'user_get_card'; //卡券 - 用户领取卡券 const EVENT_CARD_USER_DEL = 'user_del_card'; //卡券 - 用户删除卡券 ``` ### 主动接口方法: * checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌 * resetAuth($appid='') 删除验证数据 * resetJsTicket($appid='') 删除JSAPI授权TICKET * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 * createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)** * getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') * getMenu() 获取菜单 * deleteMenu() 删除菜单 * uploadMedia($data, $type) 上传临时素材,有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) * getMedia($media_id,$is_video=false) 获取临时素材(含接收到的音频、视频媒体文件) * uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材,可以在公众平台官网素材管理模块中看到 * uploadForeverArticles($data) 上传永久图文素材 * updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用) * getForeverMedia($media_id,$is_video=false) 获取永久素材 * delForeverMedia($media_id) 删除永久素材 * getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用) * getForeverCount() 获取永久素材总数 * uploadMpVideo($data) 上传视频素材,当需要群发视频时,必须使用此方法得到的MediaID,否则无法显示 * uploadArticles($data) 上传图文消息素材 * sendMassMessage($data) 高级群发消息 * sendGroupMassMessage($data) 高级群发消息(全体或分组群发) * deleteMassMessage($msg_id) 删除群发图文消息 * previewMassMessage($data) 预览群发消息 * queryMassMessage($msg_id) 查询群发消息发送状态 * getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 * getQRUrl($ticket) 获取二维码图片地址 * getShortUrl($long_url) 长链接转短链接接口 * getUserList($next_openid) 批量获取关注用户列表 * getUserInfo($openid) 获取关注者详细信息 * updateUserRemark($openid,$remark) 设置用户备注名 * getGroup() 获取用户分组列表 * getUserGroup($openid) 获取用户所在分组 * createGroup($name) 新增自定分组 * updateGroup($groupid,$name) 更改分组名称 * updateGroupMembers($groupid,$openid) 移动用户分组 * batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 * sendCustomMessage($data) 发送客服消息 * getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址 * getOauthAccessToken() 通过回调的code获取网页授权access_token * getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期 * getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料 * getOauthAuth($access_token,$openid) 检验授权凭证access_token是否有效 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * setTMIndustry($id1,$id2='') 模板消息,设置所属行业 * addTemplateMessage($tpl_id) 模板消息,添加消息模板 * sendTemplateMessage($data) 发送模板消息 * getCustomServiceMessage($data) 获取多客服会话记录 * transfer_customer_service($customer_account) 转发多客服消息 * getCustomServiceKFlist() 获取多客服客服基本信息 * getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息 * createKFSession($openid,$kf_account,$text='') 创建指定多客服会话 * closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话 * getKFSession($openid) 获取用户会话状态 * getKFSessionlist($kf_account) 获取指定客服的会话列表 * getKFSessionWait() 获取未接入会话列表 * addKFAccount($account,$nickname,$password) 添加客服账号 * updateKFAccount($account,$nickname,$password) 修改客服账号信息 * deleteKFAccount($account) 删除客服账号 * setKFHeadImg($account,$imgfile) 上传客服头像 * querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region="") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)** * getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义 > 获取统计数据方法 参数定义 > | 数据分类 | $type值(字符串) | 数据子分类 | $subtype值(字符串) | 时间跨度(天) | | --------- | :-------: | --------- | :------: | ----: | | 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 | | 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 | | 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 | | 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 | | 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 | | 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 | | 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 | | 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 | | 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 | | 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 | | 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 | | 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 | | 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 | | 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 | | 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 | 需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”(比如最大时间跨度为1时,`begin_date`和`end_date`的差值只能为0,才能小于1) * createCard($data) 创建卡券 * updateCard($data) 修改卡券 * delCard($card_id) 删除卡券 * getCardInfo($card_id) 查询卡券详情 * getCardColors() 获取颜色列表 * getCardLocations() 拉取门店列表 * addCardLocations($data) 批量导入门店信息 * createCardQrcode($card_id) 生成卡券二维码 * consumeCardCode($code) 消耗 code * decryptCardCode($encrypt_code) code 解码 * checkCardCode($code) 获取 code 的有效性 * getCardIdList($data) 批量查询卡列表 * updateCardCode($code,$card_id,$new_code) 更改 code * unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)** * modifyCardStock($data) 库存修改 * activateMemberCard($data) 激活/绑定会员卡,参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节 * updateMemberCard($data) 会员卡交易,参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节 * updateLuckyMoney($code,$balance,$card_id='') 更新红包金额 * setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单 ## 官方Wechat调用示例: ```php //test1.php include "wechat.class.php"; $options = array( 'token'=>'tokenaccesskey' //填写你设定的key ); $weObj = new Wechat($options); $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 $type = $weObj->getRev()->getRevType(); switch($type) { case Wechat::MSGTYPE_TEXT: $weObj->text("hello, I'm wechat")->reply(); exit; break; case Wechat::MSGTYPE_EVENT: break; case Wechat::MSGTYPE_IMAGE: break; default: $weObj->text("help info")->reply(); } ``` ================================================ FILE: wiki/授权登陆类库.md ================================================ # wechatauth.class.php **此扩展类库已经不再更新,原因是官方开放平台对网站应用开放的有授权登陆接口,更标准,更好用。请查看:[微信开放平台](http://open.weixin.qq.com)** 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子. ## 类主要方法: * get_login_code() 获取登陆授权码, 通过授权码才能获取二维码 * get_code_image($code='') 将上面获取的授权码转换为图片二维码 * verify_code() 鉴定是否登陆成功,返回200为最终授权成功. * get_login_info() 鉴定成功后调用此方法即可获取用户基本信息 * get_avatar($url) 获取用户头像图片数据 * logout() 注销登陆 ## 微信二维码Wechatauth登陆示例: ```php //test3.php include "../wechatauth.class.php"; session_start(); $sid = session_id(); $options = array( 'account'=>$sid, 'datapath'=>'../data/cookiecode_', ); $wechat = new Wechatauth($options); if (isset($_POST['code'])) { $logincode = $_POST['code']; $vres = $wechat->set_login_code($logincode)->verify_code(); if ($vres===false) { $result = array('status'=>0); } else { $result = array('status'=>$vres); if ($vres==200) { $result['info'] = $wechat->get_login_info(); $result['cookie'] = $wechat->get_login_cookie(true); } } die(json_encode($result)); } $logincode = $wechat->get_login_code(); //获取授权码 $qrimg = $wechat->get_code_image(); //待输出的二维码图片 ``` HTML部分请看test/test3.php, 主要是定时ajax查询是否已经授权成功 ================================================ FILE: wiki/旧版微信支付V2接口类库.md ================================================ # wechatpay.class.php 旧版微信支付类库(微信支付V2),已移动至old_version目录下。 自2014年8月开始申请到的微信支付都是V3接口,据官方说V2的会陆续升级为V3接口,但时间及升级渠道未确认。 ### 主要功能 - 获取access_token **(初级权限)** - 调用地址组件 **(支付权限)** - 生成订单签名数据 **(支付权限)** - 订单成功回调 **(支付权限)** - 发货通知 **(支付权限)** - 支付订单查询 **(支付权限)** > 备注: > 初级权限:基本权限,任何正常的公众号都有此权限 > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 > 支付权限:仅认证后的服务号可以申请此权限 ### 初始化动作 ```php $options = array( 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 'partnerid'=>'88888888', //财付通商户身份标识,支付权限专用,没有可不填 'partnerkey'=>'', //财付通商户权限密钥Key,支付权限专用 'paysignkey'=>'' //商户签名密钥Key,支付权限专用 ); $weObj = new Wechat($options); //创建实例对象 //TODO:调用$weObj各实例方法 ``` ### 主动接口方法: * checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取,或手动指定access_token * resetAuth($appid='') 删除验证数据 * getSignature($arrdata,'sha1') 生成签名字串 * generateNonceStr($length=16) 获取随机字串 * createNativeUrl($productid) 生成原生支付url * 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字符串 * getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法 * checkOrderSignature($orderxml='') 回调通知签名验证 * sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知 * getPayOrder($out_trade_no) 查询订单信息 * setUserToken($user_token) 设置用户授权密钥 * getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名 ================================================ FILE: wiki/非官方扩展类库.md ================================================ # wechatext.class.php **此扩展类库已经不再更新,原因是官方对公众号开放了众多接口,此类库继续维护的意义不大** 非官方扩展API,需要配置公众平台账户和密码,能实现对已关注用户的点对点微信,此方式不保证长期有效。 类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取. 调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决. 程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持. ## 类主要方法: * send($id,$content) 向某用户id发送微信文字信息 * sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid * getUserList($page,$pagesize,$groupid) 获取用户信息 * getGroupList($page,$pagesize) 获取群组信息 * getNewsList($page,$pagesize) 获取图文信息列表 * uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图 * getFileList($type,$page,$pagesize) 获取素材库文件列表 * sendImage($id,$fid) 发送图片消息 * sendAudio($id,$fid) 发送音频消息 * sendVideo($id,$fid) 发送视频消息 * getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取 * getNewMsgNum($lastid) 获取从$lastid算起新消息的数目 * getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据 * getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数 * 消息返回结构: {"id":"消息id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容"} * getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据 * getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据 ## 扩展包Wechatext调用示例: ```php //test2.php include "wechatext.class.php"; function logdebug($text){ file_put_contents('./data/log.txt',$text."\n",FILE_APPEND); }; $options = array( 'account'=>'demo@domain.com', 'password'=>'demo', 'datapath'=>'./data/cookie_', 'debug'=>true, 'logcallback'=>'logdebug' ); $wechat = new Wechatext($options); if ($wechat->checkValid()) { // 获取用户信息 $data = $wechat->getInfo('3974255'); var_dump($data); // 获取最新一条消息 $topmsg = $wechat->getTopMsg(); var_dump($topmsg); // 主动回复消息 if ($topmsg && $topmsg['has_reply']==0) $wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); } ```