├── .gitignore ├── README.md ├── Thinkphp ├── EasyWechat.class.php └── TPWechat.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 ├── jsapi │ ├── jsapi-demo-6.1.js │ ├── jsapi_demo.php │ └── style.css ├── qydemo.php └── test1.php ├── wechat.class.php └── wiki ├── API接口错误码.md ├── Home.md ├── README.md ├── 为开发框架进行适配.md ├── 企业号API类库.md ├── 内嵌JS.md ├── 官方API类库.md ├── 授权登陆类库.md ├── 旧版微信支付V2接口类库.md └── 非官方扩展类库.md /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ 2 | .buildpath 3 | /pages/ 4 | /my/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wechat-php-sdk 2 | ============== 3 | 4 | 微信公众平台php开发包,细化各项接口操作,支持链式调用,欢迎Fork此项目 5 | weixin developer SDK. 6 | 项目地址:**https://github.com/dodgepudding/wechat-php-sdk** 7 | 项目blog:**http://binsee.github.io/wechat-php-sdk** 8 | 9 | ## 使用详解 10 | 使用前需先打开微信帐号的开发模式,详细步骤请查看微信公众平台接口使用说明: 11 | 微信公众平台: http://mp.weixin.qq.com/wiki/ 12 | 微信企业平台: http://qydev.weixin.qq.com/wiki/ 13 | 14 | 微信支付接入文档: 15 | https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN 16 | 17 | 微信多客服:http://dkf.qq.com 18 | 19 | 20 | ## 目录 21 | > **[wechat.class.php 官方API类库](#user-content-1-wechatclassphp-官方api类库)** 22 | > **[qywechat.class.php 企业号API类库](#user-content-6-qywechatclassphp-企业号api类库)** 23 | > **[errCode.php|qyerrCode.php 全局返回码类](#user-content-5-errcodephp-全局返回码类)** 24 | > **[old_version/wechatpay.class.php 旧版微信支付V2接口类库](#user-content-7-wechatpayclassphp-旧版微信支付V2接口类库)** 25 | > ~~**[old_version/wechatext.class.php 非官方扩展API(停止维护)](#user-content-2-wechatextclassphp-非官方扩展api)**~~ 26 | > ~~**[old_version/wechatauth.class.php 授权登陆(停止维护)](#user-content-3-wechatauthclassphp-授权登陆)**~~ 27 | > ~~**[old_version/wechat.js 内嵌JS(已废弃)](#user-content-4-wechatjs-内嵌js)**~~ 28 | > **[为开发框架进行适配](#user-content-为开发框架进行适配)** 29 | > **[调用示例](#user-content-调用示例)** 30 | 31 | ---------- 32 | 33 | ## 1. wechat.class.php 官方API类库 34 | 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 35 | 36 | ### 主要功能 37 | - 接入验证 **(初级权限)** 38 | - 自动回复(文本、图片、语音、视频、音乐、图文) **(初级权限)** 39 | - 菜单操作(查询、创建、删除) **(菜单权限)** 40 | - 客服消息(文本、图片、语音、视频、音乐、图文) **(认证权限)** 41 | - 二维码(创建临时、永久二维码,获取二维码URL) **(服务号、认证权限)** 42 | - 长链接转短链接接口 **(服务号、认证权限)** 43 | - 分组操作(查询、创建、修改、移动用户到分组) **(认证权限)** 44 | - 网页授权(基本授权,用户信息授权) **(服务号、认证权限)** 45 | - 用户信息(查询用户基本信息、获取关注者列表) **(认证权限)** 46 | - 多客服功能(客服管理、获取客服记录、客服会话管理) **(认证权限)** 47 | - 媒体文件(上传、获取) **(认证权限)** 48 | - 高级群发 **(认证权限)** 49 | - 模板消息(设置所属行业、添加模板、发送模板消息) **(服务号、认证权限)** 50 | - 卡券管理(创建、修改、删除、发放、门店管理等) **(认证权限)** 51 | - 语义理解 **(服务号、认证权限)** 52 | - 获取微信服务器IP列表 **(初级权限)** 53 | - 微信JSAPI授权(获取ticket、获取签名) **(初级权限)** 54 | - 数据统计(用户、图文、消息、接口分析数据) **(认证权限)** 55 | > 备注: 56 | > 初级权限:基本权限,任何正常的公众号都有此权限 57 | > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 58 | > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 59 | > 支付权限:仅认证后的服务号可以申请此权限 60 | 61 | 62 | ### 初始化动作 63 | ```php 64 | $options = array( 65 | 'token'=>'tokenaccesskey', //填写你设定的key 66 | 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 67 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 68 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 69 | ); 70 | $weObj = new Wechat($options); //创建实例对象 71 | //TODO:调用$weObj各实例方法 72 | ``` 73 | 74 | ### 被动接口方法: 75 | * valid() 验证连接,被动接口处于加密模式时必须调用 76 | * 77 | * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 78 | * getRevData() 返回微信服务器发来的信息(数组) 79 | * getRevFrom() 返回消息发送者的userid 80 | * getRevTo() 返回消息接收者的id(即公众号id) 81 | * getRevType() 返回接收消息的类型 82 | * getRevID() 返回消息id 83 | * getRevCtime() 返回消息发送时间 84 | * getRevContent() 返回消息内容正文或语音识别结果(文本型) 85 | * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} 86 | * getRevLink() 接收消息链接(链接型信息) 返回数组{'url'=>'','title'=>'','description'=>''} 87 | * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} 88 | * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} 89 | * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} 90 | * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') 91 | * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 92 | * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 93 | * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} 94 | * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} 95 | * getRevTicket() 返回接收TICKET(扫描带参数二维码,关注或SCAN事件) 返回二维码的ticket值 96 | * getRevSceneId() 返回二维码的场景值(扫描带参数二维码的关注事件) 返回二维码的参数值 97 | * getRevTplMsgID() 返回主动推送的消息ID(群发或模板消息事件) 返回MsgID值 98 | * getRevStatus() 返回模板消息发送状态(模板消息事件) 返回文本:success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败(非用户拒绝)) 99 | * getRevResult() 返回群发或模板消息发送结果(群发或模板消息事件) 返回数组,内容依事件类型而不同,参考开发文档中群发、模板消息推送事件 100 | * getRevKFCreate() 返回多客服-接入会话的客服账号(多客服-接入会话事件) 返回文本型 101 | * getRevKFClose() 返回多客服-处理会话的客服账号(多客服-接入会话事件) 返回文本型 102 | * getRevKFSwitch() 返回多客服-转接会话信息(多客服-转接会话事件) 返回数组 {'FromKfAccount' => '','ToKfAccount' => ''} 103 | * getRevCardPass() 返回卡券-审核通过的卡券ID(卡券-卡券审核事件) 返回文本型 104 | * getRevCardGet() 返回卡券-用户领取卡券的相关信息(卡券-领取卡券事件) 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''} 105 | * getRevCardDel() 返回卡券-用户删除卡券的相关信息(卡券-删除卡券事件) 返回数组{'CardId' => '','UserCardCode' => ''} 106 | * 107 | * text($text) 设置文本型消息,参数:文本内容 108 | * image($mediaid) 设置图片型消息,参数:图片的media_id 109 | * voice($mediaid) 设置语音型消息,参数:语音的media_id 110 | * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 111 | * music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐,参数:音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id 112 | * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 113 | * image($mediaid) 设置图片型消息,参数:图片的media_id 114 | * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) 115 | * transfer_customer_service($customer_account = '') 转接多客服,如不指定客服可不提供参数,参数:指定客服的账号 116 | * reply() 将以上已经设置好的消息,回复给微信服务器 117 | 118 | ### 预定义常量列表: 119 | ```php 120 | ////消息类型,使用实例调用getRevType()方法取得 121 | const MSGTYPE_TEXT = 'text'; 122 | const MSGTYPE_IMAGE = 'image'; 123 | const MSGTYPE_LOCATION = 'location'; 124 | const MSGTYPE_LINK = 'link'; 125 | const MSGTYPE_EVENT = 'event'; 126 | const MSGTYPE_MUSIC = 'music'; 127 | const MSGTYPE_NEWS = 'news'; 128 | const MSGTYPE_VOICE = 'voice'; 129 | const MSGTYPE_VIDEO = 'video'; 130 | ////事件类型,使用实例调用getRevEvent()方法取得 131 | const EVENT_SUBSCRIBE = 'subscribe'; //订阅 132 | const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 133 | const EVENT_SCAN = 'SCAN'; //扫描带参数二维码 134 | const EVENT_LOCATION = 'LOCATION'; //上报地理位置 135 | const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 136 | const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 137 | const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) 138 | const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) 139 | const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 140 | const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 141 | const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 142 | const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 143 | const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 144 | const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 145 | const EVENT_KF_SEESION_CREATE = 'kfcreatesession'; //多客服 - 接入会话 146 | const EVENT_KF_SEESION_CLOSE = 'kfclosesession'; //多客服 - 关闭会话 147 | const EVENT_KF_SEESION_SWITCH = 'kfswitchsession'; //多客服 - 转接会话 148 | const EVENT_CARD_PASS = 'card_pass_check'; //卡券 - 审核通过 149 | const EVENT_CARD_NOTPASS = 'card_not_pass_check'; //卡券 - 审核未通过 150 | const EVENT_CARD_USER_GET = 'user_get_card'; //卡券 - 用户领取卡券 151 | const EVENT_CARD_USER_DEL = 'user_del_card'; //卡券 - 用户删除卡券 152 | ``` 153 | 154 | ### 主动接口方法: 155 | * checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌 156 | * resetAuth($appid='') 删除验证数据 157 | * resetJsTicket($appid='') 删除JSAPI授权TICKET 158 | * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET 159 | * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 160 | * createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)** 161 | * getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') 162 | * getMenu() 获取菜单 163 | * deleteMenu() 删除菜单 164 | * uploadMedia($data, $type) 上传临时素材,有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) 165 | * getMedia($media_id,$is_video=false) 获取临时素材(含接收到的音频、视频媒体文件) 166 | * uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材,可以在公众平台官网素材管理模块中看到 167 | * uploadForeverArticles($data) 上传永久图文素材 168 | * updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用) 169 | * getForeverMedia($media_id,$is_video=false) 获取永久素材 170 | * delForeverMedia($media_id) 删除永久素材 171 | * getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用) 172 | * getForeverCount() 获取永久素材总数 173 | * uploadMpVideo($data) 上传视频素材,当需要群发视频时,必须使用此方法得到的MediaID,否则无法显示 174 | * uploadArticles($data) 上传图文消息素材 175 | * sendMassMessage($data) 高级群发消息 176 | * sendGroupMassMessage($data) 高级群发消息(全体或分组群发) 177 | * deleteMassMessage($msg_id) 删除群发图文消息 178 | * previewMassMessage($data) 预览群发消息 179 | * queryMassMessage($msg_id) 查询群发消息发送状态 180 | * getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 181 | * getQRUrl($ticket) 获取二维码图片地址 182 | * getShortUrl($long_url) 长链接转短链接接口 183 | * getUserList($next_openid) 批量获取关注用户列表 184 | * getUserInfo($openid) 获取关注者详细信息 185 | * updateUserRemark($openid,$remark) 设置用户备注名 186 | * getGroup() 获取用户分组列表 187 | * getUserGroup($openid) 获取用户所在分组 188 | * createGroup($name) 新增自定分组 189 | * updateGroup($groupid,$name) 更改分组名称 190 | * updateGroupMembers($groupid,$openid) 移动用户分组 191 | * batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 192 | * sendCustomMessage($data) 发送客服消息 193 | * getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址 194 | * getOauthAccessToken() 通过回调的code获取网页授权access_token 195 | * getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期 196 | * getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料 197 | * getOauthAuth($access_token,$openid) 检验授权凭证access_token是否有效 198 | * getSignature($arrdata,'sha1') 生成签名字串 199 | * generateNonceStr($length=16) 获取随机字串 200 | * setTMIndustry($id1,$id2='') 模板消息,设置所属行业 201 | * addTemplateMessage($tpl_id) 模板消息,添加消息模板 202 | * sendTemplateMessage($data) 发送模板消息 203 | * getCustomServiceMessage($data) 获取多客服会话记录 204 | * transfer_customer_service($customer_account) 转发多客服消息 205 | * getCustomServiceKFlist() 获取多客服客服基本信息 206 | * getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息 207 | * createKFSession($openid,$kf_account,$text='') 创建指定多客服会话 208 | * closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话 209 | * getKFSession($openid) 获取用户会话状态 210 | * getKFSessionlist($kf_account) 获取指定客服的会话列表 211 | * getKFSessionWait() 获取未接入会话列表 212 | * addKFAccount($account,$nickname,$password) 添加客服账号 213 | * updateKFAccount($account,$nickname,$password) 修改客服账号信息 214 | * deleteKFAccount($account) 删除客服账号 215 | * setKFHeadImg($account,$imgfile) 上传客服头像 216 | * querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region="") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)** 217 | * getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义 218 | > 获取统计数据方法 参数定义 219 | > 220 | | 数据分类 | $type值(字符串) | 数据子分类 | $subtype值(字符串) | 时间跨度(天) | 221 | | --------- | :-------: | --------- | :------: | ----: | 222 | | 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 | 223 | | 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 | 224 | | 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 | 225 | | 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 | 226 | | 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 | 227 | | 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 | 228 | | 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 | 229 | | 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 | 230 | | 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 | 231 | | 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 | 232 | | 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 | 233 | | 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 | 234 | | 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 | 235 | | 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 | 236 | | 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 | 237 | | 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 | 238 | | 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 | 239 | 需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”(比如最大时间跨度为1时,`begin_date`和`end_date`的差值只能为0,才能小于1) 240 | 241 | * createCard($data) 创建卡券 242 | * updateCard($data) 修改卡券 243 | * delCard($card_id) 删除卡券 244 | * getCardInfo($card_id) 查询卡券详情 245 | * getCardColors() 获取颜色列表 246 | * getCardLocations() 拉取门店列表 247 | * addCardLocations($data) 批量导入门店信息 248 | * createCardQrcode($card_id) 生成卡券二维码 249 | * consumeCardCode($code) 消耗 code 250 | * decryptCardCode($encrypt_code) code 解码 251 | * checkCardCode($code) 获取 code 的有效性 252 | * getCardIdList($data) 批量查询卡列表 253 | * updateCardCode($code,$card_id,$new_code) 更改 code 254 | * unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)** 255 | * modifyCardStock($data) 库存修改 256 | * activateMemberCard($data) 激活/绑定会员卡,参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节 257 | * updateMemberCard($data) 会员卡交易,参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节 258 | * updateLuckyMoney($code,$balance,$card_id='') 更新红包金额 259 | * setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单 260 | 261 | 262 | ## ~~2. wechatext.class.php 非官方扩展API~~ 263 | **此扩展类库已经不再更新,原因是官方对公众号开放了众多接口,此类库继续维护的意义不大** 264 | 非官方扩展API,需要配置公众平台账户和密码,能实现对已关注用户的点对点微信,此方式不保证长期有效。 265 | 类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取. 266 | 调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决. 267 | 程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持. 268 | 269 | ### 类主要方法: 270 | * send($id,$content) 向某用户id发送微信文字信息 271 | * sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid 272 | * getUserList($page,$pagesize,$groupid) 获取用户信息 273 | * getGroupList($page,$pagesize) 获取群组信息 274 | * getNewsList($page,$pagesize) 获取图文信息列表 275 | * uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图 276 | * getFileList($type,$page,$pagesize) 获取素材库文件列表 277 | * sendImage($id,$fid) 发送图片消息 278 | * sendAudio($id,$fid) 发送音频消息 279 | * sendVideo($id,$fid) 发送视频消息 280 | * getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取 281 | * getNewMsgNum($lastid) 获取从$lastid算起新消息的数目 282 | * getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据 283 | * getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数 284 | * 消息返回结构: {"id":"消息id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容"} 285 | * getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据 286 | * getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据 287 | 288 | ## ~~3. wechatauth.class.php 授权登陆~~ 289 | **此扩展类库已经不再更新,原因是官方开放平台对网站应用开放的有授权登陆接口,更标准,更好用。请查看:[微信开放平台](http://open.weixin.qq.com)** 290 | 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子. 291 | ### 类主要方法: 292 | * get_login_code() 获取登陆授权码, 通过授权码才能获取二维码 293 | * get_code_image($code='') 将上面获取的授权码转换为图片二维码 294 | * verify_code() 鉴定是否登陆成功,返回200为最终授权成功. 295 | * get_login_info() 鉴定成功后调用此方法即可获取用户基本信息 296 | * get_avatar($url) 获取用户头像图片数据 297 | * logout() 注销登陆 298 | 299 | ## ~~4. wechat.js 内嵌JS~~ 300 | **此JS脚本已经废弃不再更新,原因是官方在微信6.0.2版本开放了全新的JSAPI接口,更全面好用。请查看:[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)** 301 | ### 微信内嵌网页特殊功能js调用: 302 | * WeixinJS.hideOptionMenu() 隐藏右上角按钮 303 | * WeixinJS.showOptionMenu() 显示右上角按钮 304 | * WeixinJS.hideToolbar() 隐藏工具栏 305 | * WeixinJS.showToolbar() 显示工具栏 306 | * WeixinJS.getNetworkType() 获取网络状态 307 | * WeixinJS.closeWindow() 关闭窗口 308 | * WeixinJS.scanQRCode() 扫描二维码 309 | * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 310 | * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 311 | * WeixinJS.sendEmail(title,content) 发送邮件 312 | * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 313 | * WeixinJS.addContact(username) 添加微信账号 314 | * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 315 | * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 316 | * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 317 | * 通过定义全局变量dataForWeixin配置触发分享的内容: 318 | ```javascript 319 | var dataForWeixin={ 320 | appId:"", 321 | MsgImg:"消息图片路径", 322 | TLImg:"时间线图路径", 323 | url:"分享url路径", 324 | title:"标题", 325 | desc:"描述", 326 | fakeid:"", 327 | callback:function(){} 328 | }; 329 | ``` 330 | 331 | ## 5. errCode.php 全局返回码类 332 | 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 333 | 注意:微信公众号引用`errCode.php`,企业号引用`qyerrCode.php`。 334 | 335 | ### 使用方法: 336 | ```php 337 | include "errCode.php"; //或 qyerrCode.php 338 | 339 | $ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到 340 | 341 | if ($ret) 342 | echo $ret; 343 | else 344 | echo "未找到对应的内容"; 345 | 346 | ``` 347 | 348 | ## 6. qywechat.class.php 企业号API类库 349 | 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 350 | 351 | ### 主要功能 352 | - 接入验证 353 | - 自动回复(文本、图片、语音、视频、音乐、图文) 354 | - 菜单操作(查询、创建、删除) 355 | - 部门管理(创建、更新、删除、获取部门列表) 356 | - 成员管理(创建、更新、删除、获取成员信息,获取部门成员列表) 357 | - 标签管理(创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表) 358 | - 媒体文件管理(上传、获取) 359 | - 二次验证 360 | - OAuth2(生成授权url、获取成员信息) 361 | - 获取企业微信服务器IP列表 362 | - 微信JSAPI授权(获取ticket、获取签名) 363 | 364 | 365 | ### 初始化动作 366 | ```php 367 | $options = array( 368 | 'token'=>'tokenaccesskey', //填写应用接口的Token 369 | 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 370 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 371 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 372 | 'agentid'=>'1', //应用的id 373 | 'debug'=>false, //调试开关 374 | '_logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数 375 | ); 376 | $weObj = new Wechat($options); //创建实例对象 377 | //TODO:调用$weObj各实例方法 378 | 379 | ``` 380 | 381 | ### 被动接口方法: 382 | * valid() 验证连接,被动接口必须调用 383 | * 384 | * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 385 | * getRevData() 返回微信服务器发来的信息(数组) 386 | * getRevPostXml() 返回微信服务器发来的原始加密xml信息 387 | * getRevFrom() 返回消息发送者的userid 388 | * getRevTo() 返回消息接收者的id(即公众号id,一般与等同appid一致) 389 | * getRevAgentID() 返回接收消息的应用id 390 | * getRevType() 返回接收消息的类型 391 | * getRevID() 返回消息id 392 | * getRevCtime() 返回消息发送事件 393 | * getRevContent() 返回消息内容正文(文本型消息) 394 | * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} 395 | * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} 396 | * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} 397 | * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} 398 | * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') 399 | * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 400 | * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 401 | * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} 402 | * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} 403 | * 404 | * text($text) 设置文本型消息,参数:文本内容 405 | * image($mediaid) 设置图片型消息,参数:图片的media_id 406 | * voice($mediaid) 设置语音型消息,参数:语音的media_id 407 | * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 408 | * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 409 | * image($mediaid) 设置图片型消息,参数:图片的media_id 410 | * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) 411 | * reply() 将已经设置好的消息,回复给微信服务器 412 | 413 | ### 预定义常量列表: 414 | ```php 415 | ////消息类型,使用实例调用getRevType()方法取得 416 | const MSGTYPE_TEXT = 'text'; 417 | const MSGTYPE_IMAGE = 'image'; 418 | const MSGTYPE_LOCATION = 'location'; 419 | const MSGTYPE_LINK = 'link'; //暂不支持 420 | const MSGTYPE_EVENT = 'event'; 421 | const MSGTYPE_MUSIC = 'music'; //暂不支持 422 | const MSGTYPE_NEWS = 'news'; 423 | const MSGTYPE_VOICE = 'voice'; 424 | const MSGTYPE_VIDEO = 'video'; 425 | ////事件类型,使用实例调用getRevEvent()方法取得 426 | const EVENT_SUBSCRIBE = 'subscribe'; //订阅 427 | const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 428 | const EVENT_LOCATION = 'LOCATION'; //上报地理位置 429 | const EVENT_ENTER_AGENT = 'enter_agent'; //用户进入应用 430 | const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 431 | const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 432 | const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) 433 | const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) 434 | const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 435 | const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 436 | const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 437 | const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 438 | const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 439 | const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 440 | ``` 441 | 442 | ### 主动接口方法: 443 | * checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token` 444 | * resetAuth($appid='') 清除记录的ACCESS_TOKEN 445 | * resetJsTicket($appid='') 删除JSAPI授权TICKET 446 | * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET 447 | * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 448 | * getSignature($arrdata,'sha1') 生成签名字串 449 | * generateNonceStr($length=16) 获取随机字串 450 | * createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id 451 | * getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id 452 | * deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id 453 | * uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) 454 | * getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id 455 | * getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') 456 | * createDepartment($data) 创建部门,参数: array("name"=>"邮箱产品组","parentid"=>"1","order" => "1") 457 | * updateDepartment($data) 更新部门,参数: array("id"=>"1","name"=>"邮箱产品组","parentid"=>"1","order" => "1") 458 | * deleteDepartment($id) 删除部门,参数:要删除的部门id 459 | * moveDepartment($data) 移动部门,参数:array("department_id" => "5","to_parentid" => "2","to_position" => "1") 460 | * getDepartment() 获取部门列表,返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列 461 | * createUser($data) 创建成员,参数请看php文件内方法说明 462 | * updateUser($data) 更新成员,参数请看php文件内方法说明 463 | * deleteUser($userid) 删除成员,参数:员工UserID 464 | * deleteUsers($userids) 批量删除成员,参数:员工UserID数组 465 | * getUserInfo($userid) 获取成员信息,参数:员工UserID 466 | * getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员,参数:部门id,是否递归获取子部门,获取类型。 467 | > 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 468 | * getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情,参数同上 469 | * getUserId($code,$agentid) 根据code获取员工UserID与手机设备号,参数:Oauth2.0或者二次验证返回的code值,跳转链接时所在的企业应用ID 470 | * sendInvite($userid,$invite_tips='') 邀请成员关注 471 | * createTag($data) 创建标签,参数:array("tagname" => "UI") 472 | * updateTag($data) 更新标签,参数:array("tagid" => "1","tagname" => "UI") 473 | * deleteTag($tagid) 删除标签,参数:标签TagID 474 | * getTag($tagid) 获取标签成员,参数:标签TagID 475 | * addTagUser($data) 增加标签成员,参数请看php文件内方法说明 476 | * delTagUser($data) 删除标签成员,参数请看php文件内方法说明 477 | * getTagList() 获取标签列表,返回标签数组 478 | * sendMessage($data) 主动发送信息接口,参数请看php文件内方法说明 479 | * authSucc($userid) 二次验证,参数: 员工UserID 480 | * getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url 481 | 482 | 483 | ## 7. wechatpay.class.php 旧版微信支付V2接口类库 484 | 旧版微信支付类库(微信支付V2),已移动至old_version目录下。 485 | 自2014年8月开始申请到的微信支付都是V3接口,据官方说V2的会陆续升级为V3接口,但时间及升级渠道未确认。 486 | 487 | ### 主要功能 488 | - 获取access_token **(初级权限)** 489 | - 调用地址组件 **(支付权限)** 490 | - 生成订单签名数据 **(支付权限)** 491 | - 订单成功回调 **(支付权限)** 492 | - 发货通知 **(支付权限)** 493 | - 支付订单查询 **(支付权限)** 494 | > 备注: 495 | > 初级权限:基本权限,任何正常的公众号都有此权限 496 | > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 497 | > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 498 | > 支付权限:仅认证后的服务号可以申请此权限 499 | 500 | 501 | ### 初始化动作 502 | ```php 503 | $options = array( 504 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 505 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 506 | 'partnerid'=>'88888888', //财付通商户身份标识,支付权限专用,没有可不填 507 | 'partnerkey'=>'', //财付通商户权限密钥Key,支付权限专用 508 | 'paysignkey'=>'' //商户签名密钥Key,支付权限专用 509 | ); 510 | $weObj = new Wechat($options); //创建实例对象 511 | //TODO:调用$weObj各实例方法 512 | ``` 513 | 514 | ### 主动接口方法: 515 | * checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取,或手动指定access_token 516 | * resetAuth($appid='') 删除验证数据 517 | * getSignature($arrdata,'sha1') 生成签名字串 518 | * generateNonceStr($length=16) 获取随机字串 519 | * createNativeUrl($productid) 生成原生支付url 520 | * 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字符串 521 | * getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法 522 | * checkOrderSignature($orderxml='') 回调通知签名验证 523 | * sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知 524 | * getPayOrder($out_trade_no) 查询订单信息 525 | * setUserToken($user_token) 设置用户授权密钥 526 | * getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名 527 | 528 | 529 | ## 为开发框架进行适配 530 | 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 531 | 532 | 由于微信api需要缓存access_token与jsapi_ticket,而在不同框架下的缓存方式不同,所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。 533 | 需要各位在使用不同框架时再进行修改,但确实很麻烦,因为对结构进行了修改。 534 | 535 | >取消了原先同步维护的Thinkphp版本,为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`,以及修改`log`方法可以重载。 536 | >分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。 537 | 538 | 在不同的开发框架下使用Wechat类库,请继承Wechat类,根据需要实现这4个方法。 539 | 可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。 540 | 欢迎提交其他框架的适配文件到项目库来。 541 | 542 | 为Thinkphp进行适配的示例如下: 543 | ```php 544 | /** 545 | * 微信公众平台PHP-SDK, ThinkPHP实例 546 | * @author dodgepudding@gmail.com 547 | * @link https://github.com/dodgepudding/wechat-php-sdk 548 | * @version 1.2 549 | * usage: 550 | * $options = array( 551 | * 'token'=>'tokenaccesskey', //填写你设定的key 552 | * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 553 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 554 | * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 555 | * ); 556 | * $weObj = new TPWechat($options); 557 | * $weObj->valid(); 558 | * ... 559 | * 560 | */ 561 | class TPWechat extends Wechat 562 | { 563 | /** 564 | * log overwrite 565 | * @see Wechat::log() 566 | */ 567 | protected function log($log){ 568 | if ($this->debug) { 569 | if (function_exists($this->logcallback)) { 570 | if (is_array($log)) $log = print_r($log,true); 571 | return call_user_func($this->logcallback,$log); 572 | }elseif (class_exists('Log')) { 573 | Log::write('wechat:'.$log, Log::DEBUG); 574 | } 575 | } 576 | return false; 577 | } 578 | 579 | /** 580 | * 重载设置缓存 581 | * @param string $cachename 582 | * @param mixed $value 583 | * @param int $expired 584 | * @return boolean 585 | */ 586 | protected function setCache($cachename,$value,$expired){ 587 | return S($cachename,$value,$expired); 588 | } 589 | 590 | /** 591 | * 重载获取缓存 592 | * @param string $cachename 593 | * @return mixed 594 | */ 595 | protected function getCache($cachename){ 596 | return S($cachename); 597 | } 598 | 599 | /** 600 | * 重载清除缓存 601 | * @param string $cachename 602 | * @return boolean 603 | */ 604 | protected function removeCache($cachename){ 605 | return S($cachename,null); 606 | } 607 | } 608 | ``` 609 | 610 | 611 | 612 | # 调用示例 613 | ---------- 614 | 615 | ## 官方Wechat调用示例: 616 | ```php 617 | //test1.php 618 | include "wechat.class.php"; 619 | $options = array( 620 | 'token'=>'tokenaccesskey', //填写你设定的key 621 | 'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey,如接口为明文模式可忽略 622 | ); 623 | $weObj = new Wechat($options); 624 | $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 625 | $type = $weObj->getRev()->getRevType(); 626 | switch($type) { 627 | case Wechat::MSGTYPE_TEXT: 628 | $weObj->text("hello, I'm wechat")->reply(); 629 | exit; 630 | break; 631 | case Wechat::MSGTYPE_EVENT: 632 | break; 633 | case Wechat::MSGTYPE_IMAGE: 634 | break; 635 | default: 636 | $weObj->text("help info")->reply(); 637 | } 638 | ``` 639 | 640 | ## 企业号API类库调用示例: 641 | 可参考**test**目录下的**qydemo.php** 642 | ```php 643 | include "wechat.class.php"; 644 | $options = array( 645 | 'token'=>'9Ixxxxxxx', //填写应用接口的Token 646 | 'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 647 | 'appid'=>'wxa07979baxxxxxxxx', //填写高级调用功能的appid 648 | ); 649 | $weObj = new Wechat($options); 650 | $weObj->valid(); //注意, 企业号与普通公众号不同,必须打开验证,不要注释掉 651 | $type = $weObj->getRev()->getRevType(); 652 | switch($type) { 653 | case Wechat::MSGTYPE_TEXT: 654 | $weObj->text("hello, I'm wechat")->reply(); 655 | exit; 656 | break; 657 | case Wechat::MSGTYPE_EVENT: 658 | break; 659 | case Wechat::MSGTYPE_IMAGE: 660 | break; 661 | default: 662 | $weObj->text("help info")->reply(); 663 | } 664 | ``` 665 | 666 | ## 扩展包Wechatext调用示例: 667 | ```php 668 | // old_version/test/test2.php 669 | include "wechatext.class.php"; 670 | 671 | function logdebug($text){ 672 | file_put_contents('./data/log.txt',$text."\n",FILE_APPEND); 673 | }; 674 | 675 | $options = array( 676 | 'account'=>'demo@domain.com', 677 | 'password'=>'demo', 678 | 'datapath'=>'./data/cookie_', 679 | 'debug'=>true, 680 | 'logcallback'=>'logdebug' 681 | ); 682 | $wechat = new Wechatext($options); 683 | if ($wechat->checkValid()) { 684 | // 获取用户信息 685 | $data = $wechat->getInfo('3974255'); 686 | var_dump($data); 687 | // 获取最新一条消息 688 | $topmsg = $wechat->getTopMsg(); 689 | var_dump($topmsg); 690 | // 主动回复消息 691 | if ($topmsg && $topmsg['has_reply']==0) 692 | $wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); 693 | } 694 | ``` 695 | 696 | ## 微信二维码Wechatauth登陆示例: 697 | ```php 698 | // old_version/test/test3.php 699 | include "../wechatauth.class.php"; 700 | session_start(); 701 | $sid = session_id(); 702 | $options = array( 703 | 'account'=>$sid, 704 | 'datapath'=>'../data/cookiecode_', 705 | ); 706 | $wechat = new Wechatauth($options); 707 | 708 | if (isset($_POST['code'])) { 709 | $logincode = $_POST['code']; 710 | $vres = $wechat->set_login_code($logincode)->verify_code(); 711 | if ($vres===false) { 712 | $result = array('status'=>0); 713 | } else { 714 | $result = array('status'=>$vres); 715 | if ($vres==200) { 716 | $result['info'] = $wechat->get_login_info(); 717 | $result['cookie'] = $wechat->get_login_cookie(true); 718 | } 719 | } 720 | 721 | die(json_encode($result)); 722 | } 723 | $logincode = $wechat->get_login_code(); //获取授权码 724 | $qrimg = $wechat->get_code_image(); //待输出的二维码图片 725 | ``` 726 | HTML部分请看old_version/test/test3.php, 主要是定时ajax查询是否已经授权成功 727 | 728 | ## 新版微信JSAPI调用DEMO: 729 | 请看test/jsapi目录 730 | 731 | License 732 | ------- 733 | This is licensed under the GNU LGPL, version 2.1 or later. 734 | For details, see: http://creativecommons.org/licenses/LGPL/2.1/ 735 | -------------------------------------------------------------------------------- /Thinkphp/EasyWechat.class.php: -------------------------------------------------------------------------------- 1 | 'tokenaccesskey', //填写你设定的key 10 | * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 11 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 12 | * 'cachedir'=>'./cache/', //填写缓存目录,默认为当前运行目录的子目录cache下 13 | * 'logfile'=>'run.log' //填写日志输出文件,可选项。如果没有提供logcallback回调,且设置了输出文件则将日志输出至此文件,如果省略则不输出 14 | * ); 15 | * $weObj = new EasyWechat($options); 16 | * $weObj->valid(); 17 | * ... 18 | * 19 | */ 20 | class EasyWechat extends Wechat 21 | { 22 | private $cachedir = ''; 23 | private $logfile = ''; 24 | 25 | public function __construct($options) 26 | { 27 | $this->cachedir = isset($options['cachedir']) ? dirname($options['cachedir'].'/.cache') . '/' : 'cache/'; 28 | $this->logfile = isset($options['logfile']) ? $options['logfile'] : ''; 29 | if ($this->cachedir) $this->checkDir($this->cachedir); 30 | parent::__construct($options); 31 | } 32 | 33 | /** 34 | * log overwrite 35 | * @param string|array $log 36 | */ 37 | protected function log($log){ 38 | if (is_array($log)) $log = print_r($log,true); 39 | if ($this->debug) { 40 | if (function_exists($this->logcallback)) { 41 | return call_user_func($this->logcallback,$log); 42 | }elseif ($this->logfile) { 43 | return file_put_contents($this->logfile, $log."\n", FILE_APPEND) > 0 ? true : false; 44 | } 45 | } 46 | return false; 47 | } 48 | 49 | /** 50 | * 重载设置缓存 51 | * @param string $cachename 52 | * @param mixed $value 53 | * @param int $expired 缓存秒数,如果为0则为长期缓存 54 | * @return boolean 55 | */ 56 | protected function setCache($cachename,$value,$expired=0){ 57 | $file = $this->cachedir . $cachename . '.cache'; 58 | $data = array( 59 | 'value' => $value, 60 | 'expired' => $expired ? time() + $expired : 0 61 | ); 62 | return file_put_contents( $file, serialize($data) ) ? true : false; 63 | } 64 | 65 | /** 66 | * 重载获取缓存 67 | * @param string $cachename 68 | * @return mixed 69 | */ 70 | protected function getCache($cachename){ 71 | $file = $this->cachedir . $cachename . '.cache'; 72 | if (!is_file($file)) { 73 | return false; 74 | } 75 | $data = unserialize(file_get_contents( $file )); 76 | if (!is_array($data) || !isset($data['value']) || (!empty($data['value']) && $data['expired']cachedir . $cachename . '.cache'; 90 | if ( is_file($file) ) { 91 | @unlink($file); 92 | } 93 | return true; 94 | } 95 | 96 | private function checkDir($dir, $mode=0777) { 97 | if (!$dir) return false; 98 | if(!is_dir($dir)) { 99 | if (!file_exists($dir) && @mkdir($dir, $mode, true)) 100 | return true; 101 | return false; 102 | } 103 | return true; 104 | } 105 | } 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Thinkphp/TPWechat.class.php: -------------------------------------------------------------------------------- 1 | 'tokenaccesskey', //填写你设定的key 10 | * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 11 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 12 | * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 13 | * ); 14 | * $weObj = new TPWechat($options); 15 | * $weObj->valid(); 16 | * ... 17 | * 18 | */ 19 | class TPWechat extends Wechat 20 | { 21 | /** 22 | * log overwrite 23 | * @see Wechat::log() 24 | */ 25 | protected function log($log){ 26 | if ($this->debug) { 27 | if (function_exists($this->logcallback)) { 28 | if (is_array($log)) $log = print_r($log,true); 29 | return call_user_func($this->logcallback,$log); 30 | }elseif (class_exists('Log')) { 31 | Log::write('wechat:'.$log, Log::DEBUG); 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | /** 39 | * 重载设置缓存 40 | * @param string $cachename 41 | * @param mixed $value 42 | * @param int $expired 43 | * @return boolean 44 | */ 45 | protected function setCache($cachename,$value,$expired){ 46 | return S($cachename,$value,$expired); 47 | } 48 | 49 | /** 50 | * 重载获取缓存 51 | * @param string $cachename 52 | * @return mixed 53 | */ 54 | protected function getCache($cachename){ 55 | return S($cachename); 56 | } 57 | 58 | /** 59 | * 重载清除缓存 60 | * @param string $cachename 61 | * @return boolean 62 | */ 63 | protected function removeCache($cachename){ 64 | return S($cachename,null); 65 | } 66 | 67 | } 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "dodgepudding/wechat-php-sdk", 3 | "description" : "the wechat api library without framework dependency", 4 | "version" : "1.1", 5 | "require" : { 6 | "php" : ">=5.1.0" 7 | }, 8 | "authors" : [ { 9 | "name" : "dodgepudding", 10 | "email" : "dodgepudding@gmail.com" 11 | }, { 12 | "name" : "binsee", 13 | "email" : "binsee@163.com" 14 | } ], 15 | "keywords" : [ "wechat", "weixin", "thinkphp", "sample" ], 16 | "support" : { 17 | "issues" : "https://github.com/dodgepudding/wechat-php-sdk/issues", 18 | "wiki" : "https://github.com/dodgepudding/wechat-php-sdk/wiki", 19 | "source" : "https://github.com/dodgepudding/wechat-php-sdk" 20 | }, 21 | "autoload" : { 22 | "psr-4" : { 23 | "dodgepudding\\wechat\\sdk\\" : "" 24 | } 25 | }, 26 | "type" : "library", 27 | "license" : "LGPL" 28 | } -------------------------------------------------------------------------------- /demo.php: -------------------------------------------------------------------------------- 1 | 'tokenaccesskey', //填写你设定的key 5 | 'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey,如接口为明文模式可忽略 6 | ); 7 | $weObj = new Wechat($options); 8 | $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 9 | $type = $weObj->getRev()->getRevType(); 10 | switch($type) { 11 | case Wechat::MSGTYPE_TEXT: 12 | $weObj->text("hello, I'm wechat")->reply(); 13 | exit; 14 | break; 15 | case Wechat::MSGTYPE_EVENT: 16 | break; 17 | case Wechat::MSGTYPE_IMAGE: 18 | break; 19 | default: 20 | $weObj->text("help info")->reply(); 21 | } -------------------------------------------------------------------------------- /errCode.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/binsee/wechat-php-sdk 6 | * @version 1.0 7 | * usage: 8 | * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 9 | * if ($ret) 10 | * echo $ret; 11 | * else 12 | * echo "未找到对应的内容"; 13 | */ 14 | class ErrCode 15 | { 16 | public static $errCode=array( 17 | '-1'=>'系统繁忙', 18 | '0'=>'请求成功', 19 | '40001'=>'获取access_token时AppSecret错误,或者access_token无效', 20 | '40002'=>'不合法的凭证类型', 21 | '40003'=>'不合法的OpenID', 22 | '40004'=>'不合法的媒体文件类型', 23 | '40005'=>'不合法的文件类型', 24 | '40006'=>'不合法的文件大小', 25 | '40007'=>'不合法的媒体文件id', 26 | '40008'=>'不合法的消息类型', 27 | '40009'=>'不合法的图片文件大小', 28 | '40010'=>'不合法的语音文件大小', 29 | '40011'=>'不合法的视频文件大小', 30 | '40012'=>'不合法的缩略图文件大小', 31 | '40013'=>'不合法的APPID', 32 | '40014'=>'不合法的access_token', 33 | '40015'=>'不合法的菜单类型', 34 | '40016'=>'不合法的按钮个数', 35 | '40017'=>'不合法的按钮类型', 36 | '40018'=>'不合法的按钮名字长度', 37 | '40019'=>'不合法的按钮KEY长度', 38 | '40020'=>'不合法的按钮URL长度', 39 | '40021'=>'不合法的菜单版本号', 40 | '40022'=>'不合法的子菜单级数', 41 | '40023'=>'不合法的子菜单按钮个数', 42 | '40024'=>'不合法的子菜单按钮类型', 43 | '40025'=>'不合法的子菜单按钮名字长度', 44 | '40026'=>'不合法的子菜单按钮KEY长度', 45 | '40027'=>'不合法的子菜单按钮URL长度', 46 | '40028'=>'不合法的自定义菜单使用用户', 47 | '40029'=>'不合法的oauth_code', 48 | '40030'=>'不合法的refresh_token', 49 | '40031'=>'不合法的openid列表', 50 | '40032'=>'不合法的openid列表长度', 51 | '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', 52 | '40035'=>'不合法的参数', 53 | '40038'=>'不合法的请求格式', 54 | '40039'=>'不合法的URL长度', 55 | '40050'=>'不合法的分组id', 56 | '40051'=>'分组名字不合法', 57 | '40099'=>'该 code 已被核销', 58 | '41001'=>'缺少access_token参数', 59 | '41002'=>'缺少appid参数', 60 | '41003'=>'缺少refresh_token参数', 61 | '41004'=>'缺少secret参数', 62 | '41005'=>'缺少多媒体文件数据', 63 | '41006'=>'缺少media_id参数', 64 | '41007'=>'缺少子菜单数据', 65 | '41008'=>'缺少oauth code', 66 | '41009'=>'缺少openid', 67 | '42001'=>'access_token超时', 68 | '42002'=>'refresh_token超时', 69 | '42003'=>'oauth_code超时', 70 | '42005'=>'调用接口频率超过上限', 71 | '43001'=>'需要GET请求', 72 | '43002'=>'需要POST请求', 73 | '43003'=>'需要HTTPS请求', 74 | '43004'=>'需要接收者关注', 75 | '43005'=>'需要好友关系', 76 | '44001'=>'多媒体文件为空', 77 | '44002'=>'POST的数据包为空', 78 | '44003'=>'图文消息内容为空', 79 | '44004'=>'文本消息内容为空', 80 | '45001'=>'多媒体文件大小超过限制', 81 | '45002'=>'消息内容超过限制', 82 | '45003'=>'标题字段超过限制', 83 | '45004'=>'描述字段超过限制', 84 | '45005'=>'链接字段超过限制', 85 | '45006'=>'图片链接字段超过限制', 86 | '45007'=>'语音播放时间超过限制', 87 | '45008'=>'图文消息超过限制', 88 | '45009'=>'接口调用超过限制', 89 | '45010'=>'创建菜单个数超过限制', 90 | '45015'=>'回复时间超过限制', 91 | '45016'=>'系统分组,不允许修改', 92 | '45017'=>'分组名字过长', 93 | '45018'=>'分组数量超过上限', 94 | '45024'=>'账号数量超过上限', 95 | '46001'=>'不存在媒体数据', 96 | '46002'=>'不存在的菜单版本', 97 | '46003'=>'不存在的菜单数据', 98 | '46004'=>'不存在的用户', 99 | '47001'=>'解析JSON/XML内容错误', 100 | '48001'=>'api功能未授权', 101 | '50001'=>'用户未授权该api', 102 | '61450'=>'系统错误', 103 | '61451'=>'参数错误', 104 | '61452'=>'无效客服账号', 105 | '61453'=>'账号已存在', 106 | '61454'=>'客服帐号名长度超过限制(仅允许10个英文字符,不包括@及@后的公众号的微信号)', 107 | '61455'=>'客服账号名包含非法字符(英文+数字)', 108 | '61456'=>'客服账号个数超过限制(10个客服账号)', 109 | '61457'=>'无效头像文件类型', 110 | '61458'=>'客户正在被其他客服接待', 111 | '61459'=>'客服不在线', 112 | '61500'=>'日期格式错误', 113 | '61501'=>'日期范围错误', 114 | '7000000'=>'请求正常,无语义结果', 115 | '7000001'=>'缺失请求参数', 116 | '7000002'=>'signature 参数无效', 117 | '7000003'=>'地理位置相关配置 1 无效', 118 | '7000004'=>'地理位置相关配置 2 无效', 119 | '7000005'=>'请求地理位置信息失败', 120 | '7000006'=>'地理位置结果解析失败', 121 | '7000007'=>'内部初始化失败', 122 | '7000008'=>'非法 appid(获取密钥失败)', 123 | '7000009'=>'请求语义服务失败', 124 | '7000010'=>'非法 post 请求', 125 | '7000011'=>'post 请求 json 字段无效', 126 | '7000030'=>'查询 query 太短', 127 | '7000031'=>'查询 query 太长', 128 | '7000032'=>'城市、经纬度信息缺失', 129 | '7000033'=>'query 请求语义处理失败', 130 | '7000034'=>'获取天气信息失败', 131 | '7000035'=>'获取股票信息失败', 132 | '7000036'=>'utf8 编码转换失败', 133 | ); 134 | 135 | public static function getErrText($err) { 136 | if (isset(self::$errCode[$err])) { 137 | return self::$errCode[$err]; 138 | }else { 139 | return false; 140 | }; 141 | } 142 | } 143 | 144 | ?> -------------------------------------------------------------------------------- /old_version/Thinkphp/Wechatauth.class.php: -------------------------------------------------------------------------------- 1 | 13 | * @link https://github.com/dodgepudding/wechat-php-sdk 14 | * @version 1.1 15 | * 16 | */ 17 | include "Snoopy.class.php"; 18 | class Wechatauth 19 | { 20 | private $cookie; 21 | private $skey; 22 | private $_cookiename; 23 | private $_cookieexpired = 3600; 24 | private $_account = 'test'; 25 | private $_datapath = './data/cookie_'; 26 | private $debug; 27 | private $_logcallback; 28 | public $login_user; //当前登陆用户, 调用get_login_info后获取 29 | 30 | public function __construct($options) 31 | { 32 | $this->_account = isset($options['account'])?$options['account']:''; 33 | $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; 34 | $this->debug = isset($options['debug'])?$options['debug']:false; 35 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 36 | $this->_cookiename = $this->_datapath.$this->_account; 37 | $this->getCookie($this->_cookiename); 38 | } 39 | /** 40 | * 把cookie写入缓存 41 | * @param string $filename 缓存文件名 42 | * @param string $content 文件内容 43 | * @return bool 44 | */ 45 | public function saveCookie($filename,$content){ 46 | return S($filename,$content,$this->_cookieexpired); 47 | } 48 | 49 | /** 50 | * 读取cookie缓存内容 51 | * @param string $filename 缓存文件名 52 | * @return string cookie 53 | */ 54 | public function getCookie($filename){ 55 | $data = S($filename); 56 | if ($data) $this->cookie = $data; 57 | return $this->cookie; 58 | } 59 | 60 | /* 61 | * 删除cookie 62 | */ 63 | public function deleteCookie($filename) { 64 | $this->cookie = ''; 65 | S($filename,null); 66 | return true; 67 | } 68 | 69 | private function log($log){ 70 | if ($this->debug && function_exists($this->_logcallback)) { 71 | if (is_array($log)) $log = print_r($log,true); 72 | return call_user_func($this->_logcallback,$log); 73 | } 74 | } 75 | 76 | /** 77 | * 获取登陆二维码对应的授权码 78 | */ 79 | public function get_login_code(){ 80 | if ($this->_logincode) return $this->_logincode; 81 | $t = time().strval(mt_rand(100,999)); 82 | $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; 83 | $send_snoopy = new Snoopy; 84 | $send_snoopy->fetch($codeurl); 85 | $result = $send_snoopy->results; 86 | if ($result) { 87 | preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches); 88 | if(count($matches)>1) { 89 | $this->_logincode = $matches[1]; 90 | $_SESSION['login_step'] = 0; 91 | return $this->_logincode; 92 | } 93 | } 94 | return $result; 95 | } 96 | 97 | /** 98 | * 通过授权码获取对应的二维码图片地址 99 | * @param string $code 100 | * @return string image url 101 | */ 102 | public function get_code_image($code=''){ 103 | if ($code=='') $code = $this->_logincode; 104 | if (!$code) return false; 105 | return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx'; 106 | } 107 | 108 | /** 109 | * 设置二维码对应的授权码 110 | * @param string $code 111 | * @return class $this 112 | */ 113 | public function set_login_code($code) { 114 | $this->_logincode = $code; 115 | return $this; 116 | } 117 | 118 | /** 119 | * 二维码登陆验证 120 | * 121 | * @return status: 122 | * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired 123 | * 201: just scaned but not confirm 124 | * 200: confirm then you can get user info 125 | */ 126 | public function verify_code() { 127 | if (!$this->_logincode) return false; 128 | $t = time().strval(mt_rand(100,999)); 129 | 130 | $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t; 131 | $send_snoopy = new Snoopy; 132 | $send_snoopy->referer = "https://wx.qq.com/"; 133 | $send_snoopy->fetch($url); 134 | $result = $send_snoopy->results; 135 | $this->log('step1:'.$result); 136 | if ($result) { 137 | preg_match("/window\.code=(\d+)/",$result,$matches); 138 | if(count($matches)>1) { 139 | $status = intval($matches[1]); 140 | if ($status==201) $_SESSION['login_step'] = 1; 141 | if ($status==200) { 142 | preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches); 143 | preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl); 144 | $this->log('step2:'.print_r($matches,true)); 145 | if (count($matcheurl)>1) { 146 | $ticket = $matches[1]; 147 | $scan = $matches[2]; 148 | //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new'; 149 | $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old'; 150 | $urlpart = parse_url($loginurl); 151 | $send_snoopy = new Snoopy; 152 | $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat"; 153 | $send_snoopy->fetch($loginurl); 154 | $result = $send_snoopy->results; 155 | $xml = simplexml_load_string($result); 156 | if ($xml->ret=="0") $this->skey = $xml->skey; 157 | foreach ($send_snoopy->headers as $key => $value) { 158 | $value = trim($value); 159 | if(strpos($value,'Set-Cookie: ') !== false){ 160 | $tmp = str_replace("Set-Cookie: ","",$value); 161 | $tmparray = explode(';', $tmp); 162 | $item = trim($tmparray[0]); 163 | $cookie.=$item.';'; 164 | } 165 | } 166 | $cookie .="Domain=.qq.com;"; 167 | $this->cookie = $cookie; 168 | $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result); 169 | 170 | $this->saveCookie($this->_cookiename,$this->cookie); 171 | } 172 | } 173 | return $status; 174 | } 175 | } 176 | return false; 177 | } 178 | 179 | /** 180 | * 获取登陆的cookie 181 | * 182 | * @param bool $is_array 是否以数值方式返回,默认否,返回字符串 183 | * @return string|array 184 | */ 185 | public function get_login_cookie($is_array = false){ 186 | if (!$is_array) return $this->cookie; 187 | $c_arr = explode(';',$this->cookie); 188 | $cookie = array(); 189 | foreach($c_arr as $item) { 190 | $kitem = explode('=',trim($item)); 191 | if (count($kitem)>1) { 192 | $key = trim($kitem[0]); 193 | $val = trim($kitem[1]); 194 | if (!empty($val)) $cookie[$key] = $val; 195 | } 196 | } 197 | return $cookie; 198 | } 199 | 200 | /** 201 | * 授权登陆后获取用户登陆信息 202 | */ 203 | public function get_login_info(){ 204 | if (!$this->cookie) return false; 205 | $t = time().strval(mt_rand(100,999)); 206 | $send_snoopy = new Snoopy; 207 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey); 208 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 209 | $send_snoopy->referer = "https://wx2.qq.com/"; 210 | $citems = $this->get_login_cookie(true); 211 | $post = array( 212 | "BaseRequest"=>array( 213 | array( 214 | "Uin"=>$citems['wxuin'], 215 | "Sid"=>$citems['wxsid'], 216 | "Skey"=>$this->skey, 217 | "DeviceID"=>'' 218 | ) 219 | ) 220 | ); 221 | $send_snoopy->submit($submit,json_encode($post)); 222 | $this->log('login_info:'.$send_snoopy->results); 223 | $result = json_decode($send_snoopy->results,true); 224 | if ($result['BaseResponse']['Ret']<0) return false; 225 | $this->_login_user = $result['User']; 226 | return $result; 227 | } 228 | 229 | /** 230 | * 获取头像 231 | * @param string $url 传入从用户信息接口获取到的头像地址 232 | */ 233 | public function get_avatar($url) { 234 | if (!$this->cookie) return false; 235 | if (strpos($url, 'http')===false) { 236 | $url = 'http://wx2.qq.com'.$url; 237 | } 238 | $send_snoopy = new Snoopy; 239 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 240 | $send_snoopy->referer = "https://wx2.qq.com/"; 241 | $send_snoopy->fetch($url); 242 | $result = $send_snoopy->results; 243 | if ($result) 244 | return $result; 245 | else 246 | return false; 247 | } 248 | 249 | /** 250 | * 登出当前登陆用户 251 | */ 252 | public function logout(){ 253 | if (!$this->cookie) return false; 254 | preg_match("/wxuin=(\w+);/",$this->cookie,$matches); 255 | if (count($matches)>1) $uid = $matches[1]; 256 | preg_match("/wxsid=(\w+);/",$this->cookie,$matches); 257 | if (count($matches)>1) $sid = $matches[1]; 258 | $this->log('logout: uid='.$uid.';sid='.$sid); 259 | $send_snoopy = new Snoopy; 260 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1'; 261 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 262 | $send_snoopy->referer = "https://wx2.qq.com/"; 263 | $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid)); 264 | $this->deleteCookie($this->_cookiename); 265 | return true; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /old_version/Thinkphp/Wechatext.class.php: -------------------------------------------------------------------------------- 1 | 27 | * @link https://github.com/dodgepudding/wechat-php-sdk 28 | * @version 1.2 29 | * 30 | */ 31 | 32 | include "Snoopy.class.php"; 33 | class Wechatext 34 | { 35 | private $cookie; 36 | private $_cookiename; 37 | private $_cookieexpired = 3600; 38 | private $_account; 39 | private $_password; 40 | private $_datapath = './data/cookie_'; 41 | private $debug; 42 | private $_logcallback; 43 | private $_token; 44 | 45 | public function __construct($options) 46 | { 47 | $this->_account = isset($options['account'])?$options['account']:''; 48 | $this->_password = isset($options['password'])?$options['password']:''; 49 | $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; 50 | $this->debug = isset($options['debug'])?$options['debug']:false; 51 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 52 | $this->_cookiename = $this->_datapath.$this->_account; 53 | $this->cookie = $this->getCookie($this->_cookiename); 54 | } 55 | 56 | /** 57 | * 主动发消息 58 | * @param string $id 用户的uid(即FakeId) 59 | * @param string $content 发送的内容 60 | */ 61 | public function send($id,$content) 62 | { 63 | $send_snoopy = new Snoopy; 64 | $post = array(); 65 | $post['tofakeid'] = $id; 66 | $post['type'] = 1; 67 | $post['token'] = $this->_token; 68 | $post['content'] = $content; 69 | $post['ajax'] = 1; 70 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN"; 71 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 72 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 73 | $send_snoopy->submit($submit,$post); 74 | $this->log($send_snoopy->results); 75 | return $send_snoopy->results; 76 | } 77 | 78 | /** 79 | * 群发功能 纯文本 80 | * @param string $content 81 | * @return string 82 | */ 83 | public function mass($content) { 84 | $send_snoopy = new Snoopy; 85 | $post = array(); 86 | $post['type'] = 1; 87 | $post['token'] = $this->_token; 88 | $post['content'] = $content; 89 | $post['ajax'] = 1; 90 | $post['city']=''; 91 | $post['country']=''; 92 | $post['f']='json'; 93 | $post['groupid']='-1'; 94 | $post['imgcode']=''; 95 | $post['lang']='zh_CN'; 96 | $post['province']=''; 97 | $post['random']= rand(0, 1); 98 | $post['sex']=0; 99 | $post['synctxnews']=0; 100 | $post['synctxweibo']=0; 101 | $post['t']='ajax-response'; 102 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; 103 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 104 | $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; 105 | $send_snoopy->submit($submit,$post); 106 | $this->log($send_snoopy->results); 107 | return $send_snoopy->results; 108 | } 109 | 110 | /** 111 | * 群发功能 图文素材 112 | * @param int $appmsgid 图文素材ID 113 | * @return string 114 | */ 115 | function massNews($appmsgid){ 116 | $send_snoopy = new Snoopy; 117 | $post = array(); 118 | $post['type'] = 10; 119 | $post['token'] = $this->_token; 120 | $post['appmsgid'] = $appmsgid; 121 | $post['ajax'] = 1; 122 | $post['city']=''; 123 | $post['country']=''; 124 | $post['f']='json'; 125 | $post['groupid']='-1'; 126 | $post['imgcode']=''; 127 | $post['lang']='zh_CN'; 128 | $post['province']=''; 129 | $post['random']= rand(0, 1); 130 | $post['sex']=0; 131 | $post['synctxnews']=0; 132 | $post['synctxweibo']=0; 133 | $post['t']='ajax-response'; 134 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN"; 135 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 136 | $submit = "https://mp.weixin.qq.com/cgi-bin/masssend"; 137 | $send_snoopy->submit($submit,$post); 138 | $this->log($send_snoopy->results); 139 | return $send_snoopy->results; 140 | } 141 | 142 | /** 143 | * 获取用户列表列表 144 | * @param $page 页码(从0开始) 145 | * @param $pagesize 每页大小 146 | * @param $groupid 分组id 147 | * @return array ({contacts:[{id:12345667,nick_name:"昵称",remark_name:"备注名",group_id:0},{}....]}) 148 | */ 149 | function getUserList($page=0,$pagesize=10,$groupid=0){ 150 | $send_snoopy = new Snoopy; 151 | $t = time().strval(mt_rand(100,999)); 152 | $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; 153 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 154 | $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; 155 | $send_snoopy->fetch($submit); 156 | $result = $send_snoopy->results; 157 | $this->log('userlist:'.$result); 158 | $json = json_decode($result,true); 159 | if (isset($json['contact_list'])) { 160 | $json = json_decode($json['contact_list'],true); 161 | if (isset($json['contacts'])) 162 | return $json['contacts']; 163 | } 164 | return false; 165 | } 166 | 167 | /** 168 | * 获取分组列表 169 | * 170 | */ 171 | function getGroupList(){ 172 | $send_snoopy = new Snoopy; 173 | $t = time().strval(mt_rand(100,999)); 174 | $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; 175 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 176 | $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; 177 | $send_snoopy->fetch($submit); 178 | $result = $send_snoopy->results; 179 | $this->log('userlist:'.$result); 180 | $json = json_decode($result,true); 181 | if (isset($json['group_list'])){ 182 | $json = json_decode($json['group_list'],true); 183 | if (isset($json['groups'])) 184 | return $json['groups']; 185 | } 186 | return false; 187 | } 188 | 189 | /** 190 | * 获取图文信息列表 191 | * @param $page 页码(从0开始) 192 | * @param $pagesize 每页大小 193 | * @return array 194 | */ 195 | public function getNewsList($page,$pagesize=10) { 196 | $send_snoopy = new Snoopy; 197 | $t = time().strval(mt_rand(100,999)); 198 | $type=10; 199 | $begin = $page*$pagesize; 200 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 201 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 202 | $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; 203 | $send_snoopy->fetch($submit); 204 | $result = $send_snoopy->results; 205 | $this->log('newslist:'.$result); 206 | $json = json_decode($result,true); 207 | if (isset($json['app_msg_info'])) { 208 | return $json['app_msg_info']; 209 | } 210 | return false; 211 | } 212 | 213 | /** 214 | * 获取与指定用户的对话内容 215 | * @param $fakeid 216 | * @return array 217 | */ 218 | public function getDialogMsg($fakeid) { 219 | $send_snoopy = new Snoopy; 220 | $t = time().strval(mt_rand(100,999)); 221 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 222 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 223 | $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; 224 | $send_snoopy->fetch($submit); 225 | $result = $send_snoopy->results; 226 | $this->log('DialogMsg:'.$result); 227 | $json = json_decode($result,true); 228 | if (isset($json['page_info'])) { 229 | return $json['page_info']; 230 | } 231 | return false; 232 | } 233 | 234 | /** 235 | * 发送图文信息,必须从图文库里选取消息ID发送 236 | * @param string $id 用户的uid(即FakeId) 237 | * @param string $msgid 图文消息id 238 | */ 239 | public function sendNews($id,$msgid) 240 | { 241 | $send_snoopy = new Snoopy; 242 | $post = array(); 243 | $post['tofakeid'] = $id; 244 | $post['type'] = 10; 245 | $post['token'] = $this->_token; 246 | $post['fid'] = $msgid; 247 | $post['appmsgid'] = $msgid; 248 | $post['error'] = 'false'; 249 | $post['ajax'] = 1; 250 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; 251 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 252 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 253 | $send_snoopy->submit($submit,$post); 254 | $this->log($send_snoopy->results); 255 | return $send_snoopy->results; 256 | } 257 | 258 | /** 259 | * 上传附件(图片/音频/视频) 260 | * @param string $filepath 本地文件地址 261 | * @param int $type 文件类型: 2:图片 3:音频 4:视频 262 | */ 263 | public function uploadFile($filepath,$type=2) { 264 | $send_snoopy = new Snoopy; 265 | $send_snoopy->referer = "http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1"; 266 | $t = time().strval(mt_rand(100,999)); 267 | $post = array('formId'=>''); 268 | $postfile = array('uploadfile'=>$filepath); 269 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 270 | $send_snoopy->set_submit_multipart(); 271 | $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; 272 | $send_snoopy->submit($submit,$post,$postfile); 273 | $tmp = $send_snoopy->results; 274 | $this->log('upload:'.$tmp); 275 | preg_match("/formId,.*?\'(\d+)\'/",$tmp,$matches); 276 | if (isset($matches[1])) { 277 | return $matches[1]; 278 | } 279 | return false; 280 | } 281 | 282 | /** 283 | * 创建图文消息 284 | * @param array $title 标题 285 | * @param array $summary 摘要 286 | * @param array $content 内容 287 | * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取) 288 | * @param array $srcurl 原文链接 289 | * @return json 290 | */ 291 | public function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') { 292 | $send_snoopy = new Snoopy; 293 | $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; 294 | 295 | $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=".$this->_token; 296 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 297 | 298 | $send_snoopy->set_submit_normal(); 299 | $post = array( 300 | 'token'=>$this->_token, 301 | 'type'=>10, 302 | 'lang'=>'zh_CN', 303 | 'sub'=>'create', 304 | 'ajax'=>1, 305 | 'AppMsgId'=>'', 306 | 'error'=>'false', 307 | ); 308 | if (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid)) 309 | { 310 | $i = 0; 311 | foreach($title as $v) { 312 | $post['title'.$i] = $title[$i]; 313 | $post['author'.$i] = $author[$i]; 314 | $post['digest'.$i] = $summary[$i]; 315 | $post['content'.$i] = $content[$i]; 316 | $post['fileid'.$i] = $photoid[$i]; 317 | if ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i]; 318 | 319 | $i++; 320 | } 321 | } 322 | $post['count'] = $i; 323 | $post['token'] = $this->_token; 324 | $send_snoopy->submit($submit,$post); 325 | $tmp = $send_snoopy->results; 326 | $this->log('step2:'.$tmp); 327 | $json = json_decode($tmp,true); 328 | return $json; 329 | } 330 | 331 | /** 332 | * 发送媒体文件 333 | * @param $id 用户的uid(即FakeId) 334 | * @param $fid 文件id 335 | * @param $type 文件类型 336 | */ 337 | public function sendFile($id,$fid,$type) { 338 | $send_snoopy = new Snoopy; 339 | $post = array(); 340 | $post['tofakeid'] = $id; 341 | $post['type'] = $type; 342 | $post['token'] = $this->_token; 343 | $post['fid'] = $fid; 344 | $post['fileid'] = $fid; 345 | $post['error'] = 'false'; 346 | $post['ajax'] = 1; 347 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN"; 348 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 349 | $submit = "https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 350 | $send_snoopy->submit($submit,$post); 351 | $result = $send_snoopy->results; 352 | $this->log('sendfile:'.$result); 353 | $json = json_decode($result,true); 354 | if ($json && $json['ret']==0) 355 | return true; 356 | else 357 | return false; 358 | } 359 | 360 | /** 361 | * 获取素材库文件列表 362 | * @param $type 文件类型: 2:图片 3:音频 4:视频 363 | * @param $page 页码(从0开始) 364 | * @param $pagesize 每页大小 365 | * @return array 366 | */ 367 | public function getFileList($type,$page,$pagesize=10) { 368 | $send_snoopy = new Snoopy; 369 | $t = time().strval(mt_rand(100,999)); 370 | $begin = $page*$pagesize; 371 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=".$this->_token."&lang=zh_CN"; 372 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 373 | $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"; 374 | $send_snoopy->fetch($submit); 375 | $result = $send_snoopy->results; 376 | $this->log('filelist:'.$result); 377 | $json = json_decode($result,true); 378 | if (isset($json['page_info'])) 379 | return $json['page_info']; 380 | else 381 | return false; 382 | } 383 | 384 | /** 385 | * 发送图文信息,必须从库里选取文件ID发送 386 | * @param string $id 用户的uid(即FakeId) 387 | * @param string $fid 文件id 388 | */ 389 | public function sendImage($id,$fid) 390 | { 391 | return $this->sendFile($id,$fid,2); 392 | } 393 | 394 | /** 395 | * 发送语音信息,必须从库里选取文件ID发送 396 | * @param string $id 用户的uid(即FakeId) 397 | * @param string $fid 语音文件id 398 | */ 399 | public function sendAudio($id,$fid) 400 | { 401 | return $this->sendFile($id,$fid,3); 402 | } 403 | 404 | /** 405 | * 发送视频信息,必须从库里选取文件ID发送 406 | * @param string $id 用户的uid(即FakeId) 407 | * @param string $fid 视频文件id 408 | */ 409 | public function sendVideo($id,$fid) 410 | { 411 | return $this->sendFile($id,$fid,4); 412 | } 413 | 414 | /** 415 | * 发送预览图文消息 416 | * @param string $account 账户名称(user_name) 417 | * @param string $title 标题 418 | * @param string $summary 摘要 419 | * @param string $content 内容 420 | * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取) 421 | * @param string $srcurl 原文链接 422 | * @return json 423 | */ 424 | public function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') { 425 | $send_snoopy = new Snoopy; 426 | $submit = "https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview"; 427 | $send_snoopy->set_submit_normal(); 428 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 429 | $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'; 430 | $post = array( 431 | 'AppMsgId'=>'', 432 | 'ajax'=>1, 433 | 'content0'=>$content, 434 | 'count'=>1, 435 | 'digest0'=>$summary, 436 | 'error'=>'false', 437 | 'fileid0'=>$photoid, 438 | 'preusername'=>$account, 439 | 'sourceurl0'=>$srcurl, 440 | 'title0'=>$title, 441 | ); 442 | $post['token'] = $this->_token; 443 | $send_snoopy->submit($submit,$post); 444 | $tmp = $send_snoopy->results; 445 | $this->log('sendpreview:'.$tmp); 446 | $json = json_decode($tmp,true); 447 | return $json; 448 | } 449 | 450 | /** 451 | * 获取用户的信息 452 | * @param string $id 用户的uid(即FakeId) 453 | * @return array {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]} 454 | */ 455 | public function getInfo($id) 456 | { 457 | $send_snoopy = new Snoopy; 458 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 459 | $t = time().strval(mt_rand(100,999)); 460 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 461 | $submit = "https://mp.weixin.qq.com/cgi-bin/getcontactinfo"; 462 | $post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id); 463 | $send_snoopy->submit($submit,$post); 464 | $this->log($send_snoopy->results); 465 | $result = json_decode($send_snoopy->results,true); 466 | if(isset($result['contact_info'])){ 467 | return $result['contact_info']; 468 | } 469 | return false; 470 | } 471 | 472 | /** 473 | * 获得头像数据 474 | * 475 | * @param FakeId $fakeid 476 | * @return JPG二进制数据 477 | */ 478 | public function getHeadImg($fakeid){ 479 | $send_snoopy = new Snoopy; 480 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 481 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 482 | $url = "https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=".$this->_token."&lang=zh_CN"; 483 | $send_snoopy->fetch($url); 484 | $result = $send_snoopy->results; 485 | $this->log('Head image:'.$fakeid.'; length:'.strlen($result)); 486 | if(!$result){ 487 | return false; 488 | } 489 | return $result; 490 | } 491 | 492 | /** 493 | * 获取消息更新数目 494 | * @param int $lastid 最近获取的消息ID,为0时获取总消息数目 495 | * @return int 数目 496 | */ 497 | public function getNewMsgNum($lastid=0){ 498 | $send_snoopy = new Snoopy; 499 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 500 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 501 | $submit = "https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=".$lastid; 502 | $post = array('ajax'=>1,'token'=>$this->_token); 503 | $send_snoopy->submit($submit,$post); 504 | $this->log($send_snoopy->results); 505 | $result = json_decode($send_snoopy->results,1); 506 | if(!$result){ 507 | return false; 508 | } 509 | return intval($result['newTotalMsgCount']); 510 | } 511 | 512 | /** 513 | * 获取最新一条消息 514 | * @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"} 515 | */ 516 | public function getTopMsg(){ 517 | $send_snoopy = new Snoopy; 518 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 519 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=".$this->_token; 520 | $submit = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=".$this->_token; 521 | $send_snoopy->fetch($submit); 522 | $this->log($send_snoopy->results); 523 | $result = $send_snoopy->results; 524 | $json = json_decode($result,true); 525 | if (isset($json['msg_items'])) { 526 | $json = json_decode($json['msg_items'],true); 527 | if(isset($json['msg_item'])) 528 | return array_shift($json['msg_item']); 529 | } 530 | return false; 531 | } 532 | 533 | /** 534 | * 获取新消息 535 | * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取 536 | * @param $offset lastid起算第一条的偏移量 537 | * @param $perpage 每页获取多少条 538 | * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内) 539 | * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0 540 | * @param $star 是否星标组信息 541 | * @return array[] 同getTopMsg()返回的字段结构相同 542 | */ 543 | public function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){ 544 | $send_snoopy = new Snoopy; 545 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 546 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=".$this->_token; 547 | $lastid = $lastid===0 ? '':$lastid; 548 | $addstar = $star?'&action=star':''; 549 | $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; 550 | $send_snoopy->fetch($submit); 551 | $this->log($send_snoopy->results); 552 | $result = $send_snoopy->results; 553 | $json = json_decode($result,true); 554 | if (isset($json['msg_items'])) { 555 | $json = json_decode($json['msg_items'],true); 556 | if(isset($json['msg_item'])) 557 | return $json['msg_item']; 558 | } 559 | return false; 560 | } 561 | 562 | /** 563 | * 获取图片消息 564 | * @param int $msgid 消息id 565 | * @param string $mode 图片尺寸(large/small) 566 | * @return jpg二进制文件 567 | */ 568 | public function getMsgImage($msgid,$mode='large'){ 569 | $send_snoopy = new Snoopy; 570 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 571 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 572 | $url = "https://mp.weixin.qq.com/cgi-bin/getimgdata?token=".$this->_token."&msgid=$msgid&mode=$mode&source=&fileId=0"; 573 | $send_snoopy->fetch($url); 574 | $result = $send_snoopy->results; 575 | $this->log('msg image:'.$msgid.';length:'.strlen($result)); 576 | if(!$result){ 577 | return false; 578 | } 579 | return $result; 580 | } 581 | 582 | /** 583 | * 获取语音消息 584 | * @param int $msgid 消息id 585 | * @return mp3二进制文件 586 | */ 587 | public function getMsgVoice($msgid){ 588 | $send_snoopy = new Snoopy; 589 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 590 | $send_snoopy->referer = "https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=".$this->_token; 591 | $url = "https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=".$this->_token."&msgid=$msgid&fileId=0"; 592 | $send_snoopy->fetch($url); 593 | $result = $send_snoopy->results; 594 | $this->log('msg voice:'.$msgid.';length:'.strlen($result)); 595 | if(!$result){ 596 | return false; 597 | } 598 | return $result; 599 | } 600 | 601 | /** 602 | * 开启开发者模式 603 | */ 604 | public function openDevModel() 605 | { 606 | $send_snoopy = new Snoopy; 607 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 608 | $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; 609 | $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; 610 | $post['flag']=1; 611 | $post['type']=2; 612 | $post['token']=$this->_token; 613 | $send_snoopy->submit($submit,$post); 614 | $result = $send_snoopy->results; 615 | $this->log($send_snoopy->results); 616 | $json = json_decode($result,true); 617 | if(!$result){ 618 | return false; 619 | } 620 | return true; 621 | } 622 | 623 | /** 624 | * 关闭编辑模式 625 | */ 626 | public function closeEditModel() 627 | { 628 | $send_snoopy = new Snoopy; 629 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 630 | $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=".$this->_token; 631 | $submit = "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN"; 632 | $post['flag']=0; 633 | $post['type']=1; 634 | $post['token']=$this->_token; 635 | $send_snoopy->submit($submit,$post); 636 | $result = $send_snoopy->results; 637 | $this->log($send_snoopy->results); 638 | $json = json_decode($result,true); 639 | if(!$result){ 640 | return false; 641 | } 642 | return true; 643 | } 644 | 645 | /** 646 | * 配置接口信息 647 | * @param string $url 接口回调URL 648 | * @param string $token 接口Token 649 | */ 650 | public function setUrlToken($url, $token) 651 | { 652 | $send_snoopy = new Snoopy; 653 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 654 | $send_snoopy->referer = "https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/interface&lang=zh_CN&token=".$this->_token; 655 | $submit = "https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=".$this->_token; 656 | $post['url'] = $url; 657 | $post['callback_token'] = $token; 658 | $send_snoopy->submit($submit,$post); 659 | $result = $send_snoopy->results; 660 | $this->log($send_snoopy->results); 661 | $json = json_decode($result,true); 662 | if ($json && $json['ret']==0) 663 | return true; 664 | return false; 665 | } 666 | 667 | /** 668 | * 快速设置接口 669 | * @param string $url 接口回调URL 670 | * @param string $token 接口Token 671 | */ 672 | public function quickSetInterface($url, $token) 673 | { 674 | if ($this->closeEditModel() && $this->openDevModel() && $this->setUrlToken($url, $token)) 675 | return true; 676 | return false; 677 | } 678 | 679 | /** 680 | * 模拟登录获取cookie 681 | * @return [type] [description] 682 | */ 683 | public function login(){ 684 | $snoopy = new Snoopy; 685 | $submit = "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"; 686 | $post["username"] = $this->_account; 687 | $post["pwd"] = md5($this->_password); 688 | $post["f"] = "json"; 689 | $post["imgcode"] = ""; 690 | $snoopy->referer = "https://mp.weixin.qq.com/"; 691 | $snoopy->submit($submit,$post); 692 | $cookie = ''; 693 | $this->log($snoopy->results); 694 | $result = json_decode($snoopy->results,true); 695 | 696 | if (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) { 697 | return false; 698 | } 699 | 700 | foreach ($snoopy->headers as $key => $value) { 701 | $value = trim($value); 702 | if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $value,$match)) 703 | $cookie .=$match[1].'='.$match[2].'; '; 704 | } 705 | 706 | preg_match("/token=(\d+)/i",$result['redirect_url'],$matches); 707 | if($matches){ 708 | $this->_token = $matches[1]; 709 | $this->log('token:'.$this->_token); 710 | } 711 | $cookies='{"cookie":"'.$cookie.'","token":"'.$this->_token.'"}'; 712 | $this->saveCookie($this->_cookiename,$cookies); 713 | return $cookie; 714 | } 715 | 716 | /** 717 | * 把cookie写入缓存 718 | * @param string $filename 缓存文件名 719 | * @param string $content 文件内容 720 | * @return bool 721 | */ 722 | public function saveCookie($filename,$content){ 723 | return S($filename,$content,$this->_cookieexpired); 724 | } 725 | 726 | /** 727 | * 读取cookie缓存内容 728 | * @param string $filename 缓存文件名 729 | * @return string cookie 730 | */ 731 | public function getCookie($filename){ 732 | $data = S($filename); 733 | if($data){ 734 | $login=json_decode($data,true); 735 | $send_snoopy = new Snoopy; 736 | $send_snoopy->rawheaders['Cookie']= $login['cookie']; 737 | $send_snoopy->maxredirs = 0; 738 | $url = "https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=".$login['token']; 739 | $send_snoopy->fetch($url); 740 | $header = $send_snoopy->headers; 741 | $this->log('header:'.print_r($send_snoopy->headers,true)); 742 | if( strstr($header[3], 'EXPIRED')){ 743 | return $this->login(); 744 | }else{ 745 | $this->_token =$login['token']; 746 | return $login['cookie']; 747 | } 748 | }else{ 749 | return $this->login(); 750 | } 751 | } 752 | 753 | /** 754 | * 验证cookie的有效性 755 | * @return bool 756 | */ 757 | public function checkValid() 758 | { 759 | if (!$this->cookie || !$this->_token) return false; 760 | $send_snoopy = new Snoopy; 761 | $post = array('ajax'=>1,'token'=>$this->_token); 762 | $submit = "https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN"; 763 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 764 | $send_snoopy->submit($submit,$post); 765 | $result = $send_snoopy->results; 766 | if(json_decode($result,1)){ 767 | return true; 768 | }else{ 769 | return false; 770 | } 771 | } 772 | 773 | private function log($log){ 774 | if ($this->debug ) { 775 | if (function_exists($this->_logcallback)) { 776 | if (is_array($log)) $log = print_r($log,true); 777 | return call_user_func($this->_logcallback,$log); 778 | }elseif (class_exists('Log')) { 779 | Log::write('wechat:'.$log, Log::DEBUG); 780 | } 781 | } 782 | return false; 783 | } 784 | } 785 | -------------------------------------------------------------------------------- /old_version/Thinkphp/Wechatpay.class.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/dodgepudding/wechat-php-sdk 6 | * @version 1.2 7 | * 参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN 8 | * usage: 9 | * $options = array( 10 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 11 | * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 12 | * 'partnerid'=>'88888888', //财付通商户身份标识 13 | * 'partnerkey'=>'', //财付通商户权限密钥Key 14 | * 'paysignkey'=>'' //商户签名密钥Key 15 | * ); 16 | * $payObj = new Wechatpay($options); 17 | * $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); 18 | * 19 | */ 20 | class Wechatpay 21 | { 22 | const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin'; 23 | const AUTH_URL = '/token?grant_type=client_credential&'; 24 | const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀 25 | const PAY_DELIVERNOTIFY = '/pay/delivernotify?'; 26 | const PAY_ORDERQUERY = '/pay/orderquery?'; 27 | 28 | private $appid; 29 | private $appsecret; 30 | private $access_token; 31 | private $user_token; 32 | private $partnerid; 33 | private $partnerkey; 34 | private $paysignkey; 35 | 36 | public $debug = false; 37 | public $errCode = 40001; 38 | public $errMsg = "no access"; 39 | private $_logcallback; 40 | 41 | public function __construct($options) 42 | { 43 | $this->appid = isset($options['appid'])?$options['appid']:''; 44 | $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; 45 | $this->partnerid = isset($options['partnerid'])?$options['partnerid']:''; 46 | $this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:''; 47 | $this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:''; 48 | $this->debug = isset($options['debug'])?$options['debug']:false; 49 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 50 | } 51 | 52 | private function log($log){ 53 | if ($this->debug ) { 54 | if (function_exists($this->_logcallback)) { 55 | if (is_array($log)) $log = print_r($log,true); 56 | return call_user_func($this->_logcallback,$log); 57 | }elseif (class_exists('Log')) { 58 | Log::write('wechat:'.$log, Log::DEBUG); 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | /** 65 | * GET 请求 66 | * @param string $url 67 | */ 68 | private function http_get($url){ 69 | $oCurl = curl_init(); 70 | if(stripos($url,"https://")!==FALSE){ 71 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); 72 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); 73 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 74 | } 75 | curl_setopt($oCurl, CURLOPT_URL, $url); 76 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); 77 | $sContent = curl_exec($oCurl); 78 | $aStatus = curl_getinfo($oCurl); 79 | curl_close($oCurl); 80 | if(intval($aStatus["http_code"])==200){ 81 | return $sContent; 82 | }else{ 83 | return false; 84 | } 85 | } 86 | 87 | /** 88 | * POST 请求 89 | * @param string $url 90 | * @param array $param 91 | * @param boolean $post_file 是否文件上传 92 | * @return string content 93 | */ 94 | private function http_post($url,$param,$post_file=false){ 95 | $oCurl = curl_init(); 96 | if(stripos($url,"https://")!==FALSE){ 97 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); 98 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); 99 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 100 | } 101 | if (is_string($param) || $post_file) { 102 | $strPOST = $param; 103 | } else { 104 | $aPOST = array(); 105 | foreach($param as $key=>$val){ 106 | $aPOST[] = $key."=".urlencode($val); 107 | } 108 | $strPOST = join("&", $aPOST); 109 | } 110 | curl_setopt($oCurl, CURLOPT_URL, $url); 111 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); 112 | curl_setopt($oCurl, CURLOPT_POST,true); 113 | curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); 114 | $sContent = curl_exec($oCurl); 115 | $aStatus = curl_getinfo($oCurl); 116 | curl_close($oCurl); 117 | if(intval($aStatus["http_code"])==200){ 118 | return $sContent; 119 | }else{ 120 | return false; 121 | } 122 | } 123 | 124 | /** 125 | * 获取access_token 126 | * @param string $appid 如在类初始化时已提供,则可为空 127 | * @param string $appsecret 如在类初始化时已提供,则可为空 128 | * @param string $token 手动指定access_token,非必要情况不建议用 129 | */ 130 | public function checkAuth($appid='',$appsecret='',$token=''){ 131 | if (!$appid || !$appsecret) { 132 | $appid = $this->appid; 133 | $appsecret = $this->appsecret; 134 | } 135 | $authname = 'wechat_access_token'.$appid; 136 | if ($token) { //手动指定token,优先使用 137 | $this->access_token=$token; 138 | return $this->access_token; 139 | } 140 | if ($rs = S($authname)) { 141 | $this->access_token = $rs; 142 | return $rs; 143 | } 144 | $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret); 145 | if ($result) 146 | { 147 | $json = json_decode($result,true); 148 | if (!$json || isset($json['errcode'])) { 149 | $this->errCode = $json['errcode']; 150 | $this->errMsg = $json['errmsg']; 151 | return false; 152 | } 153 | $this->access_token = $json['access_token']; 154 | $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; 155 | S($authname,$this->access_token,$expire); 156 | return $this->access_token; 157 | } 158 | return false; 159 | } 160 | 161 | /** 162 | * 删除验证数据 163 | * @param string $appid 164 | */ 165 | public function resetAuth($appid=''){ 166 | if (!$appid) $appid = $this->appid; 167 | $this->access_token = ''; 168 | $authname = 'wechat_access_token'.$appid; 169 | S($authname,null); 170 | return true; 171 | } 172 | 173 | /** 174 | * 微信api不支持中文转义的json结构 175 | * @param array $arr 176 | */ 177 | static function json_encode($arr) { 178 | $parts = array (); 179 | $is_list = false; 180 | //Find out if the given array is a numerical array 181 | $keys = array_keys ( $arr ); 182 | $max_length = count ( $arr ) - 1; 183 | if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1 184 | $is_list = true; 185 | for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position 186 | if ($i != $keys [$i]) { //A key fails at position check. 187 | $is_list = false; //It is an associative array. 188 | break; 189 | } 190 | } 191 | } 192 | foreach ( $arr as $key => $value ) { 193 | if (is_array ( $value )) { //Custom handling for arrays 194 | if ($is_list) 195 | $parts [] = self::json_encode ( $value ); /* :RECURSION: */ 196 | else 197 | $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */ 198 | } else { 199 | $str = ''; 200 | if (! $is_list) 201 | $str = '"' . $key . '":'; 202 | //Custom handling for multiple data types 203 | if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000) 204 | $str .= $value; //Numbers 205 | elseif ($value === false) 206 | $str .= 'false'; //The booleans 207 | elseif ($value === true) 208 | $str .= 'true'; 209 | else 210 | $str .= '"' . addslashes ( $value ) . '"'; //All other things 211 | // :TODO: Is there any more datatype we should be in the lookout for? (Object?) 212 | $parts [] = $str; 213 | } 214 | } 215 | $json = implode ( ',', $parts ); 216 | if ($is_list) 217 | return '[' . $json . ']'; //Return numerical JSON 218 | return '{' . $json . '}'; //Return associative JSON 219 | } 220 | 221 | /** 222 | * 获取签名 223 | * @param array $arrdata 签名数组 224 | * @param string $method 签名方法 225 | * @return boolean|string 签名值 226 | */ 227 | public function getSignature($arrdata,$method="sha1") { 228 | if (!function_exists($method)) return false; 229 | ksort($arrdata); 230 | $paramstring = ""; 231 | foreach($arrdata as $key => $value) 232 | { 233 | if(strlen($paramstring) == 0) 234 | $paramstring .= $key . "=" . $value; 235 | else 236 | $paramstring .= "&" . $key . "=" . $value; 237 | } 238 | $paySign = $method($paramstring); 239 | return $paySign; 240 | } 241 | 242 | /** 243 | * 生成随机字串 244 | * @param number $length 长度,默认为16,最长为32字节 245 | * @return string 246 | */ 247 | public function generateNonceStr($length=16){ 248 | // 密码字符集,可任意添加你需要的字符 249 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 250 | $str = ""; 251 | for($i = 0; $i < $length; $i++) 252 | { 253 | $str .= $chars[mt_rand(0, strlen($chars) - 1)]; 254 | } 255 | return $str; 256 | } 257 | 258 | /** 259 | * 生成原生支付url 260 | * @param number $productid 商品编号,最长为32字节 261 | * @return string 262 | */ 263 | public function createNativeUrl($productid){ 264 | $nativeObj["appid"] = $this->appid; 265 | $nativeObj["appkey"] = $this->paysignkey; 266 | $nativeObj["productid"] = urlencode($productid); 267 | $nativeObj["timestamp"] = time(); 268 | $nativeObj["noncestr"] = $this->generateNonceStr(); 269 | $nativeObj["sign"] = $this->getSignature($nativeObj); 270 | unset($nativeObj["appkey"]); 271 | $bizString = ""; 272 | foreach($nativeObj as $key => $value) 273 | { 274 | if(strlen($bizString) == 0) 275 | $bizString .= $key . "=" . $value; 276 | else 277 | $bizString .= "&" . $key . "=" . $value; 278 | } 279 | return "weixin://wxpay/bizpayurl?".$bizString; 280 | //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX×tamp=XXXXXX&noncestr=XXXXXX 281 | } 282 | 283 | 284 | /** 285 | * 生成订单package字符串 286 | * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一 287 | * @param string $body 必填,商品描述,128 字节以下 288 | * @param int $total_fee 必填,订单总金额,单位为分 289 | * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内 290 | * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内 291 | * @param int $fee_type 必填,现金支付币种,默认1:人民币 292 | * @param string $bank_type 必填,银行通道类型,默认WX 293 | * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK 294 | * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss 295 | * @param string $time_expire 交易结束时间,也是订单失效时间 296 | * @param int $transport_fee 物流费用,单位为分 297 | * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee 298 | * @param string $goods_tag 商品标记,优惠券时可能用到 299 | * @param string $attach 附加数据,notify接口原样返回 300 | * @return string 301 | */ 302 | 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=""){ 303 | $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); 304 | if ($time_start) $arrdata['time_start'] = $time_start; 305 | if ($time_expire) $arrdata['time_expire'] = $time_expire; 306 | if ($transport_fee) $arrdata['transport_fee'] = $transport_fee; 307 | if ($product_fee) $arrdata['product_fee'] = $product_fee; 308 | if ($goods_tag) $arrdata['goods_tag'] = $goods_tag; 309 | if ($attach) $arrdata['attach'] = $attach; 310 | ksort($arrdata); 311 | $paramstring = ""; 312 | foreach($arrdata as $key => $value) 313 | { 314 | if(strlen($paramstring) == 0) 315 | $paramstring .= $key . "=" . $value; 316 | else 317 | $paramstring .= "&" . $key . "=" . $value; 318 | } 319 | $stringSignTemp = $paramstring . "&key=" . $this->partnerkey; 320 | $signValue = strtoupper(md5($stringSignTemp)); 321 | $package = http_build_query($arrdata) . "&sign=" . $signValue; 322 | return $package; 323 | } 324 | 325 | /** 326 | * 支付签名(paySign)生成方法 327 | * @param string $package 订单详情字串 328 | * @param string $timeStamp 当前时间戳(需与JS输出的一致) 329 | * @param string $nonceStr 随机串(需与JS输出的一致) 330 | * @return string 返回签名字串 331 | */ 332 | public function getPaySign($package, $timeStamp, $nonceStr){ 333 | $arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey); 334 | $paySign = $this->getSignature($arrdata); 335 | return $paySign; 336 | } 337 | 338 | /** 339 | * 回调通知签名验证 340 | * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取 341 | * @return boolean 342 | */ 343 | public function checkOrderSignature($orderxml=''){ 344 | if (!$orderxml) { 345 | $postStr = file_get_contents("php://input"); 346 | if (!empty($postStr)) { 347 | $orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 348 | } else return false; 349 | } 350 | $arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']); 351 | $paySign = $this->getSignature($arrdata); 352 | if ($paySign!=$orderxml['AppSignature']) return false; 353 | return true; 354 | } 355 | 356 | /** 357 | * 发货通知 358 | * @param string $openid 用户open_id 359 | * @param string $transid 交易单号 360 | * @param string $out_trade_no 第三方订单号 361 | * @param int $status 0:发货失败;1:已发货 362 | * @param string $msg 失败原因 363 | * @return boolean|array 364 | */ 365 | public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){ 366 | if (!$this->access_token && !$this->checkAuth()) return false; 367 | $postdata = array( 368 | "appid"=>$this->appid, 369 | "appkey"=>$this->paysignkey, 370 | "openid"=>$openid, 371 | "transid"=>strval($transid), 372 | "out_trade_no"=>strval($out_trade_no), 373 | "deliver_timestamp"=>strval(time()), 374 | "deliver_status"=>strval($status), 375 | "deliver_msg"=>$msg, 376 | ); 377 | $postdata['app_signature'] = $this->getSignature($postdata); 378 | $postdata['sign_method'] = 'sha1'; 379 | unset($postdata['appkey']); 380 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata)); 381 | if ($result) 382 | { 383 | $json = json_decode($result,true); 384 | if (!$json || !empty($json['errcode'])) { 385 | $this->errCode = $json['errcode']; 386 | $this->errMsg = $json['errmsg']; 387 | return false; 388 | } 389 | return $json; 390 | } 391 | return false; 392 | } 393 | 394 | /** 395 | * 查询订单信息 396 | * @param string $out_trade_no 订单号 397 | * @return boolean|array 398 | */ 399 | public function getPayOrder($out_trade_no) { 400 | if (!$this->access_token && !$this->checkAuth()) return false; 401 | $sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}")); 402 | $postdata = array( 403 | "appid"=>$this->appid, 404 | "appkey"=>$this->paysignkey, 405 | "package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign", 406 | "timestamp"=>strval(time()), 407 | ); 408 | $postdata['app_signature'] = $this->getSignature($postdata); 409 | $postdata['sign_method'] = 'sha1'; 410 | unset($postdata['appkey']); 411 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata)); 412 | if ($result) 413 | { 414 | $json = json_decode($result,true); 415 | if (!$json || !empty($json['errcode'])) { 416 | $this->errCode = $json['errcode']; 417 | $this->errMsg = $json['errmsg'].json_encode($postdata); 418 | return false; 419 | } 420 | return $json["order_info"]; 421 | } 422 | return false; 423 | } 424 | 425 | /** 426 | * 设置用户授权密钥 427 | * @param string $user_token 428 | * @return string 429 | */ 430 | public function setUserToken($user_token) { 431 | return $this->user_token = $user_token; 432 | } 433 | 434 | /** 435 | * 获取收货地址JS的签名 436 | * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用 437 | * @param string $appId 438 | * @param string $url 439 | * @param int $timeStamp 440 | * @param string $nonceStr 441 | * @param string $user_token 442 | * @return Ambigous 443 | */ 444 | public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){ 445 | if (!$user_token) $user_token = $this->user_token; 446 | if (!$user_token) { 447 | $this->errMsg = 'no user access token found!'; 448 | return false; 449 | } 450 | $url = htmlspecialchars_decode($url); 451 | $arrdata = array( 452 | 'appid'=>$this->appid, 453 | 'url'=>$url, 454 | 'timestamp'=>strval($timeStamp), 455 | 'noncestr'=>$nonceStr, 456 | 'accesstoken'=>$user_token 457 | ); 458 | return $this->getSignature($arrdata); 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /old_version/test/test2.php: -------------------------------------------------------------------------------- 1 | 'demo@domain.com', 13 | 'password'=>'demo', 14 | 'datapath'=>'../data/cookie_', 15 | 'debug'=>true, 16 | 'logcallback'=>'logdebug' 17 | ); 18 | $wechat = new Wechatext($options); 19 | if ($wechat->checkValid()) { 20 | //获取分组列表 21 | $grouplist = $wechat->getGroupList(); 22 | var_dump($grouplist); 23 | //获取用户列表 24 | $userlist = $wechat->getUserlist(0,10); 25 | var_dump($userlist); 26 | $user = $userlist[0]; 27 | // 获取用户信息 28 | $userdata = $wechat->getInfo($user['id']); 29 | var_dump($userdata); 30 | // 获取已保存的图文消息 31 | $newslist = $wechat->getNewsList(0,10); 32 | var_dump($newslist); 33 | //获取用户最新消息 34 | $topmsg = $wechat->getTopMsg(); 35 | var_dump($topmsg); 36 | $msglist = $wechat->getMsg(); 37 | var_dump($msglist); 38 | // 主动回复消息 39 | if ($topmsg && $topmsg['has_reply']==0){ 40 | $wechat->send($user['id'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); 41 | $content = '这是一条Wechatext发出的测试微信'; 42 | $imgdata = file_get_contents('http://github.global.ssl.fastly.net/images/modules/dashboard/bootcamp/octocat_fork.png'); 43 | $img = '../data/send.png'; 44 | file_put_contents($img,$imgdata); 45 | //上传图片 46 | $fileid = $wechat->uploadFile($img); 47 | echo 'fileid:'.$fileid; 48 | //if ($fileid) $re = $wechat->sendImage($user['id'],$fileid); 49 | //发送图文信息 50 | $re = $wechat->sendPreview($userdata['user_name'],$content,$content,$content,$fileid,'http://github.com/dodgepudding/wechat-php-sdk'); 51 | var_dump($re); 52 | //发送视频 53 | //$re = $wechat->sendVideo($user['id'],$fileid); 54 | $re = $wechat->getFileList(2,0,10); 55 | var_dump($re); 56 | } else { 57 | echo 'no top msg'; 58 | } 59 | } else { 60 | echo "login error"; 61 | } -------------------------------------------------------------------------------- /old_version/test/test3.php: -------------------------------------------------------------------------------- 1 | $sid, 13 | 'datapath'=>'../log/cookiecode_', 14 | 'debug'=>true, 15 | 'logcallback'=>'logdebug' 16 | ); 17 | $wechat = new Wechatauth($options); 18 | 19 | if (isset($_POST['code'])) { 20 | $logincode = $_POST['code']; 21 | $vres = $wechat->set_login_code($logincode)->verify_code(); 22 | if ($vres===false) { 23 | $result = array('status'=>0); 24 | } else { 25 | $result = array('status'=>$vres); 26 | if ($vres==200) { 27 | $result['info'] = $wechat->get_login_info(); 28 | $result['cookie'] = $wechat->get_login_cookie(true); 29 | } 30 | } 31 | 32 | die(json_encode($result)); 33 | } 34 | $logincode = $wechat->get_login_code(); 35 | $qrimg = $wechat->get_code_image(); 36 | 37 | ?> 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 微信二维码登陆接口 46 | 47 | 48 |

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

49 |
50 | 51 |
52 | 90 | -------------------------------------------------------------------------------- /old_version/test/weshare.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 36 | weshare 37 | 38 | 39 | 40 |

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

41 | 42 | 43 | -------------------------------------------------------------------------------- /old_version/wechat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 微信网页端调用JS(官方于微信6.0.2版本发布新版JSAPI接口,此接口文件废弃) 3 | * @author dodge 4 | * @contact dodgepudding@gmail.com 5 | * @link http://blog.4wer.com/wechat-timeline-share 6 | * @version 1.1 7 | * 8 | * 自定义分享使用: 9 | * WeixinJS.hideOptionMenu() 隐藏右上角按钮 10 | * WeixinJS.showOptionMenu() 显示右上角按钮 11 | * WeixinJS.hideToolbar() 隐藏工具栏 12 | * WeixinJS.showToolbar() 显示工具栏 13 | * WeixinJS.getNetworkType() 获取网络状态 14 | * WeixinJS.closeWindow() 关闭窗口 15 | * WeixinJS.scanQRCode() 扫描二维码 16 | * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 17 | * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 18 | * WeixinJS.sendEmail(title,content) 发送邮件 19 | * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 20 | * WeixinJS.addContact(username) 添加微信账号 21 | * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 22 | * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 23 | * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 24 | * 自定义分享内容数据格式: 25 | * var dataForWeixin={ 26 | appId:"", 27 | MsgImg:"消息图片路径", 28 | TLImg:"时间线图路径", 29 | url:"分享url路径", 30 | title:"标题", 31 | desc:"描述", 32 | fakeid:"", 33 | prepare:function(argv){ 34 | if (typeof argv.shareTo!='undefined') 35 | switch(argv.shareTo) { 36 | case 'friend': 37 | //发送给朋友 38 | alert(argv.scene); //friend 39 | break; 40 | case 'timeline': 41 | //发送给朋友 42 | break; 43 | case 'weibo': 44 | //发送到微博 45 | alert(argv.url); 46 | break; 47 | case 'favorite': 48 | //收藏 49 | alert(argv.scene);//favorite 50 | break; 51 | case 'connector': 52 | //分享到第三方应用 53 | alert(argv.scene);//connector 54 | break; 55 | default: 56 | } 57 | }, 58 | callback:function(res){ 59 | //发送给好友或应用 60 | if (res.err_msg=='send_app_msg:confirm') { 61 | //todo:func1(); 62 | alert(res.err_desc); 63 | } 64 | if (res.err_msg=='send_app_msg:cancel') { 65 | //todo:func2(); 66 | alert(res.err_desc); 67 | } 68 | //分享到朋友圈 69 | if (res.err_msg=='share_timeline:ok') { 70 | //todo:func1(); 71 | alert(res.err_desc); 72 | } 73 | if (res.err_msg=='share_timeline:cancel') { 74 | //todo:func1(); 75 | alert(res.err_desc); 76 | } 77 | //分享到微博 78 | if (res.err_msg=='share_weibo:confirm') { 79 | //todo:func1(); 80 | alert(res.err_desc); 81 | } 82 | if (res.err_msg=='share_weibo:cancel') { 83 | //todo:func1(); 84 | alert(res.err_desc); 85 | } 86 | //收藏或分享到应用 87 | if (res.err_msg=='send_app_msg:ok') { 88 | //todo:func1(); 89 | alert(res.err_desc); 90 | } 91 | } 92 | }; 93 | */ 94 | 95 | WeixinJS = typeof WeixinJS!='undefined' || {}; 96 | //隐藏右上角按钮 97 | WeixinJS.hideOptionMenu = function() { 98 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 99 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideOptionMenu'); 100 | }); 101 | }; 102 | //显示右上角按钮 103 | WeixinJS.showOptionMenu = function() { 104 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 105 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showOptionMenu'); 106 | }); 107 | }; 108 | //隐藏底部导航栏 109 | WeixinJS.hideToolbar = function() { 110 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 111 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideToolbar'); 112 | }); 113 | }; 114 | //显示底部导航栏 115 | WeixinJS.showToolbar = function() { 116 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 117 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showToolbar'); 118 | }); 119 | }; 120 | //网页获取用户网络状态 121 | netType={"network_type:wifi":"wifi网络","network_type:edge":"非wifi,包含3G/2G","network_type:fail":"网络断开连接","network_type:wwan":"2g或者3g"}; 122 | WeixinJS.getNetworkType = function(callback) { 123 | document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { 124 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getNetworkType',{}, 125 | function(res){ 126 | //result: network_type:wifi,network_type:edge,network_type:fail,network_type:wwan 127 | //netType[e.err_msg] 128 | callback(res.err_msg); 129 | }); 130 | }); 131 | }; 132 | //关闭窗口 133 | WeixinJS.closeWindow = function() { 134 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("closeWindow", {}); 135 | }; 136 | //扫描二维码 137 | WeixinJS.scanQRCode = function() { 138 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("scanQRCode", {}); 139 | }; 140 | //使用浏览器打开网址 141 | WeixinJS.openUrlByExtBrowser=function(url){ 142 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openUrlByExtBrowser",{"url" : url}); 143 | }; 144 | //跳转到指定公众账号页面 145 | WeixinJS.jumpToBizProfile=function(username){ 146 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("jumpToBizProfile",{"tousername" : username}); 147 | }; 148 | //发送邮件 149 | WeixinJS.sendEmail=function(title,content){ 150 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("sendEmail",{ 151 | "title" : title, 152 | "content" : content 153 | }); 154 | }; 155 | //查看地图 156 | WeixinJS.openProductView=function(latitude,longitude,name,address,scale,infoUrl){ 157 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("openProductView",{ 158 | "latitude" : latitude, //纬度 159 | "longitude" : longitude, //经度 160 | "name" : name, //名称 161 | "address" : address, //地址 162 | "scale" : scale, //地图缩放级别 163 | "infoUrl" : infoUrl, //查看位置界面底部的超链接 164 | }); 165 | }; 166 | //添加微信账号 167 | WeixinJS.addContact=function weixinAddContact(username){ 168 | if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("addContact", { 169 | "webtype": "1", 170 | "username": username 171 | }, function(e) { 172 | WeixinJSBridge.log(e.err_msg); 173 | //e.err_msg:add_contact:added 已经添加 174 | //e.err_msg:add_contact:cancel 取消添加 175 | //e.err_msg:add_contact:ok 添加成功 176 | if(e.err_msg == 'add_contact:added' || e.err_msg == 'add_contact:ok'){ 177 | //关注成功,或者已经关注过 178 | } 179 | }); 180 | }; 181 | 182 | /** 183 | * 调出微信内图片预览scrollview 184 | * @param array urls 图片url数组 185 | * @param string current 当前图片url 186 | */ 187 | WeixinJS.imagePreview = function(urls,current) { 188 | if (typeof WeixinJSBridge!='undefined') 189 | WeixinJSBridge.invoke("imagePreview", { 190 | current: current, 191 | urls: urls 192 | }); 193 | }; 194 | 195 | //微信JsApi支付接口 196 | WeixinJS.payCallback = function(appId,package,timeStamp,nonceStr,signType,paySign,callback){ 197 | if (typeof WeixinJSBridge!='undefined') 198 | WeixinJSBridge.invoke('getBrandWCPayRequest',{ 199 | "appId" : appId.toString(), 200 | "timeStamp" : timeStamp.toString(), 201 | "nonceStr" : nonceStr.toString(), 202 | "package" : package.toString(), 203 | "signType" : signType.toString(), 204 | "paySign" : paySign.toString() 205 | 206 | },function(res){ 207 | // res.err_msg == "get_brand_wcpay_request:ok" return true; 208 | // res.err_msg == "get_brand_wcpay_request:cancel" return false; 209 | callback(res); 210 | }); 211 | }; 212 | //编辑收货地址 213 | WeixinJS.editAddress = function(appId,addrSign,timeStamp,nonceStr,callback){ 214 | var postdata = { 215 | "appId" : appId.toString(), 216 | "scope" : "jsapi_address", 217 | "signType" : "sha1", 218 | "addrSign" : addrSign.toString(), 219 | "timeStamp" : timeStamp.toString(), 220 | "nonceStr" : nonceStr.toString() 221 | }; 222 | if (typeof WeixinJSBridge!='undefined') 223 | WeixinJSBridge.invoke('editAddress',postdata, function(res){ 224 | //return res.proviceFirstStageName,res.addressCitySecondStageName,res.addressCountiesThirdStageName,res.addressDetailInfo,res.userName,res.addressPostalCode,res.telNumber 225 | //error return res.err_msg 226 | callback(res); 227 | }); 228 | }; 229 | 230 | (function(){ 231 | if (typeof dataForWeixin=="undefined") return; 232 | var onBridgeReady=function(){ 233 | WeixinJSBridge.on('menu:share:appmessage', function(argv){ 234 | (dataForWeixin.prepare)(argv); 235 | WeixinJSBridge.invoke('sendAppMessage',{ 236 | "appid":dataForWeixin.appId, 237 | "img_url":dataForWeixin.MsgImg, 238 | "img_width":"120", 239 | "img_height":"120", 240 | "link":dataForWeixin.url, 241 | "desc":dataForWeixin.desc, 242 | "title":dataForWeixin.title 243 | }, function(res){(dataForWeixin.callback)(res);}); 244 | }); 245 | WeixinJSBridge.on('menu:share:timeline', function(argv){ 246 | (dataForWeixin.prepare)(argv); 247 | WeixinJSBridge.invoke('shareTimeline',{ 248 | "img_url":dataForWeixin.TLImg, 249 | "img_width":"120", 250 | "img_height":"120", 251 | "link":dataForWeixin.url, 252 | "desc":dataForWeixin.desc, 253 | "title":dataForWeixin.title 254 | }, function(res){(dataForWeixin.callback)(res);}); 255 | }); 256 | WeixinJSBridge.on('menu:share:weibo', function(argv){ 257 | (dataForWeixin.prepare)(argv); 258 | WeixinJSBridge.invoke('shareWeibo',{ 259 | "content":dataForWeixin.title, 260 | "url":dataForWeixin.url 261 | }, function(res){(dataForWeixin.callback)(res);}); 262 | }); 263 | WeixinJSBridge.on('menu:share:facebook', function(argv){ 264 | (dataForWeixin.prepare)(argv); 265 | WeixinJSBridge.invoke('shareFB',{ 266 | "img_url":dataForWeixin.TLImg, 267 | "img_width":"120", 268 | "img_height":"120", 269 | "link":dataForWeixin.url, 270 | "desc":dataForWeixin.desc, 271 | "title":dataForWeixin.title 272 | }, function(res){(dataForWeixin.callback)(res);}); 273 | }); 274 | }; 275 | if(document.addEventListener){ 276 | document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); 277 | }else if(document.attachEvent){ 278 | document.attachEvent('WeixinJSBridgeReady' , onBridgeReady); 279 | document.attachEvent('onWeixinJSBridgeReady' , onBridgeReady); 280 | } 281 | })(); 282 | -------------------------------------------------------------------------------- /old_version/wechatauth.class.php: -------------------------------------------------------------------------------- 1 | 13 | * @link https://github.com/dodgepudding/wechat-php-sdk 14 | * @version 1.1 15 | * 16 | */ 17 | include "snoopy.class.php"; 18 | class Wechatauth 19 | { 20 | private $cookie; 21 | private $skey; 22 | private $_cookiename; 23 | private $_cookieexpired = 3600; 24 | private $_account = 'test'; 25 | private $_datapath = './data/cookie_'; 26 | private $debug; 27 | private $_logcallback; 28 | public $login_user; //当前登陆用户, 调用get_login_info后获取 29 | 30 | public function __construct($options) 31 | { 32 | $this->_account = isset($options['account'])?$options['account']:''; 33 | $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath; 34 | $this->debug = isset($options['debug'])?$options['debug']:false; 35 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 36 | $this->_cookiename = $this->_datapath.$this->_account; 37 | $this->getCookie($this->_cookiename); 38 | } 39 | /** 40 | * 把cookie写入缓存 41 | * @param string $filename 缓存文件名 42 | * @param string $content 文件内容 43 | * @return bool 44 | */ 45 | public function saveCookie($filename,$content){ 46 | return file_put_contents($filename,$content); 47 | } 48 | 49 | /** 50 | * 读取cookie缓存内容 51 | * @param string $filename 缓存文件名 52 | * @return string cookie 53 | */ 54 | public function getCookie($filename){ 55 | if (file_exists($filename)) { 56 | $mtime = filemtime($filename); 57 | if ($mtime_cookieexpired) return false; 58 | $data = file_get_contents($filename); 59 | if ($data) $this->cookie = $data; 60 | } 61 | return $this->cookie; 62 | } 63 | 64 | /* 65 | * 删除cookie 66 | */ 67 | public function deleteCookie($filename) { 68 | $this->cookie = ''; 69 | @unlink($filename); 70 | return true; 71 | } 72 | 73 | private function log($log){ 74 | if ($this->debug && function_exists($this->_logcallback)) { 75 | if (is_array($log)) $log = print_r($log,true); 76 | return call_user_func($this->_logcallback,$log); 77 | } 78 | } 79 | 80 | /** 81 | * 获取登陆二维码对应的授权码 82 | */ 83 | public function get_login_code(){ 84 | if ($this->_logincode) return $this->_logincode; 85 | $t = time().strval(mt_rand(100,999)); 86 | $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; 87 | $send_snoopy = new Snoopy; 88 | $send_snoopy->fetch($codeurl); 89 | $result = $send_snoopy->results; 90 | if ($result) { 91 | preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches); 92 | if(count($matches)>1) { 93 | $this->_logincode = $matches[1]; 94 | $_SESSION['login_step'] = 0; 95 | return $this->_logincode; 96 | } 97 | } 98 | return $result; 99 | } 100 | 101 | /** 102 | * 通过授权码获取对应的二维码图片地址 103 | * @param string $code 104 | * @return string image url 105 | */ 106 | public function get_code_image($code=''){ 107 | if ($code=='') $code = $this->_logincode; 108 | if (!$code) return false; 109 | return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx'; 110 | } 111 | 112 | /** 113 | * 设置二维码对应的授权码 114 | * @param string $code 115 | * @return class $this 116 | */ 117 | public function set_login_code($code) { 118 | $this->_logincode = $code; 119 | return $this; 120 | } 121 | 122 | /** 123 | * 二维码登陆验证 124 | * 125 | * @return status: 126 | * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired 127 | * 201: just scaned but not confirm 128 | * 200: confirm then you can get user info 129 | */ 130 | public function verify_code() { 131 | if (!$this->_logincode) return false; 132 | $t = time().strval(mt_rand(100,999)); 133 | 134 | $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t; 135 | $send_snoopy = new Snoopy; 136 | $send_snoopy->referer = "https://wx.qq.com/"; 137 | $send_snoopy->fetch($url); 138 | $result = $send_snoopy->results; 139 | $this->log('step1:'.$result); 140 | if ($result) { 141 | preg_match("/window\.code=(\d+)/",$result,$matches); 142 | if(count($matches)>1) { 143 | $status = intval($matches[1]); 144 | if ($status==201) $_SESSION['login_step'] = 1; 145 | if ($status==200) { 146 | preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches); 147 | preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl); 148 | $this->log('step2:'.print_r($matches,true)); 149 | if (count($matcheurl)>1) { 150 | $ticket = $matches[1]; 151 | $scan = $matches[2]; 152 | //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new'; 153 | $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old'; 154 | $urlpart = parse_url($loginurl); 155 | $send_snoopy = new Snoopy; 156 | $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat"; 157 | $send_snoopy->fetch($loginurl); 158 | $result = $send_snoopy->results; 159 | $xml = simplexml_load_string($result); 160 | if ($xml->ret=="0") $this->skey = $xml->skey; 161 | foreach ($send_snoopy->headers as $key => $value) { 162 | $value = trim($value); 163 | if(strpos($value,'Set-Cookie: ') !== false){ 164 | $tmp = str_replace("Set-Cookie: ","",$value); 165 | $tmparray = explode(';', $tmp); 166 | $item = trim($tmparray[0]); 167 | $cookie.=$item.';'; 168 | } 169 | } 170 | $cookie .="Domain=.qq.com;"; 171 | $this->cookie = $cookie; 172 | $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result); 173 | 174 | $this->saveCookie($this->_cookiename,$this->cookie); 175 | } 176 | } 177 | return $status; 178 | } 179 | } 180 | return false; 181 | } 182 | 183 | /** 184 | * 获取登陆的cookie 185 | * 186 | * @param bool $is_array 是否以数值方式返回,默认否,返回字符串 187 | * @return string|array 188 | */ 189 | public function get_login_cookie($is_array = false){ 190 | if (!$is_array) return $this->cookie; 191 | $c_arr = explode(';',$this->cookie); 192 | $cookie = array(); 193 | foreach($c_arr as $item) { 194 | $kitem = explode('=',trim($item)); 195 | if (count($kitem)>1) { 196 | $key = trim($kitem[0]); 197 | $val = trim($kitem[1]); 198 | if (!empty($val)) $cookie[$key] = $val; 199 | } 200 | } 201 | return $cookie; 202 | } 203 | 204 | /** 205 | * 授权登陆后获取用户登陆信息 206 | */ 207 | public function get_login_info(){ 208 | if (!$this->cookie) return false; 209 | $t = time().strval(mt_rand(100,999)); 210 | $send_snoopy = new Snoopy; 211 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey); 212 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 213 | $send_snoopy->referer = "https://wx2.qq.com/"; 214 | $citems = $this->get_login_cookie(true); 215 | $post = array( 216 | "BaseRequest"=>array( 217 | array( 218 | "Uin"=>$citems['wxuin'], 219 | "Sid"=>$citems['wxsid'], 220 | "Skey"=>$this->skey, 221 | "DeviceID"=>'' 222 | ) 223 | ) 224 | ); 225 | $send_snoopy->submit($submit,json_encode($post)); 226 | $this->log('login_info:'.$send_snoopy->results); 227 | $result = json_decode($send_snoopy->results,true); 228 | if ($result['BaseResponse']['Ret']<0) return false; 229 | $this->_login_user = $result['User']; 230 | return $result; 231 | } 232 | 233 | /** 234 | * 获取头像 235 | * @param string $url 传入从用户信息接口获取到的头像地址 236 | */ 237 | public function get_avatar($url) { 238 | if (!$this->cookie) return false; 239 | if (strpos($url, 'http')===false) { 240 | $url = 'http://wx2.qq.com'.$url; 241 | } 242 | $send_snoopy = new Snoopy; 243 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 244 | $send_snoopy->referer = "https://wx2.qq.com/"; 245 | $send_snoopy->fetch($url); 246 | $result = $send_snoopy->results; 247 | if ($result) 248 | return $result; 249 | else 250 | return false; 251 | } 252 | 253 | /** 254 | * 登出当前登陆用户 255 | */ 256 | public function logout(){ 257 | if (!$this->cookie) return false; 258 | preg_match("/wxuin=(\w+);/",$this->cookie,$matches); 259 | if (count($matches)>1) $uid = $matches[1]; 260 | preg_match("/wxsid=(\w+);/",$this->cookie,$matches); 261 | if (count($matches)>1) $sid = $matches[1]; 262 | $this->log('logout: uid='.$uid.';sid='.$sid); 263 | $send_snoopy = new Snoopy; 264 | $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1'; 265 | $send_snoopy->rawheaders['Cookie']= $this->cookie; 266 | $send_snoopy->referer = "https://wx2.qq.com/"; 267 | $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid)); 268 | $this->deleteCookie($this->_cookiename); 269 | return true; 270 | } 271 | } -------------------------------------------------------------------------------- /old_version/wechatpay.class.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/dodgepudding/wechat-php-sdk 6 | * @version 1.2 7 | * 参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN 8 | * usage: 9 | * $options = array( 10 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 11 | * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 12 | * 'partnerid'=>'88888888', //财付通商户身份标识 13 | * 'partnerkey'=>'', //财付通商户权限密钥Key 14 | * 'paysignkey'=>'' //商户签名密钥Key 15 | * ); 16 | * $payObj = new Wechatpay($options); 17 | * $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); 18 | * 19 | */ 20 | class Wechatpay 21 | { 22 | const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin'; 23 | const AUTH_URL = '/token?grant_type=client_credential&'; 24 | const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀 25 | const PAY_DELIVERNOTIFY = '/pay/delivernotify?'; 26 | const PAY_ORDERQUERY = '/pay/orderquery?'; 27 | 28 | private $appid; 29 | private $appsecret; 30 | private $access_token; 31 | private $user_token; 32 | private $partnerid; 33 | private $partnerkey; 34 | private $paysignkey; 35 | 36 | public $debug = false; 37 | public $errCode = 40001; 38 | public $errMsg = "no access"; 39 | private $_logcallback; 40 | 41 | public function __construct($options) 42 | { 43 | $this->appid = isset($options['appid'])?$options['appid']:''; 44 | $this->appsecret = isset($options['appsecret'])?$options['appsecret']:''; 45 | $this->partnerid = isset($options['partnerid'])?$options['partnerid']:''; 46 | $this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:''; 47 | $this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:''; 48 | $this->debug = isset($options['debug'])?$options['debug']:false; 49 | $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false; 50 | } 51 | 52 | private function log($log){ 53 | if ($this->debug && function_exists($this->_logcallback)) { 54 | if (is_array($log)) $log = print_r($log,true); 55 | return call_user_func($this->_logcallback,$log); 56 | } 57 | } 58 | 59 | /** 60 | * GET 请求 61 | * @param string $url 62 | */ 63 | private function http_get($url){ 64 | $oCurl = curl_init(); 65 | if(stripos($url,"https://")!==FALSE){ 66 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); 67 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE); 68 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 69 | } 70 | curl_setopt($oCurl, CURLOPT_URL, $url); 71 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); 72 | $sContent = curl_exec($oCurl); 73 | $aStatus = curl_getinfo($oCurl); 74 | curl_close($oCurl); 75 | if(intval($aStatus["http_code"])==200){ 76 | return $sContent; 77 | }else{ 78 | return false; 79 | } 80 | } 81 | 82 | /** 83 | * POST 请求 84 | * @param string $url 85 | * @param array $param 86 | * @param boolean $post_file 是否文件上传 87 | * @return string content 88 | */ 89 | private function http_post($url,$param,$post_file=false){ 90 | $oCurl = curl_init(); 91 | if(stripos($url,"https://")!==FALSE){ 92 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); 93 | curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); 94 | curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 95 | } 96 | if (is_string($param) || $post_file) { 97 | $strPOST = $param; 98 | } else { 99 | $aPOST = array(); 100 | foreach($param as $key=>$val){ 101 | $aPOST[] = $key."=".urlencode($val); 102 | } 103 | $strPOST = join("&", $aPOST); 104 | } 105 | curl_setopt($oCurl, CURLOPT_URL, $url); 106 | curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); 107 | curl_setopt($oCurl, CURLOPT_POST,true); 108 | curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); 109 | $sContent = curl_exec($oCurl); 110 | $aStatus = curl_getinfo($oCurl); 111 | curl_close($oCurl); 112 | if(intval($aStatus["http_code"])==200){ 113 | return $sContent; 114 | }else{ 115 | return false; 116 | } 117 | } 118 | 119 | /** 120 | * 获取access_token 121 | * @param string $appid 如在类初始化时已提供,则可为空 122 | * @param string $appsecret 如在类初始化时已提供,则可为空 123 | * @param string $token 手动指定access_token,非必要情况不建议用 124 | */ 125 | public function checkAuth($appid='',$appsecret='',$token=''){ 126 | if (!$appid || !$appsecret) { 127 | $appid = $this->appid; 128 | $appsecret = $this->appsecret; 129 | } 130 | if ($token) { //手动指定token,优先使用 131 | $this->access_token=$token; 132 | return $this->access_token; 133 | } 134 | //TODO: get the cache access_token 135 | $result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret); 136 | if ($result) 137 | { 138 | $json = json_decode($result,true); 139 | if (!$json || isset($json['errcode'])) { 140 | $this->errCode = $json['errcode']; 141 | $this->errMsg = $json['errmsg']; 142 | return false; 143 | } 144 | $this->access_token = $json['access_token']; 145 | $expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600; 146 | //TODO: cache access_token 147 | return $this->access_token; 148 | } 149 | return false; 150 | } 151 | 152 | /** 153 | * 删除验证数据 154 | * @param string $appid 155 | */ 156 | public function resetAuth($appid=''){ 157 | if (!$appid) $appid = $this->appid; 158 | $this->access_token = ''; 159 | //TODO: remove cache 160 | return true; 161 | } 162 | 163 | /** 164 | * 微信api不支持中文转义的json结构 165 | * @param array $arr 166 | */ 167 | static function json_encode($arr) { 168 | $parts = array (); 169 | $is_list = false; 170 | //Find out if the given array is a numerical array 171 | $keys = array_keys ( $arr ); 172 | $max_length = count ( $arr ) - 1; 173 | if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1 174 | $is_list = true; 175 | for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position 176 | if ($i != $keys [$i]) { //A key fails at position check. 177 | $is_list = false; //It is an associative array. 178 | break; 179 | } 180 | } 181 | } 182 | foreach ( $arr as $key => $value ) { 183 | if (is_array ( $value )) { //Custom handling for arrays 184 | if ($is_list) 185 | $parts [] = self::json_encode ( $value ); /* :RECURSION: */ 186 | else 187 | $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */ 188 | } else { 189 | $str = ''; 190 | if (! $is_list) 191 | $str = '"' . $key . '":'; 192 | //Custom handling for multiple data types 193 | if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000) 194 | $str .= $value; //Numbers 195 | elseif ($value === false) 196 | $str .= 'false'; //The booleans 197 | elseif ($value === true) 198 | $str .= 'true'; 199 | else 200 | $str .= '"' . addslashes ( $value ) . '"'; //All other things 201 | // :TODO: Is there any more datatype we should be in the lookout for? (Object?) 202 | $parts [] = $str; 203 | } 204 | } 205 | $json = implode ( ',', $parts ); 206 | if ($is_list) 207 | return '[' . $json . ']'; //Return numerical JSON 208 | return '{' . $json . '}'; //Return associative JSON 209 | } 210 | 211 | /** 212 | * 获取签名 213 | * @param array $arrdata 签名数组 214 | * @param string $method 签名方法 215 | * @return boolean|string 签名值 216 | */ 217 | public function getSignature($arrdata,$method="sha1") { 218 | if (!function_exists($method)) return false; 219 | ksort($arrdata); 220 | $paramstring = ""; 221 | foreach($arrdata as $key => $value) 222 | { 223 | if(strlen($paramstring) == 0) 224 | $paramstring .= $key . "=" . $value; 225 | else 226 | $paramstring .= "&" . $key . "=" . $value; 227 | } 228 | $paySign = $method($paramstring); 229 | return $paySign; 230 | } 231 | 232 | /** 233 | * 生成随机字串 234 | * @param number $length 长度,默认为16,最长为32字节 235 | * @return string 236 | */ 237 | public function generateNonceStr($length=16){ 238 | // 密码字符集,可任意添加你需要的字符 239 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 240 | $str = ""; 241 | for($i = 0; $i < $length; $i++) 242 | { 243 | $str .= $chars[mt_rand(0, strlen($chars) - 1)]; 244 | } 245 | return $str; 246 | } 247 | 248 | /** 249 | * 生成原生支付url 250 | * @param number $productid 商品编号,最长为32字节 251 | * @return string 252 | */ 253 | public function createNativeUrl($productid){ 254 | $nativeObj["appid"] = $this->appid; 255 | $nativeObj["appkey"] = $this->paysignkey; 256 | $nativeObj["productid"] = urlencode($productid); 257 | $nativeObj["timestamp"] = time(); 258 | $nativeObj["noncestr"] = $this->generateNonceStr(); 259 | $nativeObj["sign"] = $this->getSignature($nativeObj); 260 | unset($nativeObj["appkey"]); 261 | $bizString = ""; 262 | foreach($nativeObj as $key => $value) 263 | { 264 | if(strlen($bizString) == 0) 265 | $bizString .= $key . "=" . $value; 266 | else 267 | $bizString .= "&" . $key . "=" . $value; 268 | } 269 | return "weixin://wxpay/bizpayurl?".$bizString; 270 | //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX×tamp=XXXXXX&noncestr=XXXXXX 271 | } 272 | 273 | 274 | /** 275 | * 生成订单package字符串 276 | * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一 277 | * @param string $body 必填,商品描述,128 字节以下 278 | * @param int $total_fee 必填,订单总金额,单位为分 279 | * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内 280 | * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内 281 | * @param int $fee_type 必填,现金支付币种,默认1:人民币 282 | * @param string $bank_type 必填,银行通道类型,默认WX 283 | * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK 284 | * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss 285 | * @param string $time_expire 交易结束时间,也是订单失效时间 286 | * @param int $transport_fee 物流费用,单位为分 287 | * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee 288 | * @param string $goods_tag 商品标记,优惠券时可能用到 289 | * @param string $attach 附加数据,notify接口原样返回 290 | * @return string 291 | */ 292 | 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=""){ 293 | $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); 294 | if ($time_start) $arrdata['time_start'] = $time_start; 295 | if ($time_expire) $arrdata['time_expire'] = $time_expire; 296 | if ($transport_fee) $arrdata['transport_fee'] = $transport_fee; 297 | if ($product_fee) $arrdata['product_fee'] = $product_fee; 298 | if ($goods_tag) $arrdata['goods_tag'] = $goods_tag; 299 | if ($attach) $arrdata['attach'] = $attach; 300 | ksort($arrdata); 301 | $paramstring = ""; 302 | foreach($arrdata as $key => $value) 303 | { 304 | if(strlen($paramstring) == 0) 305 | $paramstring .= $key . "=" . $value; 306 | else 307 | $paramstring .= "&" . $key . "=" . $value; 308 | } 309 | $stringSignTemp = $paramstring . "&key=" . $this->partnerkey; 310 | $signValue = strtoupper(md5($stringSignTemp)); 311 | $package = http_build_query($arrdata) . "&sign=" . $signValue; 312 | return $package; 313 | } 314 | 315 | /** 316 | * 支付签名(paySign)生成方法 317 | * @param string $package 订单详情字串 318 | * @param string $timeStamp 当前时间戳(需与JS输出的一致) 319 | * @param string $nonceStr 随机串(需与JS输出的一致) 320 | * @return string 返回签名字串 321 | */ 322 | public function getPaySign($package, $timeStamp, $nonceStr){ 323 | $arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey); 324 | $paySign = $this->getSignature($arrdata); 325 | return $paySign; 326 | } 327 | 328 | /** 329 | * 回调通知签名验证 330 | * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取 331 | * @return boolean 332 | */ 333 | public function checkOrderSignature($orderxml=''){ 334 | if (!$orderxml) { 335 | $postStr = file_get_contents("php://input"); 336 | if (!empty($postStr)) { 337 | $orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 338 | } else return false; 339 | } 340 | $arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']); 341 | $paySign = $this->getSignature($arrdata); 342 | if ($paySign!=$orderxml['AppSignature']) return false; 343 | return true; 344 | } 345 | 346 | /** 347 | * 发货通知 348 | * @param string $openid 用户open_id 349 | * @param string $transid 交易单号 350 | * @param string $out_trade_no 第三方订单号 351 | * @param int $status 0:发货失败;1:已发货 352 | * @param string $msg 失败原因 353 | * @return boolean|array 354 | */ 355 | public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){ 356 | if (!$this->access_token && !$this->checkAuth()) return false; 357 | $postdata = array( 358 | "appid"=>$this->appid, 359 | "appkey"=>$this->paysignkey, 360 | "openid"=>$openid, 361 | "transid"=>strval($transid), 362 | "out_trade_no"=>strval($out_trade_no), 363 | "deliver_timestamp"=>strval(time()), 364 | "deliver_status"=>strval($status), 365 | "deliver_msg"=>$msg, 366 | ); 367 | $postdata['app_signature'] = $this->getSignature($postdata); 368 | $postdata['sign_method'] = 'sha1'; 369 | unset($postdata['appkey']); 370 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata)); 371 | if ($result) 372 | { 373 | $json = json_decode($result,true); 374 | if (!$json || !empty($json['errcode'])) { 375 | $this->errCode = $json['errcode']; 376 | $this->errMsg = $json['errmsg']; 377 | return false; 378 | } 379 | return $json; 380 | } 381 | return false; 382 | } 383 | 384 | /** 385 | * 查询订单信息 386 | * @param string $out_trade_no 订单号 387 | * @return boolean|array 388 | */ 389 | public function getPayOrder($out_trade_no) { 390 | if (!$this->access_token && !$this->checkAuth()) return false; 391 | $sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}")); 392 | $postdata = array( 393 | "appid"=>$this->appid, 394 | "appkey"=>$this->paysignkey, 395 | "package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign", 396 | "timestamp"=>strval(time()), 397 | ); 398 | $postdata['app_signature'] = $this->getSignature($postdata); 399 | $postdata['sign_method'] = 'sha1'; 400 | unset($postdata['appkey']); 401 | $result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata)); 402 | if ($result) 403 | { 404 | $json = json_decode($result,true); 405 | if (!$json || !empty($json['errcode'])) { 406 | $this->errCode = $json['errcode']; 407 | $this->errMsg = $json['errmsg'].json_encode($postdata); 408 | return false; 409 | } 410 | return $json["order_info"]; 411 | } 412 | return false; 413 | } 414 | 415 | /** 416 | * 设置用户授权密钥 417 | * @param string $user_token 418 | * @return string 419 | */ 420 | public function setUserToken($user_token) { 421 | return $this->user_token = $user_token; 422 | } 423 | 424 | /** 425 | * 获取收货地址JS的签名 426 | * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用 427 | * @param string $appId 428 | * @param string $url 429 | * @param int $timeStamp 430 | * @param string $nonceStr 431 | * @param string $user_token 432 | * @return Ambigous 433 | */ 434 | public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){ 435 | if (!$user_token) $user_token = $this->user_token; 436 | if (!$user_token) { 437 | $this->errMsg = 'no user access token found!'; 438 | return false; 439 | } 440 | $url = htmlspecialchars_decode($url); 441 | $arrdata = array( 442 | 'appid'=>$this->appid, 443 | 'url'=>$url, 444 | 'timestamp'=>strval($timeStamp), 445 | 'noncestr'=>$nonceStr, 446 | 'accesstoken'=>$user_token 447 | ); 448 | return $this->getSignature($arrdata); 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /qyerrCode.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://github.com/binsee/wechat-php-sdk 6 | * @version 1.0 7 | * usage: 8 | * $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到 9 | * if ($ret) 10 | * echo $ret; 11 | * else 12 | * echo "未找到对应的内容"; 13 | */ 14 | class ErrCode 15 | { 16 | public static $errCode=array( 17 | '-1'=>'系统繁忙', 18 | '0'=>'请求成功', 19 | '40001'=>'获取access_token时AppSecret错误,或者access_token无效', 20 | '40002'=>'不合法的凭证类型', 21 | '40003'=>'不合法的UserID', 22 | '40004'=>'不合法的媒体文件类型', 23 | '40005'=>'不合法的文件类型', 24 | '40006'=>'不合法的文件大小', 25 | '40007'=>'不合法的媒体文件id', 26 | '40008'=>'不合法的消息类型', 27 | '40013'=>'不合法的corpid', 28 | '40014'=>'不合法的access_token', 29 | '40015'=>'不合法的菜单类型', 30 | '40016'=>'不合法的按钮个数', 31 | '40017'=>'不合法的按钮类型', 32 | '40018'=>'不合法的按钮名字长度', 33 | '40019'=>'不合法的按钮KEY长度', 34 | '40020'=>'不合法的按钮URL长度', 35 | '40021'=>'不合法的菜单版本号', 36 | '40022'=>'不合法的子菜单级数', 37 | '40023'=>'不合法的子菜单按钮个数', 38 | '40024'=>'不合法的子菜单按钮类型', 39 | '40025'=>'不合法的子菜单按钮名字长度', 40 | '40026'=>'不合法的子菜单按钮KEY长度', 41 | '40027'=>'不合法的子菜单按钮URL长度', 42 | '40028'=>'不合法的自定义菜单使用员工', 43 | '40029'=>'不合法的oauth_code', 44 | '40031'=>'不合法的UserID列表', 45 | '40032'=>'不合法的UserID列表长度', 46 | '40033'=>'不合法的请求字符,不能包含\uxxxx格式的字符', 47 | '40035'=>'不合法的参数', 48 | '40038'=>'不合法的请求格式', 49 | '40039'=>'不合法的URL长度', 50 | '40040'=>'不合法的插件token', 51 | '40041'=>'不合法的插件id', 52 | '40042'=>'不合法的插件会话', 53 | '40048'=>'url中包含不合法domain', 54 | '40054'=>'不合法的子菜单url域名', 55 | '40055'=>'不合法的按钮url域名', 56 | '40056'=>'不合法的agentid', 57 | '40057'=>'不合法的callbackurl', 58 | '40058'=>'不合法的红包参数', 59 | '40059'=>'不合法的上报地理位置标志位', 60 | '40060'=>'设置上报地理位置标志位时没有设置callbackurl', 61 | '40061'=>'设置应用头像失败', 62 | '40062'=>'不合法的应用模式', 63 | '40063'=>'红包参数为空', 64 | '40064'=>'管理组名字已存在', 65 | '40065'=>'不合法的管理组名字长度', 66 | '40066'=>'不合法的部门列表', 67 | '40067'=>'标题长度不合法', 68 | '40068'=>'不合法的标签ID', 69 | '40069'=>'不合法的标签ID列表', 70 | '40070'=>'列表中所有标签(用户)ID都不合法', 71 | '40071'=>'不合法的标签名字,标签名字已经存在', 72 | '40072'=>'不合法的标签名字长度', 73 | '40073'=>'不合法的openid', 74 | '40074'=>'news消息不支持指定为高保密消息', 75 | '41001'=>'缺少access_token参数', 76 | '41002'=>'缺少corpid参数', 77 | '41003'=>'缺少refresh_token参数', 78 | '41004'=>'缺少secret参数', 79 | '41005'=>'缺少多媒体文件数据', 80 | '41006'=>'缺少media_id参数', 81 | '41007'=>'缺少子菜单数据', 82 | '41008'=>'缺少oauth code', 83 | '41009'=>'缺少UserID', 84 | '41010'=>'缺少url', 85 | '41011'=>'缺少agentid', 86 | '41012'=>'缺少应用头像mediaid', 87 | '41013'=>'缺少应用名字', 88 | '41014'=>'缺少应用描述', 89 | '41015'=>'缺少Content', 90 | '41016'=>'缺少标题', 91 | '41017'=>'缺少标签ID', 92 | '41018'=>'缺少标签名字', 93 | '42001'=>'access_token超时', 94 | '42002'=>'refresh_token超时', 95 | '42003'=>'oauth_code超时', 96 | '42004'=>'插件token超时', 97 | '43001'=>'需要GET请求', 98 | '43002'=>'需要POST请求', 99 | '43003'=>'需要HTTPS', 100 | '43004'=>'需要接收者关注', 101 | '43005'=>'需要好友关系', 102 | '43006'=>'需要订阅', 103 | '43007'=>'需要授权', 104 | '43008'=>'需要支付授权', 105 | '43009'=>'需要员工已关注', 106 | '43010'=>'需要处于企业模式', 107 | '43011'=>'需要企业授权', 108 | '44001'=>'多媒体文件为空', 109 | '44002'=>'POST的数据包为空', 110 | '44003'=>'图文消息内容为空', 111 | '44004'=>'文本消息内容为空', 112 | '45001'=>'多媒体文件大小超过限制', 113 | '45002'=>'消息内容超过限制', 114 | '45003'=>'标题字段超过限制', 115 | '45004'=>'描述字段超过限制', 116 | '45005'=>'链接字段超过限制', 117 | '45006'=>'图片链接字段超过限制', 118 | '45007'=>'语音播放时间超过限制', 119 | '45008'=>'图文消息超过限制', 120 | '45009'=>'接口调用超过限制', 121 | '45010'=>'创建菜单个数超过限制', 122 | '45015'=>'回复时间超过限制', 123 | '45016'=>'系统分组,不允许修改', 124 | '45017'=>'分组名字过长', 125 | '45018'=>'分组数量超过上限', 126 | '46001'=>'不存在媒体数据', 127 | '46002'=>'不存在的菜单版本', 128 | '46003'=>'不存在的菜单数据', 129 | '46004'=>'不存在的员工', 130 | '47001'=>'解析JSON/XML内容错误', 131 | '48002'=>'Api禁用', 132 | '50001'=>'redirect_uri未授权', 133 | '50002'=>'员工不在权限范围', 134 | '50003'=>'应用已停用', 135 | '50004'=>'员工状态不正确(未关注状态)', 136 | '50005'=>'企业已禁用', 137 | '60001'=>'部门长度不符合限制', 138 | '60002'=>'部门层级深度超过限制', 139 | '60003'=>'部门不存在', 140 | '60004'=>'父亲部门不存在', 141 | '60005'=>'不允许删除有成员的部门', 142 | '60006'=>'不允许删除有子部门的部门', 143 | '60007'=>'不允许删除根部门', 144 | '60008'=>'部门名称已存在', 145 | '60009'=>'部门名称含有非法字符', 146 | '60010'=>'部门存在循环关系', 147 | '60011'=>'管理员权限不足,(user/department/agent)无权限', 148 | '60012'=>'不允许删除默认应用', 149 | '60013'=>'不允许关闭应用', 150 | '60014'=>'不允许开启应用', 151 | '60015'=>'不允许修改默认应用可见范围', 152 | '60016'=>'不允许删除存在成员的标签', 153 | '60017'=>'不允许设置企业', 154 | '60102'=>'UserID已存在', 155 | '60103'=>'手机号码不合法', 156 | '60104'=>'手机号码已存在', 157 | '60105'=>'邮箱不合法', 158 | '60106'=>'邮箱已存在', 159 | '60107'=>'微信号不合法', 160 | '60108'=>'微信号已存在', 161 | '60109'=>'QQ号已存在', 162 | '60110'=>'部门个数超出限制', 163 | '60111'=>'UserID不存在', 164 | '60112'=>'成员姓名不合法', 165 | '60113'=>'身份认证信息(微信号/手机/邮箱)不能同时为空', 166 | '60114'=>'性别不合法', 167 | '60119'=>'用户已关注', 168 | '60120'=>'用户已禁用', 169 | '60121'=>'找不到该用户', 170 | '60023'=>'应用已授权予第三方,不允许通过分级管理员修改菜单', 171 | '80001'=>'可信域名没有IPC备案,后续将不能在该域名下正常使用jssdk', 172 | 173 | ); 174 | 175 | public static function getErrText($err) { 176 | if (isset(self::$errCode[$err])) { 177 | return self::$errCode[$err]; 178 | }else { 179 | return false; 180 | }; 181 | } 182 | } 183 | 184 | ?> -------------------------------------------------------------------------------- /test/auth.php: -------------------------------------------------------------------------------- 1 | options = $options; 13 | $this->wxoauth(); 14 | session_start(); 15 | } 16 | 17 | public function wxoauth(){ 18 | $scope = 'snsapi_base'; 19 | $code = isset($_GET['code'])?$_GET['code']:''; 20 | $token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0; 21 | if(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600) 22 | { 23 | if (!$this->wxuser) { 24 | $this->wxuser = $_SESSION['wxuser']; 25 | } 26 | $this->open_id = $_SESSION['open_id']; 27 | return $this->open_id; 28 | } 29 | else 30 | { 31 | $options = array( 32 | 'token'=>$this->options["token"], //填写你设定的key 33 | 'appid'=>$this->options["appid"], //填写高级调用功能的app id 34 | 'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥 35 | ); 36 | $we_obj = new Wechat($options); 37 | if ($code) { 38 | $json = $we_obj->getOauthAccessToken(); 39 | if (!$json) { 40 | unset($_SESSION['wx_redirect']); 41 | die('获取用户授权失败,请重新确认'); 42 | } 43 | $_SESSION['open_id'] = $this->open_id = $json["openid"]; 44 | $access_token = $json['access_token']; 45 | $_SESSION['user_token'] = $access_token; 46 | $_SESSION['token_time'] = time(); 47 | $userinfo = $we_obj->getUserInfo($this->open_id); 48 | if ($userinfo && !empty($userinfo['nickname'])) { 49 | $this->wxuser = array( 50 | 'open_id'=>$this->open_id, 51 | 'nickname'=>$userinfo['nickname'], 52 | 'sex'=>intval($userinfo['sex']), 53 | 'location'=>$userinfo['province'].'-'.$userinfo['city'], 54 | 'avatar'=>$userinfo['headimgurl'] 55 | ); 56 | } elseif (strstr($json['scope'],'snsapi_userinfo')!==false) { 57 | $userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id); 58 | if ($userinfo && !empty($userinfo['nickname'])) { 59 | $this->wxuser = array( 60 | 'open_id'=>$this->open_id, 61 | 'nickname'=>$userinfo['nickname'], 62 | 'sex'=>intval($userinfo['sex']), 63 | 'location'=>$userinfo['province'].'-'.$userinfo['city'], 64 | 'avatar'=>$userinfo['headimgurl'] 65 | ); 66 | } else { 67 | return $this->open_id; 68 | } 69 | } 70 | if ($this->wxuser) { 71 | $_SESSION['wxuser'] = $this->wxuser; 72 | $_SESSION['open_id'] = $json["openid"]; 73 | unset($_SESSION['wx_redirect']); 74 | return $this->open_id; 75 | } 76 | $scope = 'snsapi_userinfo'; 77 | } 78 | if ($scope=='snsapi_base') { 79 | $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; 80 | $_SESSION['wx_redirect'] = $url; 81 | } else { 82 | $url = $_SESSION['wx_redirect']; 83 | } 84 | if (!$url) { 85 | unset($_SESSION['wx_redirect']); 86 | die('获取用户授权失败'); 87 | } 88 | $oauth_url = $we_obj->getOauthRedirect($url,"wxbase",$scope); 89 | header('Location: ' . $oauth_url); 90 | } 91 | } 92 | } 93 | $options = array( 94 | 'token'=>'tokenaccesskey', //填写你设定的key 95 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 96 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 97 | ); 98 | $auth = new wxauth($options); 99 | var_dump($auth->wxuser); 100 | -------------------------------------------------------------------------------- /test/jsapi/jsapi-demo-6.1.js: -------------------------------------------------------------------------------- 1 | wx.ready(function () { 2 | alert("启动jsapi!"); 3 | // 1 判断当前版本是否支持指定 JS 接口,支持批量判断 4 | document.querySelector('#checkJsApi').onclick = function () { 5 | wx.checkJsApi({ 6 | jsApiList: [ 7 | 'checkJsApi', 8 | 'onMenuShareTimeline', 9 | 'onMenuShareAppMessage', 10 | 'onMenuShareQQ', 11 | 'onMenuShareWeibo', 12 | 'hideMenuItems', 13 | 'showMenuItems', 14 | 'hideAllNonBaseMenuItem', 15 | 'showAllNonBaseMenuItem', 16 | 'translateVoice', 17 | 'startRecord', 18 | 'stopRecord', 19 | 'onRecordEnd', 20 | 'playVoice', 21 | 'pauseVoice', 22 | 'stopVoice', 23 | 'uploadVoice', 24 | 'downloadVoice', 25 | 'chooseImage', 26 | 'previewImage', 27 | 'uploadImage', 28 | 'downloadImage', 29 | 'getNetworkType', 30 | 'openLocation', 31 | 'getLocation', 32 | 'hideOptionMenu', 33 | 'showOptionMenu', 34 | 'closeWindow', 35 | 'scanQRCode', 36 | 'chooseWXPay', 37 | 'openProductSpecificView', 38 | 'addCard', 39 | 'chooseCard', 40 | 'openCard' 41 | ], 42 | success: function (res) { 43 | alert("检测通过:" +JSON.stringify(res)); 44 | }, 45 | fail: function(res) { 46 | alert("检测失败:" +JSON.stringify(res)); 47 | }, 48 | complete: function(res) { 49 | alert("检测结束"); 50 | } 51 | }); 52 | }; 53 | 54 | // 2. 分享接口 55 | // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口 56 | document.querySelector('#onMenuShareAppMessage').onclick = function () { 57 | wx.onMenuShareAppMessage({ 58 | title: 'wechat-php-sdk博客', 59 | desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', 60 | link: 'http://binsee.github.io/wechat-php-sdk/', 61 | imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', 62 | trigger: function (res) { 63 | alert("点击分享:" +JSON.stringify(res)); 64 | // 用户确认分享后执行的回调函数 65 | }, 66 | success: function (res) { 67 | alert("分享成功:" +JSON.stringify(res)); 68 | // 用户确认分享后执行的回调函数 69 | }, 70 | cancel: function (res) { 71 | alert("取消分享:" +JSON.stringify(res)); 72 | // 用户取消分享后执行的回调函数 73 | }, 74 | fail:function (res) { 75 | alert("分享失败:" +JSON.stringify(res)); 76 | } 77 | }); 78 | alert('已注册获取“发送给朋友”状态事件'); 79 | }; 80 | 81 | // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口 82 | document.querySelector('#onMenuShareTimeline').onclick = function () { 83 | wx.onMenuShareTimeline({ 84 | title: 'wechat-php-sdk博客', 85 | desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', 86 | link: 'http://binsee.github.io/wechat-php-sdk/', 87 | imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', 88 | trigger: function (res) { 89 | alert("点击分享:" +JSON.stringify(res)); 90 | // 用户确认分享后执行的回调函数 91 | }, 92 | success: function (res) { 93 | alert("分享成功:" +JSON.stringify(res)); 94 | // 用户确认分享后执行的回调函数 95 | }, 96 | cancel: function (res) { 97 | alert("取消分享:" +JSON.stringify(res)); 98 | // 用户取消分享后执行的回调函数 99 | }, 100 | fail:function (res) { 101 | alert("分享失败:" +JSON.stringify(res)); 102 | } 103 | }); 104 | alert('已注册获取“分享到朋友圈”状态事件'); 105 | }; 106 | 107 | // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口 108 | document.querySelector('#onMenuShareQQ').onclick = function () { 109 | wx.onMenuShareQQ({ 110 | title: 'wechat-php-sdk博客', 111 | desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', 112 | link: 'http://binsee.github.io/wechat-php-sdk/', 113 | imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', 114 | trigger: function (res) { 115 | alert("点击分享:" +JSON.stringify(res)); 116 | // 用户确认分享后执行的回调函数 117 | }, 118 | success: function (res) { 119 | alert("分享成功:" +JSON.stringify(res)); 120 | // 用户确认分享后执行的回调函数 121 | }, 122 | cancel: function (res) { 123 | alert("取消分享:" +JSON.stringify(res)); 124 | // 用户取消分享后执行的回调函数 125 | }, 126 | fail:function (res) { 127 | alert("分享失败:" +JSON.stringify(res)); 128 | } 129 | }); 130 | alert('已注册获取“分享到 QQ”状态事件'); 131 | }; 132 | 133 | // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口 134 | document.querySelector('#onMenuShareWeibo').onclick = function () { 135 | wx.onMenuShareWeibo({ 136 | title: 'wechat-php-sdk博客', 137 | desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人:dodgepudding 项目地址:https://github.com/dodgepudding/wechat-php-sdk', 138 | link: 'http://binsee.github.io/wechat-php-sdk/', 139 | imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg', 140 | trigger: function (res) { 141 | alert("点击分享:" +JSON.stringify(res)); 142 | // 用户确认分享后执行的回调函数 143 | }, 144 | success: function (res) { 145 | alert("分享成功:" +JSON.stringify(res)); 146 | // 用户确认分享后执行的回调函数 147 | }, 148 | cancel: function (res) { 149 | alert("取消分享:" +JSON.stringify(res)); 150 | // 用户取消分享后执行的回调函数 151 | }, 152 | fail:function (res) { 153 | alert("分享失败:" +JSON.stringify(res)); 154 | } 155 | }); 156 | alert('已注册获取“分享到微博”状态事件'); 157 | }; 158 | 159 | 160 | // 3 智能接口 161 | var voice = { 162 | localId: '', 163 | serverId: '' 164 | }; 165 | // 3.1 识别音频并返回识别结果 166 | document.querySelector('#translateVoice').onclick = function () { 167 | if (voice.localId == '') { 168 | alert('请先使用 startRecord 接口录制一段声音'); 169 | return; 170 | } 171 | wx.translateVoice({ 172 | localId: voice.localId, 173 | complete: function (res) { 174 | if (res.hasOwnProperty('translateResult')) { 175 | alert('识别结果:' + res.translateResult); 176 | } else { 177 | alert('无法识别'); 178 | } 179 | } 180 | }); 181 | }; 182 | 183 | // 4 音频接口 184 | // 4.2 开始录音 185 | document.querySelector('#startRecord').onclick = function () { 186 | wx.startRecord({ 187 | cancel: function () { 188 | alert('用户拒绝授权录音'); 189 | } 190 | }); 191 | }; 192 | 193 | // 4.3 停止录音 194 | document.querySelector('#stopRecord').onclick = function () { 195 | wx.stopRecord({ 196 | success: function (res) { 197 | voice.localId = res.localId; 198 | }, 199 | fail: function (res) { 200 | alert(JSON.stringify(res)); 201 | } 202 | }); 203 | }; 204 | 205 | // 4.4 监听录音自动停止 206 | wx.onVoiceRecordEnd({ 207 | complete: function (res) { 208 | voice.localId = res.localId; 209 | alert('录音时间已超过一分钟'); 210 | } 211 | }); 212 | 213 | // 4.5 播放音频 214 | document.querySelector('#playVoice').onclick = function () { 215 | if (voice.localId == '') { 216 | alert('请先使用 startRecord 接口录制一段声音'); 217 | return; 218 | } 219 | wx.playVoice({ 220 | localId: voice.localId 221 | }); 222 | }; 223 | 224 | // 4.6 暂停播放音频 225 | document.querySelector('#pauseVoice').onclick = function () { 226 | wx.pauseVoice({ 227 | localId: voice.localId 228 | }); 229 | }; 230 | 231 | // 4.7 停止播放音频 232 | document.querySelector('#stopVoice').onclick = function () { 233 | wx.stopVoice({ 234 | localId: voice.localId 235 | }); 236 | }; 237 | 238 | // 4.8 监听录音播放停止 239 | wx.onVoicePlayEnd({ 240 | complete: function (res) { 241 | alert('录音(' + res.localId + ')播放结束'); 242 | } 243 | }); 244 | 245 | // 4.8 上传语音 246 | document.querySelector('#uploadVoice').onclick = function () { 247 | if (voice.localId == '') { 248 | alert('请先使用 startRecord 接口录制一段声音'); 249 | return; 250 | } 251 | wx.uploadVoice({ 252 | localId: voice.localId, 253 | success: function (res) { 254 | alert('上传语音成功,serverId 为' + res.serverId); 255 | voice.serverId = res.serverId; 256 | alert("上传语音信息:" + JSON.stringify(res)); 257 | } 258 | }); 259 | }; 260 | 261 | // 4.9 下载语音 262 | document.querySelector('#downloadVoice').onclick = function () { 263 | if (voice.serverId == '') { 264 | alert('请先使用 uploadVoice 上传声音'); 265 | return; 266 | } 267 | wx.downloadVoice({ 268 | serverId: voice.serverId, 269 | success: function (res) { 270 | alert('下载语音成功,localId 为' + res.localId); 271 | voice.localId = res.localId; 272 | alert("下载语音信息:" + JSON.stringify(res)); 273 | } 274 | }); 275 | }; 276 | 277 | // 5 图片接口 278 | // 5.1 拍照、本地选图 279 | var images = { 280 | localId: [], 281 | serverId: [] 282 | }; 283 | document.querySelector('#chooseImage').onclick = function () { 284 | wx.chooseImage({ 285 | success: function (res) { 286 | images.localId = res.localIds; 287 | alert('已选择 ' + res.localIds.length + ' 张图片'); 288 | } 289 | }); 290 | }; 291 | 292 | // 5.2 图片预览 293 | document.querySelector('#previewImage').onclick = function () { 294 | wx.previewImage({ 295 | current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', 296 | urls: [ 297 | 'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg', 298 | 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', 299 | 'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg' 300 | ] 301 | }); 302 | }; 303 | 304 | // 5.3 上传图片 305 | document.querySelector('#uploadImage').onclick = function () { 306 | if (images.localId.length == 0) { 307 | alert('请先使用 chooseImage 接口选择图片'); 308 | return; 309 | } 310 | var i = 0, length = images.localId.length; 311 | images.serverId = []; 312 | function upload() { 313 | wx.uploadImage({ 314 | localId: images.localId[i], 315 | success: function (res) { 316 | i++; 317 | alert('已上传:' + i + '/' + length); 318 | alert("上传图片信息:" + JSON.stringify(res)); 319 | images.serverId.push(res.serverId); 320 | if (i < length) { 321 | upload(); 322 | } 323 | }, 324 | fail: function (res) { 325 | alert(JSON.stringify(res)); 326 | } 327 | }); 328 | } 329 | upload(); 330 | }; 331 | 332 | // 5.4 下载图片 333 | document.querySelector('#downloadImage').onclick = function () { 334 | if (images.serverId.length === 0) { 335 | alert('请先使用 uploadImage 上传图片'); 336 | return; 337 | } 338 | var i = 0, length = images.serverId.length; 339 | images.localId = []; 340 | function download() { 341 | wx.downloadImage({ 342 | serverId: images.serverId[i], 343 | success: function (res) { 344 | i++; 345 | alert('已下载:' + i + '/' + length); 346 | alert("下载图片信息:" + JSON.stringify(res)); 347 | images.localId.push(res.localId); 348 | if (i < length) { 349 | download(); 350 | } 351 | } 352 | }); 353 | } 354 | download(); 355 | }; 356 | 357 | // 6 设备信息接口 358 | // 6.1 获取当前网络状态 359 | document.querySelector('#getNetworkType').onclick = function () { 360 | wx.getNetworkType({ 361 | success: function (res) { 362 | alert(res.networkType); 363 | }, 364 | fail: function (res) { 365 | alert(JSON.stringify(res)); 366 | } 367 | }); 368 | }; 369 | 370 | // 7 地理位置接口 371 | // 7.1 查看地理位置 372 | document.querySelector('#openLocation').onclick = function () { 373 | wx.openLocation({ 374 | latitude: 23.099994, 375 | longitude: 113.324520, 376 | name: 'TIT 创意园', 377 | address: '广州市海珠区新港中路 397 号', 378 | scale: 14, 379 | infoUrl: 'http://weixin.qq.com' 380 | }); 381 | }; 382 | 383 | // 7.2 获取当前地理位置 384 | document.querySelector('#getLocation').onclick = function () { 385 | wx.getLocation({ 386 | success: function (res) { 387 | alert(JSON.stringify(res)); 388 | }, 389 | cancel: function (res) { 390 | alert('用户拒绝授权获取地理位置'); 391 | } 392 | }); 393 | }; 394 | 395 | // 8 界面操作接口 396 | // 8.1 隐藏右上角菜单 397 | document.querySelector('#hideOptionMenu').onclick = function () { 398 | wx.hideOptionMenu(); 399 | }; 400 | 401 | // 8.2 显示右上角菜单 402 | document.querySelector('#showOptionMenu').onclick = function () { 403 | wx.showOptionMenu(); 404 | }; 405 | 406 | // 8.3 批量隐藏菜单项 407 | document.querySelector('#hideMenuItems').onclick = function () { 408 | wx.hideMenuItems({ 409 | menuList: [ 410 | 'menuItem:readMode', // 阅读模式 411 | 'menuItem:share:timeline', // 分享到朋友圈 412 | 'menuItem:copyUrl' // 复制链接 413 | ], 414 | success: function (res) { 415 | alert('已隐藏“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); 416 | }, 417 | fail: function (res) { 418 | alert(JSON.stringify(res)); 419 | } 420 | }); 421 | }; 422 | 423 | // 8.4 批量显示菜单项 424 | document.querySelector('#showMenuItems').onclick = function () { 425 | wx.showMenuItems({ 426 | menuList: [ 427 | 'menuItem:readMode', // 阅读模式 428 | 'menuItem:share:timeline', // 分享到朋友圈 429 | 'menuItem:copyUrl' // 复制链接 430 | ], 431 | success: function (res) { 432 | alert('已显示“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); 433 | }, 434 | fail: function (res) { 435 | alert(JSON.stringify(res)); 436 | } 437 | }); 438 | }; 439 | 440 | // 8.5 隐藏所有非基本菜单项 441 | document.querySelector('#hideAllNonBaseMenuItem').onclick = function () { 442 | wx.hideAllNonBaseMenuItem({ 443 | success: function () { 444 | alert('已隐藏所有非基本菜单项'); 445 | } 446 | }); 447 | }; 448 | 449 | // 8.6 显示所有被隐藏的非基本菜单项 450 | document.querySelector('#showAllNonBaseMenuItem').onclick = function () { 451 | wx.showAllNonBaseMenuItem({ 452 | success: function () { 453 | alert('已显示所有非基本菜单项'); 454 | } 455 | }); 456 | }; 457 | 458 | // 8.7 关闭当前窗口 459 | document.querySelector('#closeWindow').onclick = function () { 460 | wx.closeWindow(); 461 | }; 462 | 463 | // 9 微信原生接口 464 | // 9.1.1 扫描二维码并返回结果 465 | document.querySelector('#scanQRCode0').onclick = function () { 466 | wx.scanQRCode({ 467 | desc: 'scanQRCode desc' 468 | }); 469 | }; 470 | // 9.1.2 扫描二维码并返回结果 471 | document.querySelector('#scanQRCode1').onclick = function () { 472 | wx.scanQRCode({ 473 | needResult: 1, 474 | desc: 'scanQRCode desc', 475 | success: function (res) { 476 | alert(JSON.stringify(res)); 477 | } 478 | }); 479 | }; 480 | 481 | // 10 微信支付接口 482 | // 10.1 发起一个支付请求 483 | document.querySelector('#chooseWXPay').onclick = function () { 484 | wx.chooseWXPay({ 485 | timestamp: 1414723227, 486 | nonceStr: 'noncestr', 487 | 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', 488 | paySign: 'bd5b1933cda6e9548862944836a9b52e8c9a2b69' 489 | }); 490 | }; 491 | 492 | // 11.3 跳转微信商品页 493 | document.querySelector('#openProductSpecificView').onclick = function () { 494 | wx.openProductSpecificView({ 495 | productId: 'pDF3iY0ptap-mIIPYnsM5n8VtCR0' 496 | }); 497 | }; 498 | 499 | // 12 微信卡券接口 500 | // 12.1 添加卡券 501 | document.querySelector('#addCard').onclick = function () { 502 | wx.addCard({ 503 | cardList: [ 504 | { 505 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', 506 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' 507 | }, 508 | { 509 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', 510 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' 511 | } 512 | ], 513 | success: function (res) { 514 | alert('已添加卡券:' + JSON.stringify(res.cardList)); 515 | } 516 | }); 517 | }; 518 | 519 | // 12.2 选择卡券 520 | document.querySelector('#chooseCard').onclick = function () { 521 | wx.chooseCard({ 522 | cardSign: '97e9c5e58aab3bdf6fd6150e599d7e5806e5cb91', 523 | timestamp: 1417504553, 524 | nonceStr: 'k0hGdSXKZEj3Min5', 525 | success: function (res) { 526 | alert('已选择卡券:' + JSON.stringify(res.cardList)); 527 | } 528 | }); 529 | }; 530 | 531 | // 12.3 查看卡券 532 | document.querySelector('#openCard').onclick = function () { 533 | alert('您没有该公众号的卡券无法打开卡券。'); 534 | wx.openCard({ 535 | cardList: [ 536 | ] 537 | }); 538 | }; 539 | }); 540 | 541 | wx.error(function (res) { 542 | alert(res.errMsg); 543 | }); 544 | -------------------------------------------------------------------------------- /test/jsapi/jsapi_demo.php: -------------------------------------------------------------------------------- 1 | 'xxxxxxxxxxxxxxxxxxxxxxxxxx',//填写高级调用功能的密钥 7 | 'appid'=>'wxxxxxxxxxxxxxx' //填写高级调用功能的appid 8 | ); 9 | 10 | $we = new Wechat($opt); 11 | $auth = $we->checkAuth(); 12 | $js_ticket = $we->getJsTicket(); 13 | if (!$js_ticket) { 14 | echo "获取js_ticket失败!
"; 15 | echo '错误码:'.$we->errCode; 16 | echo ' 错误原因:'.ErrCode::getErrText($weObj->errCode); 17 | exit; 18 | } 19 | $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; 20 | $js_sign = $we->getJsSign($url); 21 | ?> 22 | 23 | 24 | 25 | 26 | JS-SDK测试页 27 | 28 | 29 | 30 | 31 |
32 | 48 |
49 | 50 | 判断当前客户端是否支持指定JS接口 51 | 52 | 53 | 54 | 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 55 | 56 | 获取“分享给朋友”按钮点击状态及自定义分享内容接口 57 | 58 | 获取“分享到QQ”按钮点击状态及自定义分享内容接口 59 | 60 | 获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 61 | 62 | 63 | 64 | 拍照或从手机相册中选图接口 65 | 66 | 预览图片接口 67 | 68 | 上传图片接口 69 | 70 | 下载图片接口 71 | 72 | 73 | 74 | 开始录音接口 75 | 76 | 停止录音接口 77 | 78 | 播放语音接口 79 | 80 | 暂停播放接口 81 | 82 | 停止播放接口 83 | 84 | 上传语音接口 85 | 86 | 下载语音接口 87 | 88 | 89 | 90 | 识别音频并返回识别结果接口 91 | 92 | 93 | 94 | 获取网络状态接口 95 | 96 | 97 | 98 | 使用微信内置地图查看位置接口 99 | 100 | 获取地理位置接口 101 | 102 | 103 | 104 | 隐藏右上角菜单接口 105 | 106 | 显示右上角菜单接口 107 | 108 | 关闭当前网页窗口接口 109 | 110 | 批量隐藏功能按钮接口 111 | 112 | 批量显示功能按钮接口 113 | 114 | 隐藏所有非基础按钮接口 115 | 116 | 显示所有功能按钮接口 117 | 118 | 119 | 120 | 调起微信扫一扫接口 121 | 122 | 123 | 124 | 125 | 跳转微信商品页接口 126 | 127 | 128 | 129 | 批量添加卡券接口 130 | 131 | 调起适用于门店的卡券列表并获取用户选择列表 132 | 133 | 查看微信卡包中的卡券接口 134 | 135 | 136 | 137 | 发起一个微信支付请求 138 | 139 |
140 |
141 | 142 | 143 | 188 | 189 | -------------------------------------------------------------------------------- /test/jsapi/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | -ms-text-size-adjust: 100%; 3 | -webkit-text-size-adjust: 100%; 4 | -webkit-user-select: none; 5 | user-select: none; 6 | } 7 | body { 8 | line-height: 1.6; 9 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 10 | background-color: #f1f0f6; 11 | } 12 | * { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | button { 17 | font-family: inherit; 18 | font-size: 100%; 19 | margin: 0; 20 | *font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 21 | } 22 | ul, 23 | ol { 24 | padding-left: 0; 25 | list-style-type: none; 26 | } 27 | a { 28 | text-decoration: none; 29 | } 30 | .label_box { 31 | background-color: #ffffff; 32 | } 33 | .label_item { 34 | padding-left: 15px; 35 | } 36 | .label_inner { 37 | padding-top: 10px; 38 | padding-bottom: 10px; 39 | min-height: 24px; 40 | position: relative; 41 | } 42 | .label_inner:before { 43 | content: " "; 44 | position: absolute; 45 | left: 0; 46 | top: 0; 47 | width: 200%; 48 | height: 1px; 49 | border-top: 1px solid #ededed; 50 | -webkit-transform-origin: 0 0; 51 | transform-origin: 0 0; 52 | -webkit-transform: scale(0.5); 53 | transform: scale(0.5); 54 | top: auto; 55 | bottom: -2px; 56 | } 57 | .lbox_close { 58 | position: relative; 59 | } 60 | .lbox_close:before { 61 | content: " "; 62 | position: absolute; 63 | left: 0; 64 | top: 0; 65 | width: 200%; 66 | height: 1px; 67 | border-top: 1px solid #ededed; 68 | -webkit-transform-origin: 0 0; 69 | transform-origin: 0 0; 70 | -webkit-transform: scale(0.5); 71 | transform: scale(0.5); 72 | } 73 | .lbox_close:after { 74 | content: " "; 75 | position: absolute; 76 | left: 0; 77 | top: 0; 78 | width: 200%; 79 | height: 1px; 80 | border-top: 1px solid #ededed; 81 | -webkit-transform-origin: 0 0; 82 | transform-origin: 0 0; 83 | -webkit-transform: scale(0.5); 84 | transform: scale(0.5); 85 | top: auto; 86 | bottom: -2px; 87 | } 88 | .lbox_close .label_item:last-child .label_inner:before { 89 | display: none; 90 | } 91 | .btn { 92 | display: block; 93 | margin-left: auto; 94 | margin-right: auto; 95 | padding-left: 14px; 96 | padding-right: 14px; 97 | font-size: 18px; 98 | text-align: center; 99 | text-decoration: none; 100 | overflow: visible; 101 | /*.btn_h(@btnHeight);*/ 102 | height: 42px; 103 | border-radius: 5px; 104 | -moz-border-radius: 5px; 105 | -webkit-border-radius: 5px; 106 | box-sizing: border-box; 107 | -moz-box-sizing: border-box; 108 | -webkit-box-sizing: border-box; 109 | color: #ffffff; 110 | line-height: 42px; 111 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 112 | } 113 | .btn.btn_inline { 114 | display: inline-block; 115 | } 116 | .btn_primary { 117 | background-color: #04be02; 118 | } 119 | .btn_primary:not(.btn_disabled):visited { 120 | color: #ffffff; 121 | } 122 | .btn_primary:not(.btn_disabled):active { 123 | color: rgba(255, 255, 255, 0.9); 124 | background-color: #039702; 125 | } 126 | button.btn { 127 | width: 100%; 128 | border: 0; 129 | outline: 0; 130 | -webkit-appearance: none; 131 | } 132 | button.btn:focus { 133 | outline: 0; 134 | } 135 | .wxapi_container { 136 | font-size: 16px; 137 | } 138 | h1 { 139 | font-size: 14px; 140 | font-weight: 400; 141 | line-height: 2em; 142 | padding-left: 15px; 143 | color: #8d8c92; 144 | } 145 | .desc { 146 | font-size: 14px; 147 | font-weight: 400; 148 | line-height: 2em; 149 | color: #8d8c92; 150 | } 151 | .wxapi_index_item a { 152 | display: block; 153 | color: #3e3e3e; 154 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 155 | } 156 | .wxapi_form { 157 | background-color: #ffffff; 158 | padding: 0 15px; 159 | margin-top: 30px; 160 | padding-bottom: 15px; 161 | } 162 | h3 { 163 | padding-top: 16px; 164 | margin-top: 25px; 165 | font-size: 16px; 166 | font-weight: 400; 167 | color: #3e3e3e; 168 | position: relative; 169 | } 170 | h3:first-child { 171 | padding-top: 15px; 172 | } 173 | h3:before { 174 | content: " "; 175 | position: absolute; 176 | left: 0; 177 | top: 0; 178 | width: 200%; 179 | height: 1px; 180 | border-top: 1px solid #ededed; 181 | -webkit-transform-origin: 0 0; 182 | transform-origin: 0 0; 183 | -webkit-transform: scale(0.5); 184 | transform: scale(0.5); 185 | } 186 | .btn { 187 | margin-bottom: 15px; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /test/qydemo.php: -------------------------------------------------------------------------------- 1 | '9xxxxxxxxxxxx', //填写应用接口的Token 10 | 'encodingaeskey'=>'d4oxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 11 | 'appid'=>'wxa0xxxxxxxxxx', //填写高级调用功能的appid 12 | 'debug'=>true, 13 | 'logcallback'=>'logg' 14 | 15 | ); 16 | logg("GET参数为:\n".var_export($_GET,true)); 17 | $weObj = new Wechat($options); 18 | $ret=$weObj->valid(); 19 | if (!$ret) { 20 | logg("验证失败!"); 21 | var_dump($ret); 22 | exit; 23 | } 24 | $f = $weObj->getRev()->getRevFrom(); 25 | $t = $weObj->getRevType(); 26 | $d = $weObj->getRevData(); 27 | $weObj->text("你好!来自星星的:".$f."\n你发送的".$t."类型信息:\n原始信息如下:\n".var_export($d,true))->reply(); 28 | logg("-----------------------------------------"); 29 | ?> -------------------------------------------------------------------------------- /test/test1.php: -------------------------------------------------------------------------------- 1 | 'tokenaccesskey', //填写你设定的key 13 | 'debug'=>true, 14 | 'logcallback'=>'logdebug' 15 | ); 16 | $weObj = new Wechat($options); 17 | $weObj->valid(); 18 | $type = $weObj->getRev()->getRevType(); 19 | switch($type) { 20 | case Wechat::MSGTYPE_TEXT: 21 | $weObj->text("hello, I'm wechat")->reply(); 22 | exit; 23 | break; 24 | case Wechat::MSGTYPE_EVENT: 25 | break; 26 | case Wechat::MSGTYPE_IMAGE: 27 | break; 28 | default: 29 | $weObj->text("help info")->reply(); 30 | } 31 | -------------------------------------------------------------------------------- /wiki/API接口错误码.md: -------------------------------------------------------------------------------- 1 | ## errCode.php 2 | ### 关于API接口错误码有两个版本: 3 | **一个是普通公众号平台的errCode.php; 4 | 一个是企业号平台的 qyerrCode.php 5 | 用法都是一样的。** 6 | 7 | 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 8 | 9 | 使用方法: 10 | ```php 11 | include "errCode.php"; //或 qyerrCode.php 12 | 13 | $ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到 14 | 15 | if ($ret) 16 | echo $ret; 17 | else 18 | echo "未找到对应的内容"; 19 | 20 | ``` -------------------------------------------------------------------------------- /wiki/Home.md: -------------------------------------------------------------------------------- 1 | # wechat-php-sdk 2 | 3 | 微信公众平台php开发包,细化各项接口操作,支持链式调用 4 | 项目地址:**https://github.com/dodgepudding/wechat-php-sdk** 5 | 项目wiki:**http://binsee.github.io/wechat-php-sdk** 6 | 7 | ---- 8 | 9 | ## 使用详解 10 | 使用前需先打开微信帐号的开发模式,详细步骤请查看微信公众平台接口使用说明: 11 | 微信公众平台: http://mp.weixin.qq.com/wiki/ 12 | 微信企业平台: http://qydev.weixin.qq.com/wiki/ 13 | 14 | 微信支付接入文档:https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl 15 | 16 | 微信多客服:http://dkf.qq.com 17 | 18 | ## 功能目录 19 | 20 | - [[官方API类库]] 21 | > wechat.class.php 22 | > 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 23 | 24 | - [[企业号API类库]] 25 | > qywechat.class.php 26 | > 微信公众平台企业号PHP-SDK 27 | > 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 28 | 29 | - [[API接口错误码]] 30 | > errCode.php 或 qyerrCode.php 31 | > 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 32 | 33 | - [[旧版微信支付V2接口类库]] 34 | > old_version/wechatpay.class.php 35 | > 当调用API接口失败时,可以用此类来换取失败原因的中文说明。 36 | 37 | - ~~[[非官方扩展类库]]~~*(停止维护)* 38 | > old_version/wechatext.class.php 39 | > 非官方扩展API,模拟人工操作微信平台,此方式不保证长期有效。 40 | 41 | - ~~[[授权登陆类库]]~~*(停止维护)* 42 | > old_version/wechatauth.class.php 43 | > 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆 44 | 45 | - ~~[[内嵌JS]]~~*(已废弃)* 46 | > old_version/wechat.js 47 | > 微信内嵌网页功能调用js 48 | 49 | - [[为开发框架进行适配]] 50 | > 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /wiki/README.md: -------------------------------------------------------------------------------- 1 | wiki目录说明 2 | ============== 3 | 这个目录是wechat-php-sdk项目的wiki文档 4 | Make By:binsee 5 | 6 | ##说明 7 | **这里的wiki文档可以让你更好的了解`wechat-php-sdk`项目,更好的使用。 ** 8 | 9 | **欢迎对wiki文档内容进行补充,把`wechat-php-sdk`项目变得更清晰易懂。** 10 | 11 | ##为你的github生成wiki 12 | **如果你在github上fork了`wechat-php-sdk`项目,而且想为项目生成wiki,可以用这里的文件来生成。** 13 | 14 | 15 | ###使用步骤: 16 | 1. 在你的github上,fork或者创建`wechat-php-sdk`项目 17 | 18 | 2. 激活项目wiki,已激活的请跳过 19 | ``` 20 | 进入项目的设置页面: 21 | https://github.com/你的用户名/wechat-php-sdk/settings 22 | 找到Features一栏,把 Wikis 选项打钩,就可以激活你项目的wiki了 23 | ``` 24 | 25 | 3. 进入项目的wiki页面: 26 | `https://github.com/你的用户名/wechat-php-sdk/wiki` 27 | 28 | 4. 点绿色的 `Create the first page` 按钮 29 | 30 | 5. 直接到下方点 `Save Page` 按钮 31 | 32 | 6. 在右边找到 `Clone this wiki locally` 一栏,复制git地址: 33 | `git@github.com:你的用户名/wechat-php-sdk.wiki.git` 34 | 35 | 7. 在项目的上一层目录执行 36 | `git clone git@github.com:你的用户名/wechat-php-sdk.wiki.git` 37 | 38 | 8. 进入新出现的 `wechat-php-sdk.wiki` 目录,把wiki目录下的文件都复制过来 39 | > **这里有个高级用法,就是使用连接方式把wiki目录链接过来,而不是复制** 40 | > windows下的用法: 41 | ``` 42 | #如项目目录为:E:\wechat-php-sdk\ 43 | #项目wiki目录为:E:\wechat-php-sdk.wiki\ 44 | 执行命令: 45 | mklink /j E:\wechat-php-sdk.wiki\wiki E:\wechat-php-sdk\wiki 46 | ``` 47 | > 这样的话,两个目录就会被联接到一起。 48 | > 以后进行更改wiki在哪个目录都行,另一个目录都是同步的。 49 | > 分别在 项目目录 和 项目wiki目录 进行git提交就可以了。 50 | 51 | 9. 然后直接缓存上传即可。 52 | 53 | 10. 现在去你github上项目的wiki目录里看一下吧 54 | 55 | 56 | ##生成page 57 | **你也可以将wiki文档,生成为个人站点,会更加直观。** 58 | 59 | **比如使用 Hexo 或者其他的框架之类。** 60 | 61 | **这块的话,请自行搜索相关资料。** -------------------------------------------------------------------------------- /wiki/为开发框架进行适配.md: -------------------------------------------------------------------------------- 1 | #为开发框架进行适配 2 | 3 | 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket),及输出调试日志。 4 | 5 | 由于微信api需要缓存access_token与jsapi_ticket,而在不同框架下的缓存方式不同,所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。 6 | 需要各位在使用不同框架时再进行修改,但确实很麻烦,因为对结构进行了修改。 7 | 8 | >取消了原先同步维护的Thinkphp版本,为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`,以及修改`log`方法可以重载。 9 | >分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。 10 | 11 | 在不同的开发框架下使用Wechat类库,请继承Wechat类,根据需要实现这4个方法。 12 | 可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。 13 | 欢迎提交其他框架的适配文件到项目库来。 14 | 15 | 为Thinkphp进行适配的示例如下: 16 | ```php 17 | /** 18 | * 微信公众平台PHP-SDK, ThinkPHP实例 19 | * @author dodgepudding@gmail.com 20 | * @link https://github.com/dodgepudding/wechat-php-sdk 21 | * @version 1.2 22 | * usage: 23 | * $options = array( 24 | * 'token'=>'tokenaccesskey', //填写你设定的key 25 | * 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 26 | * 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 27 | * 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 28 | * ); 29 | * $weObj = new TPWechat($options); 30 | * $weObj->valid(); 31 | * ... 32 | * 33 | */ 34 | class TPWechat extends Wechat 35 | { 36 | /** 37 | * log overwrite 38 | * @see Wechat::log() 39 | */ 40 | protected function log($log){ 41 | if ($this->debug) { 42 | if (function_exists($this->logcallback)) { 43 | if (is_array($log)) $log = print_r($log,true); 44 | return call_user_func($this->logcallback,$log); 45 | }elseif (class_exists('Log')) { 46 | Log::write('wechat:'.$log, Log::DEBUG); 47 | } 48 | } 49 | return false; 50 | } 51 | 52 | /** 53 | * 重载设置缓存 54 | * @param string $cachename 55 | * @param mixed $value 56 | * @param int $expired 57 | * @return boolean 58 | */ 59 | protected function setCache($cachename,$value,$expired){ 60 | return S($cachename,$value,$expired); 61 | } 62 | 63 | /** 64 | * 重载获取缓存 65 | * @param string $cachename 66 | * @return mixed 67 | */ 68 | protected function getCache($cachename){ 69 | return S($cachename); 70 | } 71 | 72 | /** 73 | * 重载清除缓存 74 | * @param string $cachename 75 | * @return boolean 76 | */ 77 | protected function removeCache($cachename){ 78 | return S($cachename,null); 79 | } 80 | } 81 | ``` -------------------------------------------------------------------------------- /wiki/企业号API类库.md: -------------------------------------------------------------------------------- 1 | # qywechat.class.php 2 | 3 | ## 企业号API类库 4 | 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 5 | 6 | ## 主要功能 7 | - 接入验证 8 | - 自动回复(文本、图片、语音、视频、音乐、图文) 9 | - 菜单操作(查询、创建、删除) 10 | - 部门管理(创建、更新、删除、获取部门列表) 11 | - 成员管理(创建、更新、删除、获取成员信息,获取部门成员列表) 12 | - 标签管理(创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表) 13 | - 媒体文件管理(上传、获取) 14 | - 二次验证 15 | - OAuth2(生成授权url、获取成员信息) 16 | - 获取企业微信服务器IP列表 17 | - 微信JSAPI授权(获取ticket、获取签名) 18 | 19 | 20 | ## 初始化动作 21 | ```php 22 | $options = array( 23 | 'token'=>'tokenaccesskey', //填写应用接口的Token 24 | 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 25 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id 26 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 27 | 'agentid'=>'1', //应用的id 28 | 'debug'=>false, //调试开关 29 | '_logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数 30 | ); 31 | $weObj = new Wechat($options); //创建实例对象 32 | //TODO:调用$weObj各实例方法 33 | 34 | ``` 35 | 36 | ## 被动接口方法: 37 | * valid() 验证连接,被动接口必须调用 38 | * 39 | * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 40 | * getRevData() 返回微信服务器发来的信息(数组) 41 | * getRevPostXml() 返回微信服务器发来的原始加密xml信息 42 | * getRevFrom() 返回消息发送者的userid 43 | * getRevTo() 返回消息接收者的id(即公众号id,一般与等同appid一致) 44 | * getRevAgentID() 返回接收消息的应用id 45 | * getRevType() 返回接收消息的类型 46 | * getRevID() 返回消息id 47 | * getRevCtime() 返回消息发送事件 48 | * getRevContent() 返回消息内容正文(文本型消息) 49 | * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} 50 | * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} 51 | * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} 52 | * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} 53 | * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') 54 | * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 55 | * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 56 | * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} 57 | * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} 58 | * 59 | * text($text) 设置文本型消息,参数:文本内容 60 | * image($mediaid) 设置图片型消息,参数:图片的media_id 61 | * voice($mediaid) 设置语音型消息,参数:语音的media_id 62 | * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 63 | * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 64 | * image($mediaid) 设置图片型消息,参数:图片的media_id 65 | * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) 66 | * reply() 将已经设置好的消息,回复给微信服务器 67 | 68 | ### 预定义常量列表: 69 | ```php 70 | ////消息类型,使用实例调用getRevType()方法取得 71 | const MSGTYPE_TEXT = 'text'; 72 | const MSGTYPE_IMAGE = 'image'; 73 | const MSGTYPE_LOCATION = 'location'; 74 | const MSGTYPE_LINK = 'link'; //暂不支持 75 | const MSGTYPE_EVENT = 'event'; 76 | const MSGTYPE_MUSIC = 'music'; //暂不支持 77 | const MSGTYPE_NEWS = 'news'; 78 | const MSGTYPE_VOICE = 'voice'; 79 | const MSGTYPE_VIDEO = 'video'; 80 | ////事件类型,使用实例调用getRevEvent()方法取得 81 | const EVENT_SUBSCRIBE = 'subscribe'; //订阅 82 | const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 83 | const EVENT_LOCATION = 'LOCATION'; //上报地理位置 84 | const EVENT_ENTER_AGENT = 'enter_agent'; //用户进入应用 85 | const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 86 | const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 87 | const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) 88 | const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) 89 | const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 90 | const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 91 | const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 92 | const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 93 | const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 94 | const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 95 | ``` 96 | 97 | ## 主动接口方法: 98 | * checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token` 99 | * resetAuth($appid='') 清除记录的ACCESS_TOKEN 100 | * resetJsTicket($appid='') 删除JSAPI授权TICKET 101 | * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET 102 | * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 103 | * getSignature($arrdata,'sha1') 生成签名字串 104 | * generateNonceStr($length=16) 获取随机字串 105 | * createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id 106 | * getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id 107 | * deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id 108 | * uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) 109 | * getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id 110 | * getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') 111 | * createDepartment($data) 创建部门,参数: array("name"=>"邮箱产品组","parentid"=>"1","order" => "1") 112 | * updateDepartment($data) 更新部门,参数: array("id"=>"1","name"=>"邮箱产品组","parentid"=>"1","order" => "1") 113 | * deleteDepartment($id) 删除部门,参数:要删除的部门id 114 | * moveDepartment($data) 移动部门,参数:array("department_id" => "5","to_parentid" => "2","to_position" => "1") 115 | * getDepartment() 获取部门列表,返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列 116 | * createUser($data) 创建成员,参数请看php文件内方法说明 117 | * updateUser($data) 更新成员,参数请看php文件内方法说明 118 | * deleteUser($userid) 删除成员,参数:员工UserID 119 | * deleteUsers($userids) 批量删除成员,参数:员工UserID数组 120 | * getUserInfo($userid) 获取成员信息,参数:员工UserID 121 | * getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员,参数:部门id,是否递归获取子部门,获取类型。 122 | > 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 123 | * getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情,参数同上 124 | * getUserId($code,$agentid) 根据code获取员工UserID与手机设备号,参数:Oauth2.0或者二次验证返回的code值,跳转链接时所在的企业应用ID 125 | * sendInvite($userid,$invite_tips='') 邀请成员关注 126 | * createTag($data) 创建标签,参数:array("tagname" => "UI") 127 | * updateTag($data) 更新标签,参数:array("tagid" => "1","tagname" => "UI") 128 | * deleteTag($tagid) 删除标签,参数:标签TagID 129 | * getTag($tagid) 获取标签成员,参数:标签TagID 130 | * addTagUser($data) 增加标签成员,参数请看php文件内方法说明 131 | * delTagUser($data) 删除标签成员,参数请看php文件内方法说明 132 | * getTagList() 获取标签列表,返回标签数组 133 | * sendMessage($data) 主动发送信息接口,参数请看php文件内方法说明 134 | * authSucc($userid) 二次验证,参数: 员工UserID 135 | * getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url 136 | 137 | 138 | 139 | 140 | 141 | 142 | ## 企业号API类库调用示例: 143 | -------- 144 | 可参考**test**目录下的**qydemo.php** 145 | ```php 146 | include "wechat.class.php"; 147 | $options = array( 148 | 'token'=>'9Ixxxxxxx', //填写应用接口的Token 149 | 'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey 150 | 'appid'=>'wxa07979baxxxxxxxx', //填写高级调用功能的appid 151 | ); 152 | $weObj = new Wechat($options); 153 | $weObj->valid(); //注意, 企业号与普通公众号不同,必须打开验证,不要注释掉 154 | $type = $weObj->getRev()->getRevType(); 155 | switch($type) { 156 | case Wechat::MSGTYPE_TEXT: 157 | $weObj->text("hello, I'm wechat")->reply(); 158 | exit; 159 | break; 160 | case Wechat::MSGTYPE_EVENT: 161 | break; 162 | case Wechat::MSGTYPE_IMAGE: 163 | break; 164 | default: 165 | $weObj->text("help info")->reply(); 166 | } 167 | ``` 168 | -------------------------------------------------------------------------------- /wiki/内嵌JS.md: -------------------------------------------------------------------------------- 1 | #wechat.js 2 | 3 | **此JS脚本已经废弃不再更新,原因是官方在微信6.0.2版本开放了全新的JSAPI接口,更全面好用。请查看:[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)** 4 | 5 | ##微信内嵌网页特殊功能js调用: 6 | * WeixinJS.hideOptionMenu() 隐藏右上角按钮 7 | * WeixinJS.showOptionMenu() 显示右上角按钮 8 | * WeixinJS.hideToolbar() 隐藏工具栏 9 | * WeixinJS.showToolbar() 显示工具栏 10 | * WeixinJS.getNetworkType() 获取网络状态 11 | * WeixinJS.closeWindow() 关闭窗口 12 | * WeixinJS.scanQRCode() 扫描二维码 13 | * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址 14 | * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面 15 | * WeixinJS.sendEmail(title,content) 发送邮件 16 | * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图 17 | * WeixinJS.addContact(username) 添加微信账号 18 | * WeixinJS.imagePreview(urls,current) 调出微信内图片预览 19 | * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口 20 | * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口 21 | * 通过定义全局变量dataForWeixin配置触发分享的内容: 22 | 23 | ```javascript 24 | var dataForWeixin={ 25 | appId:"", 26 | MsgImg:"消息图片路径", 27 | TLImg:"时间线图路径", 28 | url:"分享url路径", 29 | title:"标题", 30 | desc:"描述", 31 | fakeid:"", 32 | callback:function(){} 33 | }; 34 | ``` 35 | 可以参考weshare.html及wechat.js的备注进行使用 -------------------------------------------------------------------------------- /wiki/官方API类库.md: -------------------------------------------------------------------------------- 1 | # wechat.class.php 2 | 3 | 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 4 | 5 | ## 主要功能 6 | - 接入验证 **(初级权限)** 7 | - 自动回复(文本、图片、语音、视频、音乐、图文) **(初级权限)** 8 | - 菜单操作(查询、创建、删除) **(菜单权限)** 9 | - 客服消息(文本、图片、语音、视频、音乐、图文) **(认证权限)** 10 | - 二维码(创建临时、永久二维码,获取二维码URL) **(服务号、认证权限)** 11 | - 长链接转短链接接口 **(服务号、认证权限)** 12 | - 分组操作(查询、创建、修改、移动用户到分组) **(认证权限)** 13 | - 网页授权(基本授权,用户信息授权) **(服务号、认证权限)** 14 | - 用户信息(查询用户基本信息、获取关注者列表) **(认证权限)** 15 | - 多客服功能(客服管理、获取客服记录、客服会话管理) **(认证权限)** 16 | - 媒体文件(上传、获取) **(认证权限)** 17 | - 高级群发 **(认证权限)** 18 | - 模板消息(设置所属行业、添加模板、发送模板消息) **(服务号、认证权限)** 19 | - 卡券管理(创建、修改、删除、发放、门店管理等) **(认证权限)** 20 | - 语义理解 **(服务号、认证权限)** 21 | - 获取微信服务器IP列表 **(初级权限)** 22 | - 微信JSAPI授权(获取ticket、获取签名) **(初级权限)** 23 | - 数据统计(用户、图文、消息、接口分析数据) **(认证权限)** 24 | > 备注: 25 | > 初级权限:基本权限,任何正常的公众号都有此权限 26 | > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 27 | > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 28 | > 支付权限:仅认证后的服务号可以申请此权限 29 | 30 | 31 | ## 初始化动作 32 | ```php 33 | $options = array( 34 | 'token'=>'tokenaccesskey', //填写你设定的key 35 | 'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey 36 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 37 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥 38 | ); 39 | $weObj = new Wechat($options); //创建实例对象 40 | //TODO:调用$weObj各实例方法 41 | ``` 42 | 43 | ### 被动接口方法: 44 | * valid() 验证连接,被动接口处于加密模式时必须调用 45 | * 46 | * getRev() 获取微信服务器发来信息(不返回结果),被动接口必须调用 47 | * getRevData() 返回微信服务器发来的信息(数组) 48 | * getRevFrom() 返回消息发送者的userid 49 | * getRevTo() 返回消息接收者的id(即公众号id) 50 | * getRevType() 返回接收消息的类型 51 | * getRevID() 返回消息id 52 | * getRevCtime() 返回消息发送时间 53 | * getRevContent() 返回消息内容正文或语音识别结果(文本型) 54 | * getRevPic() 返回图片信息(图片型信息) 返回数组{'mediaid'=>'','picurl'=>''} 55 | * getRevLink() 接收消息链接(链接型信息) 返回数组{'url'=>'','title'=>'','description'=>''} 56 | * getRevGeo() 返回地理位置(位置型信息) 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''} 57 | * getRevEventGeo() 返回事件地理位置(事件型信息) 返回数组{'x'=>'','y'=>'','precision'=>''} 58 | * getRevEvent() 返回事件类型(事件型信息) 返回数组{'event'=>'','key'=>''} 59 | * getRevScanInfo() 获取自定义菜单的扫码推事件信息,事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123') 60 | * getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明 61 | * getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送,事件类型为`location_select` 数组结构见php文件内方法说明 62 | * getRevVoice() 返回语音信息(语音型信息) 返回数组{'mediaid'=>'','format'=>''} 63 | * getRevVideo() 返回视频信息(视频型信息) 返回数组{'mediaid'=>'','thumbmediaid'=>''} 64 | * getRevTicket() 返回接收TICKET(扫描带参数二维码,关注或SCAN事件) 返回二维码的ticket值 65 | * getRevSceneId() 返回二维码的场景值(扫描带参数二维码的关注事件) 返回二维码的参数值 66 | * getRevTplMsgID() 返回主动推送的消息ID(群发或模板消息事件) 返回MsgID值 67 | * getRevStatus() 返回模板消息发送状态(模板消息事件) 返回文本:success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败(非用户拒绝)) 68 | * getRevResult() 返回群发或模板消息发送结果(群发或模板消息事件) 返回数组,内容依事件类型而不同,参考开发文档中群发、模板消息推送事件 69 | * getRevKFCreate() 返回多客服-接入会话的客服账号(多客服-接入会话事件) 返回文本型 70 | * getRevKFClose() 返回多客服-处理会话的客服账号(多客服-接入会话事件) 返回文本型 71 | * getRevKFSwitch() 返回多客服-转接会话信息(多客服-转接会话事件) 返回数组 {'FromKfAccount' => '','ToKfAccount' => ''} 72 | * getRevCardPass() 返回卡券-审核通过的卡券ID(卡券-卡券审核事件) 返回文本型 73 | * getRevCardGet() 返回卡券-用户领取卡券的相关信息(卡券-领取卡券事件) 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''} 74 | * getRevCardDel() 返回卡券-用户删除卡券的相关信息(卡券-删除卡券事件) 返回数组{'CardId' => '','UserCardCode' => ''} 75 | * 76 | * text($text) 设置文本型消息,参数:文本内容 77 | * image($mediaid) 设置图片型消息,参数:图片的media_id 78 | * voice($mediaid) 设置语音型消息,参数:语音的media_id 79 | * video($mediaid='',$title,$description) 设置视频型消息,参数:视频的media_id、标题、摘要 80 | * music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐,参数:音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id 81 | * news($newsData) 设置图文型消息,参数:数组。数组结构见php文件内方法说明 82 | * image($mediaid) 设置图片型消息,参数:图片的media_id 83 | * Message($msg = '',$append = false) 设置发送的消息(一般不需要调用这个方法) 84 | * transfer_customer_service($customer_account = '') 转接多客服,如不指定客服可不提供参数,参数:指定客服的账号 85 | * reply() 将以上已经设置好的消息,回复给微信服务器 86 | 87 | ### 预定义常量列表: 88 | ```php 89 | ////消息类型,使用实例调用getRevType()方法取得 90 | const MSGTYPE_TEXT = 'text'; 91 | const MSGTYPE_IMAGE = 'image'; 92 | const MSGTYPE_LOCATION = 'location'; 93 | const MSGTYPE_LINK = 'link'; 94 | const MSGTYPE_EVENT = 'event'; 95 | const MSGTYPE_MUSIC = 'music'; 96 | const MSGTYPE_NEWS = 'news'; 97 | const MSGTYPE_VOICE = 'voice'; 98 | const MSGTYPE_VIDEO = 'video'; 99 | ////事件类型,使用实例调用getRevEvent()方法取得 100 | const EVENT_SUBSCRIBE = 'subscribe'; //订阅 101 | const EVENT_UNSUBSCRIBE = 'unsubscribe'; //取消订阅 102 | const EVENT_SCAN = 'SCAN'; //扫描带参数二维码 103 | const EVENT_LOCATION = 'LOCATION'; //上报地理位置 104 | const EVENT_MENU_VIEW = 'VIEW'; //菜单 - 点击菜单跳转链接 105 | const EVENT_MENU_CLICK = 'CLICK'; //菜单 - 点击菜单拉取消息 106 | const EVENT_MENU_SCAN_PUSH = 'scancode_push'; //菜单 - 扫码推事件(客户端跳URL) 107 | const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL) 108 | const EVENT_MENU_PIC_SYS = 'pic_sysphoto'; //菜单 - 弹出系统拍照发图 109 | const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图 110 | const EVENT_MENU_PIC_WEIXIN = 'pic_weixin'; //菜单 - 弹出微信相册发图器 111 | const EVENT_MENU_LOCATION = 'location_select'; //菜单 - 弹出地理位置选择器 112 | const EVENT_SEND_MASS = 'MASSSENDJOBFINISH'; //发送结果 - 高级群发完成 113 | const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果 114 | const EVENT_KF_SEESION_CREATE = 'kfcreatesession'; //多客服 - 接入会话 115 | const EVENT_KF_SEESION_CLOSE = 'kfclosesession'; //多客服 - 关闭会话 116 | const EVENT_KF_SEESION_SWITCH = 'kfswitchsession'; //多客服 - 转接会话 117 | const EVENT_CARD_PASS = 'card_pass_check'; //卡券 - 审核通过 118 | const EVENT_CARD_NOTPASS = 'card_not_pass_check'; //卡券 - 审核未通过 119 | const EVENT_CARD_USER_GET = 'user_get_card'; //卡券 - 用户领取卡券 120 | const EVENT_CARD_USER_DEL = 'user_del_card'; //卡券 - 用户删除卡券 121 | ``` 122 | 123 | ### 主动接口方法: 124 | * checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌 125 | * resetAuth($appid='') 删除验证数据 126 | * resetJsTicket($appid='') 删除JSAPI授权TICKET 127 | * getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET 128 | * getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组,可只提供url地址 129 | * createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)** 130 | * getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1') 131 | * getMenu() 获取菜单 132 | * deleteMenu() 删除菜单 133 | * uploadMedia($data, $type) 上传临时素材,有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时) 134 | * getMedia($media_id,$is_video=false) 获取临时素材(含接收到的音频、视频媒体文件) 135 | * uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材,可以在公众平台官网素材管理模块中看到 136 | * uploadForeverArticles($data) 上传永久图文素材 137 | * updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用) 138 | * getForeverMedia($media_id,$is_video=false) 获取永久素材 139 | * delForeverMedia($media_id) 删除永久素材 140 | * getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用) 141 | * getForeverCount() 获取永久素材总数 142 | * uploadMpVideo($data) 上传视频素材,当需要群发视频时,必须使用此方法得到的MediaID,否则无法显示 143 | * uploadArticles($data) 上传图文消息素材 144 | * sendMassMessage($data) 高级群发消息 145 | * sendGroupMassMessage($data) 高级群发消息(全体或分组群发) 146 | * deleteMassMessage($msg_id) 删除群发图文消息 147 | * previewMassMessage($data) 预览群发消息 148 | * queryMassMessage($msg_id) 查询群发消息发送状态 149 | * getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 150 | * getQRUrl($ticket) 获取二维码图片地址 151 | * getShortUrl($long_url) 长链接转短链接接口 152 | * getUserList($next_openid) 批量获取关注用户列表 153 | * getUserInfo($openid) 获取关注者详细信息 154 | * updateUserRemark($openid,$remark) 设置用户备注名 155 | * getGroup() 获取用户分组列表 156 | * getUserGroup($openid) 获取用户所在分组 157 | * createGroup($name) 新增自定分组 158 | * updateGroup($groupid,$name) 更改分组名称 159 | * updateGroupMembers($groupid,$openid) 移动用户分组 160 | * batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 161 | * sendCustomMessage($data) 发送客服消息 162 | * getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址 163 | * getOauthAccessToken() 通过回调的code获取网页授权access_token 164 | * getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期 165 | * getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料 166 | * getOauthAuth($access_token,$openid) 检验授权凭证access_token是否有效 167 | * getSignature($arrdata,'sha1') 生成签名字串 168 | * generateNonceStr($length=16) 获取随机字串 169 | * setTMIndustry($id1,$id2='') 模板消息,设置所属行业 170 | * addTemplateMessage($tpl_id) 模板消息,添加消息模板 171 | * sendTemplateMessage($data) 发送模板消息 172 | * getCustomServiceMessage($data) 获取多客服会话记录 173 | * transfer_customer_service($customer_account) 转发多客服消息 174 | * getCustomServiceKFlist() 获取多客服客服基本信息 175 | * getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息 176 | * createKFSession($openid,$kf_account,$text='') 创建指定多客服会话 177 | * closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话 178 | * getKFSession($openid) 获取用户会话状态 179 | * getKFSessionlist($kf_account) 获取指定客服的会话列表 180 | * getKFSessionWait() 获取未接入会话列表 181 | * addKFAccount($account,$nickname,$password) 添加客服账号 182 | * updateKFAccount($account,$nickname,$password) 修改客服账号信息 183 | * deleteKFAccount($account) 删除客服账号 184 | * setKFHeadImg($account,$imgfile) 上传客服头像 185 | * querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city="",$region="") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)** 186 | * getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义 187 | > 获取统计数据方法 参数定义 188 | > 189 | | 数据分类 | $type值(字符串) | 数据子分类 | $subtype值(字符串) | 时间跨度(天) | 190 | | --------- | :-------: | --------- | :------: | ----: | 191 | | 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 | 192 | | 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 | 193 | | 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 | 194 | | 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 | 195 | | 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 | 196 | | 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 | 197 | | 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 | 198 | | 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 | 199 | | 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 | 200 | | 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 | 201 | | 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 | 202 | | 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 | 203 | | 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 | 204 | | 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 | 205 | | 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 | 206 | | 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 | 207 | | 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 | 208 | 需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”(比如最大时间跨度为1时,`begin_date`和`end_date`的差值只能为0,才能小于1) 209 | 210 | * createCard($data) 创建卡券 211 | * updateCard($data) 修改卡券 212 | * delCard($card_id) 删除卡券 213 | * getCardInfo($card_id) 查询卡券详情 214 | * getCardColors() 获取颜色列表 215 | * getCardLocations() 拉取门店列表 216 | * addCardLocations($data) 批量导入门店信息 217 | * createCardQrcode($card_id) 生成卡券二维码 218 | * consumeCardCode($code) 消耗 code 219 | * decryptCardCode($encrypt_code) code 解码 220 | * checkCardCode($code) 获取 code 的有效性 221 | * getCardIdList($data) 批量查询卡列表 222 | * updateCardCode($code,$card_id,$new_code) 更改 code 223 | * unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)** 224 | * modifyCardStock($data) 库存修改 225 | * activateMemberCard($data) 激活/绑定会员卡,参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节 226 | * updateMemberCard($data) 会员卡交易,参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节 227 | * updateLuckyMoney($code,$balance,$card_id='') 更新红包金额 228 | * setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单 229 | 230 | 231 | 232 | ## 官方Wechat调用示例: 233 | ```php 234 | //test1.php 235 | include "wechat.class.php"; 236 | $options = array( 237 | 'token'=>'tokenaccesskey' //填写你设定的key 238 | ); 239 | $weObj = new Wechat($options); 240 | $weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句,但加密模式一定不能注释,否则会验证失败 241 | $type = $weObj->getRev()->getRevType(); 242 | switch($type) { 243 | case Wechat::MSGTYPE_TEXT: 244 | $weObj->text("hello, I'm wechat")->reply(); 245 | exit; 246 | break; 247 | case Wechat::MSGTYPE_EVENT: 248 | break; 249 | case Wechat::MSGTYPE_IMAGE: 250 | break; 251 | default: 252 | $weObj->text("help info")->reply(); 253 | } 254 | ``` 255 | -------------------------------------------------------------------------------- /wiki/授权登陆类库.md: -------------------------------------------------------------------------------- 1 | # wechatauth.class.php 2 | 3 | **此扩展类库已经不再更新,原因是官方开放平台对网站应用开放的有授权登陆接口,更标准,更好用。请查看:[微信开放平台](http://open.weixin.qq.com)** 4 | 5 | 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子. 6 | 7 | ## 类主要方法: 8 | * get_login_code() 获取登陆授权码, 通过授权码才能获取二维码 9 | * get_code_image($code='') 将上面获取的授权码转换为图片二维码 10 | * verify_code() 鉴定是否登陆成功,返回200为最终授权成功. 11 | * get_login_info() 鉴定成功后调用此方法即可获取用户基本信息 12 | * get_avatar($url) 获取用户头像图片数据 13 | * logout() 注销登陆 14 | 15 | ## 微信二维码Wechatauth登陆示例: 16 | ```php 17 | //test3.php 18 | include "../wechatauth.class.php"; 19 | session_start(); 20 | $sid = session_id(); 21 | $options = array( 22 | 'account'=>$sid, 23 | 'datapath'=>'../data/cookiecode_', 24 | ); 25 | $wechat = new Wechatauth($options); 26 | 27 | if (isset($_POST['code'])) { 28 | $logincode = $_POST['code']; 29 | $vres = $wechat->set_login_code($logincode)->verify_code(); 30 | if ($vres===false) { 31 | $result = array('status'=>0); 32 | } else { 33 | $result = array('status'=>$vres); 34 | if ($vres==200) { 35 | $result['info'] = $wechat->get_login_info(); 36 | $result['cookie'] = $wechat->get_login_cookie(true); 37 | } 38 | } 39 | 40 | die(json_encode($result)); 41 | } 42 | $logincode = $wechat->get_login_code(); //获取授权码 43 | $qrimg = $wechat->get_code_image(); //待输出的二维码图片 44 | ``` 45 | HTML部分请看test/test3.php, 主要是定时ajax查询是否已经授权成功 -------------------------------------------------------------------------------- /wiki/旧版微信支付V2接口类库.md: -------------------------------------------------------------------------------- 1 | # wechatpay.class.php 2 | 3 | 旧版微信支付类库(微信支付V2),已移动至old_version目录下。 4 | 自2014年8月开始申请到的微信支付都是V3接口,据官方说V2的会陆续升级为V3接口,但时间及升级渠道未确认。 5 | 6 | ### 主要功能 7 | - 获取access_token **(初级权限)** 8 | - 调用地址组件 **(支付权限)** 9 | - 生成订单签名数据 **(支付权限)** 10 | - 订单成功回调 **(支付权限)** 11 | - 发货通知 **(支付权限)** 12 | - 支付订单查询 **(支付权限)** 13 | > 备注: 14 | > 初级权限:基本权限,任何正常的公众号都有此权限 15 | > 菜单权限:正常的服务号、认证后的订阅号拥有此权限 16 | > 认证权限:分为订阅号、服务号认证,如前缀服务号则仅认证的服务号有此权限,否则为认证后的订阅号、服务号都有此权限 17 | > 支付权限:仅认证后的服务号可以申请此权限 18 | 19 | 20 | ### 初始化动作 21 | ```php 22 | $options = array( 23 | 'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询 24 | 'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥 25 | 'partnerid'=>'88888888', //财付通商户身份标识,支付权限专用,没有可不填 26 | 'partnerkey'=>'', //财付通商户权限密钥Key,支付权限专用 27 | 'paysignkey'=>'' //商户签名密钥Key,支付权限专用 28 | ); 29 | $weObj = new Wechat($options); //创建实例对象 30 | //TODO:调用$weObj各实例方法 31 | ``` 32 | 33 | ### 主动接口方法: 34 | * checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取,或手动指定access_token 35 | * resetAuth($appid='') 删除验证数据 36 | * getSignature($arrdata,'sha1') 生成签名字串 37 | * generateNonceStr($length=16) 获取随机字串 38 | * createNativeUrl($productid) 生成原生支付url 39 | * 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字符串 40 | * getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法 41 | * checkOrderSignature($orderxml='') 回调通知签名验证 42 | * sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知 43 | * getPayOrder($out_trade_no) 查询订单信息 44 | * setUserToken($user_token) 设置用户授权密钥 45 | * getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名 -------------------------------------------------------------------------------- /wiki/非官方扩展类库.md: -------------------------------------------------------------------------------- 1 | # wechatext.class.php 2 | 3 | **此扩展类库已经不再更新,原因是官方对公众号开放了众多接口,此类库继续维护的意义不大** 4 | 5 | 非官方扩展API,需要配置公众平台账户和密码,能实现对已关注用户的点对点微信,此方式不保证长期有效。 6 | 类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取. 7 | 调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决. 8 | 程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持. 9 | 10 | ## 类主要方法: 11 | * send($id,$content) 向某用户id发送微信文字信息 12 | * sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid 13 | * getUserList($page,$pagesize,$groupid) 获取用户信息 14 | * getGroupList($page,$pagesize) 获取群组信息 15 | * getNewsList($page,$pagesize) 获取图文信息列表 16 | * uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图 17 | * getFileList($type,$page,$pagesize) 获取素材库文件列表 18 | * sendImage($id,$fid) 发送图片消息 19 | * sendAudio($id,$fid) 发送音频消息 20 | * sendVideo($id,$fid) 发送视频消息 21 | * getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取 22 | * getNewMsgNum($lastid) 获取从$lastid算起新消息的数目 23 | * getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据 24 | * getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数 25 | * 消息返回结构: {"id":"消息id","type":"类型号(1为文字,2为图片,3为语音)","fileId":"0","hasReply":"0","fakeId":"用户uid","nickName":"昵称","dateTime":"时间戳","content":"文字内容"} 26 | * getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据 27 | * getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据 28 | 29 | 30 | ## 扩展包Wechatext调用示例: 31 | ```php 32 | //test2.php 33 | include "wechatext.class.php"; 34 | 35 | function logdebug($text){ 36 | file_put_contents('./data/log.txt',$text."\n",FILE_APPEND); 37 | }; 38 | 39 | $options = array( 40 | 'account'=>'demo@domain.com', 41 | 'password'=>'demo', 42 | 'datapath'=>'./data/cookie_', 43 | 'debug'=>true, 44 | 'logcallback'=>'logdebug' 45 | ); 46 | $wechat = new Wechatext($options); 47 | if ($wechat->checkValid()) { 48 | // 获取用户信息 49 | $data = $wechat->getInfo('3974255'); 50 | var_dump($data); 51 | // 获取最新一条消息 52 | $topmsg = $wechat->getTopMsg(); 53 | var_dump($topmsg); 54 | // 主动回复消息 55 | if ($topmsg && $topmsg['has_reply']==0) 56 | $wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']); 57 | } 58 | ``` --------------------------------------------------------------------------------