wxsrv.php 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. <?php
  2. /**
  3. * error code 说明.
  4. * <ul>
  5. * <li>-41001: encodingAesKey 非法</li>
  6. * <li>-41003: aes 解密失败</li>
  7. * <li>-41004: 解密后得到的buffer非法</li>
  8. * <li>-41005: base64加密失败</li>
  9. * <li>-41016: base64解密失败</li>
  10. * </ul>
  11. */
  12. class Weixin_ErrorCode
  13. {
  14. public static $OK = 0;
  15. public static $IllegalAesKey = -41001;
  16. public static $IllegalIv = -41002;
  17. public static $IllegalBuffer = -41003;
  18. public static $DecodeBase64Error = -41004;
  19. }
  20. /**
  21. * 检验数据的真实性,并且获取解密后的明文.
  22. * @param $encryptedData string 加密的用户数据
  23. * @param $iv string 与用户数据一同返回的初始向量
  24. * @param $data string 解密后的原文
  25. * @return int 成功0,失败返回对应的错误码
  26. */
  27. function weixin_decryptData($appid, $sessionKey, $encryptedData, $iv){
  28. eeglobal_log_handler('weixin_decryptData','info',"start appid=$appid sessionKey=$sessionKey encryptedData=$encryptedData iv=$iv ");
  29. if (empty($sessionKey) || strlen($sessionKey) != 24) {
  30. throw new GeneralException('无效的会话Key IllegalAesKey','无效的会话Key IllegalAesKey');
  31. }
  32. $aesKey = base64_decode($sessionKey);
  33. if (strlen($iv) != 24) {
  34. throw new GeneralException('无效的iv IllegalIv','无效的iv IllegalIv');
  35. }
  36. $aesIV = base64_decode($iv);
  37. if (empty($encryptedData)) {
  38. throw new GeneralException('无效的encryptedData','无效的encryptedData');
  39. }
  40. $aesCipher = base64_decode($encryptedData);
  41. $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
  42. eeglobal_log_handler('weixin_decryptData','info',"finish $result");
  43. // $result=mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$aesKey,$aesCipher,MCRYPT_MODE_CBC,$aesIV);
  44. // $loc = strpos($result,"}}");
  45. // $result=substr($result, 0, $loc+2);
  46. $dataObj = @json_decode($result,true);
  47. if (empty($dataObj)) {
  48. throw new GeneralException('解密失败 IllegalBuffer','解密失败 IllegalBuffer');
  49. }
  50. if ($dataObj["watermark"]["appid"] != $appid) {
  51. throw new GeneralException('解密失败 appid不一致','解密失败 appid不一致');
  52. }
  53. return $dataObj;
  54. }
  55. function weixin_sign_xml($paySignKey,$package){
  56. ksort($package, SORT_STRING);
  57. $string1 = '';
  58. foreach($package as $key => $v) {
  59. if(empty($v)){
  60. throw new Exception("微信提交的数据项的值不能为空!!:".@json_encode($package));
  61. }
  62. $string1 .= "{$key}={$v}&";
  63. }
  64. if(empty($paySignKey))
  65. throw new Exception("与微信通信过程中的数据项的签名值不能为空!!");
  66. $string1 .= "key=".$paySignKey;
  67. $package['sign'] = strtoupper(md5($string1));
  68. $xml = "<xml>";
  69. foreach ($package as $key=>$val){
  70. if (is_numeric($val)){
  71. $xml.="<".$key.">".$val."</".$key.">";
  72. }else
  73. $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
  74. }
  75. $xml.="</xml>";
  76. return $xml;
  77. }
  78. function weixin_getOAuth2AccessToken($appid,$appSecret,$code){
  79. session_start();
  80. $reqres=$_SESSION["OAuth2AccessToken_{$appid}_{$code}"];
  81. if(empty($reqres)){
  82. $url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$appSecret&code=$code&grant_type=authorization_code";
  83. $reqres = http_get($url);
  84. }
  85. $jsonres = @json_decode($reqres, true);
  86. if(empty($jsonres['openid'])){
  87. eeglobal_log_handler('weixin_handle','debug'," 获取oauth2 AccessToken时发生异常 url=$url postdata=$reqres");
  88. throw new GeneralException('',"获取oauth2 AccessToken时发生异常,没有返回openid");
  89. }
  90. $_SESSION["OAuth2AccessToken_{$appid}_{$code}"]=$reqres;
  91. return $jsonres;
  92. }
  93. function weixin_getOAuth2Userinfo($access_token,$appid){
  94. $url="https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$appid&lang=zh_CN";
  95. $reqres = http_get($url);
  96. $jsonres = @json_decode($reqres, true);
  97. if(empty($jsonres['openid'])){
  98. eeglobal_log_handler('weixin_handle','debug'," 获取oauth2 Userinfo时发生异常 url=$url postdata=$reqres");
  99. throw new GeneralException('',"获取oauth2 Userinfo时发生异常,没有返回openid");
  100. }
  101. return $jsonres;
  102. }
  103. function weixin_oauthCallBack($logCatalog,$bizPreHandle,$userInfoHandle){
  104. try{
  105. $GPC=input_param_handle(false);//提取参数
  106. $ajaxdata = input_getPostObj();
  107. $cb302=isset($ajaxdata["cb302"])?$ajaxdata["cb302"]:$GPC["cb302"];
  108. if(empty($cb302)) throw new GeneralException("","没有传递必要的参数cb302");
  109. $appid=isset($ajaxdata["appid"])?$ajaxdata["appid"]:$GPC["appid"];
  110. if(empty($appid)) throw new GeneralException("","没有传递必要的参数appid");
  111. $code=isset($ajaxdata["code"])?$ajaxdata["code"]:$GPC["code"];
  112. if(empty($code)) throw new GeneralException("","没有传递必要的参数code");
  113. $state=isset($ajaxdata["state"])?$ajaxdata["state"]:$GPC["state"];
  114. $bizData=$bizPreHandle($appid,$state);
  115. $needUnionid=$bizData["needUnionid"];
  116. $needUserInfo=$bizData["needUserInfo"];
  117. $appSecret=$bizData["appSecret"];
  118. $jsonres=weixin_getOAuth2AccessToken($appid,$appSecret,$code);
  119. $openid=$jsonres["openid"];
  120. $cb302.=(mb_strpos($cb302,"?")===false?"?":"&")."openid=".urlencode($openid);
  121. $unionid=$jsonres["unionid"];
  122. if((empty($unionid) && !empty($needUnionid) && $needUnionid==true)
  123. ||(!empty($needUserInfo) && $needUserInfo==true)){
  124. $jsonres=weixin_getOAuth2Userinfo($jsonres["access_token"],$appid);
  125. $userInfoHandle($appid,$state,$jsonres);
  126. $unionid=$jsonres["unionid"];
  127. if(empty($unionid) && !empty($needUnionid) && $needUnionid==true)
  128. throw new GeneralException('',"获取oauth2 Userinfo时发生异常,没有返回unionid");
  129. }
  130. $cb302.=(mb_strpos($cb302,"?")===false?"?":"&")."unionid=".urlencode($unionid);
  131. ob_clean();
  132. header("Location:$cb302");
  133. exit;
  134. }catch(Throwable $e){
  135. $friendMsg=get_class($e)=='GeneralException'?$e->friendmsg:"非常抱歉,获取微信登录信息时发生错误";
  136. if(empty($cb302)) throw new GeneralException("获取微信登录信息时发生错误",$friendMsg.$e->getMessage(),$logCatalog,0,$e);
  137. ob_clean();
  138. header("Location:$cb302".(mb_strpos($cb302,"?")===false?"?":"&")."errmsg=".urlencode($friendMsg));
  139. exit;
  140. }
  141. }
  142. function weixin_getAccessToken($appid,$appSecret){
  143. $WXbyAPPID = basecfg_getConfig("WXbyAPPID",$appid);
  144. if(!empty($WXbyAPPID['access_token']) && !empty($WXbyAPPID['expires_in'])
  145. && !empty($WXbyAPPID['fetchtime']) && (time()-intval($WXbyAPPID['fetchtime']))<3600 ){
  146. eeglobal_log_handler('weixin_handle','debug'," 从缓存提取token appid=$appid ");
  147. return $WXbyAPPID['access_token'];
  148. }else{
  149. $url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$appSecret";
  150. $reqres = http_get($url);
  151. $jsonres = @json_decode($reqres, true);
  152. if(empty($jsonres['access_token'])){
  153. eeglobal_log_handler('weixin_handle','debug'," 获取base AccessToken时发生异常 url=$url postdata=$reqres");
  154. throw new GeneralException('',"获取base AccessToken时发生异常");
  155. }
  156. $jsonres['fetchtime'] = time();
  157. basecfg_setConfig("WXbyAPPID", $appid, $jsonres);
  158. eeglobal_log_handler('weixin_handle','debug'," 刷新获取新token appid=$appid ");
  159. return $jsonres['access_token'];
  160. }
  161. }
  162. function weixin_getMinaQCode($appid,$appSecret,$savePath,$bizCatalog,$bizId,$userId,$minaPagePath="",
  163. $width=280,$auto_color=false,$line_color=null,$is_hyaline=false){
  164. $access_token=weixin_getAccessToken($appid,$appSecret);
  165. $url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=$access_token";
  166. $len=mb_strlen($bizCatalog)+mb_strlen($bizId)+mb_strlen($userId);
  167. if (($len + 2) > 32) throw new GeneralException('', "生成小程序码时,scene信息超出32位");
  168. $width=intval($width);
  169. if($width<280) $width=280;
  170. if($width>1280) $width=1280;
  171. $postdata=array(
  172. 'scene'=>"$bizCatalog:$bizId:$userId",
  173. 'page'=>$minaPagePath,
  174. 'width'=>$width,
  175. 'auto_color'=>$auto_color,
  176. 'line_color'=>empty($line_color)?array("r"=>0,"g"=>0,"b"=>0):$line_color,
  177. 'is_hyaline'=>$is_hyaline,
  178. );
  179. $postdata = json_encode($postdata);
  180. eeglobal_log_handler('weixin_handle','debug'," 生成小程序码 appid=$appid postdata=$postdata");
  181. $httpHeader = array();
  182. $data = http_post($url, $httpHeader, $postdata);
  183. if(strpos($data,'errcode')!=false||strpos($data,'404')!=false){
  184. eeglobal_log_handler('weixin_handle','error','获取生成小程序码失败,错误信息:'.$data."--".$postdata);
  185. throw new GeneralException('', "获取生成小程序码失败 $data");
  186. }
  187. $savePathDIR=dirname($savePath);
  188. if(!is_dir($savePathDIR)){
  189. if(!mkdir($savePathDIR,0777,true)){
  190. throw new Exception("创建相关目录时发生错误!");
  191. }
  192. chmod($savePathDIR,0777);
  193. }
  194. if (file_put_contents($savePath, $data) == false) {
  195. eeglobal_log_handler('weixin_handle','error','保存小程序码失败 savePath=>'.$savePath);
  196. throw new GeneralException('', "保存小程序码失败");
  197. }
  198. return $data;
  199. }
  200. function weixin_buildPayInstV2($mchid,$apiKeyV2){
  201. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  202. //获取微信api证书
  203. $api_cert_pem_path = WEB_PHY_ROOT."/ssl/wxpaycert_".$mchid.".abc";
  204. $api_cert_key_path = WEB_PHY_ROOT."/ssl/wxpaycert_".$mchid.".xyz";
  205. if(!file_exists($api_cert_pem_path)){
  206. throw new GeneralException('微信打款必须在微信支付中配置API证书,请先配置API证书:)','微信打款必须在微信支付中配置API证书,请先配置API证书:)');
  207. }
  208. if(!file_exists($api_cert_key_path)){
  209. throw new GeneralException('微信打款必须在微信支付中配置API证书,请先配置API证书:)','微信打款必须在微信支付中配置API证书,请先配置API证书:)');
  210. }
  211. $instance = WeChatPay\Builder::factory([
  212. 'mchid' => $mchid,
  213. 'serial' => 'nop',
  214. 'privateKey' => 'any',
  215. 'certs' => ['any' => null],
  216. 'secret' => $apiKeyV2,//apiv2密钥
  217. 'merchant' => [
  218. 'cert' => $api_cert_pem_path,
  219. 'key' => $api_cert_key_path,
  220. ],
  221. ]);
  222. return $instance;
  223. }
  224. function weixin_buildPayInstV3($mchid,$apiKeyV3){
  225. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  226. $merchantCertificateFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.abc";
  227. $merchantCertificateSerial = WeChatPay\Util\PemUtil::parseCertificateSerialNo($merchantCertificateFilePath);
  228. $merchantPrivateKeyFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.xyz";
  229. $merchantPrivateKeyInstance = WeChatPay\Crypto\Rsa::from($merchantPrivateKeyFilePath, WeChatPay\Crypto\Rsa::KEY_TYPE_PRIVATE);
  230. $platformCertificateFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.wxp";
  231. $platformPublicKeyInstance = WeChatPay\Crypto\Rsa::from($platformCertificateFilePath, WeChatPay\Crypto\Rsa::KEY_TYPE_PUBLIC);
  232. $platformCertificateSerial = WeChatPay\Util\PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
  233. $instance = WeChatPay\Builder::factory([
  234. 'mchid' => $mchid,
  235. 'serial' => $merchantCertificateSerial,
  236. 'privateKey' => $merchantPrivateKeyInstance,
  237. 'certs' => [
  238. $platformCertificateSerial => $platformPublicKeyInstance,
  239. ],
  240. ]);
  241. return $instance;
  242. }
  243. function weixin_transfers($appid,$openid,$trade_no,$realname,$total_fee,$desc=''){
  244. $mchId=WeiXinPay_mchId;
  245. if(empty($mchId)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  246. $paySignKey=WeiXinPay_apiKey;
  247. if(empty($paySignKey)) throw new GeneralException('还没有配置微信商户apikey','还没有配置微信商户apikey');
  248. $package = array();
  249. $package['mch_appid'] = $appid;
  250. $package['mchid'] = $mchId;
  251. $package['nonce_str'] = random(8);
  252. $package['partner_trade_no'] = $trade_no;
  253. $package['openid'] = $openid;
  254. $package['check_name'] = empty($realname)?"NO_CHECK":'FORCE_CHECK';
  255. $package['re_user_name'] = empty($realname)?"":trim($realname);
  256. $package['amount'] = intval($total_fee);
  257. $package['desc'] = $desc;
  258. $package['spbill_create_ip'] = '127.0.0.1';
  259. ksort($package, SORT_STRING);
  260. $string1 = '';
  261. foreach($package as $key => $v) {
  262. $string1 .= "{$key}={$v}&";
  263. }
  264. $string1 .= "key=".$paySignKey;
  265. $package['sign'] = strtoupper(md5($string1));
  266. $xml = "<xml>";
  267. foreach ($package as $key=>$val){
  268. if (is_numeric($val)){
  269. $xml.="<".$key.">".$val."</".$key.">";
  270. }else
  271. $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
  272. }
  273. $xml.="</xml>";
  274. $api_cert_pem_path = WEB_PHY_ROOT."/ssl/wxpaycert_".$mchId.".abc";
  275. $api_cert_key_path = WEB_PHY_ROOT."/ssl/wxpaycert_".$mchId.".xyz";
  276. if(!file_exists($api_cert_pem_path)){
  277. throw new GeneralException('微信打款必须在微信支付中配置API证书,请先配置API证书:)','微信打款必须在微信支付中配置API证书,请先配置API证书:)');
  278. }
  279. if(!file_exists($api_cert_key_path)){
  280. throw new GeneralException('微信打款必须在微信支付中配置API证书,请先配置API证书:)','微信打款必须在微信支付中配置API证书,请先配置API证书:)');
  281. }
  282. $httpHeader = array();
  283. $result =http_post("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers",$httpHeader,$xml,true,$api_cert_pem_path,$api_cert_key_path); // 向微信发起企业付款请求
  284. eeglobal_log_handler('weixin_transfers','info','微信提现 xml=>'.$xml.' result=>'.$result);
  285. try{
  286. $data= new SimpleXMLElement($result,LIBXML_NOCDATA);
  287. $data = @json_decode(json_encode($data),true);
  288. }catch(Throwable $ex){
  289. $data=null;
  290. }
  291. if($data && !empty($data["payment_no"])){
  292. return $data;
  293. }else{
  294. eeglobal_log_handler('weixin_transfers','error','微信企业付款发生错误:微信返回失败信息 xml=>'.$xml.' result=>'.$result);
  295. $message="";
  296. if($data && !empty($data["return_code"]))
  297. $message.="【".$data["return_code"]."】";
  298. if($data && !empty($data["return_msg"]))
  299. $message.=" ".$data["return_msg"]."";
  300. if($data && !empty($data["err_code"]))
  301. $message.="【".$data["err_code"]."】";
  302. if($data && !empty($data["err_code_des"]))
  303. $message.=" ".$data["err_code_des"]."";
  304. $message=empty($message)?"向微信发起企业付款请求发生错误":"微信企业付款失败:".$message;
  305. throw new GeneralException($message,$message);
  306. }
  307. }
  308. function weixin_transfersV2($appid,$openid,$trade_no,$realname,$total_fee,$desc=''){
  309. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  310. $mchid=WeiXinPay_mchId;
  311. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  312. $paySignKey=WeiXinPay_apiKey;
  313. if(empty($paySignKey)) throw new GeneralException('还没有配置微信商户apikey','还没有配置微信商户apikey');
  314. $instance = weixin_buildPayInstV2($mchid,$paySignKey);
  315. $reqData=[
  316. 'xml' => [
  317. 'mch_appid' => $appid,
  318. 'mchid' => $mchid,
  319. 'partner_trade_no' => $trade_no,
  320. 'openid' => $openid,
  321. 'check_name' => 'FORCE_CHECK',
  322. 're_user_name' => $realname,
  323. 'amount' => $total_fee,
  324. 'desc' => $desc,
  325. 'spbill_create_ip' => '192.168.0.1',
  326. ],
  327. 'security' => true,
  328. 'debug' => false
  329. ];
  330. $strResult="";
  331. try{
  332. $strResult = (string)$instance->v2->mmpaymkttransfers->promotion->transfers->post($reqData)->getBody();
  333. }catch(Throwable $e){
  334. $strResult.="【".$e->getMessage()."】";
  335. $strResult.=" trace=>".$e->getTraceAsString();
  336. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  337. $r = $e->getResponse();
  338. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  339. $strResult.=" body=>".$r->getBody();
  340. }
  341. if ($e instanceof \GuzzleHttp\Promise\RejectionException
  342. && $e->getReason() instanceof \GuzzleHttp\Psr7\Response) {
  343. $r = $e->getReason();
  344. $rejectReason=$r->getBody();
  345. if(!empty($rejectReason)){//直接设置为结果信息
  346. $strResult=(string)$r->getBody();
  347. }else{
  348. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  349. $strResult.=" body=>".$r->getBody();
  350. }
  351. }
  352. }
  353. try{
  354. $data= new SimpleXMLElement($strResult,LIBXML_NOCDATA);
  355. $data = @json_decode(json_encode($data),true);
  356. }catch(Throwable $ex){
  357. $data=null;
  358. }
  359. if($data && !empty($data["payment_no"])){
  360. return $data;
  361. }else{
  362. eeglobal_log_handler('weixin_transfers','error','微信企业付款发生错误:微信返回失败信息 reqData=>'.@json_encode($reqData).' result=>'.$strResult);
  363. $message="";
  364. if($data && !empty($data["return_code"]))
  365. $message.="【".$data["return_code"]."】";
  366. if($data && !empty($data["return_msg"]))
  367. $message.=" ".$data["return_msg"]."";
  368. if($data && !empty($data["err_code"]))
  369. $message.="【".$data["err_code"]."】";
  370. if($data && !empty($data["err_code_des"]))
  371. $message.=" ".$data["err_code_des"]."";
  372. $message=empty($message)?"向微信发起企业付款请求发生错误":"微信企业付款失败:".$message;
  373. throw new GeneralException($message,$message);
  374. }
  375. }
  376. function weixin_prepayV2($payScene,$appid,$out_trade_no,$total,$description,$attach,$openid="",$product_id=""){
  377. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  378. $mchid=WeiXinPay_mchId;
  379. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  380. $paySignKey=WeiXinPay_apiKey;
  381. if(empty($paySignKey)) throw new GeneralException('还没有配置微信商户apikey','还没有配置微信商户apikey');
  382. $instance = weixin_buildPayInstV2($mchid,$paySignKey);
  383. $notify_url=WeiXinPay_notifyurlV2;
  384. $reqData=[
  385. 'xml' => [
  386. 'appid' => $appid,
  387. 'mch_id' => $mchid,
  388. 'nonce_str' => random(8),
  389. 'body' => $description,
  390. 'out_trade_no' => $out_trade_no,
  391. 'total_fee' => $total,
  392. 'notify_url' => $notify_url,
  393. 'attach' => $attach,
  394. 'trade_type' => $payScene,
  395. 'openid' => $openid,
  396. 'product_id' => $product_id,
  397. 'time_start' => date("YmdHis",time()),
  398. 'time_expire' => date("YmdHis",time()+15*60),
  399. 'spbill_create_ip' => $_SERVER['HTTP_X_REAL_IP']?$_SERVER['HTTP_X_REAL_IP']:$_SERVER['REMOTE_ADDR'],
  400. ],
  401. 'security' => false,
  402. 'debug' => false
  403. ];
  404. $strResult="";
  405. try{
  406. $strResult = (string)$instance->v2->pay->unifiedorder->post($reqData)->getBody();
  407. }catch(Throwable $e){
  408. $strResult.="【".$e->getMessage()."】";
  409. $strResult.=" trace=>".$e->getTraceAsString();
  410. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  411. $r = $e->getResponse();
  412. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  413. $strResult.=" body=>".$r->getBody();
  414. }
  415. if ($e instanceof \GuzzleHttp\Promise\RejectionException
  416. && $e->getReason() instanceof \GuzzleHttp\Psr7\Response) {
  417. $r = $e->getReason();
  418. $rejectReason=$r->getBody();
  419. if(!empty($rejectReason)){//直接设置为结果信息
  420. $strResult=(string)$r->getBody();
  421. }else{
  422. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  423. $strResult.=" body=>".$r->getBody();
  424. }
  425. }
  426. }
  427. try{
  428. $data= new SimpleXMLElement($strResult,LIBXML_NOCDATA);
  429. $data = @json_decode(json_encode($data),true);
  430. }catch(Throwable $ex){
  431. $data=null;
  432. }
  433. if($data && !empty($data["prepay_id"])){
  434. return $data;
  435. }else{
  436. eeglobal_log_handler('weixin_prepay','error','生成预支付订单异常:微信返回失败信息 reqData=>'.@json_encode($reqData).' result=>'.$strResult);
  437. $message="";
  438. if($data && !empty($data["return_code"]))
  439. $message.="【".$data["return_code"]."】";
  440. if($data && !empty($data["return_msg"]))
  441. $message.=" ".$data["return_msg"]."";
  442. if($data && !empty($data["err_code"]))
  443. $message.="【".$data["err_code"]."】";
  444. if($data && !empty($data["err_code_des"]))
  445. $message.=" ".$data["err_code_des"]."";
  446. $message=empty($message)?"生成预支付订单异常":"生成预支付订单异常:".$message;
  447. throw new GeneralException($message,$message);
  448. }
  449. }
  450. function weixin_prepayBuildV2($logCatalog,$bizHandle){
  451. //提取参数
  452. $GPC=input_param_handle(false);
  453. $ajaxdata = input_getPostObj();
  454. $bizCatalog=isset($ajaxdata["bizCatalog"])?$ajaxdata["bizCatalog"]:$GPC["bizCatalog"];
  455. if(empty($bizCatalog)) throw new GeneralException("","没有传递必要的参数bizCatalog");
  456. $bizId=isset($ajaxdata["bizId"])?$ajaxdata["bizId"]:$GPC["bizId"];
  457. if(empty($bizId)) throw new GeneralException("","没有传递必要的参数bizId");
  458. $payScene=isset($ajaxdata["payScene"])?$ajaxdata["payScene"]:$GPC["payScene"];
  459. if(empty($payScene)) throw new GeneralException("","没有传递必要的参数payScene");
  460. $bizCatalog=intval($bizCatalog);
  461. $bizId=intval($bizId);
  462. $bizAttach="$bizCatalog:$bizId";
  463. $payInfo=$bizHandle($bizCatalog,$bizId,$ajaxdata,$GPC);
  464. $prepayData = weixin_prepayV2($payScene,$payInfo["appid"],$payInfo["out_trade_no"],$payInfo["total"],$payInfo["description"],$bizAttach,$payInfo["openid"],$payInfo["product_id"]);
  465. $prepayid = $prepayData["prepay_id"];
  466. $ajaxData=array();
  467. switch($payScene){
  468. case "JSAPI":
  469. $ajaxData['appId'] = $payInfo["appid"];
  470. $ajaxData['timeStamp'] = time();
  471. $ajaxData['nonceStr'] = random(8);
  472. $ajaxData['package'] = 'prepay_id='.$prepayid;
  473. $ajaxData['signType'] = 'MD5';
  474. ksort($ajaxData, SORT_STRING);
  475. $string="";
  476. foreach($ajaxData as $key => $v) {
  477. $string .= "{$key}={$v}&";
  478. }
  479. $string .= "key=".$payInfo["mch_paykeyV2"];
  480. $ajaxData['paySign'] = strtoupper(md5($string));
  481. break;
  482. case "APP":
  483. $ajaxData['appId'] = $payInfo["appid"];
  484. $ajaxData['partnerId'] = $payInfo["mchId"];
  485. $ajaxData['prepayId'] = $prepayid;
  486. $ajaxData['packageValue'] = 'Sign=WXPay';
  487. $ajaxData['nonceStr'] = random(8);
  488. $ajaxData['timeStamp'] = time();
  489. ksort($ajaxData, SORT_STRING);
  490. $string="";
  491. foreach($ajaxData as $key => $v) {
  492. $string .= "{$key}={$v}&";
  493. }
  494. $string .= "key=".$payInfo["mch_paykeyV2"];
  495. $ajaxData['sign'] = strtoupper(md5($string));
  496. break;
  497. case "MWEB":
  498. $mweb_url = $prepayData["mweb_url"];
  499. if(empty($mweb_url)){
  500. throw new GeneralException("","H5预支付订单没有生成出mweb_url,无法继续");
  501. }
  502. $ajaxData['mweb_url']=$mweb_url;
  503. $ajaxData['out_trade_no']=$payInfo["out_trade_no"];
  504. break;
  505. case "NATIVE":
  506. $code_url = $prepayData["code_url"];
  507. if(empty($code_url)){
  508. throw new GeneralException("","NATIVE预支付订单没有生成出code_url,无法继续");
  509. }
  510. require_once WEB_PHY_ROOT."/base/lib_qr.php";
  511. $ajaxData['base64QR']=lib_qrbuild("png",$code_url);
  512. break;
  513. default:
  514. throw new GeneralException("","传递的支付场景payScene参数值不正确!");
  515. }
  516. return $ajaxData;
  517. }
  518. function weixin_prepayV3($payScene,$appid,$out_trade_no,$total,$description,$attach,$openid="",$product_id=""){
  519. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  520. $mchid=WeiXinPay_mchId;
  521. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  522. $apiKeyV3=WeiXinPay_apiKeyV3;
  523. if(empty($apiKeyV3)) throw new GeneralException('还没有配置微信商户apiKeyV3','还没有配置微信商户apiKeyV3');
  524. $instance = weixin_buildPayInstV3($mchid,$apiKeyV3);
  525. $notify_url=WeiXinPay_notifyurlV3;
  526. $reqData=['json' => [
  527. 'mchid' => $mchid,
  528. 'out_trade_no' => $out_trade_no,
  529. 'appid' => $appid,
  530. 'description' => $description,
  531. 'notify_url' => $notify_url,
  532. "attach" => $attach,
  533. 'time_expire' => (date("Y-m-d\TH:i:s+08:00",time()+15*60)),//15分钟超时
  534. 'amount' => [
  535. 'total' => intval($total),
  536. 'currency' => 'CNY'
  537. ],
  538. ]];
  539. $strResult="";
  540. try{
  541. switch($payScene){
  542. case "JSAPI":
  543. $reqData['json']["payer"]= [
  544. 'openid' => $openid
  545. ];
  546. $strResult = (string)$instance->v3->pay->transactions->jsapi
  547. ->postAsync($reqData)
  548. ->then(static function($response) {
  549. eeglobal_log_handler('weixin_prepayV3','info',"then正常返回JSAPI");
  550. return (string)$response->getBody();
  551. })
  552. ->otherwise(static function($e) {
  553. eeglobal_log_handler('weixin_prepayV3','info',"otherwise异常返回JSAPI");
  554. $owReturn="";
  555. $owReturn.="【".$e->getMessage()."】";
  556. $owReturn.=" trace=>".$e->getTraceAsString();
  557. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  558. $r = $e->getResponse();//直接设置为结果信息,即跳过验签异常
  559. $owReturn=(string)$r->getBody();
  560. }
  561. return $owReturn;
  562. })
  563. ->wait();
  564. break;
  565. case "NATIVE":
  566. $strResult = (string)$instance->v3->pay->transactions->native
  567. ->postAsync($reqData)
  568. ->then(static function($response) {
  569. eeglobal_log_handler('weixin_prepayV3','info',"then正常返回NATIVE");
  570. return (string)$response->getBody();
  571. })
  572. ->otherwise(static function($e) {
  573. eeglobal_log_handler('weixin_prepayV3','info',"otherwise异常返回NATIVE");
  574. $owReturn="";
  575. $owReturn.="【".$e->getMessage()."】";
  576. $owReturn.=" trace=>".$e->getTraceAsString();
  577. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  578. $r = $e->getResponse();//直接设置为结果信息,即跳过验签异常
  579. $owReturn=(string)$r->getBody();
  580. }
  581. return $owReturn;
  582. })
  583. ->wait();
  584. break;
  585. case "APP":
  586. $strResult = (string)$instance->v3->pay->transactions->app
  587. ->postAsync($reqData)
  588. ->then(static function($response) {
  589. eeglobal_log_handler('weixin_prepayV3','info',"then正常返回APP");
  590. return (string)$response->getBody();
  591. })
  592. ->otherwise(static function($e) {
  593. eeglobal_log_handler('weixin_prepayV3','info',"otherwise异常返回APP");
  594. $owReturn="";
  595. $owReturn.="【".$e->getMessage()."】";
  596. $owReturn.=" trace=>".$e->getTraceAsString();
  597. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  598. $r = $e->getResponse();//直接设置为结果信息,即跳过验签异常
  599. $owReturn=(string)$r->getBody();
  600. }
  601. return $owReturn;
  602. })
  603. ->wait();
  604. break;
  605. default:
  606. throw new GeneralException("","传递的支付场景payScene参数值不正确!");
  607. }
  608. }catch(Throwable $e){
  609. $strResult.="【".$e->getMessage()."】";
  610. $strResult.=" trace=>".$e->getTraceAsString();
  611. }
  612. try{
  613. $data = @json_decode($strResult,true);
  614. }catch(Throwable $ex){
  615. $data=null;
  616. }
  617. if($data && (!empty($data["prepay_id"]) || !empty($data["code_url"]))){
  618. return $data;
  619. }else{
  620. eeglobal_log_handler('weixin_prepayV3','error','生成预支付订单异常:reqData=>'.@json_encode($reqData).' result=>'.$strResult);
  621. $message="";
  622. if($data && !empty($data["code"]))
  623. $message.="【".$data["code"]."】";
  624. if($data && !empty($data["message"]))
  625. $message.=" ".$data["message"]."";
  626. if($data && !empty($data["detail"]))
  627. $message.=" ".@json_encode($data["detail"])."";
  628. $message=empty($message)?"生成预支付订单异常":"生成预支付订单异常:".$message;
  629. throw new GeneralException($message,$message);
  630. }
  631. }
  632. function weixin_payRsaSignV3($mchid,$apiKeyV3,$appid,$prepay_id){
  633. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  634. $merchantPrivateKeyFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.xyz";
  635. $merchantPrivateKeyInstance = WeChatPay\Crypto\Rsa::from($merchantPrivateKeyFilePath);
  636. $params = [
  637. 'appId' => $appid,
  638. 'timeStamp' => (string)WeChatPay\Formatter::timestamp(),
  639. 'nonceStr' => WeChatPay\Formatter::nonce(),
  640. 'package' => 'prepay_id='.$prepay_id,
  641. ];
  642. $params += ['paySign' => WeChatPay\Crypto\Rsa::sign(
  643. WeChatPay\Formatter::joinedByLineFeed(...array_values($params)),
  644. $merchantPrivateKeyInstance
  645. ), 'signType' => 'RSA'];
  646. return $params;
  647. }
  648. function weixin_prepayBuildV3($prePayArgs,$bizArgs,$bizHandle){
  649. if(!isset($prePayArgs["payBackBizCatalog"])) throw new GeneralException("","没有传递必要的参数payBackBizCatalog");
  650. $bizCatalog=$prePayArgs["payBackBizCatalog"];
  651. $bizId=$prePayArgs["payBackBizId"];
  652. if (intval($bizId) <= 0) throw new GeneralException("", "没有传递必要的参数payBackBizId");
  653. $payScene=$prePayArgs["payScene"];
  654. if(empty($payScene)) throw new GeneralException("","没有传递必要的参数payScene");
  655. $bizCatalog=intval($bizCatalog);
  656. $bizId=intval($bizId);
  657. $bizAttach="$bizCatalog:$bizId";
  658. $payInfo=$bizHandle($bizCatalog,$bizId,$bizArgs);
  659. $prepayData = weixin_prepayV3($payScene,$payInfo["appid"],$payInfo["out_trade_no"],$payInfo["total"],$payInfo["description"],$bizAttach,$payInfo["openid"],$payInfo["product_id"]);
  660. $ajaxData=array();
  661. switch($payScene){
  662. case "JSAPI":
  663. $prepayid = $prepayData["prepay_id"];
  664. $signedData=weixin_payRsaSignV3($payInfo["mchId"],$payInfo["mch_paykeyV3"],$payInfo["appid"],$prepayid);
  665. $ajaxData['appId'] = $payInfo["appid"];
  666. $ajaxData['timeStamp'] = $signedData["timeStamp"];
  667. $ajaxData['nonceStr'] = $signedData["nonceStr"];
  668. $ajaxData['package'] = 'prepay_id='.$prepayid;
  669. $ajaxData['signType'] = 'RSA';
  670. $ajaxData['paySign'] = $signedData["paySign"];
  671. break;
  672. case "APP":
  673. $prepayid = $prepayData["prepay_id"];
  674. $signedData=weixin_payRsaSignV3($payInfo["mchId"],$payInfo["mch_paykeyV3"],$payInfo["appid"],$prepayid);
  675. $ajaxData['appId'] = $payInfo["appid"];
  676. $ajaxData['partnerId'] = $payInfo["mchId"];
  677. $ajaxData['prepayId'] = $prepayid;
  678. $ajaxData['packageValue'] = 'Sign=WXPay';
  679. $ajaxData['nonceStr'] = $signedData["nonceStr"];
  680. $ajaxData['timeStamp'] = $signedData["timeStamp"];
  681. $ajaxData['sign'] = $signedData["paySign"];
  682. break;
  683. case "NATIVE":
  684. $code_url = $prepayData["code_url"];
  685. if(empty($code_url)){
  686. throw new GeneralException("","NATIVE预支付订单没有生成出code_url,无法继续");
  687. }
  688. require_once WEB_PHY_ROOT."/base/lib_qr.php";
  689. $ajaxData['base64QR']=lib_qrbuild("png",$code_url);
  690. break;
  691. default:
  692. throw new GeneralException("","传递的支付场景payScene参数值不正确!");
  693. }
  694. return $ajaxData;
  695. }
  696. function weixin_orderqueryV2($appid,$out_trade_no,$transaction_id){
  697. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  698. $mchid=WeiXinPay_mchId;
  699. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  700. $paySignKey=WeiXinPay_apiKey;
  701. if(empty($paySignKey)) throw new GeneralException('还没有配置微信商户apikey','还没有配置微信商户apikey');
  702. $instance = weixin_buildPayInstV2($mchid,$paySignKey);
  703. $reqData=[
  704. 'xml' => [
  705. 'appid' => $appid,
  706. 'mch_id' => $mchid,
  707. 'nonce_str' => random(8),
  708. 'out_trade_no' => $out_trade_no,
  709. 'transaction_id' => $transaction_id,
  710. ],
  711. 'security' => false,
  712. 'debug' => false
  713. ];
  714. $strResult="";
  715. try{
  716. $strResult = (string)$instance->v2->pay->orderquery->post($reqData)->getBody();
  717. }catch(Throwable $e){
  718. $strResult.="【".$e->getMessage()."】";
  719. $strResult.=" trace=>".$e->getTraceAsString();
  720. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  721. $r = $e->getResponse();
  722. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  723. $strResult.=" body=>".$r->getBody();
  724. }
  725. if ($e instanceof \GuzzleHttp\Promise\RejectionException
  726. && $e->getReason() instanceof \GuzzleHttp\Psr7\Response) {
  727. $r = $e->getReason();
  728. $rejectReason=$r->getBody();
  729. if(!empty($rejectReason)){//直接设置为结果信息
  730. $strResult=(string)$r->getBody();
  731. }else{
  732. $strResult.=" innerResponse=>".$r->getStatusCode().' '.$r->getReasonPhrase();
  733. $strResult.=" body=>".$r->getBody();
  734. }
  735. }
  736. }
  737. try{
  738. $data= new SimpleXMLElement($strResult,LIBXML_NOCDATA);
  739. $data = @json_decode(json_encode($data),true);
  740. }catch(Throwable $ex){
  741. $data=null;
  742. }
  743. if($data && !empty($data["trade_state"])){
  744. return $data;
  745. }else{
  746. eeglobal_log_handler('weixin_orderquery','error','查询支付订单时发生异常:微信返回失败信息 reqData=>'.@json_encode($reqData).' result=>'.$strResult);
  747. $message="";
  748. if($data && !empty($data["return_code"]))
  749. $message.="【".$data["return_code"]."】";
  750. if($data && !empty($data["return_msg"]))
  751. $message.=" ".$data["return_msg"]."";
  752. if($data && !empty($data["err_code"]))
  753. $message.="【".$data["err_code"]."】";
  754. if($data && !empty($data["err_code_des"]))
  755. $message.=" ".$data["err_code_des"]."";
  756. $message=empty($message)?"查询支付订单时发生异常":"查询支付订单时发生异常:".$message;
  757. throw new GeneralException($message,$message);
  758. }
  759. }
  760. function weixin_orderqueryV3($appid,$out_trade_no,$transaction_id){
  761. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  762. $mchid=WeiXinPay_mchId;
  763. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  764. $apiKeyV3=WeiXinPay_apiKeyV3;
  765. if(empty($apiKeyV3)) throw new GeneralException('还没有配置微信商户apiKeyV3','还没有配置微信商户apiKeyV3');
  766. $instance = weixin_buildPayInstV3($mchid,$apiKeyV3);
  767. $strResult="";
  768. try{
  769. if(!empty($transaction_id)){
  770. $reqData=[
  771. 'query' => ['mchid' => $mchid ],
  772. 'transaction_id' => $transaction_id,
  773. ];
  774. $strResult = (string) $instance->v3->pay->transactions->id->{'{transaction_id}'}
  775. ->getAsync($reqData)
  776. ->then(static function($response) {
  777. eeglobal_log_handler('weixin_orderquery3','info',"then正常返回transaction_id");
  778. return (string)$response->getBody();
  779. })
  780. ->otherwise(static function($e) {
  781. eeglobal_log_handler('weixin_orderquery3','info',"otherwise异常返回transaction_id");
  782. $owReturn="";
  783. $owReturn.="【".$e->getMessage()."】";
  784. $owReturn.=" trace=>".$e->getTraceAsString();
  785. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  786. $r = $e->getResponse();
  787. $owReturn=(string)$r->getBody();
  788. }
  789. return $owReturn;
  790. })
  791. ->wait();
  792. }else{
  793. $reqData=[
  794. 'query' => ['mchid' => $mchid ],
  795. 'out_trade_no' => $out_trade_no,
  796. ];
  797. $strResult= (string) $instance->v3->pay->transactions->outTradeNo->{'{out_trade_no}'}
  798. ->getAsync($reqData)
  799. ->then(static function($response) {
  800. eeglobal_log_handler('weixin_orderquery3','info',"then正常返回out_trade_no");
  801. return (string)$response->getBody();
  802. })
  803. ->otherwise(static function($e) {
  804. eeglobal_log_handler('weixin_orderquery3','info',"otherwise异常返回out_trade_no");
  805. $owReturn="";
  806. $owReturn.="【".$e->getMessage()."】";
  807. $owReturn.=" trace=>".$e->getTraceAsString();
  808. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  809. $r = $e->getResponse();
  810. $owReturn=(string)$r->getBody();
  811. }
  812. return $owReturn;
  813. })
  814. ->wait();
  815. }
  816. }catch(Throwable $e){
  817. $strResult.="【".$e->getMessage()."】";
  818. $strResult.=" trace=>".$e->getTraceAsString();
  819. }
  820. try{
  821. $data = @json_decode($strResult,true);
  822. }catch(Throwable $ex){
  823. $data=null;
  824. }
  825. if($data && !empty($data["trade_state"])){
  826. return $data;
  827. }else{
  828. eeglobal_log_handler('weixin_orderquery3','error',"查询支付订单时发生异常:reqData=>".@json_encode($reqData)." result=>$strResult");
  829. $message="";
  830. if($data && !empty($data["code"]))
  831. $message.="【".$data["code"]."】";
  832. if($data && !empty($data["message"]))
  833. $message.=" ".$data["message"]."";
  834. if($data && !empty($data["detail"]))
  835. $message.=" ".@json_encode($data["detail"])."";
  836. $message=empty($message)?"查询支付订单时发生异常":"查询支付订单时发生异常:".$message;
  837. throw new GeneralException($message,$message);
  838. }
  839. }
  840. function weixin_ordercloseV3($out_trade_no){
  841. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  842. $mchid=WeiXinPay_mchId;
  843. if(empty($mchid)) throw new GeneralException('还没有配置微信商户号','还没有配置微信商户号');
  844. $apiKeyV3=WeiXinPay_apiKeyV3;
  845. if(empty($apiKeyV3)) throw new GeneralException('还没有配置微信商户apiKeyV3','还没有配置微信商户apiKeyV3');
  846. $instance = weixin_buildPayInstV3($mchid,$apiKeyV3);
  847. $strResult="";
  848. try{
  849. $reqData=[
  850. 'json' => ['mchid' => $mchid],
  851. 'out_trade_no' => $out_trade_no,
  852. ];
  853. $strResult= (string) $instance->v3->pay->transactions->outTradeNo->{'{out_trade_no}'}->close
  854. ->postAsync($reqData)
  855. ->then(static function($response) {
  856. eeglobal_log_handler('weixin_ordercloseV3','info',"then正常返回out_trade_no");
  857. return (string)$response->getBody();
  858. })
  859. ->otherwise(static function($e) {
  860. eeglobal_log_handler('weixin_ordercloseV3','info',"otherwise异常返回out_trade_no");
  861. $owReturn="";
  862. $owReturn.="【".$e->getMessage()."】";
  863. $owReturn.=" trace=>".$e->getTraceAsString();
  864. if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
  865. $r = $e->getResponse();
  866. $owReturn=(string)$r->getBody();
  867. }
  868. return $owReturn;
  869. })
  870. ->wait();
  871. }catch(Throwable $e){
  872. $strResult.="【".$e->getMessage()."】";
  873. $strResult.=" trace=>".$e->getTraceAsString();
  874. }
  875. try{
  876. if ($strResult == "") $strResult = "\"SUCCESS\"";
  877. $data = @json_decode($strResult,true);
  878. }catch(Throwable $ex){
  879. $data=null;
  880. }
  881. if($data=="SUCCESS"){
  882. return $data;
  883. }else{
  884. eeglobal_log_handler('weixin_ordercloseV3','error',"关闭支付订单时发生异常:reqData=>".@json_encode($reqData)." result=>$strResult");
  885. $message="";
  886. if($data && !empty($data["code"]))
  887. $message.="【".$data["code"]."】";
  888. if($data && !empty($data["message"]))
  889. $message.=" ".$data["message"]."";
  890. if($data && !empty($data["detail"]))
  891. $message.=" ".@json_encode($data["detail"])."";
  892. $message=empty($message)?"关闭支付订单时发生异常":"关闭支付订单时发生异常:".$message;
  893. throw new GeneralException($message,$message);
  894. }
  895. }
  896. function weixin_payedNotifyV2($logCatalog,$bizHandle){
  897. try{
  898. error_reporting(0);
  899. eeglobal_log_handler($logCatalog,"debug","支付后回调-start");
  900. $xml = file_get_contents('php://input');
  901. eeglobal_log_handler($logCatalog,"debug","支付后回调-xml=$xml");
  902. $dom_xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  903. $array_result =@json_decode(@json_encode($dom_xml),true);
  904. $out_trade_no=$array_result["out_trade_no"];
  905. $transaction_id=$array_result["transaction_id"];
  906. if(empty($out_trade_no) || empty($transaction_id)) throw new Exception("无效回调,out_trade_no或transaction_id为空");
  907. $attach=$array_result["attach"];
  908. $attachInfo=explode(":",$attach);
  909. $bizCatalog=0;
  910. $bizId=0;
  911. if(count($attachInfo)==2){
  912. $bizCatalog=intval($attachInfo[0]);
  913. $bizId=intval($attachInfo[1]);
  914. }
  915. eeglobal_log_handler($logCatalog,"debug","支付后回调 attach=$attach bizCatalog=$bizCatalog bizId=$bizId");
  916. $bizHandle($bizCatalog,$bizId,$array_result);
  917. ob_clean();
  918. header("Content-type: text/xml;charset=utf-8");
  919. echo "<xml>";
  920. echo "<return_code><![CDATA[SUCCESS]]></return_code>";
  921. echo "<return_msg><![CDATA[OK]]></return_msg>";
  922. echo "</xml>";
  923. exit;
  924. }catch(Throwable $e){
  925. eeglobal_log_handler($logCatalog,"error","支付后回调处理异常:xml=>$xml 异常消息:".$e->getMessage()." trace=>".$e->getTraceAsString());
  926. ob_clean();
  927. echo $logCatalog."";
  928. exit;
  929. }
  930. }
  931. function weixin_payedNotifyV3($logCatalog,$bizHandle){
  932. try{
  933. error_reporting(0);
  934. $mchid=WeiXinPay_mchId;
  935. $apiv3Key =WeiXinPay_apiKeyV3;// 在商户平台上设置的APIv3密钥
  936. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  937. eeglobal_log_handler($logCatalog,"debug","支付后回调-start");
  938. $inBody = file_get_contents('php://input');
  939. eeglobal_log_handler($logCatalog,"debug","支付后回调-inBody=$inBody");
  940. $inBodyArray = input_getPostObj();
  941. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  942. ['resource' => [
  943. 'ciphertext' => $ciphertext,
  944. 'nonce' => $nonce,
  945. 'associated_data' => $aad
  946. ]] = $inBodyArray;
  947. // 加密文本消息解密
  948. $inBodyResource = WeChatPay\Crypto\AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  949. eeglobal_log_handler($logCatalog,"debug","支付后回调-inBodyResource=$inBodyResource");
  950. // 把解密后的文本转换为PHP Array数组
  951. $array_result = (array)json_decode($inBodyResource, true);
  952. $out_trade_no=$array_result["out_trade_no"];
  953. $transaction_id=$array_result["transaction_id"];
  954. if(empty($out_trade_no) || empty($transaction_id)) throw new Exception("无效回调,out_trade_no或transaction_id为空");
  955. $attach=$array_result["attach"];
  956. $attachInfo=explode(":",$attach);
  957. $bizCatalog=0;
  958. $bizId=0;
  959. if(count($attachInfo)==2){
  960. $bizCatalog=intval($attachInfo[0]);
  961. $bizId=intval($attachInfo[1]);
  962. }
  963. eeglobal_log_handler($logCatalog,"debug","支付后回调 attach=$attach bizCatalog=$bizCatalog bizId=$bizId");
  964. $bizHandle($bizCatalog,$bizId,$array_result);
  965. ob_clean();
  966. header("Content-type:application/json;charset=utf-8");
  967. echo `{"code":"SUCCESS","message":"成功"}`;
  968. exit;
  969. }catch(Throwable $e){
  970. eeglobal_log_handler($logCatalog,"error","支付后回调处理异常:inBody=>$inBody inBodyResource=$inBodyResource 异常消息:".$e->getMessage()." trace=>".$e->getTraceAsString());
  971. ob_clean();
  972. echo $logCatalog."";
  973. exit;
  974. }
  975. }
  976. function weixin_getPlatCertificatesV3($mchid){
  977. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  978. $merchantCertificateFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.abc";
  979. $merchantCertificateSerial = WeChatPay\Util\PemUtil::parseCertificateSerialNo($merchantCertificateFilePath);
  980. $merchantPrivateKeyFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.xyz";
  981. $merchantPrivateKeyInstance = WeChatPay\Crypto\Rsa::from($merchantPrivateKeyFilePath, WeChatPay\Crypto\Rsa::KEY_TYPE_PRIVATE);
  982. $platformCertificateFilePath = "file://".WEB_PHY_ROOT."/ssl/wxpaycert_$mchid.wxp";
  983. $platformPublicKeyInstance = WeChatPay\Crypto\Rsa::from($platformCertificateFilePath, WeChatPay\Crypto\Rsa::KEY_TYPE_PUBLIC);
  984. $platformCertificateSerial = WeChatPay\Util\PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
  985. $url="https://api.mch.weixin.qq.com/v3/certificates";
  986. $http_method="GET";
  987. $timestamp = time();//时间戳
  988. $nonce = $timestamp . rand(10000, 99999);//随机字符串
  989. $url_parts = parse_url($url);
  990. $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
  991. $body="";
  992. $message =
  993. $http_method . "\n" .
  994. $canonical_url . "\n" .
  995. $timestamp . "\n" .
  996. $nonce . "\n" .
  997. $body . "\n";
  998. openssl_sign($message, $raw_sign, $merchantPrivateKeyInstance, 'sha256WithRSAEncryption');
  999. $raw_sign = base64_encode($raw_sign);
  1000. $sign = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
  1001. $mchid, $nonce, $timestamp, $merchantCertificateSerial, $raw_sign);
  1002. $header[] = 'User-Agent:https://WS.COM/User_agent';
  1003. $header[] = 'Accept:application/json';
  1004. $header[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign;
  1005. $rawdata = http_get($url, $header);
  1006. $jsondata = json_decode($rawdata, true);
  1007. $jsondata = $jsondata["data"];
  1008. /* "serial_no": "50062CE505775F070CAB06E697F1BBD1AD4F4D87",
  1009. "effective_time ": "2018-12-07T10:34:56+08:00",
  1010. "expire_time ": "2020-12-07T10:34:56+08:00",
  1011. "encrypt_certificate": {
  1012. "algorithm": "AEAD_AES_256_GCM",
  1013. "nonce": "35f9c719727b",
  1014. "associated_data": "certificate",
  1015. "ciphertext": "aBvt… "
  1016. }
  1017. */
  1018. return $jsondata;
  1019. }
  1020. /*****************
  1021. */
  1022. use TencentCloud\Common\Credential;
  1023. use TencentCloud\Common\Profile\ClientProfile;
  1024. use TencentCloud\Common\Profile\HttpProfile;
  1025. use TencentCloud\Common\Exception\TencentCloudSDKException;
  1026. use TencentCloud\Ocr\V20181119\OcrClient;
  1027. use TencentCloud\Ocr\V20181119\Models\IDCardOCRRequest;
  1028. use TencentCloud\Ocr\V20181119\Models\BizLicenseOCRRequest;
  1029. /*****************
  1030. */
  1031. function txcloud_ocr($catalog,$params){
  1032. try {
  1033. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  1034. $cred = new Credential(TXCLOUD_SecretId, TXCLOUD_SecretKey);
  1035. $httpProfile = new HttpProfile();
  1036. $httpProfile->setEndpoint("ocr.tencentcloudapi.com");
  1037. $clientProfile = new ClientProfile();
  1038. $clientProfile->setHttpProfile($httpProfile);
  1039. $client = new OcrClient($cred, "ap-beijing", $clientProfile);
  1040. switch($catalog){
  1041. case "IDCardOCR":
  1042. $req = new IDCardOCRRequest();
  1043. $params["Config"]=array(
  1044. "InvalidDateWarn"=>true,
  1045. "DetectPsWarn"=>true,
  1046. );
  1047. $params = array(
  1048. "ImageUrl" => empty($params["ImageUrl"])?"":$params["ImageUrl"],
  1049. "ImageBase64" => empty($params["ImageBase64"])?"":$params["ImageBase64"],
  1050. "CardSide" => empty($params["CardSide"])?"":$params["CardSide"],
  1051. "Config" => empty($params["Config"])?array("Quality"=>50,"BorderCheckWarn"=>50,"ReflectWarn"=>true):json_encode($params["Config"]),
  1052. );
  1053. $req->fromJsonString(json_encode($params));
  1054. $resp = $client->IDCardOCR($req);
  1055. txcloud_ocrErr($resp);
  1056. $validDate=$resp->getValidDate(); //2010.07.21-2020.07.21
  1057. if(!empty($validDate)){
  1058. $validDate2=explode("-",$validDate)[1];
  1059. $validDate2=mb_ereg_replace("\.","-",$validDate2);
  1060. $validDate2=strtotime($validDate2);
  1061. if(strtotime(date("Y-m-d",time()))>$validDate2) throw new GeneralException("", "身份证已过期");
  1062. }
  1063. return json_decode($resp->toJsonString(),true);
  1064. break;
  1065. case "BizLicenseOCR":
  1066. $req = new BizLicenseOCRRequest();
  1067. $params = array(
  1068. "ImageUrl" => empty($params["ImageUrl"])?"":$params["ImageUrl"],
  1069. "ImageBase64" => empty($params["ImageBase64"])?"":$params["ImageBase64"],
  1070. );
  1071. $req->fromJsonString(json_encode($params));
  1072. $resp = $client->BizLicenseOCR($req);
  1073. txcloud_ocrErr($resp);
  1074. return json_decode($resp->toJsonString(),true);
  1075. break;
  1076. default:throw new GeneralException("", " 不支持的catalog!");
  1077. }
  1078. }
  1079. catch(TencentCloudSDKException $e) {
  1080. $err=array();
  1081. $err["errorCode"]=$e->getErrorCode();
  1082. $err["message"]=$e->getMessage();
  1083. $friendMsg="【".$err["errorCode"]."】".$err["message"];
  1084. $friendMsg="无法识别";
  1085. $err["requestId"]=$e->getRequestId();
  1086. $err["code"]=$e->getCode();
  1087. $err["file"]=$e->getFile();
  1088. $err["line"]=$e->getLine();
  1089. $err["trace"]=json_encode($e->getTrace());
  1090. $fullMsg=json_encode($err);
  1091. throw new GeneralException("ocr_error",$friendMsg);
  1092. }
  1093. }
  1094. function txcloud_ocrErr($resp){
  1095. if(!empty($resp->getRecognizeWarnCode())){
  1096. $msgs=$resp->getRecognizeWarnMsg();
  1097. $err="";
  1098. foreach($resp->getRecognizeWarnCode() as $index=>$code){
  1099. //$err.="【".$msgs[$index]." 错误码:{$code}】 ";
  1100. $err.="【".$msgs[$index]."】 ";
  1101. }
  1102. throw new GeneralException("ocr_error",$err);
  1103. }
  1104. if(!empty($resp->getAdvancedInfo())){
  1105. $advancedInfo=json_decode($resp->getAdvancedInfo(),true);
  1106. if(!empty($advancedInfo) && is_array($advancedInfo)){
  1107. $warnInfos=$advancedInfo["WarnInfos"];
  1108. if(!empty($warnInfos) && is_array($warnInfos)){
  1109. $err="";
  1110. foreach($warnInfos as $code){
  1111. switch($code){
  1112. case "-9100":
  1113. $err.="【身份证有效日期不合法告警";
  1114. break;
  1115. case "-9101":
  1116. $err.="【身份证边框不完整告警";
  1117. break;
  1118. case "-9102":
  1119. $err.="【身份证复印件告警";
  1120. break;
  1121. case "-9103":
  1122. $err.="【身份证翻拍告警";
  1123. break;
  1124. case "-9105":
  1125. $err.="【身份证框内遮挡告警";
  1126. break;
  1127. case "-9104":
  1128. $err.="【临时身份证告警";
  1129. break;
  1130. case "-9106":
  1131. $err.="【身份证 PS 告警";
  1132. break;
  1133. case "-9107":
  1134. $err.="【身份证反光告警";
  1135. break;
  1136. default:
  1137. $err.="【未知错误";
  1138. break;
  1139. }
  1140. //$err.=" 错误码:{$code}】 ";
  1141. $err.="】 ";
  1142. }
  1143. throw new GeneralException("ocr_error",$err);
  1144. }
  1145. }
  1146. }
  1147. }
  1148. //*******/
  1149. use TencentCloud\Faceid\V20180301\FaceidClient;
  1150. use TencentCloud\Faceid\V20180301\Models\IdCardVerificationRequest;
  1151. function txcloud_idcardCheck($trueName,$idCard){
  1152. try {
  1153. require_once WEB_PHY_ROOT."/base/composer/vendor/autoload.php";
  1154. $cred = new Credential(TXCLOUD_SecretId, TXCLOUD_SecretKey);
  1155. $httpProfile = new HttpProfile();
  1156. $httpProfile->setEndpoint("faceid.tencentcloudapi.com");
  1157. $clientProfile = new ClientProfile();
  1158. $clientProfile->setHttpProfile($httpProfile);
  1159. $client = new FaceidClient($cred, "ap-beijing", $clientProfile);
  1160. $req = new IdCardVerificationRequest();
  1161. $params = array(
  1162. "Name" => $trueName,
  1163. "IdCard" => $idCard,
  1164. );
  1165. $req->fromJsonString(json_encode($params));
  1166. $resp = $client->IdCardVerification($req);
  1167. $Description = $resp->getDescription();
  1168. $RequestId = $resp->getRequestId();
  1169. $Result = $resp->getResult();
  1170. if($Result!="0") throw new GeneralException("idcardCheck_error"," ".$Description);
  1171. return json_decode($resp->toJsonString(),true);
  1172. }
  1173. catch(TencentCloudSDKException $e) {
  1174. $err=array();
  1175. $err["errorCode"]=$e->getErrorCode();
  1176. $err["message"]=$e->getMessage();
  1177. $friendMsg="【".$err["errorCode"]."】".$err["message"];
  1178. $friendMsg="无法识别";
  1179. $err["requestId"]=$e->getRequestId();
  1180. $err["code"]=$e->getCode();
  1181. $err["file"]=$e->getFile();
  1182. $err["line"]=$e->getLine();
  1183. $err["trace"]=json_encode($e->getTrace());
  1184. $fullMsg=json_encode($err);
  1185. throw new GeneralException("idcardCheck_error",$friendMsg);
  1186. }
  1187. }