WeChatApi.php 107 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666
  1. <?php
  2. // .-----------------------------------------------------------------------------------
  3. // | Software: [wechat framework]
  4. // | Version: 2017.7
  5. // | Site:
  6. // |-----------------------------------------------------------------------------------
  7. // | Author: bug <732680577@qq.com>
  8. // | Copyright (c) 2017, . All Rights Reserved.
  9. // |-----------------------------------------------------------------------------------
  10. /**
  11. * 基于thinkphp5的微信自定义接口开发
  12. * @package wechat
  13. * @author bug <732680577@qq.com>
  14. * @createtime 2017-07-26 10:48:30
  15. * @description
  16. */
  17. // |-----------------------------------------------------------------------------------
  18. namespace wechat;
  19. use think\Exception;
  20. /**
  21. * 微信公共类文件
  22. * @package util
  23. * @author bug <732680577@qq.com>
  24. * @createtime 2017-07-26 11:10:14
  25. *
  26. */
  27. class WeChatApi
  28. {
  29. /**********************基础配置 开始********/
  30. /**
  31. * 微信公众号AppId
  32. * @var string
  33. */
  34. private $appid;
  35. /**
  36. * 微信公众号AppSecret
  37. * @var string
  38. */
  39. private $appsecret;
  40. /**
  41. * 微信公众号验证服务器token
  42. * @var string
  43. */
  44. private $token;
  45. /**
  46. * 微信公众号EncodingAESKey
  47. * @var string
  48. */
  49. private $encoding_aeskey;
  50. /**
  51. * 微信公众号商户号
  52. * @var string
  53. */
  54. private $mch_id;
  55. /**
  56. * 微信公众号商户支付密钥
  57. * @var string
  58. */
  59. private $key;
  60. /**
  61. * 微信支付APIv3密钥
  62. * @var string
  63. */
  64. private $apiv3_key;
  65. /**
  66. * 微信支付分用户开启状态服务ID
  67. * @var string
  68. */
  69. private $service_id;
  70. /**
  71. * API证书序列号
  72. * @var string
  73. */
  74. private $cert_no;
  75. //=======【证书路径设置】=====================================
  76. /**
  77. * TODO:设置商户证书路径
  78. * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,
  79. * API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)
  80. * @var path
  81. */
  82. const SSLCERT_PATH = "/cert/apiclient_cert.pem";
  83. const SSLKEY_PATH = "/cert/apiclient_key.pem";
  84. //=======【curl代理设置】===================================
  85. /**
  86. * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0
  87. * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,
  88. * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)
  89. * @var unknown_type
  90. */
  91. const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
  92. const CURL_PROXY_PORT = 0;//8080;
  93. //=======【上报信息配置】===================================
  94. /**
  95. * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,
  96. * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少
  97. * 开启错误上报。
  98. * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报
  99. * @var int
  100. */
  101. const REPORT_LEVENL = 1;
  102. /**
  103. * 微信公共类构造方法
  104. * @author bug <732680577@qq.com>
  105. * @createtime 2017-07-26 11:10:14
  106. * @param string $appid 微信公众号AppId
  107. * @param string $appsecret 微信公众号AppSecret
  108. * @param string $token 微信公众号验证服务器token
  109. * @param string $encoding_aeskey 微信公众号EncodingAESKey
  110. * @param string $mch_id 微信公众号商户号
  111. * @param string $key 微信公众号商户支付密钥
  112. * @param string $service_id 微信支付分服务ID
  113. */
  114. public function __construct(){
  115. $this->appid = config('config.wechat_appid');//获取配置文件中的微信公众号AppId
  116. $this->appsecret = config('config.wechat_secret');//获取配置文件中的微信公众号AppSecret
  117. $this->token = config('config.token');//获取配置文件中的微信公众号验证服务器token
  118. $this->encoding_aeskey = config('config.encoding_aeskey');//获取配置文件中的微信公众号EncodingAESKey
  119. $this->mch_id = config('config.wechat_mch_id');//获取配置文件中的微信公众号商户号
  120. $this->key = config('config.wechat_key');//获取配置文件中的微信公众号商户支付密钥
  121. $this->apiv3_key = config('config.apiv3_key');//获取配置文件中的APIv3密钥
  122. $this->service_id = config('config.service_id');//获取配置文件中的微信支付分服务ID
  123. $this->cert_no = config('config.cert_no');//获取配置文件中的API证书序列号
  124. }
  125. /**********************基础配置 结束********/
  126. /**********************开始开发 开始********/
  127. /**
  128. * token验证服务器
  129. * @author bug <732680577@qq.com>
  130. * @createtime 2017-07-26 11:10:14
  131. * @param string $nonce 随机数
  132. * @param string $timestamp 时间戳
  133. * @param string $echostr 随机字符串
  134. * @param string $signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
  135. * @return echostr参数内容或false
  136. */
  137. public function token($nonce,$timestamp,$echostr,$signature){
  138. //参数字典序排序
  139. $array = array();
  140. $array = array($nonce, $timestamp, $this->token);
  141. sort($array);
  142. //验证
  143. $str = sha1( implode( $array ) );//sha1加密
  144. //对比验证处理好的str与signature,若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
  145. if( $str == $signature && $echostr ){
  146. return $echostr;
  147. }
  148. else{
  149. return false;
  150. }
  151. }
  152. /**
  153. * 通过appid和appsecret获取access_token 或从缓存获取基础支持access_token
  154. * @author bug <732680577@qq.com>
  155. * @createtime 2017-07-26 11:07:24
  156. * @param type true强制获取access_token,用于本地存储的access_token在微信服务器已过期,但在本地服务器未过期时
  157. * @return string 获取到的凭证
  158. */
  159. private function access_token($type){
  160. // //session开始
  161. // if(!isset($_SESSION)){
  162. // session_start();
  163. // }
  164. // //判断缓存中是否存在access_token,如果存在取出来返回
  165. // if(!$type&&isset($_SESSION['access_token'])&&$_SESSION['access_token']['endtime']>time()){
  166. // $result = array('status'=>1,'data'=>$_SESSION['access_token']['val']);
  167. // return $result;
  168. // }
  169. // //配置请求url,加入appid和appsecret参数
  170. // $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->appid.'&secret='.$this->appsecret;
  171. // //获取请求结果,并将请求结果转换成json
  172. // $data = string_json($url);
  173. // if(array_key_exists('errcode', $data)){
  174. // $result = array('status'=>40014,'data'=>$data);
  175. // return $result;
  176. // }
  177. // /**将新获取的access_token缓存到服务器 开始**/
  178. // session_set_cookie_params($data['expires_in']); //设置access_token在服务器session中缓存时间
  179. // $access_token['val'] = $data['access_token'];
  180. // $access_token['endtime'] = time()+$data['expires_in'];
  181. // $_SESSION['access_token']= $access_token;
  182. // /**将新获取的access_token缓存到服务器 结束**/
  183. // //返回结果
  184. // $result = array('status'=>2,'data'=>$data['access_token']);
  185. // return $result;
  186. // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
  187. $datas = json_decode(file_get_contents(dirname(__FILE__)."/json/access_token.json"));
  188. if (!$type && $datas->expire_time > time()) {
  189. $result = array('status'=>1,'data'=>$datas->access_token);
  190. return $result;
  191. }
  192. //配置请求url,加入appid和appsecret参数
  193. // 如果是企业号用以下URL获取access_token
  194. // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
  195. $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->appid.'&secret='.$this->appsecret;
  196. //获取请求结果,并将请求结果转换成json
  197. $data = string_json($url);
  198. if(array_key_exists('errcode', $data)){
  199. $result = array('status'=>40014,'data'=>$data);
  200. return $result;
  201. }
  202. //将access_token 写入文件
  203. $datas->expire_time = time()+$data['expires_in'];
  204. $datas->access_token = $data['access_token'];
  205. $fp = fopen(dirname(__FILE__)."/json/access_token.json", "w");
  206. fwrite($fp, json_encode($datas));
  207. fclose($fp);
  208. //返回结果
  209. $result = array('status'=>2,'data'=>$data['access_token']);
  210. return $result;
  211. }
  212. /**
  213. * 接口调用上限清零
  214. * @author bug <732680577@qq.com>
  215. * @createtime 2017-08-30 13:44:55
  216. * @param post_data 请求数据
  217. * @return json 请求结果
  218. */
  219. public function clear_quota(){
  220. //获取access_token
  221. $access_token= $this->access_token(false);
  222. if($access_token['status']!=1 && $access_token['status']!=2){
  223. return $access_token;
  224. }
  225. $post_data['appid'] = $this->appid;
  226. //配置请求url,加入access_token参数
  227. $url = 'https://api.weixin.qq.com/cgi-bin/clear_quota?access_token='.$access_token['data'];
  228. //将json数组转换成字符串数组
  229. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  230. //获取请求结果,并将请求结果转换成json
  231. $data = json_decode(https_request($url,$post_data),true);
  232. //access_token 过期重新获取并重新回调当前函数
  233. if($data['errcode']==42001 || $data['errcode']==40001){
  234. $access_token = $this->access_token(true);
  235. return $this->clear_quota();
  236. }
  237. //返回结果
  238. if($data['errcode']==0&&$data['errmsg']=='ok'){
  239. $result = array('status'=>1,'data'=>$data);
  240. return $result;
  241. }
  242. $result = array('status'=>40014,'data'=>$data);
  243. return $result;
  244. }
  245. /**
  246. * 获取微信服务器IP地址
  247. * @author bug <732680577@qq.com>
  248. * @createtime 2017-07-26 13:51:20
  249. * @return json ip_list 微信服务器IP地址
  250. */
  251. public function ip_list(){
  252. //获取access_token
  253. $access_token= $this->access_token(false);
  254. if($access_token['status']!=1 && $access_token['status']!=2){
  255. return $access_token;
  256. }
  257. //配置请求url,加入access_token参数
  258. $url = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token='.$access_token['data'];
  259. //获取请求结果,并将请求结果转换成json
  260. $data = string_json($url);
  261. if(array_key_exists('errcode', $data)){
  262. //access_token 过期重新获取并重新回调当前函数
  263. if($data['errcode']==42001 || $data['errcode']==40001){
  264. $access_token = $this->access_token(true);
  265. return $this->ip_list();
  266. }
  267. $result = array('status'=>40014,'data'=>$data);
  268. return $result;
  269. }
  270. //返回结果
  271. $result = array('status'=>1,'data'=>$data);
  272. return $result;
  273. }
  274. /**********************开始开发 结束********/
  275. /**********************自定义菜单 开始********/
  276. /**
  277. * 自定义菜单创建接口
  278. * @author bug <732680577@qq.com>
  279. * @createtime 2017-07-26 14:24:40
  280. * @param post_data 请求数据
  281. * @return json 请求结果
  282. */
  283. public function create_menu($post_data){
  284. //获取access_token
  285. $access_token= $this->access_token(false);
  286. if($access_token['status']!=1 && $access_token['status']!=2){
  287. return $access_token;
  288. }
  289. //配置请求url,加入access_token参数
  290. $url = 'https://api.weixin.qq.com/cgi-bin/menu/create?access_token='.$access_token['data'];
  291. //将json数组转换成字符串数组
  292. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  293. //获取请求结果,并将请求结果转换成json
  294. $data = json_decode(https_request($url,$post_data),true);
  295. //access_token 过期重新获取并重新回调当前函数
  296. if($data['errcode']==42001 || $data['errcode']==40001){
  297. $access_token = $this->access_token(true);
  298. return $this->create_menu($post_data);
  299. }
  300. //返回结果
  301. if($data['errcode']==0&&$data['errmsg']=='ok'){
  302. $result = array('status'=>1,'data'=>$data);
  303. return $result;
  304. }
  305. $result = array('status'=>40014,'data'=>$data);
  306. return $result;
  307. }
  308. /**
  309. * 自定义菜单查询接口
  310. * @author bug <732680577@qq.com>
  311. * @createtime 2017-07-26 14:17:05
  312. * @return json 菜单列表
  313. */
  314. public function get_menu(){
  315. //获取access_token
  316. $access_token= $this->access_token(false);
  317. if($access_token['status']!=1 && $access_token['status']!=2){
  318. return $access_token;
  319. }
  320. //配置请求url,加入access_token参数
  321. $url = 'https://api.weixin.qq.com/cgi-bin/menu/get?access_token='.$access_token['data'];
  322. //获取请求结果,并将请求结果转换成json
  323. $data = string_json($url);
  324. if(array_key_exists('errcode', $data)){
  325. //access_token 过期重新获取并重新回调当前函数
  326. if($data['errcode']==42001 || $data['errcode']==40001){
  327. $access_token = $this->access_token(true);
  328. return $this->get_menu();
  329. }
  330. $result = array('status'=>40014,'data'=>$data);
  331. return $result;
  332. }
  333. //返回结果
  334. $result = array('status'=>1,'data'=>$data);
  335. return $result;
  336. }
  337. /**
  338. * 自定义菜单删除接口
  339. * @author bug <732680577@qq.com>
  340. * @createtime 2017-07-26 14:17:05
  341. * @return json 请求结果
  342. */
  343. public function delete_menu(){
  344. //获取access_token
  345. $access_token= $this->access_token(false);
  346. if($access_token['status']!=1 && $access_token['status']!=2){
  347. return $access_token;
  348. }
  349. //配置请求url,加入access_token参数
  350. $url = 'https://api.weixin.qq.com/cgi-bin/menu/delete?access_token='.$access_token['data'];
  351. //获取请求结果,并将请求结果转换成json
  352. $data = string_json($url);
  353. //access_token 过期重新获取并重新回调当前函数
  354. if($data['errcode']==42001 || $data['errcode']==40001){
  355. $access_token = $this->access_token(true);
  356. return $this->delete_menu();
  357. }
  358. $status = $data['errcode']==0?1:40014;
  359. //返回结果
  360. $result = array('status'=>$status,'data'=>$data);
  361. return $result;
  362. }
  363. /**
  364. * 获取自定义菜单配置接口
  365. * @author bug <732680577@qq.com>
  366. * @createtime 2017-07-26 14:17:05
  367. * @return json 菜单列表
  368. */
  369. public function menu_config(){
  370. //获取access_token
  371. $access_token= $this->access_token(false);
  372. if($access_token['status']!=1 && $access_token['status']!=2){
  373. return $access_token;
  374. }
  375. //配置请求url,加入access_token参数
  376. $url = 'https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token='.$access_token['data'];
  377. //获取请求结果,并将请求结果转换成json
  378. $data = string_json($url);
  379. if(array_key_exists('errcode', $data)){
  380. //access_token 过期重新获取并重新回调当前函数
  381. if($data['errcode']==42001 || $data['errcode']==40001){
  382. $access_token = $this->access_token(true);
  383. return $this->menu_config();
  384. }
  385. $result = array('status'=>40014,'data'=>$data);
  386. return $result;
  387. }
  388. //返回结果
  389. $result = array('status'=>1,'data'=>$data);
  390. return $result;
  391. }
  392. /**********************自定义菜单 结束********/
  393. /**********************消息管理 开始********/
  394. /**
  395. * 消息/事件推送处理
  396. * @author bug <732680577@qq.com>
  397. * @createtime 2017-08-16 15:58:42
  398. * @param poststr xml 推送数据字符串
  399. * @return [type] [description]
  400. */
  401. public function responseMsg($poststr){
  402. //如果推送消息 或者推送事件存在,进行处理
  403. if(!empty($poststr)){
  404. libxml_disable_entity_loader(true);
  405. $postObj = simplexml_load_string($poststr, 'SimpleXMLElement', LIBXML_NOCDATA);
  406. $msgType = $postObj->MsgType;
  407. //判断事件类型,进行对应事件类型处理
  408. switch ($msgType) {
  409. case 'text':
  410. $resultStr = $this->handleText($postObj);
  411. break;
  412. case 'image':
  413. $resultStr = $this->handleImage($postObj);
  414. break;
  415. case 'voice':
  416. $resultStr = $this->handleVoice($postObj);
  417. break;
  418. case 'video':
  419. $resultStr = $this->handleVideo($postObj);
  420. break;
  421. case 'shortvideo':
  422. $resultStr = $this->handleShortVideo($postObj);
  423. break;
  424. case 'location':
  425. $resultStr = $this->handleLocation($postObj);
  426. break;
  427. case 'link':
  428. $resultStr = $this->handleLink($postObj);
  429. break;
  430. case 'event':
  431. $resultStr = $this->handleEvent($postObj);
  432. break;
  433. default:
  434. $resultStr = "Unknow msg type: ".$msgType;
  435. break;
  436. }
  437. return $resultStr;
  438. }
  439. }
  440. /**---------------------------------------------------------接收消息-接收普通消息 开始-------------------------------------------------------------**/
  441. /**
  442. * 接收文本消息
  443. * @author bug <732680577@qq.com>
  444. * @createtime 2017-08-16 17:22:58
  445. * @param postObj object 推送事件对象
  446. * @return [type] [description]
  447. */
  448. public function handleText($postObj){
  449. $contentStr = "您发送了文本消息:".$postObj->Content;
  450. return $this->responseText($postObj,$contentStr);
  451. }
  452. /**
  453. * 接收图片消息
  454. * @author bug <732680577@qq.com>
  455. * @createtime 2017-08-16 17:22:58
  456. * @param postObj object 推送事件对象
  457. * @return [type] [description]
  458. */
  459. public function handleImage($postObj){
  460. $contentStr = "您发送了图片,图片链接:".$postObj->PicUrl.",图片消息媒体id:".$postObj->MediaId.",消息id:".$postObj->MsgId;
  461. return $this->responseText($postObj,$contentStr);
  462. }
  463. /**
  464. * 接收语音消息
  465. * @author bug <732680577@qq.com>
  466. * @createtime 2017-08-16 17:22:58
  467. * @param postObj object 推送事件对象
  468. * @return [type] [description]
  469. */
  470. public function handleVoice($postObj){
  471. $contentStr = "您发送了语音,语音消息媒体id:".$postObj->MediaId.",语音格式:".$postObj->Format.",消息id".$postObj->MsgId.",Recognition:".$postObj->Recognition;
  472. return $this->responseText($postObj,$contentStr);
  473. }
  474. /**
  475. * 接收视频消息
  476. * @author bug <732680577@qq.com>
  477. * @createtime 2017-08-16 17:22:58
  478. * @param postObj object 推送事件对象
  479. * @return [type] [description]
  480. */
  481. public function handleVideo($postObj){
  482. $contentStr = "您发送了视频,语音消息媒体id:".$postObj->MediaId.",视频消息缩略图的媒体id:".$postObj->ThumbMediaId.",消息id".$postObj->MsgId;
  483. return $this->responseText($postObj,$contentStr);
  484. }
  485. /**
  486. * 接收小视频消息
  487. * @author bug <732680577@qq.com>
  488. * @createtime 2017-08-16 17:22:58
  489. * @param postObj object 推送事件对象
  490. * @return [type] [description]
  491. */
  492. public function handleShortVideo($postObj){
  493. $contentStr = "您发送了小视频,语音消息媒体id:".$postObj->MediaId.",视频消息缩略图的媒体id:".$postObj->ThumbMediaId.",消息id".$postObj->MsgId;
  494. return $this->responseText($postObj,$contentStr);
  495. }
  496. /**
  497. * 接收地理位置消息
  498. * @author bug <732680577@qq.com>
  499. * @createtime 2017-08-16 17:22:58
  500. * @param postObj object 推送事件对象
  501. * @return [type] [description]
  502. */
  503. public function handleLocation($postObj){
  504. $contentStr = "您发送了地理位置,地理位置维度:".$postObj->Location_X.",地理位置经度:".$postObj->Location_Y.",地图缩放大小:".$postObj->Scale.",地理位置信息:".$postObj->Label.",消息id:".$postObj->MsgId;
  505. return $this->responseText($postObj,$contentStr);
  506. }
  507. /**
  508. * 接收链接消息
  509. * @author bug <732680577@qq.com>
  510. * @createtime 2017-08-16 17:22:58
  511. * @param postObj object 推送事件对象
  512. * @return [type] [description]
  513. */
  514. public function handleLink($postObj){
  515. $contentStr = "您发送了链接,消息标题:".$postObj->Title.",消息描述:".$postObj->Description.",消息链接:".$postObj->Url.",消息id:".$postObj->MsgId;
  516. return $this->responseText($postObj,$contentStr);
  517. }
  518. /**---------------------------------------------------------接收消息-接收普通消息 结束-------------------------------------------------------------**/
  519. /**---------------------------------------------接收消息-接收事件推送 开始-------------------------------------------------------------**/
  520. /**
  521. * 接收事件推送
  522. * @author bug <732680577@qq.com>
  523. * @createtime 2017-08-16 17:22:58
  524. * @param postObj object 推送事件对象
  525. * @return [type] [description]
  526. */
  527. public function handleEvent($postObj){
  528. $event = $postObj->Event;
  529. switch ($event) {
  530. case 'subscribe'://关注
  531. break;
  532. case 'unsubscribe'://取消关注
  533. break;
  534. case 'scancode_waitmsg'://扫描二维码
  535. break;
  536. case 'SCAN':
  537. break;
  538. case 'LOCATION':
  539. break;
  540. case 'VIEW':
  541. break;
  542. default:
  543. # code...
  544. break;
  545. }
  546. $contentStr = "您触发了事件:".$event;
  547. return $this->responseText($postObj,$contentStr);
  548. }
  549. /**------------------------------------------------------------接收消息-接收事件推送 结束-------------------------------------------------------------**/
  550. /**------------------------------------------------------发送消息-被动回复用户消息 开始-------------------------------------------------------------**/
  551. /**
  552. * 推送事件回复文本消息
  553. * @author bug <732680577@qq.com>
  554. * @createtime 2017-08-16 16:05:33
  555. * @param object 接收的推送对象
  556. * @param content string 回复的文本消息
  557. * @return [type] [description]
  558. */
  559. public function responseText($object,$content)
  560. {
  561. $textTpl = "<xml>
  562. <ToUserName><![CDATA[%s]]></ToUserName>
  563. <FromUserName><![CDATA[%s]]></FromUserName>
  564. <CreateTime>%s</CreateTime>
  565. <MsgType><![CDATA[text]]></MsgType>
  566. <Content><![CDATA[%s]]></Content>
  567. </xml>";
  568. $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$content);
  569. return $resultStr;
  570. }
  571. /**
  572. * 推送事件回复图片消息
  573. * @author bug <732680577@qq.com>
  574. * @createtime 2017-08-16 16:05:33
  575. * @param object 接收的推送对象
  576. * @param MediaId 通过素材管理中的接口上传多媒体文件,得到的id
  577. * @return [type] [description]
  578. */
  579. public function responseImage($object,$media_id)
  580. {
  581. $textTpl = "<xml>
  582. <ToUserName><![CDATA[%s]]></ToUserName>
  583. <FromUserName><![CDATA[%s]]></FromUserName>
  584. <CreateTime>%s</CreateTime>
  585. <MsgType><![CDATA[image]]></MsgType>
  586. <Image>
  587. <MediaId><![CDATA[media_id]]></MediaId>
  588. </Image>
  589. </xml>";
  590. $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$media_id);
  591. return $resultStr;
  592. }
  593. /**
  594. * 推送事件回复语音消息
  595. * @author bug <732680577@qq.com>
  596. * @createtime 2017-08-16 16:05:33
  597. * @param object 接收的推送对象
  598. * @param MediaId 通过素材管理中的接口上传多媒体文件,得到的id
  599. * @return [type] [description]
  600. */
  601. public function responseVoice($object,$media_id)
  602. {
  603. $textTpl = "<xml>
  604. <ToUserName><![CDATA[%s]]></ToUserName>
  605. <FromUserName><![CDATA[%s]]></FromUserName>
  606. <CreateTime>%s</CreateTime>
  607. <MsgType><![CDATA[voice]]></MsgType>
  608. <Voice>
  609. <MediaId><![CDATA[%s]]></MediaId>
  610. </Voice>
  611. </xml>";
  612. $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$media_id);
  613. return $resultStr;
  614. }
  615. /**
  616. * 推送事件回复视频消息
  617. * @author bug <732680577@qq.com>
  618. * @createtime 2017-08-16 16:05:33
  619. * @param object 接收的推送对象
  620. * @param media_id 通过素材管理中的接口上传多媒体文件,得到的id
  621. * @param title 视频消息的标题
  622. * @param description 视频消息的描述
  623. * @return
  624. */
  625. public function responseVideo($object,$media_id,$title='',$description='')
  626. {
  627. $textTpl = "<xml>
  628. <ToUserName><![CDATA[%s]]></ToUserName>
  629. <FromUserName><![CDATA[%s]]></FromUserName>
  630. <CreateTime>%s</CreateTime>
  631. <MsgType><![CDATA[video]]></MsgType>
  632. <Video>
  633. <MediaId><![CDATA[%s]]></MediaId>
  634. <Title><![CDATA[%s]]></Title>
  635. <Description><![CDATA[%s]]></Description>
  636. </Video>
  637. </xml>";
  638. $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$media_id,$title,$description);
  639. return $resultStr;
  640. }
  641. /**
  642. * 推送事件回复音乐消息
  643. * @author bug <732680577@qq.com>
  644. * @createtime 2017-08-16 16:05:33
  645. * @param object 接收的推送对象
  646. * @param media_id 缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id
  647. * @param title 音乐标题
  648. * @param description 音乐描述
  649. * @param music_url 音乐链接
  650. * @param hqmusic_url 高质量音乐链接,WIFI环境优先使用该链接播放音乐
  651. * @return
  652. */
  653. public function responseMusic($object,$media_id,$title='',$description='',$music_url='',$hqmusic_url='')
  654. {
  655. $textTpl = "<xml>
  656. <ToUserName><![CDATA[%s]]></ToUserName>
  657. <FromUserName><![CDATA[%s]]></FromUserName>
  658. <CreateTime>%s</CreateTime>
  659. <MsgType><![CDATA[music]]></MsgType>
  660. <Music>
  661. <Title><![CDATA[TITLE]]></Title>
  662. <Description><![CDATA[DESCRIPTION]]></Description>
  663. <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
  664. <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
  665. <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
  666. </Music>
  667. </xml>";
  668. $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$title,$description,$music_url,$hqmusic_url,$media_id);
  669. return $resultStr;
  670. }
  671. /**
  672. * 推送事件回复图文消息
  673. * @author bug <732680577@qq.com>
  674. * @createtime 2017-08-16 16:05:33
  675. * @param object 接收的推送对象
  676. * @param articles 图文消息数组
  677. * @param article_count 图文消息个数,限制为8条以内
  678. * @param title 图文消息标题
  679. * @param description 图文消息描述
  680. * @param pic_url 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
  681. * @param url 点击图文消息跳转链接
  682. * @return
  683. */
  684. public function responseNews($object,$articles)
  685. {
  686. $article_count = count($articles);
  687. $textTpl = "<xml>
  688. <ToUserName><![CDATA[%s]]></ToUserName>
  689. <FromUserName><![CDATA[%s]]></FromUserName>
  690. <CreateTime>%s</CreateTime>
  691. <MsgType><![CDATA[news]]></MsgType>
  692. <ArticleCount>%s</ArticleCount>
  693. <Articles>";
  694. $textTpl = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(),$article_count);
  695. $article_tpl ="<item>
  696. <Title><![CDATA[%s]]></Title>
  697. <Description><![CDATA[%s]]></Description>
  698. <PicUrl><![CDATA[%s]]></PicUrl>
  699. <Url><![CDATA[%s]]></Url>
  700. </item>";
  701. foreach ($articles as $value) {
  702. $article_str = sprintf($article_tpl,$value['title'],$value['description'],$value['picurl'],$value['url']);
  703. $textTpl .= $article_str;
  704. }
  705. $textTpl .= "</Articles>
  706. </xml>";
  707. return $textTpl;
  708. }
  709. /**--------------------------------------------------------发送消息-被动回复用户消息 结束-------------------------------------------------------------**/
  710. /**
  711. * 客服接口-发消息
  712. * @author bug <732680577@qq.com>
  713. * @createtime 2017-07-28 09:27:56
  714. * @param openid string 微信用户唯一凭证openid
  715. * @param type string 1 文本 2图文
  716. * @param data json 请求数据集
  717. * @return [type] [description]
  718. */
  719. public function custom_send($openid,$type,$msg,$customservice=''){
  720. //获取access_token
  721. $access_token= $this->access_token(false);
  722. if($access_token['status']!=1 && $access_token['status']!=2){
  723. return $access_token;
  724. }
  725. //配置请求url,加入access_token参数
  726. $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token['data'];
  727. $post_data['touser'] = $openid;
  728. $post_data['msgtype'] = $type;
  729. switch ($type) {
  730. case 'text'://发送文本消息
  731. $post_data['text']['content'] = $msg;
  732. break;
  733. case 'image'://发送图片消息
  734. $post_data['image']['media_id'] = $msg;
  735. break;
  736. case 'voice'://发送语音消息
  737. $post_data['voice']['media_id'] = $msg;
  738. break;
  739. case 'video'://发送视频消息,参数media_id,thumb_media_id,title,description
  740. $post_data['video'] = $msg;
  741. break;
  742. case 'music'://发送音乐消息,参数title,description,musicurl,hqmusicurl,thumb_media_id
  743. $post_data['music'] = $msg;
  744. break;
  745. case 'news'://发送图文消息(点击跳转到外链) 图文消息条数限制在8条以内,注意,如果图文数超过8,则将会无响应
  746. $post_data['news']['articles'] =$msg;
  747. break;
  748. case 'mpnews'://发送图文消息(点击跳转到图文消息页面) 图文消息条数限制在8条以内,注意,如果图文数超过8,则将会无响应
  749. $post_data['mpnews']['media_id'] = $msg;
  750. break;
  751. case 'wxcard'://发送卡券
  752. $post_data['wxcard']['card_id'] = $msg;
  753. break;
  754. case 'miniprogrampage'://发送小程序卡片,参数title,appid,pagepath,thumb_media_id
  755. $post_data['miniprogrampage'] = $msg;
  756. break;
  757. default:
  758. $post_data['msgtype'] = 'text';
  759. $post_data['text']['content'] = '这是一个默认图文消息:无消息类型';
  760. break;
  761. }
  762. //请注意,如果需要以某个客服帐号来发消息(在微信6.0.2及以上版本中显示自定义头像),则需在JSON数据包的后半部分加入customservice参数
  763. if(!empty($customservice)){
  764. $post_data['customservice']['kf_account'] = $customservice;
  765. }
  766. //将json数组转换成字符串数组
  767. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  768. //获取请求结果,并将请求结果转换成json
  769. $data = json_decode(https_request($url,$post_data),true);
  770. //access_token 过期重新获取并重新回调当前函数
  771. if($data['errcode']==42001 || $data['errcode']==40001){
  772. $access_token = $this->access_token(true);
  773. return $this->custom_send($openid,$type,$msg,$customservice);
  774. }
  775. //返回结果
  776. if($data['errcode']==0&&$data['errmsg']=='ok'){
  777. $result = array('status'=>1,'data'=>$data);
  778. return $result;
  779. }
  780. $result = array('status'=>40014,'data'=>$data);
  781. return $result;
  782. }
  783. /**
  784. * 获取模板列表
  785. * @author bug <732680577@qq.com>
  786. * @createtime 2017-08-25 14:43:54
  787. * @param access_token 微信调用接口唯一凭证
  788. * @return json
  789. */
  790. public function template_list(){
  791. //获取access_token
  792. $access_token= $this->access_token(false);
  793. if($access_token['status']!=1 && $access_token['status']!=2){
  794. return $access_token;
  795. }
  796. //配置请求url,加入access_token参数
  797. $url = 'https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token='.$access_token['data'];
  798. //获取请求结果,并将请求结果转换成json
  799. $data = string_json($url);
  800. if(array_key_exists('errcode', $data)){
  801. //access_token 过期重新获取并重新回调当前函数
  802. if($data['errcode']==42001 || $data['errcode']==40001){
  803. $access_token = $this->access_token(true);
  804. return $this->template_list();
  805. }
  806. $result = array('status'=>40014,'data'=>$data);
  807. return $result;
  808. }
  809. //返回结果
  810. $result = array('status'=>1,'data'=>$data);
  811. return $result;
  812. }
  813. /**
  814. * 模板接口-发消息
  815. * @author bug <732680577@qq.com>
  816. * @createtime 2017-08-01 15:50:11
  817. * @param data json 请求数据集
  818. * @return [type] [description]
  819. */
  820. public function template_send($post_data){
  821. //获取access_token
  822. $access_token= $this->access_token(false);
  823. if($access_token['status']!=1 && $access_token['status']!=2){
  824. return $access_token;
  825. }
  826. //配置请求url,加入access_token参数
  827. $url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.$access_token['data'];
  828. //将json数组转换成字符串数组
  829. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  830. //获取请求结果,并将请求结果转换成json
  831. $data = json_decode(https_request($url,$post_data),true);
  832. //access_token 过期重新获取并重新回调当前函数
  833. if($data['errcode']==42001 || $data['errcode']==40001){
  834. $access_token = $this->access_token(true);
  835. return $this->template_send($post_data);
  836. }
  837. //返回结果
  838. if($data['errcode']==0&&$data['errmsg']=='ok'){
  839. $result = array('status'=>1,'data'=>$data);
  840. return $result;
  841. }
  842. $result = array('status'=>40014,'data'=>$data);
  843. return $result;
  844. }
  845. /**********************消息管理 结束********/
  846. /**********************微信网页授权 开始********/
  847. /**
  848. * 通过微信授权code获取微信用户access_token
  849. * @author bug <732680577@qq.com>
  850. * @createtime 2017-07-27 11:26:30
  851. * @param code 微信用户授权获取的code
  852. * @return json 返回结果
  853. */
  854. public function user_access_token($code){
  855. //session开始
  856. if(!isset($_SESSION)){
  857. session_start();
  858. }
  859. //如果code已经被使用过了返回缓存里面的微信用户access_token相关信息
  860. if(isset($_SESSION['user_access_token'])&&$code==$_SESSION['user_access_token']['code']){
  861. $data = $_SESSION['user_access_token'];
  862. $result = array('status'=>2,'data'=>$data);
  863. return $result;
  864. }
  865. //配置请求url,加入access_token参数
  866. $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$this->appid.'&secret='.$this->appsecret.'&code='.$code.'&grant_type=authorization_code';
  867. //获取请求结果,并将请求结果转换成json
  868. $data = string_json($url);
  869. //获取当前时间
  870. $time = time();
  871. if(array_key_exists('errcode', $data)){
  872. $result = array('status'=>40014,'data'=>$data);
  873. return $result;
  874. }
  875. session_set_cookie_params(60*5); //设置access_token在服务器session中缓存时间
  876. //添加access_token过期时间
  877. $data['access_endtime'] = $time+$data['expires_in'];
  878. //添加refresh_token过期时间
  879. $data['refresh_endtime'] = $time+60*60*24*30;
  880. $data['code'] = $code;
  881. $_SESSION['user_access_token'] = $data;
  882. //返回结果
  883. $result = array('status'=>1,'data'=>$data);
  884. return $result;
  885. }
  886. /**
  887. * 刷新微信用户access_token
  888. * @author bug <732680577@qq.com>
  889. * @createtime 2017-07-27 11:26:27
  890. * @param refresh_token 微信用户用来刷新access_token用的
  891. * @param state int 微信用户获取access_token的state
  892. * @return json 返回结果
  893. */
  894. public function user_refresh_token($refresh_token,$state=0){
  895. //配置请求url,加入access_token参数
  896. $url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid='.$this->appid.'&grant_type=refresh_token&refresh_token='.$refresh_token;
  897. //获取请求结果,并将请求结果转换成json
  898. $data = string_json($url);
  899. //获取当前时间
  900. $time = time();
  901. if(array_key_exists('errcode', $data)){
  902. if($data['errcode']==42002){
  903. //如果超时重定向首页重新获取refresh_token
  904. header("location: https://open.weixin.qq.com/connect/oauth2/authorize?appid=".config('crm_config.wechat_appid')."&redirect_uri=".urlencode(config('crm_config.user_accesstoken_uri'))."&response_type=code&scope=snsapi_base&state=".$state."#wechat_redirect");exit;
  905. }
  906. $result = array('status'=>40014,'data'=>$data);
  907. return $result;
  908. }
  909. //添加access_token过期时间
  910. $data['access_endtime'] = $time+$data['expires_in'];
  911. //返回结果
  912. $result = array('status'=>1,'data'=>$data);
  913. return $result;
  914. }
  915. /**
  916. * 拉取用户信息(需scope为 snsapi_userinfo)
  917. * @author bug <732680577@qq.com>
  918. * @createtime 2017-07-27 11:26:27
  919. * @param string access_token 微信用户授权获取的access_token
  920. * @param string openid 微信用户唯一凭证
  921. * @return json 返回结果
  922. */
  923. public function userinfo($access_token,$openid){
  924. //配置请求url,加入access_token参数
  925. $url = 'https://api.weixin.qq.com/sns/userinfo?access_token='.$access_token.'&openid='.$openid.'&lang=zh_CN';
  926. //获取请求结果,并将请求结果转换成json
  927. $data = string_json($url);
  928. if(array_key_exists('errcode', $data)){
  929. $result = array('status'=>40014,'data'=>$data);
  930. return $result;
  931. }
  932. //返回结果
  933. $result = array('status'=>1,'data'=>$data);
  934. return $result;
  935. }
  936. /**
  937. * 检验授权凭证(access_token)是否有效
  938. * @author bug <732680577@qq.com>
  939. * @createtime 2017-07-27 11:26:27
  940. * @param string access_token 微信用户授权获取的access_token
  941. * @param string openid 微信用户唯一凭证
  942. * @return json 返回结果
  943. */
  944. public function auth($access_token,$openid){
  945. //配置请求url,加入access_token参数
  946. $url = 'https://api.weixin.qq.com/sns/auth?access_token='.$access_token.'&openid='.$openid;
  947. //获取请求结果,并将请求结果转换成json
  948. $data = string_json($url);
  949. //返回结果
  950. $status = $data['errcode']==0?1:40014;
  951. $result = array('status'=>$status,'data'=>$data);
  952. return $result;
  953. }
  954. /**********************微信网页授权 结束********/
  955. /**********************微信自定义分享 开始********/
  956. /**
  957. * 获取微信自定已分享配置参数包
  958. * @author bug <732680577@qq.com>
  959. * @createtime 2017-08-05 14:21:04
  960. * @return array 配置参数包
  961. */
  962. public function getSignPackage() {
  963. $jsapiTicket = $this->getJsApiTicket();
  964. // 注意 URL 一定要动态获取,不能 hardcode.
  965. $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
  966. $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
  967. $timestamp = time();
  968. $nonceStr = $this->createNonceStr();
  969. // 这里参数的顺序要按照 key 值 ASCII 码升序排序
  970. $string = "jsapi_ticket=".$jsapiTicket."&noncestr=".$nonceStr."&timestamp=".$timestamp."&url=".$url;
  971. $signature = sha1($string);
  972. $signPackage = array(
  973. "appId" => $this->appid,
  974. "nonceStr" => $nonceStr,
  975. "timestamp" => $timestamp,
  976. "url" => $url,
  977. "signature" => $signature,
  978. "rawString" => $string
  979. );
  980. return $signPackage;
  981. }
  982. /**
  983. * 微信自定义分享随机字符串
  984. * @author bug <732680577@qq.com>
  985. * @createtime 2017-08-05 14:22:15
  986. * @param length int 随机字符串长度
  987. * @return str string 随机字符串
  988. */
  989. private function createNonceStr($length = 16) {
  990. $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  991. $str = "";
  992. for ($i = 0; $i < $length; $i++) {
  993. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  994. }
  995. return $str;
  996. }
  997. /**
  998. * 微信自定义分享获取jsapiticket
  999. * @author bug <732680577@qq.com>
  1000. * @createtime 2017-08-05 14:23:30
  1001. * @return jsapi_ticket string 微信jsapi调用凭证
  1002. */
  1003. private function getJsApiTicket() {
  1004. // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
  1005. $data = json_decode(file_get_contents(dirname(__FILE__)."/json/jsapi_ticket.json"));
  1006. if ($data->expire_time < time()) {
  1007. $access_token = $this->access_token(false);
  1008. if($access_token['status']!=1 && $access_token['status']!=2){
  1009. return $access_token;
  1010. }
  1011. // 如果是企业号用以下 URL 获取 ticket
  1012. // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=".$access_token['data'];
  1013. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=".$access_token['data'];
  1014. $res = json_decode(https_get($url));
  1015. if(!isset($res->ticket)){
  1016. $access_token = $this->access_token(true);
  1017. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=".$access_token['data'];
  1018. $res = json_decode(https_get($url));
  1019. }
  1020. $ticket = $res->ticket;
  1021. if ($ticket) {
  1022. $data->expire_time = time() + 7200;
  1023. $data->jsapi_ticket = $ticket;
  1024. $fp = fopen(dirname(__FILE__)."/json/jsapi_ticket.json", "w");
  1025. fwrite($fp, json_encode($data));
  1026. fclose($fp);
  1027. }
  1028. } else {
  1029. $ticket = $data->jsapi_ticket;
  1030. }
  1031. return $ticket;
  1032. }
  1033. /**********************微信自定义分享 结束********/
  1034. /**********************素材管理 开始********/
  1035. /**
  1036. * 获取永久素材列表
  1037. * @author bug <732680577@qq.com>
  1038. * @createtime 2017-09-02 16:44:24
  1039. * @param type string 素材的类型,图片(image)、视频(video)、语音 (voice)、图文(news)
  1040. * @param offset int 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回
  1041. * @param count int 返回素材的数量,取值在1到20之间
  1042. * @return [type] [description]
  1043. */
  1044. public function material_list($type,$offset=0,$count=20){
  1045. //获取access_token
  1046. $access_token= $this->access_token(false);
  1047. if($access_token['status']!=1 && $access_token['status']!=2){
  1048. return $access_token;
  1049. }
  1050. //组装参数
  1051. $post_data['type'] = $type;
  1052. $post_data['offset'] = $offset;
  1053. $post_data['count'] = $count;
  1054. //配置请求url,加入access_token参数
  1055. $url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token='.$access_token['data'];
  1056. //将json数组转换成字符串数组
  1057. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  1058. //获取请求结果,并将请求结果转换成json
  1059. $data = json_decode(https_request($url,$post_data),true);
  1060. //access_token 过期重新获取并重新回调当前函数
  1061. if(isset($data['errcode']) && ($data['errcode']==42001 || $data['errcode']==40001)){
  1062. $access_token = $this->access_token(true);
  1063. return $this->create_menu($type,$offset,$count);
  1064. }
  1065. //返回结果
  1066. if(!isset($data['errcode'])){
  1067. $result = array('status'=>1,'data'=>$data);
  1068. return $result;
  1069. }
  1070. $result = array('status'=>40014,'data'=>$data);
  1071. return $result;
  1072. }
  1073. /**********************素材管理 结束********/
  1074. /**********************用户管理 开始********/
  1075. /**
  1076. * 获取关注用户个人信息
  1077. * @author bug <732680577@qq.com>
  1078. * @createtime 2017-08-25 14:43:54
  1079. * @param access_token string 微信调用接口唯一凭证
  1080. * @param openid string 用户的openid
  1081. * @return json
  1082. */
  1083. public function user_info($openid=''){
  1084. //获取access_token
  1085. $access_token= $this->access_token(false);
  1086. if($access_token['status']!=1 && $access_token['status']!=2){
  1087. return $access_token;
  1088. }
  1089. //配置请求url,加入access_token参数
  1090. $url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$access_token['data'].'&openid='.$openid.'&lang=zh_CN ';
  1091. //获取请求结果,并将请求结果转换成json
  1092. $data = string_json($url);
  1093. if(array_key_exists('errcode', $data)){
  1094. //access_token 过期重新获取并重新回调当前函数
  1095. if($data['errcode']==42001 || $data['errcode']==40001 ){
  1096. $access_token = $this->access_token(true);
  1097. return $this->user_info($openid);
  1098. }
  1099. $result = array('status'=>40014,'data'=>$data);
  1100. return $result;
  1101. }
  1102. //返回结果
  1103. $result = array('status'=>1,'data'=>$data);
  1104. return $result;
  1105. }
  1106. /**
  1107. * 批量获取用户信息
  1108. * @author bug <732680577@qq.com>
  1109. * @createtime 2017-10-16 14:20:46
  1110. * @param data json 请求数据集
  1111. * @return [type] [description]
  1112. */
  1113. public function user_list_info($post_data){
  1114. //获取access_token
  1115. $access_token= $this->access_token(false);
  1116. if($access_token['status']!=1 && $access_token['status']!=2){
  1117. return $access_token;
  1118. }
  1119. //配置请求url,加入access_token参数
  1120. $url = 'https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token='.$access_token['data'];
  1121. //将json数组转换成字符串数组
  1122. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  1123. //获取请求结果,并将请求结果转换成json
  1124. $data = json_decode(https_request($url,$post_data),true);
  1125. //access_token 过期重新获取并重新回调当前函数
  1126. if(isset($data['errcode'])&&($data['errcode']==42001 || $data['errcode']==40001)){
  1127. $access_token = $this->access_token(true);
  1128. return $this->user_list_info($post_data);
  1129. }
  1130. //返回结果
  1131. if(isset($data['user_info_list'])){
  1132. $result = array('status'=>1,'data'=>$data);
  1133. return $result;
  1134. }
  1135. $result = array('status'=>40014,'data'=>$data);
  1136. return $result;
  1137. }
  1138. /**
  1139. * 获取关注用户列表
  1140. * @author bug <732680577@qq.com>
  1141. * @createtime 2017-08-25 14:43:54
  1142. * @param access_token string 微信调用接口唯一凭证
  1143. * @param next_openid string 上次请求最后一个用户的openid
  1144. * @return json
  1145. */
  1146. public function user_list($next_openid=''){
  1147. //获取access_token
  1148. $access_token= $this->access_token(false);
  1149. if($access_token['status']!=1 && $access_token['status']!=2){
  1150. return $access_token;
  1151. }
  1152. //配置请求url,加入access_token参数
  1153. $url = 'https://api.weixin.qq.com/cgi-bin/user/get?access_token='.$access_token['data'];
  1154. if(!empty($next_openid)){
  1155. $url.='&next_openid='.$next_openid;
  1156. }
  1157. //获取请求结果,并将请求结果转换成json
  1158. $data = string_json($url);
  1159. if(array_key_exists('errcode', $data)){
  1160. //access_token 过期重新获取并重新回调当前函数
  1161. if($data['errcode']==42001 || $data['errcode']==40001){
  1162. $access_token = $this->access_token(true);
  1163. return $this->user_list($next_openid);
  1164. }
  1165. $result = array('status'=>40014,'data'=>$data);
  1166. return $result;
  1167. }
  1168. //返回结果
  1169. $result = array('status'=>1,'data'=>$data);
  1170. return $result;
  1171. }
  1172. /**********************用户管理 结束********/
  1173. /**********************新版客服 开始********/
  1174. /**
  1175. * 添加客服账号
  1176. * @author bug <732680577@qq.com>
  1177. * @createtime 2017-08-02 15:39:36
  1178. * @param post_data 请求数据
  1179. * @param kf_account string 完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符
  1180. * @param nickname 客服昵称,最长16个字
  1181. * @return [type] [description]
  1182. */
  1183. public function customservice_add($post_data){
  1184. //获取access_token
  1185. $access_token= $this->access_token(false);
  1186. if($access_token['status']!=1 && $access_token['status']!=2){
  1187. return $access_token;
  1188. }
  1189. //配置请求url,加入access_token参数
  1190. $url = 'https://api.weixin.qq.com/customservice/kfaccount/add?access_token='.$access_token['data'];
  1191. //将json数组转换成字符串数组
  1192. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  1193. //获取请求结果,并将请求结果转换成json
  1194. $data = json_decode(https_request($url,$post_data),true);
  1195. //access_token 过期重新获取并重新回调当前函数
  1196. if($data['errcode']==42001 || $data['errcode']==40001){
  1197. $access_token = $this->access_token(true);
  1198. return $this->customservice_add($post_data);
  1199. }
  1200. //返回结果
  1201. if($data['errcode']==0&&$data['errmsg']=='ok'){
  1202. $result = array('status'=>1,'data'=>$data);
  1203. return $result;
  1204. }
  1205. $result = array('status'=>40014,'data'=>$data);
  1206. return $result;
  1207. }
  1208. /**
  1209. * 获取聊天记录
  1210. * @author bug <732680577@qq.com>
  1211. * @createtime 2017-08-01 17:20:03
  1212. * @param starttime int 起始时间,unix时间戳
  1213. * @param endtime int 结束时间,unix时间戳,每次查询时段不能超过24小时
  1214. * @param msgid int 消息id顺序从小到大,从1开始
  1215. * @param number int 每次获取条数,最多10000条
  1216. * @return json 返回结果
  1217. */
  1218. public function customservice_getmsglist($starttime,$endtime,$msgid,$number){
  1219. //获取access_token
  1220. $access_token= $this->access_token(false);
  1221. if($access_token['status']!=1 && $access_token['status']!=2){
  1222. return $access_token;
  1223. }
  1224. //配置请求url,加入access_token参数
  1225. $url = 'https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token='.$access_token['data'];
  1226. //将json数组转换成字符串数组
  1227. $post_data = array('starttime'=>$starttime,
  1228. 'endtime'=>$endtime,
  1229. 'msgid'=>$msgid,
  1230. 'number'=>$number);
  1231. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  1232. //获取请求结果,并将请求结果转换成json
  1233. $data = json_decode(https_request($url,$post_data),true);
  1234. //access_token 过期重新获取并重新回调当前函数
  1235. if(isset($data['errcode']) && ($data['errcode']==42001 || $data['errcode']==40001)){
  1236. $access_token = $this->access_token(true);
  1237. return $this->customservice_getmsglist($starttime,$endtime,$msgid,$number);
  1238. }
  1239. //返回结果
  1240. if(isset($data['recordlist'])){
  1241. $result = array('status'=>1,'data'=>$data);
  1242. return $result;
  1243. }
  1244. $result = array('status'=>40014,'data'=>$data);
  1245. return $result;
  1246. }
  1247. /**********************新版客服 结束********/
  1248. /**********************微信支付 开始********/
  1249. /**
  1250. * 统一下单,WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
  1251. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1252. * @author bug <732680577@qq.com>
  1253. * @createtime 2017-09-09 16:22:38\
  1254. * @param WxPayUnifiedOrder $inputObj
  1255. * @param int $timeOut
  1256. * @throws WxPayException
  1257. * @return [type] [description]
  1258. */
  1259. public function unifiedOrder($data, $timeOut = 6){
  1260. //配置请求url
  1261. $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  1262. //检查请求参数
  1263. if(!isset($data['out_trade_no'])){
  1264. $result = array('status'=>40010,'data'=>'缺少统一支付接口必填参数out_trade_no!');
  1265. return $result;
  1266. }
  1267. if(!isset($data['body'])){
  1268. $result = array('status'=>40010,'data'=>'缺少统一支付接口必填参数body!');
  1269. return $result;
  1270. }
  1271. if(!isset($data['total_fee'])){
  1272. $result = array('status'=>40010,'data'=>'缺少统一支付接口必填参数total_fee!');
  1273. return $result;
  1274. }
  1275. if(!isset($data['trade_type'])){
  1276. $result = array('status'=>40010,'data'=>'缺少统一支付接口必填参数trade_type!');
  1277. return $result;
  1278. }
  1279. if($data['trade_type'] == 'JSAPI' && !isset($data['openid'])){
  1280. $result = array('status'=>40010,'data'=>'统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!');
  1281. return $result;
  1282. }
  1283. if($data['trade_type'] == "NATIVE" && !isset($data['product_id'])){
  1284. $result = array('status'=>40010,'data'=>'统一支付接口中,缺少必填参数product_id!trade_type为NATIVE时,product_id为必填参数!');
  1285. return $result;
  1286. }
  1287. //异步通知url未设置,则使用配置文件中的url
  1288. if(!isset($data['notify_url'])){
  1289. $data['notify_url'] = config('crm_config.wechat_notify_url');
  1290. }
  1291. $data['appid'] = $this->appid;//微信支付分配的公众账号ID(企业号corpid即为此appId)
  1292. $data['mch_id'] = $this->mch_id;//微信支付分配的商户号
  1293. $data['spbill_create_ip'] = get_client_ip();//APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
  1294. $data['nonce_str'] = $this->createNonceStr(32);
  1295. $data['sign'] = $this->makeSign($data);
  1296. $xml = $this->arr_xml($data);
  1297. $startTimeStamp = $this->milli_time();//请求开始时间
  1298. $response = $this->postXml($xml, $url, false, $timeOut);
  1299. $datas = $this->xml_arr($response);//将xml转为array
  1300. //判断请求结果是否成功
  1301. if($datas['return_code'] == 'FAIL'){
  1302. $result = array('status'=>40014,'data'=>$datas['return_msg']);
  1303. return $result;
  1304. }if($datas['result_code'] == 'FAIL'){
  1305. $result = array('status'=>40014,'data'=>$datas['err_code'].":".$datas['err_code_des']);
  1306. return $result;
  1307. }
  1308. //判断签名是否存在,及签名是否正确
  1309. if(!array_key_exists('sign', $datas)){
  1310. $result = array('status'=>40011,'data'=>'签名错误!');
  1311. return $result;
  1312. }
  1313. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1314. //返回结果
  1315. $result = array('status'=>1,'data'=>$datas);
  1316. return $result;
  1317. }
  1318. //生成 APP端支付参数
  1319. public function getAppParam($prepayId)
  1320. {
  1321. $data['appid'] = $this->appid;
  1322. $data['partnerid'] = $this->mch_id;
  1323. $data['prepayid'] = $prepayId;
  1324. $data['package'] = 'Sign=WXPay';
  1325. $data['noncestr'] = $this->createNonceStr(32);
  1326. $data['timestamp'] = time();
  1327. $data['sign_type'] = 'MD5';
  1328. $data['sign'] = $this->makeSign($data);
  1329. return $data;
  1330. }
  1331. //生成微信内H5支付、小程序端支付参数 注意参数大小写
  1332. public function getJsapiParam($prepayId)
  1333. {
  1334. $data['appId'] = $this->appid;
  1335. $data['nonceStr'] = $this->createNonceStr(32);
  1336. $data['package'] = 'prepay_id=' . $prepayId;
  1337. $data['timeStamp'] = time();
  1338. $data['signType'] = 'MD5';
  1339. $data['paySign'] = $this->makeSign($data);
  1340. return $data;
  1341. }
  1342. //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。
  1343. //通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒
  1344. public function replyNotify()
  1345. {
  1346. $data['return_code'] = 'SUCCESS';
  1347. $data['return_msg'] = 'OK';
  1348. $xml = $this->arr_xml($data);
  1349. echo $xml;
  1350. die();
  1351. }
  1352. /**
  1353. * 查询订单,WxPayOrderQuery中out_trade_no、transaction_id至少填一个
  1354. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1355. * @author bug <732680577@qq.com>
  1356. * @createtime 2017-09-11 11:37:20
  1357. * @param $data 参数集
  1358. * @param $timeOut 超时
  1359. * @return 成功时返回,其他抛异常
  1360. */
  1361. public function orderQuery($data, $timeOut = 6){
  1362. $url = "https://api.mch.weixin.qq.com/pay/orderquery";
  1363. //检测必填参数
  1364. if(!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
  1365. $result = array('status'=>40010,'data'=>'订单查询接口中,out_trade_no、transaction_id至少填一个!');
  1366. return $result;
  1367. }
  1368. //组装参数
  1369. $data['appid'] = $this->appid;//公众账号ID
  1370. $data['mch_id'] = $this->mch_id;//商户号
  1371. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1372. $data['sign'] = $this->makeSign($data);//签名
  1373. $xml = $this->arr_xml($data);
  1374. $startTimeStamp = $this->milli_time();//请求开始时间
  1375. $response = $this->postXml($xml, $url, false, $timeOut);
  1376. $datas = $this->xml_arr($response);//将xml转为array
  1377. //判断请求结果是否成功
  1378. if($datas['return_code'] != 'SUCCESS'){
  1379. $result = array('status'=>40014,'data'=>$datas);
  1380. return $result;
  1381. }
  1382. //判断签名是否存在,及签名是否正确
  1383. if(!array_key_exists('sign', $datas) ){
  1384. $result = array('status'=>40011,'data'=>'签名错误!');
  1385. return $result;
  1386. }
  1387. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1388. //返回结果
  1389. $result = array('status'=>1,'data'=>$datas);
  1390. return $result;
  1391. }
  1392. /**
  1393. * 关闭订单,WxPayCloseOrder中out_trade_no必填
  1394. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1395. * @author bug <732680577@qq.com>
  1396. * @createtime 2017-09-11 11:48:48
  1397. * @param $data 参数集
  1398. * @param $timeOut 超时
  1399. * @return 成功时返回,其他抛异常
  1400. */
  1401. public function closeOrder($data, $timeOut = 6){
  1402. $url = "https://api.mch.weixin.qq.com/pay/closeorder";
  1403. //检测必填参数
  1404. if(!array_key_exists('out_trade_no', $data)) {
  1405. $result = array('status'=>40010,'data'=>'订单关闭接口中,out_trade_no必填!');
  1406. return $result;
  1407. }
  1408. //组装参数
  1409. $data['appid'] = $this->appid;//公众账号ID
  1410. $data['mch_id'] = $this->mch_id;//商户号
  1411. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1412. $data['sign'] = $this->makeSign($data);//签名
  1413. $xml = $this->arr_xml($data);
  1414. $startTimeStamp = $this->milli_time();//请求开始时间
  1415. $response = $this->postXml($xml, $url, false, $timeOut);
  1416. $datas = $this->xml_arr($response);//将xml转为array
  1417. //判断请求结果是否成功
  1418. if($datas['return_code'] != 'SUCCESS'){
  1419. $result = array('status'=>40014,'data'=>$datas);
  1420. return $result;
  1421. }
  1422. //判断签名是否存在,及签名是否正确
  1423. if(!array_key_exists('sign', $datas) ){
  1424. $result = array('status'=>40011,'data'=>'签名错误!');
  1425. return $result;
  1426. }
  1427. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1428. //返回结果
  1429. $result = array('status'=>1,'data'=>$datas);
  1430. return $result;
  1431. }
  1432. /**
  1433. * 申请退款,WxPayRefund中out_trade_no、transaction_id至少填一个且
  1434. * out_refund_no、total_fee、refund_fee、op_user_id为必填参数
  1435. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1436. * @author bug <732680577@qq.com>
  1437. * @createtime 2017-09-11 13:42:49
  1438. * @param $data 参数集
  1439. * @param $timeOut 超时
  1440. * @return 成功时返回,其他抛异常
  1441. */
  1442. public function refund($data, $timeOut = 6){
  1443. $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
  1444. //检测必填参数
  1445. if(!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
  1446. $result = array('status'=>40010,'data'=>'退款申请接口中,out_trade_no、transaction_id至少填一个!');
  1447. return $result;
  1448. }
  1449. if(!array_key_exists('out_refund_no', $data)) {
  1450. $result = array('status'=>40010,'data'=>'退款申请接口中,缺少必填参数out_refund_no!');
  1451. return $result;
  1452. }
  1453. if(!array_key_exists('total_fee', $data)) {
  1454. $result = array('status'=>40010,'data'=>'退款申请接口中,缺少必填参数total_fee!');
  1455. return $result;
  1456. }
  1457. if(!array_key_exists('refund_fee', $data)) {
  1458. $result = array('status'=>40010,'data'=>'退款申请接口中,缺少必填参数refund_fee!');
  1459. return $result;
  1460. }
  1461. //组装参数
  1462. $data['appid'] = $this->appid;//公众账号ID
  1463. $data['mch_id'] = $this->mch_id;//商户号
  1464. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1465. /**
  1466. * 仅针对老资金流商户使用
  1467. *REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款)
  1468. *REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
  1469. */
  1470. $data['refund_account'] = 'REFUND_SOURCE_RECHARGE_FUNDS';//退款资金来源
  1471. $data['sign'] = $this->makeSign($data);//签名
  1472. $xml = $this->arr_xml($data);
  1473. $startTimeStamp = $this->milli_time();//请求开始时间
  1474. $response = $this->postXml($xml, $url, true, $timeOut);
  1475. $datas = $this->xml_arr($response);//将xml转为array
  1476. //判断请求结果是否成功
  1477. if($datas['return_code'] != 'SUCCESS'){
  1478. $result = array('status'=>40014,'data'=>$datas);
  1479. return $result;
  1480. }
  1481. //判断签名是否存在,及签名是否正确
  1482. if(!array_key_exists('sign', $datas) ){
  1483. $result = array('status'=>40011,'data'=>'签名错误!');
  1484. return $result;
  1485. }
  1486. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1487. //返回结果
  1488. $result = array('status'=>1,'data'=>$datas);
  1489. return $result;
  1490. }
  1491. /**
  1492. * 查询退款
  1493. * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,
  1494. * 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
  1495. * WxPayRefundQuery中out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
  1496. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1497. * @author bug <732680577@qq.com>
  1498. * @createtime 2017-09-11 13:56:23
  1499. * @param $data 参数集
  1500. * @param $timeOut 超时
  1501. * @return 成功时返回,其他抛异常
  1502. */
  1503. public function refundQuery($data, $timeOut = 6){
  1504. $url = "https://api.mch.weixin.qq.com/pay/refundquery";
  1505. //检测必填参数
  1506. if(!array_key_exists('out_refund_no', $data) && !array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data) && !array_key_exists('refund_id', $data) ) {
  1507. $result = array('status'=>40010,'data'=>'退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个');
  1508. return $result;
  1509. }
  1510. //组装参数
  1511. $data['appid'] = $this->appid;//公众账号ID
  1512. $data['mch_id'] = $this->mch_id;//商户号
  1513. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1514. $data['sign'] = $this->makeSign($data);//签名
  1515. $xml = $this->arr_xml($data);
  1516. $startTimeStamp = $this->milli_time();//请求开始时间
  1517. $response = $this->postXml($xml, $url, false, $timeOut);
  1518. $datas = $this->xml_arr($response);//将xml转为array
  1519. //判断请求结果是否成功
  1520. if($datas['return_code'] != 'SUCCESS'){
  1521. $result = array('status'=>40014,'data'=>$datas);
  1522. return $result;
  1523. }
  1524. //判断签名是否存在,及签名是否正确
  1525. if(!array_key_exists('sign', $datas) ){
  1526. $result = array('status'=>40011,'data'=>'签名错误!');
  1527. return $result;
  1528. }
  1529. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1530. //返回结果
  1531. $result = array('status'=>1,'data'=>$datas);
  1532. return $result;
  1533. }
  1534. /**
  1535. * 下载对账单,WxPayDownloadBill中bill_date为必填参数
  1536. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1537. * @author bug <732680577@qq.com>
  1538. * @createtime 2017-09-11 14:04:10
  1539. * @param $data 参数集
  1540. * @param $timeOut 超时
  1541. * @return 成功时返回,其他抛异常
  1542. */
  1543. public function downloadBill($data, $timeOut = 6){
  1544. $url = "https://api.mch.weixin.qq.com/pay/downloadbill";
  1545. //检测必填参数
  1546. if(!array_key_exists('bill_date', $data)) {
  1547. $result = array('status'=>40010,'data'=>'对账单接口中,缺少必填参数bill_date!');
  1548. return $result;
  1549. }
  1550. //组装参数
  1551. $data['appid'] = $this->appid;//公众账号ID
  1552. $data['mch_id'] = $this->mch_id;//商户号
  1553. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1554. $data['sign'] = $this->makeSign($data);//签名
  1555. $xml = $this->arr_xml($data);
  1556. $startTimeStamp = $this->milli_time();//请求开始时间
  1557. $response = $this->postXml($xml, $url, false, $timeOut);
  1558. $datas = $this->xml_arr($response);//将xml转为array
  1559. //判断请求结果是否成功
  1560. if($datas['return_code'] != 'SUCCESS'){
  1561. $result = array('status'=>40014,'data'=>$datas);
  1562. return $result;
  1563. }
  1564. //判断签名是否存在,及签名是否正确
  1565. if(!array_key_exists('sign', $datas) ){
  1566. $result = array('status'=>40011,'data'=>'签名错误!');
  1567. return $result;
  1568. }
  1569. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1570. //返回结果
  1571. $result = array('status'=>1,'data'=>$datas);
  1572. return $result;
  1573. }
  1574. /**
  1575. * 提交被扫支付API
  1576. * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
  1577. * 由商户收银台或者商户后台调用该接口发起支付。
  1578. * WxPayWxPayMicroPay中body、out_trade_no、total_fee、auth_code参数必填
  1579. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1580. * @author bug <732680577@qq.com>
  1581. * @createtime 2017-09-11 14:06:11
  1582. * @param $data 参数集
  1583. * @param $timeOut 超时
  1584. * @return 成功时返回,其他抛异常
  1585. */
  1586. public function micropay($data, $timeOut = 6){
  1587. $url = "https://api.mch.weixin.qq.com/pay/micropay";
  1588. //检测必填参数
  1589. if(!array_key_exists('body', $data)) {
  1590. $result = array('status'=>40010,'data'=>'提交被扫支付API接口中,缺少必填参数body!');
  1591. return $result;
  1592. }
  1593. if(!array_key_exists('out_trade_no', $data)) {
  1594. $result = array('status'=>40010,'data'=>'提交被扫支付API接口中,缺少必填参数out_trade_no!');
  1595. return $result;
  1596. }
  1597. if(!array_key_exists('total_fee', $data)) {
  1598. $result = array('status'=>40010,'data'=>'提交被扫支付API接口中,缺少必填参数total_fee!');
  1599. return $result;
  1600. }
  1601. if(!array_key_exists('auth_code', $data)) {
  1602. $result = array('status'=>40010,'data'=>'提交被扫支付API接口中,缺少必填参数auth_code!');
  1603. return $result;
  1604. }
  1605. //组装参数
  1606. $data['spbill_create_ip'] = get_client_ip();//终端ip
  1607. $data['appid'] = $this->appid;//公众账号ID
  1608. $data['mch_id'] = $this->mch_id;//商户号
  1609. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1610. $data['sign'] = $this->makeSign($data);//签名
  1611. $xml = $this->arr_xml($data);
  1612. $startTimeStamp = $this->milli_time();//请求开始时间
  1613. $response = $this->postXml($xml, $url, false, $timeOut);
  1614. $datas = $this->xml_arr($response);//将xml转为array
  1615. //判断请求结果是否成功
  1616. if($datas['return_code'] != 'SUCCESS'){
  1617. $result = array('status'=>40014,'data'=>$datas);
  1618. return $result;
  1619. }
  1620. //判断签名是否存在,及签名是否正确
  1621. if(!array_key_exists('sign', $datas) ){
  1622. $result = array('status'=>40011,'data'=>'签名错误!');
  1623. return $result;
  1624. }
  1625. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1626. //返回结果
  1627. $result = array('status'=>1,'data'=>$datas);
  1628. return $result;
  1629. }
  1630. /**
  1631. * 微信扫码支付模式一成功返回参数包装
  1632. * @author bug <732680577@qq.com>
  1633. * @createtime 2017-09-16 16:22:40
  1634. * @param $data 参数集
  1635. * @return [type] [description]
  1636. */
  1637. public function native_pkg($data){
  1638. $data['nonce_str'] =$this->createNonceStr(32);//随机字符串
  1639. $data['sign'] = $this->makeSign($data);//签名
  1640. return $data;
  1641. }
  1642. /**
  1643. * 撤销订单API接口,WxPayReverse中参数out_trade_no和transaction_id必须填写一个
  1644. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1645. * @author bug <732680577@qq.com>
  1646. * @createtime 2017-09-11 14:06:11
  1647. * @param $data 参数集
  1648. * @param $timeOut 超时
  1649. * @return 成功时返回,其他抛异常
  1650. */
  1651. public function reverse($data, $timeOut = 6){
  1652. $url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
  1653. //检测必填参数
  1654. if(!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
  1655. $result = array('status'=>40010,'data'=>'撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个');
  1656. return $result;
  1657. }
  1658. //组装参数
  1659. $data['appid'] = $this->appid;//公众账号ID
  1660. $data['mch_id'] = $this->mch_id;//商户号
  1661. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1662. $data['sign'] = $this->makeSign($data);//签名
  1663. $xml = $this->arr_xml($data);
  1664. $startTimeStamp = $this->milli_time();//请求开始时间
  1665. $response = $this->postXml($xml, $url, false, $timeOut);
  1666. $datas = $this->xml_arr($response);//将xml转为array
  1667. //判断请求结果是否成功
  1668. if($datas['return_code'] != 'SUCCESS'){
  1669. $result = array('status'=>40014,'data'=>$datas);
  1670. return $result;
  1671. }
  1672. //判断签名是否存在,及签名是否正确
  1673. if(!array_key_exists('sign', $datas) ){
  1674. $result = array('status'=>40011,'data'=>'签名错误!');
  1675. return $result;
  1676. }
  1677. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1678. //返回结果
  1679. $result = array('status'=>1,'data'=>$datas);
  1680. return $result;
  1681. }
  1682. /**
  1683. * 测速上报,该方法内部封装在report中,使用时请注意异常流程
  1684. * interface_url、return_code、result_code、user_ip、execute_time_必填
  1685. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1686. * @author bug <732680577@qq.com>
  1687. * @createtime 2017-09-11 11:18:44
  1688. * @param $data 参数集
  1689. * @param $timeOut 超时
  1690. * @return 成功时返回,其他抛异常
  1691. */
  1692. private function report($data, $timeOut = 1)
  1693. {
  1694. $url = "https://api.mch.weixin.qq.com/payitil/report";
  1695. //检测必填参数
  1696. if(!array_key_exists('interface_url', $data)){
  1697. throw new Exception("接口URL,缺少必填参数interface_url!");
  1698. }
  1699. if(!array_key_exists('return_code', $data)){
  1700. throw new Exception("返回状态码,缺少必填参数return_code!");
  1701. }
  1702. if(!array_key_exists('result_code', $data)){
  1703. throw new Exception("业务结果,缺少必填参数result_code!");
  1704. }
  1705. if(!array_key_exists('user_ip', $data)){
  1706. throw new Exception("访问接口IP,缺少必填参数user_ip!");
  1707. }
  1708. if(!array_key_exists('execute_time_', $data)){
  1709. throw new Exception("接口耗时,缺少必填参数execute_time_!");
  1710. }
  1711. //组装参数
  1712. $data['appid'] = $this->appid;//公众账号ID
  1713. $data['mch_id'] = $this->mch_id;//商户号
  1714. $data['user_ip'] = get_client_ip();//终端ip
  1715. $data['time'] = date("YmdHis");//商户上报时间
  1716. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1717. $data['sign'] = $this->makeSign($data);//签名
  1718. $xml = $this->arr_xml($data);
  1719. $startTimeStamp = $this->milli_time();//请求开始时间
  1720. $response = $this->postXml($xml, $url, false, $timeOut);
  1721. return $response;
  1722. }
  1723. /**
  1724. * 生成二维码规则,模式一生成支付二维码
  1725. * appid、mchid、spbill_create_ip、nonce_str不需要填入2017-09-11 14:20:05
  1726. * @author bug <732680577@qq.com>
  1727. * @createtime 2017-09-11 14:20:05
  1728. * @param $data 参数集
  1729. * @param $timeOut 超时
  1730. * @return 成功时返回,其他抛异常
  1731. */
  1732. private function bizpayurl($data, $timeOut = 6){
  1733. //检测必填参数
  1734. if(!array_key_exists('product_id', $data)) {
  1735. $result = array('status'=>40010,'data'=>'生成二维码,缺少必填参数product_id!');
  1736. return $result;
  1737. }
  1738. //组装参数
  1739. $data['appid'] = $this->appid;//公众账号ID
  1740. $data['mch_id'] = $this->mch_id;//商户号
  1741. $data['time_stamp'] = time();//时间戳
  1742. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1743. $data['sign'] = $this->makeSign($data);//签名
  1744. $result = array('status'=>1,'data'=>$data);
  1745. return $result;
  1746. }
  1747. /**
  1748. * 转换短链接
  1749. * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
  1750. * 减小二维码数据量,提升扫描速度和精确度。
  1751. * appid、mchid、spbill_create_ip、nonce_str不需要填入
  1752. * @author bug <732680577@qq.com>
  1753. * @createtime 2017-09-11 14:41:02
  1754. * @param $data 参数集
  1755. * @param $timeOut 超时
  1756. * @return 成功时返回,其他抛异常
  1757. */
  1758. public function shorturl($data, $timeOut = 6){
  1759. $url = "https://api.mch.weixin.qq.com/tools/shorturl";
  1760. //检测必填参数
  1761. if(!array_key_exists('long_url', $data)) {
  1762. $result = array('status'=>40010,'data'=>'需要转换的URL,签名用原串,传输需URL encode!');
  1763. return $result;
  1764. }
  1765. //组装参数
  1766. $data['appid'] = $this->appid;//公众账号ID
  1767. $data['mch_id'] = $this->mch_id;//商户号
  1768. $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
  1769. $data['sign'] = $this->makeSign($data);//签名
  1770. $xml = $this->arr_xml($data);
  1771. $startTimeStamp = $this->milli_time();//请求开始时间
  1772. $response = $this->postXml($xml, $url, false, $timeOut);
  1773. $datas = $this->xml_arr($response);//将xml转为array
  1774. //判断请求结果是否成功
  1775. if($datas['return_code'] != 'SUCCESS'){
  1776. $result = array('status'=>40014,'data'=>$datas);
  1777. return $result;
  1778. }
  1779. //判断签名是否存在,及签名是否正确
  1780. if(!array_key_exists('sign', $datas) ){
  1781. $result = array('status'=>40011,'data'=>'签名错误!');
  1782. return $result;
  1783. }
  1784. $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
  1785. //返回结果
  1786. $result = array('status'=>1,'data'=>$datas);
  1787. return $result;
  1788. }
  1789. /**
  1790. * 支付结果通用通知
  1791. * 直接回调函数使用方法: notify(you_function);
  1792. * 回调类成员函数方法:notify(array($this, you_function));
  1793. * $callback 原型为:function function_name($data){}
  1794. * @author bug <732680577@qq.com>
  1795. * @createtime 2017-09-11 14:41:02
  1796. * @param function $callback
  1797. */
  1798. public function notify(){
  1799. //获取通知的数据
  1800. $xml = file_get_contents('php://input');
  1801. //将xml转为array
  1802. $datas = $this->xml_arr($xml);
  1803. //判断请求结果是否成功
  1804. if($datas['return_code'] != 'SUCCESS'){
  1805. $result = array('status'=>40014,'data'=>$datas['return_msg']);
  1806. return $result;
  1807. }
  1808. //判断业务结果是否成功
  1809. if($datas['result_code'] != 'SUCCESS'){
  1810. $result = array('status'=>40011,'data'=>$datas['err_code_des']);
  1811. return $result;
  1812. }
  1813. //判断签名是否存在
  1814. if(!array_key_exists('sign', $datas)){
  1815. $result = array('status'=>40011,'data'=>'缺少签名参数!');
  1816. return $result;
  1817. }
  1818. //签名验证
  1819. $data = $datas;
  1820. unset($data['sign']);
  1821. if($this->makeSign($data)!=$datas['sign']){
  1822. $result = array('status'=>40011,'data'=>'签名验证失败!');
  1823. return $result;
  1824. }
  1825. //返回结果
  1826. $result = array('status'=>1,'data'=>$datas);
  1827. return $result;
  1828. }
  1829. /**
  1830. * 退款结果通知
  1831. * @author bug <732680577@qq.com>
  1832. * @createtime 2017-09-22 17:10:21
  1833. * @param array
  1834. */
  1835. public function refund_notify(){
  1836. //获取通知的数据
  1837. $xml = file_get_contents('php://input');
  1838. log_result("//////////// start //////////////");
  1839. log_result($xml);
  1840. //将xml转为array
  1841. $datas = $this->xml_arr($xml);
  1842. log_result("datas:".json_encode($datas));
  1843. //判断请求结果是否成功
  1844. if($datas['return_code'] != 'SUCCESS'){
  1845. $result = array('status'=>40014,'data'=>$datas['return_msg']);
  1846. log_result($datas['return_code'] != 'SUCCESS');
  1847. return $result;
  1848. }
  1849. //对加密字符串进行解密
  1850. $req_info = base64_decode($datas['req_info']);//对加密串A做base64解码,得到加密串B
  1851. log_result("req_info:".$req_info);
  1852. $md5_key = md5($this->key);//对商户key做md5,得到32位小写key
  1853. log_result("md5_key:".$md5_key);
  1854. $info = aes_decode($req_info,$md5_key);//用key*对加密串B做AES-256-ECB解密
  1855. log_result("info:".$info);
  1856. $datas = array_merge($datas,$info);//合并数组 返回结果
  1857. log_result("//////////// end //////////////");
  1858. //返回结果
  1859. $result = array('status'=>1,'data'=>$datas);
  1860. return $result;
  1861. }
  1862. /**
  1863. * 共享收货地址js函数需要的参数
  1864. * @author bug <732680577@qq.com>
  1865. * @createtime 2017-09-11 16:37:27
  1866. * @param
  1867. * @return 获取共享收货地址js函数需要的参数,json格式可以直接做参数使用
  1868. */
  1869. public function editAddressParameters(){
  1870. $data = array();
  1871. $data["appid"] = $this->appid;
  1872. $data["url"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  1873. $time = time();
  1874. $data["timestamp"] = "$time";
  1875. $data["noncestr"] = "1234568";
  1876. $data["accesstoken"] = $this->access_token(false);
  1877. ksort($data);
  1878. $params = $this->toUrlParams($data);
  1879. $addrSign = sha1($params);
  1880. $afterData = array(
  1881. "addrSign" => $addrSign,
  1882. "signType" => "sha1",
  1883. "scope" => "jsapi_address",
  1884. "appId" => $this->appid,
  1885. "timeStamp" => $data["timestamp"],
  1886. "nonceStr" => $data["noncestr"]
  1887. );
  1888. $parameters = json_encode($afterData);
  1889. return $parameters;
  1890. }
  1891. /**
  1892. * 生成扫描支付URL,模式一
  1893. * @author bug <732680577@qq.com>
  1894. * @createtime 2017-09-16 10:40:10
  1895. * @param $productId 产品编号
  1896. * @return [type] [description]
  1897. */
  1898. public function getPrePayUrl($productId)
  1899. {
  1900. $data['product_id'] = $productId;
  1901. $result = $this->bizpayurl($data);
  1902. if($result['status']!=1){
  1903. return $result;
  1904. }
  1905. $url = "weixin://wxpay/bizpayurl?" . $this->toUrlParams($result['data'],true);
  1906. $result['data'] = $url;
  1907. return $result;
  1908. }
  1909. /**
  1910. * 生成二维码
  1911. * @author bug <732680577@qq.com>
  1912. * @createtime 2017-09-16 11:15:17
  1913. * @param $data 参数集
  1914. * @return [type] [description]
  1915. */
  1916. public function qrcode($data){
  1917. error_reporting(E_ERROR);
  1918. require_once 'phpqrcode/phpqrcode.php';
  1919. $url = urldecode($data);
  1920. header('Content-Type: image/png');
  1921. ob_clean();
  1922. $errorCorrectionLevel = "L"; // 纠错级别:L、M、Q、H
  1923. $matrixPointSize = "4"; //生成图片大小 :1到10
  1924. \QRcode::png($url, false, $errorCorrectionLevel, $matrixPointSize);
  1925. exit();
  1926. }
  1927. /**
  1928. * 格式化参数格式化成url参数
  1929. * @author bug <732680577@qq.com>
  1930. * @createtime 2017-09-11 09:58:58
  1931. * @param $data 参数集
  1932. * @param $sign 是否考虑sign
  1933. * @return
  1934. */
  1935. private function toUrlParams($data,$sign=false)
  1936. {
  1937. $buff = "";
  1938. foreach ($data as $k => $v)
  1939. {
  1940. if($sign && $v != "" && !is_array($v)){
  1941. $buff .= $k . "=" . $v . "&";
  1942. }else{
  1943. if($k != "sign" && $v != "" && !is_array($v)){
  1944. $buff .= $k . "=" . $v . "&";
  1945. }
  1946. }
  1947. }
  1948. $buff = trim($buff, "&");
  1949. return $buff;
  1950. }
  1951. /**
  1952. * 生成签名
  1953. * @author bug <732680577@qq.com>
  1954. * @createtime 2017-09-11 10:01:14
  1955. * @param
  1956. * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
  1957. */
  1958. public function makeSign($data)
  1959. {
  1960. //签名步骤一:按字典序排序参数
  1961. ksort($data);
  1962. $string = $this->toUrlParams($data);
  1963. //签名步骤二:在string后加入KEY
  1964. $string = $string . "&key=".$this->key;
  1965. //签名步骤三:MD5加密
  1966. $string = md5($string);
  1967. //签名步骤四:所有字符转为大写
  1968. $result = strtoupper($string);
  1969. return $result;
  1970. }
  1971. /**
  1972. * 以post方式提交xml到对应的接口url
  1973. * @author bug <732680577@qq.com>
  1974. * @createtime 2017-09-11 10:08:11
  1975. * @param string $xml 需要post的xml数据
  1976. * @param string $url url
  1977. * @param bool $useCert 是否需要证书,默认不需要
  1978. * @param int $second url执行超时时间,默认30s
  1979. * @return [type] [description]
  1980. */
  1981. private function postXml($xml, $url, $useCert = false, $second = 30)
  1982. {
  1983. header("Content-type: text/html; charset=utf-8");
  1984. $ch = curl_init();
  1985. curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  1986. curl_setopt($ch, CURLOPT_URL, $url);
  1987. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  1988. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  1989. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
  1990. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
  1991. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  1992. curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
  1993. curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  1994. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  1995. //如果有配置代理这里就设置代理
  1996. if(self::CURL_PROXY_HOST != "0.0.0.0" && self::CURL_PROXY_PORT != 0){
  1997. curl_setopt($ch,CURLOPT_PROXY, self::CURL_PROXY_HOST);
  1998. curl_setopt($ch,CURLOPT_PROXYPORT, self::CURL_PROXY_PORT);
  1999. }
  2000. if($useCert == true){
  2001. //设置证书
  2002. //使用证书:cert 与 key 分别属于两个.pem文件
  2003. curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
  2004. curl_setopt($ch,CURLOPT_SSLCERT, dirname(__FILE__).self::SSLCERT_PATH);
  2005. curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
  2006. curl_setopt($ch,CURLOPT_SSLKEY, dirname(__FILE__).self::SSLKEY_PATH);
  2007. }
  2008. $data = curl_exec($ch);
  2009. if (curl_errno($ch)) {
  2010. return curl_error($ch);
  2011. }
  2012. curl_close($ch);
  2013. return $data;
  2014. // $ch = curl_init();
  2015. // //设置超时
  2016. // curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  2017. // //如果有配置代理这里就设置代理
  2018. // if(self::CURL_PROXY_HOST != "0.0.0.0" && self::CURL_PROXY_PORT != 0){
  2019. // curl_setopt($ch,CURLOPT_PROXY, self::CURL_PROXY_HOST);
  2020. // curl_setopt($ch,CURLOPT_PROXYPORT, self::CURL_PROXY_PORT);
  2021. // }
  2022. // curl_setopt($ch,CURLOPT_URL, $url);
  2023. // curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
  2024. // curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
  2025. // //设置header
  2026. // curl_setopt($ch, CURLOPT_HEADER, FALSE);
  2027. // //要求结果为字符串且输出到屏幕上
  2028. // curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  2029. // if($useCert == true){
  2030. // //设置证书
  2031. // //使用证书:cert 与 key 分别属于两个.pem文件
  2032. // curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
  2033. // curl_setopt($ch,CURLOPT_SSLCERT, dirname(__FILE__).self::SSLCERT_PATH);
  2034. // curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
  2035. // curl_setopt($ch,CURLOPT_SSLKEY, dirname(__FILE__).self::SSLKEY_PATH);
  2036. // }
  2037. // //post提交方式
  2038. // curl_setopt($ch, CURLOPT_POST, TRUE);
  2039. // curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  2040. //运行curl
  2041. // $data = curl_exec($ch);
  2042. //返回结果
  2043. // if($data){
  2044. // curl_close($ch);
  2045. // return $data;
  2046. // } else {
  2047. // $error = curl_errno($ch);
  2048. // curl_close($ch);
  2049. // return $error;
  2050. // }
  2051. }
  2052. /**
  2053. * 上报数据, 上报的时候将屏蔽所有异常流程
  2054. * @author bug <732680577@qq.com>
  2055. * @createtime 2017-09-11 11:10:20
  2056. * @param string $usrl
  2057. * @param int $startTimeStamp
  2058. * @param array $data
  2059. * @return [type] [description]
  2060. */
  2061. private function reportCostTime($url, $startTimeStamp, $data)
  2062. {
  2063. //如果不需要上报数据
  2064. if(self::REPORT_LEVENL == 0){
  2065. return;
  2066. }
  2067. //如果仅失败上报
  2068. if(self::REPORT_LEVENL == 1 &&
  2069. array_key_exists("return_code", $data) &&
  2070. $data["return_code"] == "SUCCESS" &&
  2071. array_key_exists("result_code", $data) &&
  2072. $data["result_code"] == "SUCCESS")
  2073. {
  2074. return;
  2075. }
  2076. //上报逻辑
  2077. $endTimeStamp = $this->milli_time();
  2078. $result['interface_url'] = $url;
  2079. $result['execute_time_'] = $endTimeStamp - $startTimeStamp;
  2080. //返回状态码
  2081. if(array_key_exists("return_code", $data)){
  2082. $result['return_code'] = $data["return_code"];
  2083. }
  2084. //返回信息
  2085. if(array_key_exists("return_msg", $data)){
  2086. $result['return_msg'] = $data["return_msg"];
  2087. }
  2088. //业务结果
  2089. if(array_key_exists("result_code", $data)){
  2090. $result['result_code'] = $data["result_code"];
  2091. }
  2092. //错误代码
  2093. if(array_key_exists("err_code", $data)){
  2094. $result['err_code'] = $data["err_code"];
  2095. }
  2096. //错误代码描述
  2097. if(array_key_exists("err_code_des", $data)){
  2098. $result['err_code_des'] = $data["err_code_des"];
  2099. }
  2100. //商户订单号
  2101. if(array_key_exists("out_trade_no", $data)){
  2102. $result['out_trade_no'] = $data["out_trade_no"];
  2103. }
  2104. //设备号
  2105. if(array_key_exists("device_info", $data)){
  2106. $result['device_info'] = $data["device_info"];
  2107. }
  2108. try{
  2109. $this->report($result);
  2110. } catch (Exception $e){
  2111. //不做任何处理
  2112. }
  2113. }
  2114. /**********************微信支付 结束********/
  2115. /**********************微信卡券 开始********/
  2116. /**
  2117. * 微信卡券获取api_ticket
  2118. * @author bug <732680577@qq.com>
  2119. * @createtime 2017-10-17 13:30:13
  2120. * @return api_ticket string 微信卡券调用凭证
  2121. */
  2122. private function api_ticket() {
  2123. // api_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
  2124. $data = json_decode(file_get_contents(dirname(__FILE__)."/json/api_ticket.json"));
  2125. if ($data->expire_time < time()) {
  2126. $access_token = $this->access_token(false);
  2127. if($access_token['status']!=1 && $access_token['status']!=2){
  2128. return $access_token;
  2129. }
  2130. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=".$access_token['data'];
  2131. $res = json_decode(https_get($url));
  2132. if(!isset($res->ticket)){
  2133. $access_token = $this->access_token(true);
  2134. $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=".$access_token['data'];
  2135. $res = json_decode(https_get($url));
  2136. }
  2137. $ticket = $res->ticket;
  2138. if ($ticket) {
  2139. $data->expire_time = time() + 7200;
  2140. $data->ticket = $ticket;
  2141. $fp = fopen(dirname(__FILE__)."/json/api_ticket.json", "w");
  2142. fwrite($fp, json_encode($data));
  2143. fclose($fp);
  2144. }
  2145. } else {
  2146. $ticket = $data->ticket;
  2147. }
  2148. return $ticket;
  2149. }
  2150. /**
  2151. * 卡券签名
  2152. * @author bug <732680577@qq.com>
  2153. * @createtime 2017-10-17 16:43:44
  2154. * @param
  2155. * @return [type] [description]
  2156. */
  2157. public function card_sign($card_id){
  2158. $api_ticket = $this->api_ticket();
  2159. $timestamp = time();
  2160. $nonce_str = $this->createNonceStr();
  2161. $arr = array($api_ticket,$timestamp,$nonce_str,$card_id);
  2162. sort($arr, SORT_STRING);
  2163. $signature = sha1(implode($arr));
  2164. $signPackage = array(
  2165. "timestamp" => $timestamp,
  2166. "nonce_str" => $nonce_str,
  2167. "signature" => $signature,
  2168. );
  2169. return $signPackage;
  2170. }
  2171. /**
  2172. * 查看卡券详情
  2173. * @author bug <732680577@qq.com>
  2174. * @createtime 2017-10-17 13:59:51
  2175. * @param post_data 请求数据 卡券ID
  2176. * @return json 请求结果
  2177. */
  2178. public function get_card($post_data){
  2179. //获取access_token
  2180. $access_token= $this->access_token(false);
  2181. if($access_token['status']!=1 && $access_token['status']!=2){
  2182. return $access_token;
  2183. }
  2184. //配置请求url,加入access_token参数
  2185. $url = 'https://api.weixin.qq.com/card/get?access_token='.$access_token['data'];
  2186. //将json数组转换成字符串数组
  2187. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2188. //获取请求结果,并将请求结果转换成json
  2189. $data = json_decode(https_request($url,$post_data),true);
  2190. //access_token 过期重新获取并重新回调当前函数
  2191. if($data['errcode']==42001 || $data['errcode']==40001){
  2192. $access_token = $this->access_token(true);
  2193. return $this->get_card($post_data);
  2194. }
  2195. //返回结果
  2196. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2197. $result = array('status'=>1,'data'=>$data);
  2198. return $result;
  2199. }
  2200. $result = array('status'=>40014,'data'=>$data);
  2201. return $result;
  2202. }
  2203. /**
  2204. * 批量查询卡券列表
  2205. * @author bug <732680577@qq.com>
  2206. * @createtime 2017-10-17 14:02:10
  2207. * @param post_data 请求数据
  2208. * offset 查询卡列表的起始偏移量,从0开始,即offset: 5是指从从列表里的第六个开始读取。
  2209. * count 需要查询的卡片的数量(数量最大50)。
  2210. * status_list 支持开发者拉出指定状态的卡券列表“CARD_STATUS_NOT_VERIFY”,待审核;“CARD_STATUS_VERIFY_FAIL”,审核失败;“CARD_STATUS_VERIFY_OK”,通过审核;“CARD_STATUS_DELETE”,卡券被商户删除;“CARD_STATUS_DISPATCH”,在公众平台投放过的卡券;
  2211. * @return json 请求结果
  2212. */
  2213. public function card_list($post_data){
  2214. //获取access_token
  2215. $access_token= $this->access_token(false);
  2216. if($access_token['status']!=1 && $access_token['status']!=2){
  2217. return $access_token;
  2218. }
  2219. //配置请求url,加入access_token参数
  2220. $url = 'https://api.weixin.qq.com/card/batchget?access_token='.$access_token['data'];
  2221. //将json数组转换成字符串数组
  2222. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2223. //获取请求结果,并将请求结果转换成json
  2224. $data = json_decode(https_request($url,$post_data),true);
  2225. //access_token 过期重新获取并重新回调当前函数
  2226. if($data['errcode']==42001 || $data['errcode']==40001){
  2227. $access_token = $this->access_token(true);
  2228. return $this->get_card($card_id);
  2229. }
  2230. //返回结果
  2231. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2232. $result = array('status'=>1,'data'=>$data);
  2233. return $result;
  2234. }
  2235. $result = array('status'=>40014,'data'=>$data);
  2236. return $result;
  2237. }
  2238. /**************************微信会员卡 开始****************************/
  2239. /**
  2240. * 拉取会员信息(积分查询)接口
  2241. * @author bug <732680577@qq.com>
  2242. * @createtime 2017-10-27 09:24:15
  2243. * @param card_id 查询会员卡的cardid
  2244. * @param code 所查询用户领取到的code值
  2245. * @return [type] [description]
  2246. */
  2247. public function membercard_info($post_data){
  2248. //获取access_token
  2249. $access_token= $this->access_token(false);
  2250. if($access_token['status']!=1 && $access_token['status']!=2){
  2251. return $access_token;
  2252. }
  2253. //配置请求url,加入access_token参数
  2254. $url = 'https://api.weixin.qq.com/card/membercard/userinfo/get?access_token='.$access_token['data'];
  2255. //将json数组转换成字符串数组
  2256. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2257. //获取请求结果,并将请求结果转换成json
  2258. $data = json_decode(https_request($url,$post_data),true);
  2259. //access_token 过期重新获取并重新回调当前函数
  2260. if($data['errcode']==42001 || $data['errcode']==40001){
  2261. $access_token = $this->access_token(true);
  2262. return $this->membercard_info($post_data);
  2263. }
  2264. //返回结果
  2265. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2266. $result = array('status'=>1,'data'=>$data);
  2267. return $result;
  2268. }
  2269. $result = array('status'=>40014,'data'=>$data);
  2270. return $result;
  2271. }
  2272. /**
  2273. * 更新会员信息
  2274. * @author bug <732680577@qq.com>
  2275. * @createtime 2017-10-27 15:08:46
  2276. * @param
  2277. * @return [type] [description]
  2278. */
  2279. public function membercard_update($post_data){
  2280. //获取access_token
  2281. $access_token= $this->access_token(false);
  2282. if($access_token['status']!=1 && $access_token['status']!=2){
  2283. return $access_token;
  2284. }
  2285. //配置请求url,加入access_token参数
  2286. $url = 'https://api.weixin.qq.com/card/membercard/updateuser?access_token='.$access_token['data'];
  2287. //将json数组转换成字符串数组
  2288. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2289. //获取请求结果,并将请求结果转换成json
  2290. $data = json_decode(https_request($url,$post_data),true);
  2291. //access_token 过期重新获取并重新回调当前函数
  2292. if($data['errcode']==42001 || $data['errcode']==40001){
  2293. $access_token = $this->access_token(true);
  2294. return $this->membercard_update($post_data);
  2295. }
  2296. //返回结果
  2297. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2298. $result = array('status'=>1,'data'=>$data);
  2299. return $result;
  2300. }
  2301. $result = array('status'=>40014,'data'=>$data);
  2302. return $result;
  2303. }
  2304. /**
  2305. * 激活会员卡
  2306. * @author bug <732680577@qq.com>
  2307. * @createtime 2017-10-27 11:45:44
  2308. * @param
  2309. * @return [type] [description]
  2310. */
  2311. public function membercard_active($post_data){
  2312. //获取access_token
  2313. $access_token= $this->access_token(false);
  2314. if($access_token['status']!=1 && $access_token['status']!=2){
  2315. return $access_token;
  2316. }
  2317. //配置请求url,加入access_token参数
  2318. $url = 'https://api.weixin.qq.com/card/membercard/activate?access_token='.$access_token['data'];
  2319. //将json数组转换成字符串数组
  2320. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2321. //获取请求结果,并将请求结果转换成json
  2322. $data = json_decode(https_request($url,$post_data),true);
  2323. //access_token 过期重新获取并重新回调当前函数
  2324. if($data['errcode']==42001 || $data['errcode']==40001){
  2325. $access_token = $this->access_token(true);
  2326. return $this->membercard_active($post_data);
  2327. }
  2328. //返回结果
  2329. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2330. $result = array('status'=>1,'data'=>$data);
  2331. return $result;
  2332. }
  2333. $result = array('status'=>40014,'data'=>$data);
  2334. return $result;
  2335. }
  2336. /**
  2337. * 设置开卡字段接口
  2338. * @author bug <732680577@qq.com>
  2339. * @createtime 2017-10-27 13:54:02
  2340. * @param
  2341. * @return [type] [description]
  2342. */
  2343. public function activateuserform($post_data){
  2344. //获取access_token
  2345. $access_token= $this->access_token(false);
  2346. if($access_token['status']!=1 && $access_token['status']!=2){
  2347. return $access_token;
  2348. }
  2349. //配置请求url,加入access_token参数
  2350. $url = 'https://api.weixin.qq.com/card/membercard/activateuserform/set?access_token='.$access_token['data'];
  2351. //将json数组转换成字符串数组
  2352. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2353. //获取请求结果,并将请求结果转换成json
  2354. $data = json_decode(https_request($url,$post_data),true);
  2355. //access_token 过期重新获取并重新回调当前函数
  2356. if($data['errcode']==42001 || $data['errcode']==40001){
  2357. $access_token = $this->access_token(true);
  2358. return $this->activateuserform($post_data);
  2359. }
  2360. //返回结果
  2361. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2362. $result = array('status'=>1,'data'=>$data);
  2363. return $result;
  2364. }
  2365. $result = array('status'=>40014,'data'=>$data);
  2366. return $result;
  2367. }
  2368. /**
  2369. * 更改会员卡信息接口
  2370. * @author bug <732680577@qq.com>
  2371. * @createtime 2017-10-27 09:36:18
  2372. * @param 提交修改的数据集
  2373. * @return [type] [description]
  2374. */
  2375. public function card_update($post_data){
  2376. //获取access_token
  2377. $access_token= $this->access_token(false);
  2378. if($access_token['status']!=1 && $access_token['status']!=2){
  2379. return $access_token;
  2380. }
  2381. //配置请求url,加入access_token参数
  2382. $url = 'https://api.weixin.qq.com/card/update?access_token='.$access_token['data'];
  2383. //将json数组转换成字符串数组
  2384. $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2385. //获取请求结果,并将请求结果转换成json
  2386. $data = json_decode(https_request($url,$post_data),true);
  2387. //access_token 过期重新获取并重新回调当前函数
  2388. if($data['errcode']==42001 || $data['errcode']==40001){
  2389. $access_token = $this->access_token(true);
  2390. return $this->card_update($post_data);
  2391. }
  2392. //返回结果
  2393. if($data['errcode']==0&&$data['errmsg']=='ok'){
  2394. $result = array('status'=>1,'data'=>$data);
  2395. return $result;
  2396. }
  2397. $result = array('status'=>40014,'data'=>$data);
  2398. return $result;
  2399. }
  2400. /**************************微信会员卡 结束****************************/
  2401. /**********************微信卡券 结束********/
  2402. /**************************微信支付分 开始****************************/
  2403. public function getPrivateKey() {
  2404. return openssl_get_privatekey(file_get_contents(dirname(__FILE__).self::SSLKEY_PATH));
  2405. }
  2406. // WechatPay-API-v3 Authorization
  2407. public function signAuthorization($http_method, $url, $timestamp, $nonce_str, $body=""){
  2408. $url_parts = parse_url($url);
  2409. $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
  2410. $nonce_str = empty($nonce_str) ? $this->createNonceStr(32) : $nonce_str;
  2411. $message = $http_method."\n".
  2412. $canonical_url."\n".
  2413. $timestamp."\n".
  2414. $nonce_str."\n".
  2415. $body."\n";
  2416. // var_dump( strlen($privatekey) );
  2417. $raw_sign = "";
  2418. $privatekey = $this->getPrivateKey();
  2419. openssl_sign($message, $raw_sign, $privatekey, 'sha256WithRSAEncryption');
  2420. $signature = base64_encode($raw_sign);
  2421. $schema = 'WECHATPAY2-SHA256-RSA2048';
  2422. $authorization = $schema.' '.sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $this->mch_id, $nonce_str, $timestamp, $this->cert_no, $signature);
  2423. // var_dump( strlen($authorization) );
  2424. return $authorization;
  2425. }
  2426. /**
  2427. * 查询用户开启状态
  2428. * @author bug <732680577@qq.com>
  2429. * @createtime 2017-10-27 09:36:18
  2430. * @param 提交请求的数据集
  2431. * @return [type] [description]
  2432. */
  2433. public function payscore_state($get_openid){
  2434. //配置请求url
  2435. $url = 'https://api.mch.weixin.qq.com/payscore/user-service-state?service_id='.$this->service_id.'&appid='.$this->appid.'&openid='.$get_openid;
  2436. $url_parts = '/payscore/user-service-state?service_id='.$this->service_id.'&appid='.$this->appid.'&openid='.$get_openid;
  2437. $timestamp = time();
  2438. $nonce_str = '1671c416bc6fcb50720ba2e51da01da6';
  2439. // Authorization: <schema> <token>
  2440. // $url_parts = parse_url($url);
  2441. // $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
  2442. $message = "GET\n".
  2443. $url_parts."\n".
  2444. $timestamp."\n".
  2445. $nonce_str."\n".
  2446. ""."\n";
  2447. openssl_sign($message, $raw_sign, 'xianchangaixiechunbingbrandy5211', 'sha256WithRSAEncryption');
  2448. $sign = base64_encode($raw_sign);
  2449. $schema = 'WECHATPAY2-SHA256-RSA2048';
  2450. $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
  2451. '1552581081', $nonce_str, $timestamp, 'xianchangaixiechunbingbrandy5211', $sign);
  2452. // $data = json_decode(https_request($url,json_decode(array())),true);
  2453. // $data = json_decode(array('state'=>true));
  2454. // $this->service_id
  2455. // //将json数组转换成字符串数组
  2456. // $post_data = json_encode($post_data,JSON_UNESCAPED_UNICODE);
  2457. // //获取请求结果,并将请求结果转换成json
  2458. // $data = json_decode(https_request($url,$post_data),true);
  2459. // //access_token 过期重新获取并重新回调当前函数
  2460. // if($data['errcode']==42001 || $data['errcode']==40001){
  2461. // $access_token = $this->access_token(true);
  2462. // return $this->card_update($post_data);
  2463. // }
  2464. // //返回结果
  2465. // if($data['errcode']==0&&$data['errmsg']=='ok'){
  2466. // $result = array('status'=>1,'data'=>$data);
  2467. // return $result;
  2468. // }
  2469. $result = array('status'=>40014,'data'=>https_request($url,json_decode(array())));
  2470. return $result;
  2471. }
  2472. /**************************公共方法 开始****************************/
  2473. /**************************先享后付 结束****************************/
  2474. /**************************微信支付分 结束****************************/
  2475. /**************************先享后付 开始****************************/
  2476. /**
  2477. * 输出xml字符(数组转换成xml)
  2478. * @param $params 参数名称
  2479. * return string 返回组装的xml
  2480. **/
  2481. public function arr_xml( $params ){
  2482. if(!is_array($params)|| count($params) <= 0)
  2483. {
  2484. return false;
  2485. }
  2486. $xml = "<xml>";
  2487. foreach ($params as $key=>$val)
  2488. {
  2489. if (is_numeric($val)){
  2490. $xml .= "<".$key.">".$val."</".$key.">";
  2491. }else{
  2492. $xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
  2493. }
  2494. }
  2495. $xml .= "</xml>";
  2496. return $xml;
  2497. }
  2498. /**
  2499. * 输出Array数组(xml转换成array)
  2500. * @param $xml 参数名称
  2501. * return array 返回转换的array
  2502. **/
  2503. function xml_arr($xml) {
  2504. $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  2505. return $arr;
  2506. }
  2507. /**
  2508. * return int 返回当前时间戳
  2509. **/
  2510. public function milli_time(){
  2511. return time();
  2512. }
  2513. /**************************公共方法 结束****************************/
  2514. }
  2515. ?>