Wechat.class.php 110 KB

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