├── app.js
├── app.json
├── app.wxss
├── lib
├── WxPayPubHelper
│ ├── SDKRuntimeException.php
│ ├── WxPay.pub.config.php
│ └── WxPayPubHelper.php
├── demo.php
├── wechat.php
├── wxBizMsgCrypt.php
└── wxapi.php
├── pages
├── index
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
└── logs
│ ├── logs.js
│ ├── logs.json
│ ├── logs.wxml
│ └── logs.wxss
├── utils
└── util.js
├── wx.php
└── wxmini.php
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | onLaunch: function() {
4 | //调用API从本地缓存中获取数据
5 | var logs = wx.getStorageSync('logs') || []
6 | logs.unshift(Date.now())
7 | wx.setStorageSync('logs', logs)
8 | },
9 |
10 | getUserInfo: function(cb) {
11 | var that = this
12 | if (this.globalData.userInfo) {
13 | typeof cb == "function" && cb(this.globalData.userInfo)
14 | } else {
15 | //调用登录接口
16 | wx.getUserInfo({
17 | withCredentials: false,
18 | success: function(res) {
19 | that.globalData.userInfo = res.userInfo
20 | typeof cb == "function" && cb(that.globalData.userInfo)
21 | }
22 | })
23 | }
24 | },
25 | globalData: {
26 | userInfo: null,
27 | url : 'https://www.example.com/'
28 | }
29 | })
30 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index",
4 | "pages/logs/logs"
5 | ],
6 | "window":{
7 | "backgroundTextStyle":"light",
8 | "navigationBarBackgroundColor": "#fff",
9 | "navigationBarTitleText": "小程序支付",
10 | "navigationBarTextStyle":"black"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | .container {
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 180rpx 0;
9 | box-sizing: border-box;
10 | }
11 |
--------------------------------------------------------------------------------
/lib/WxPayPubHelper/SDKRuntimeException.php:
--------------------------------------------------------------------------------
1 | getMessage();
7 | }
8 | }
9 |
10 | ?>
--------------------------------------------------------------------------------
/lib/WxPayPubHelper/WxPay.pub.config.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/WxPayPubHelper/WxPayPubHelper.php:
--------------------------------------------------------------------------------
1 | $v)
76 | {
77 | if($urlencode)
78 | {
79 | $v = urlencode($v);
80 | }
81 | //$buff .= strtolower($k) . "=" . $v . "&";
82 | $buff .= $k . "=" . $v . "&";
83 | }
84 | $reqPar = '';
85 | if (strlen($buff) > 0)
86 | {
87 | $reqPar = substr($buff, 0, strlen($buff)-1);
88 | }
89 | return $reqPar;
90 | }
91 |
92 | /**
93 | * 作用:生成签名
94 | */
95 | public function getSign($Obj)
96 | {
97 | foreach ($Obj as $k => $v)
98 | {
99 | $Parameters[$k] = $v;
100 | }
101 | //签名步骤一:按字典序排序参数
102 | ksort($Parameters);
103 | $String = $this->formatBizQueryParaMap($Parameters, false);
104 | //echo '【string1】'.$String.'';
105 | //签名步骤二:在string后加入KEY
106 | $String = $String."&key=".WxPayConf_pub::KEY;
107 | //echo "【string2】".$String."";
108 | //签名步骤三:MD5加密
109 | $String = md5($String);
110 | //echo "【string3】 ".$String."";
111 | //签名步骤四:所有字符转为大写
112 | $result_ = strtoupper($String);
113 | //echo "【result】 ".$result_."";
114 | return $result_;
115 | }
116 |
117 | /**
118 | * 作用:array转xml
119 | */
120 | function arrayToXml($arr)
121 | {
122 | $xml = "";
123 | foreach ($arr as $key=>$val)
124 | {
125 | if (is_numeric($val))
126 | {
127 | $xml.="<".$key.">".$val."".$key.">";
128 |
129 | }
130 | else
131 | $xml.="<".$key.">".$key.">";
132 | }
133 | $xml.="";
134 | return $xml;
135 | }
136 |
137 | /**
138 | * 作用:将xml转为array
139 | */
140 | public function xmlToArray($xml)
141 | {
142 | //将XML转为array
143 | $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
144 | return $array_data;
145 | }
146 |
147 | /**
148 | * 作用:以post方式提交xml到对应的接口url
149 | */
150 | public function postXmlCurl($xml,$url,$second=30)
151 | {
152 | //初始化curl
153 | $ch = curl_init();
154 | //设置超时
155 | curl_setopt($ch, CURLOPT_TIMEOUT, $second);
156 | //这里设置代理,如果有的话
157 | //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
158 | //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
159 | curl_setopt($ch,CURLOPT_URL, $url);
160 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
161 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
162 | //设置header
163 | curl_setopt($ch, CURLOPT_HEADER, FALSE);
164 | //要求结果为字符串且输出到屏幕上
165 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
166 | //post提交方式
167 | curl_setopt($ch, CURLOPT_POST, TRUE);
168 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
169 | //运行curl
170 | $data = curl_exec($ch);
171 | curl_close($ch);
172 | file_put_contents('/tmp/wxmini', print_r($data, 1), FILE_APPEND);
173 | //返回结果
174 | if($data)
175 | {
176 | curl_close($ch);
177 | return $data;
178 | }
179 | else
180 | {
181 | $error = curl_errno($ch);
182 | echo "curl出错,错误码:$error"."
";
183 | echo "错误原因查询";
184 | curl_close($ch);
185 | return false;
186 | }
187 | }
188 |
189 | /**
190 | * 作用:使用证书,以post方式提交xml到对应的接口url
191 | */
192 | function postXmlSSLCurl($xml,$url,$second=30)
193 | {
194 | $ch = curl_init();
195 | //超时时间
196 | curl_setopt($ch,CURLOPT_TIMEOUT,$second);
197 | //这里设置代理,如果有的话
198 | //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
199 | //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
200 | curl_setopt($ch,CURLOPT_URL, $url);
201 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
202 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
203 | //设置header
204 | curl_setopt($ch,CURLOPT_HEADER,FALSE);
205 | //要求结果为字符串且输出到屏幕上
206 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
207 | //设置证书
208 | //使用证书:cert 与 key 分别属于两个.pem文件
209 | //默认格式为PEM,可以注释
210 | curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
211 | curl_setopt($ch,CURLOPT_SSLCERT, WxPayConf_pub::SSLCERT_PATH);
212 | //默认格式为PEM,可以注释
213 | curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
214 | curl_setopt($ch,CURLOPT_SSLKEY, WxPayConf_pub::SSLKEY_PATH);
215 | //post提交方式
216 | curl_setopt($ch,CURLOPT_POST, true);
217 | curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
218 | $data = curl_exec($ch);
219 | //返回结果
220 | if($data){
221 | curl_close($ch);
222 | return $data;
223 | }
224 | else {
225 | $error = curl_errno($ch);
226 | echo "curl出错,错误码:$error"."
";
227 | echo "错误原因查询";
228 | curl_close($ch);
229 | return false;
230 | }
231 | }
232 |
233 | /**
234 | * 作用:打印数组
235 | */
236 | function printErr($wording='',$err='')
237 | {
238 | print_r('
');
239 | echo $wording."";
240 | var_dump($err);
241 | print_r('
');
242 | }
243 | }
244 |
245 | /**
246 | * 请求型接口的基类
247 | */
248 | class Wxpay_client_pub extends Common_util_pub
249 | {
250 | var $parameters;//请求参数,类型为关联数组
251 | public $response;//微信返回的响应
252 | public $result;//返回参数,类型为关联数组
253 | var $url;//接口链接
254 | var $curl_timeout;//curl超时时间
255 |
256 | /**
257 | * 作用:设置请求参数
258 | */
259 | function setParameter($parameter, $parameterValue)
260 | {
261 | $this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
262 | }
263 |
264 | /**
265 | * 作用:设置标配的请求参数,生成签名,生成接口参数xml
266 | */
267 | function createXml()
268 | {
269 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
270 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
271 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
272 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
273 | return $this->arrayToXml($this->parameters);
274 | }
275 |
276 | /**
277 | * 作用:post请求xml
278 | */
279 | function postXml()
280 | {
281 | $xml = $this->createXml();
282 | $this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
283 | return $this->response;
284 | }
285 |
286 | /**
287 | * 作用:使用证书post请求xml
288 | */
289 | function postXmlSSL()
290 | {
291 | $xml = $this->createXml();
292 | $this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
293 | return $this->response;
294 | }
295 |
296 | /**
297 | * 作用:获取结果,默认不使用证书
298 | */
299 | function getResult()
300 | {
301 | $this->postXml();
302 | $this->result = $this->xmlToArray($this->response);
303 | return $this->result;
304 | }
305 | }
306 |
307 |
308 | /**
309 | * 统一支付接口类
310 | */
311 | class UnifiedOrder_pub extends Wxpay_client_pub
312 | {
313 | function __construct()
314 | {
315 | //设置接口链接
316 | $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
317 | //设置curl超时时间
318 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
319 | }
320 |
321 | /**
322 | * 生成接口参数xml
323 | */
324 | function createXml()
325 | {
326 | try
327 | {
328 | //检测必填参数
329 | if($this->parameters["out_trade_no"] == null)
330 | {
331 | throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."
");
332 | }elseif($this->parameters["body"] == null){
333 | throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."
");
334 | }elseif ($this->parameters["total_fee"] == null ) {
335 | throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."
");
336 | }elseif ($this->parameters["notify_url"] == null) {
337 | throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."
");
338 | }elseif ($this->parameters["trade_type"] == null) {
339 | throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."
");
340 | }elseif ($this->parameters["trade_type"] == "JSAPI" &&
341 | $this->parameters["openid"] == NULL){
342 | throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."
");
343 | }
344 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
345 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
346 | $this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];//终端ip
347 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
348 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
349 | return $this->arrayToXml($this->parameters);
350 | }catch (SDKRuntimeException $e)
351 | {
352 | die($e->errorMessage());
353 | }
354 | }
355 |
356 | /**
357 | * 获取prepay_id
358 | */
359 | function getPrepayId()
360 | {
361 | $this->postXml();
362 | $this->result = $this->xmlToArray($this->response);
363 | $prepay_id = $this->result["prepay_id"];
364 | return $prepay_id;
365 | }
366 |
367 | }
368 |
369 | /**
370 | * 订单查询接口
371 | */
372 | class OrderQuery_pub extends Wxpay_client_pub
373 | {
374 | function __construct()
375 | {
376 | //设置接口链接
377 | $this->url = "https://api.mch.weixin.qq.com/pay/orderquery";
378 | //设置curl超时时间
379 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
380 | }
381 |
382 | /**
383 | * 生成接口参数xml
384 | */
385 | function createXml()
386 | {
387 | try
388 | {
389 | //检测必填参数
390 | if($this->parameters["out_trade_no"] == null &&
391 | $this->parameters["transaction_id"] == null)
392 | {
393 | throw new SDKRuntimeException("订单查询接口中,out_trade_no、transaction_id至少填一个!"."
");
394 | }
395 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
396 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
397 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
398 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
399 | return $this->arrayToXml($this->parameters);
400 | }catch (SDKRuntimeException $e)
401 | {
402 | die($e->errorMessage());
403 | }
404 | }
405 |
406 | }
407 |
408 | /**
409 | * 退款申请接口
410 | */
411 | class Refund_pub extends Wxpay_client_pub
412 | {
413 |
414 | function __construct() {
415 | //设置接口链接
416 | $this->url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
417 | //设置curl超时时间
418 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
419 | }
420 |
421 | /**
422 | * 生成接口参数xml
423 | */
424 | function createXml()
425 | {
426 | try
427 | {
428 | //检测必填参数
429 | if($this->parameters["out_trade_no"] == null && $this->parameters["transaction_id"] == null) {
430 | throw new SDKRuntimeException("退款申请接口中,out_trade_no、transaction_id至少填一个!"."
");
431 | }elseif($this->parameters["out_refund_no"] == null){
432 | throw new SDKRuntimeException("退款申请接口中,缺少必填参数out_refund_no!"."
");
433 | }elseif($this->parameters["total_fee"] == null){
434 | throw new SDKRuntimeException("退款申请接口中,缺少必填参数total_fee!"."
");
435 | }elseif($this->parameters["refund_fee"] == null){
436 | throw new SDKRuntimeException("退款申请接口中,缺少必填参数refund_fee!"."
");
437 | }elseif($this->parameters["op_user_id"] == null){
438 | throw new SDKRuntimeException("退款申请接口中,缺少必填参数op_user_id!"."
");
439 | }
440 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
441 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
442 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
443 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
444 | return $this->arrayToXml($this->parameters);
445 | }catch (SDKRuntimeException $e)
446 | {
447 | die($e->errorMessage());
448 | }
449 | }
450 | /**
451 | * 作用:获取结果,使用证书通信
452 | */
453 | function getResult()
454 | {
455 | $this->postXmlSSL();
456 | $this->result = $this->xmlToArray($this->response);
457 | return $this->result;
458 | }
459 |
460 | }
461 |
462 |
463 | /**
464 | * 退款查询接口
465 | */
466 | class RefundQuery_pub extends Wxpay_client_pub
467 | {
468 |
469 | function __construct() {
470 | //设置接口链接
471 | $this->url = "https://api.mch.weixin.qq.com/pay/refundquery";
472 | //设置curl超时时间
473 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
474 | }
475 |
476 | /**
477 | * 生成接口参数xml
478 | */
479 | function createXml()
480 | {
481 | try
482 | {
483 | if($this->parameters["out_refund_no"] == null &&
484 | $this->parameters["out_trade_no"] == null &&
485 | $this->parameters["transaction_id"] == null &&
486 | $this->parameters["refund_id "] == null)
487 | {
488 | throw new SDKRuntimeException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!"."
");
489 | }
490 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
491 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
492 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
493 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
494 | return $this->arrayToXml($this->parameters);
495 | }catch (SDKRuntimeException $e)
496 | {
497 | die($e->errorMessage());
498 | }
499 | }
500 |
501 | /**
502 | * 作用:获取结果,使用证书通信
503 | */
504 | function getResult()
505 | {
506 | $this->postXmlSSL();
507 | $this->result = $this->xmlToArray($this->response);
508 | return $this->result;
509 | }
510 |
511 | }
512 |
513 | /**
514 | * 对账单接口
515 | */
516 | class DownloadBill_pub extends Wxpay_client_pub
517 | {
518 |
519 | function __construct()
520 | {
521 | //设置接口链接
522 | $this->url = "https://api.mch.weixin.qq.com/pay/downloadbill";
523 | //设置curl超时时间
524 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
525 | }
526 |
527 | /**
528 | * 生成接口参数xml
529 | */
530 | function createXml()
531 | {
532 | try
533 | {
534 | if($this->parameters["bill_date"] == null )
535 | {
536 | throw new SDKRuntimeException("对账单接口中,缺少必填参数bill_date!"."
");
537 | }
538 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
539 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
540 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
541 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
542 | return $this->arrayToXml($this->parameters);
543 | }catch (SDKRuntimeException $e)
544 | {
545 | die($e->errorMessage());
546 | }
547 | }
548 |
549 | /**
550 | * 作用:获取结果,默认不使用证书
551 | */
552 | function getResult()
553 | {
554 | $this->postXml();
555 | $this->result = $this->xmlToArray($this->result_xml);
556 | return $this->result;
557 | }
558 |
559 |
560 |
561 | }
562 |
563 | /**
564 | * 短链接转换接口
565 | */
566 | class ShortUrl_pub extends Wxpay_client_pub
567 | {
568 | function __construct()
569 | {
570 | //设置接口链接
571 | $this->url = "https://api.mch.weixin.qq.com/tools/shorturl";
572 | //设置curl超时时间
573 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
574 | }
575 |
576 | /**
577 | * 生成接口参数xml
578 | */
579 | function createXml()
580 | {
581 | try
582 | {
583 | if($this->parameters["long_url"] == null )
584 | {
585 | throw new SDKRuntimeException("短链接转换接口中,缺少必填参数long_url!"."
");
586 | }
587 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
588 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
589 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
590 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
591 | return $this->arrayToXml($this->parameters);
592 | }catch (SDKRuntimeException $e)
593 | {
594 | die($e->errorMessage());
595 | }
596 | }
597 |
598 | /**
599 | * 获取prepay_id
600 | */
601 | function getShortUrl()
602 | {
603 | $this->postXml();
604 | $prepay_id = $this->result["short_url"];
605 | return $prepay_id;
606 | }
607 |
608 | }
609 |
610 | /**
611 | * 响应型接口基类
612 | */
613 | class Wxpay_server_pub extends Common_util_pub
614 | {
615 | public $data;//接收到的数据,类型为关联数组
616 | var $returnParameters;//返回参数,类型为关联数组
617 |
618 | /**
619 | * 将微信的请求xml转换成关联数组,以方便数据处理
620 | */
621 | function saveData($xml)
622 | {
623 | $this->data = $this->xmlToArray($xml);
624 | }
625 |
626 | function checkSign()
627 | {
628 | $tmpData = $this->data;
629 | unset($tmpData['sign']);
630 | $sign = $this->getSign($tmpData);//本地签名
631 | if ($this->data['sign'] == $sign) {
632 | return TRUE;
633 | }
634 | return FALSE;
635 | }
636 |
637 | /**
638 | * 获取微信的请求数据
639 | */
640 | function getData()
641 | {
642 | return $this->data;
643 | }
644 |
645 | /**
646 | * 设置返回微信的xml数据
647 | */
648 | function setReturnParameter($parameter, $parameterValue)
649 | {
650 | $this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
651 | }
652 |
653 | /**
654 | * 生成接口参数xml
655 | */
656 | function createXml()
657 | {
658 | return $this->arrayToXml($this->returnParameters);
659 | }
660 |
661 | /**
662 | * 将xml数据返回微信
663 | */
664 | function returnXml()
665 | {
666 | $returnXml = $this->createXml();
667 | return $returnXml;
668 | }
669 | }
670 |
671 |
672 | /**
673 | * 通用通知接口
674 | */
675 | class Notify_pub extends Wxpay_server_pub
676 | {
677 |
678 | }
679 |
680 |
681 |
682 |
683 | /**
684 | * 请求商家获取商品信息接口
685 | */
686 | class NativeCall_pub extends Wxpay_server_pub
687 | {
688 | /**
689 | * 生成接口参数xml
690 | */
691 | function createXml()
692 | {
693 | if($this->returnParameters["return_code"] == "SUCCESS"){
694 | $this->returnParameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
695 | $this->returnParameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
696 | $this->returnParameters["nonce_str"] = $this->createNoncestr();//随机字符串
697 | $this->returnParameters["sign"] = $this->getSign($this->returnParameters);//签名
698 | }
699 | return $this->arrayToXml($this->returnParameters);
700 | }
701 |
702 | /**
703 | * 获取product_id
704 | */
705 | function getProductId()
706 | {
707 | $product_id = $this->data["product_id"];
708 | return $product_id;
709 | }
710 |
711 | }
712 |
713 | /**
714 | * 静态链接二维码
715 | */
716 | class NativeLink_pub extends Common_util_pub
717 | {
718 | var $parameters;//静态链接参数
719 | var $url;//静态链接
720 |
721 | function __construct()
722 | {
723 | }
724 |
725 | /**
726 | * 设置参数
727 | */
728 | function setParameter($parameter, $parameterValue)
729 | {
730 | $this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
731 | }
732 |
733 | /**
734 | * 生成Native支付链接二维码
735 | */
736 | function createLink()
737 | {
738 | try
739 | {
740 | if($this->parameters["product_id"] == null)
741 | {
742 | throw new SDKRuntimeException("缺少Native支付二维码链接必填参数product_id!"."
");
743 | }
744 | $this->parameters["appid"] = WxPayConf_pub::APPID;//公众账号ID
745 | $this->parameters["mch_id"] = WxPayConf_pub::MCHID;//商户号
746 | $time_stamp = time();
747 | $this->parameters["time_stamp"] = "$time_stamp";//时间戳
748 | $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
749 | $this->parameters["sign"] = $this->getSign($this->parameters);//签名
750 | $bizString = $this->formatBizQueryParaMap($this->parameters, false);
751 | $this->url = "weixin://wxpay/bizpayurl?".$bizString;
752 | }catch (SDKRuntimeException $e)
753 | {
754 | die($e->errorMessage());
755 | }
756 | }
757 |
758 | /**
759 | * 返回链接
760 | */
761 | function getUrl()
762 | {
763 | $this->createLink();
764 | return $this->url;
765 | }
766 | }
767 |
768 | /**
769 | * JSAPI支付——H5网页端调起支付接口
770 | */
771 | class JsApi_pub extends Common_util_pub
772 | {
773 | var $code;//code码,用以获取openid
774 | var $openid;//用户的openid
775 | var $parameters;//jsapi参数,格式为json
776 | var $prepay_id;//使用统一支付接口得到的预支付id
777 | var $curl_timeout;//curl超时时间
778 |
779 | function __construct()
780 | {
781 | //设置curl超时时间
782 | $this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
783 | }
784 |
785 | /**
786 | * 作用:生成可以获得code的url
787 | */
788 | function createOauthUrlForCode($redirectUrl)
789 | {
790 | $urlObj["appid"] = WxPayConf_pub::APPID;
791 | $urlObj["redirect_uri"] = urlencode($redirectUrl);
792 | $urlObj["response_type"] = "code";
793 | $urlObj["scope"] = "snsapi_base";
794 | $urlObj["state"] = "STATE"."#wechat_redirect";
795 | $bizString = $this->formatBizQueryParaMap($urlObj, false);
796 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
797 | }
798 |
799 | /**
800 | * 作用:生成可以获得openid的url
801 | */
802 | function createOauthUrlForOpenid()
803 | {
804 | $urlObj["appid"] = WxPayConf_pub::APPID;
805 | $urlObj["secret"] = WxPayConf_pub::APPSECRET;
806 | $urlObj["code"] = $this->code;
807 | $urlObj["grant_type"] = "authorization_code";
808 | $bizString = $this->formatBizQueryParaMap($urlObj, false);
809 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
810 | }
811 |
812 |
813 | /**
814 | * 作用:通过curl向微信提交code,以获取openid
815 | */
816 | function getOpenid()
817 | {
818 | $url = $this->createOauthUrlForOpenid();
819 | //初始化curl
820 | $ch = curl_init();
821 | //设置超时
822 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
823 | curl_setopt($ch, CURLOPT_URL, $url);
824 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
825 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
826 | curl_setopt($ch, CURLOPT_HEADER, FALSE);
827 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
828 | //运行curl,结果以jason形式返回
829 | $res = curl_exec($ch);
830 | curl_close($ch);
831 | //取出openid
832 | $data = json_decode($res,true);
833 | $this->openid = $data['openid'];
834 | return $this->openid;
835 | }
836 |
837 | /**
838 | * 作用:设置prepay_id
839 | */
840 | function setPrepayId($prepayId)
841 | {
842 | $this->prepay_id = $prepayId;
843 | }
844 |
845 | /**
846 | * 作用:设置code
847 | */
848 | function setCode($code_)
849 | {
850 | $this->code = $code_;
851 | }
852 |
853 | /**
854 | * 作用:设置jsapi的参数
855 | */
856 | public function getParameters()
857 | {
858 | $jsApiObj["appId"] = WxPayConf_pub::APPID;
859 | $timeStamp = time();
860 | $jsApiObj["timeStamp"] = "$timeStamp";
861 | $jsApiObj["nonceStr"] = $this->createNoncestr();
862 | $jsApiObj["package"] = "prepay_id=$this->prepay_id";
863 | $jsApiObj["signType"] = "MD5";
864 | $jsApiObj["paySign"] = $this->getSign($jsApiObj);
865 | $this->parameters = json_encode($jsApiObj);
866 |
867 | return $this->parameters;
868 | }
869 | }
870 |
871 | ?>
872 |
--------------------------------------------------------------------------------
/lib/demo.php:
--------------------------------------------------------------------------------
1 | 1407743423";
12 |
13 |
14 | $pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId);
15 | $encryptMsg = '';
16 | $errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg);
17 | if ($errCode == 0) {
18 | print("加密后: " . $encryptMsg . "\n");
19 | } else {
20 | print($errCode . "\n");
21 | }
22 |
23 | $xml_tree = new DOMDocument();
24 | $xml_tree->loadXML($encryptMsg);
25 | $array_e = $xml_tree->getElementsByTagName('Encrypt');
26 | $array_s = $xml_tree->getElementsByTagName('MsgSignature');
27 | $encrypt = $array_e->item(0)->nodeValue;
28 | $msg_sign = $array_s->item(0)->nodeValue;
29 |
30 | $format = "";
31 | $from_xml = sprintf($format, $encrypt);
32 |
33 | // 第三方收到公众号平台发送的消息
34 | $msg = '';
35 | $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
36 | if ($errCode == 0) {
37 | print("解密后: " . $msg . "\n");
38 | } else {
39 | print($errCode . "\n");
40 | }
41 |
--------------------------------------------------------------------------------
/lib/wechat.php:
--------------------------------------------------------------------------------
1 | signature = $_GET["signature"];
16 | $this->timestamp = $_GET["timestamp"];
17 | $this->nonce = $_GET["nonce"];
18 | $this->token = $token;
19 | $this->msg_signature = $_GET["msg_signature"];
20 | $this->auth() || exit;
21 | if (strtolower($_SERVER['REQUEST_METHOD']) == 'get') {
22 | echo($_GET['echostr']);
23 | exit;
24 | }else {
25 | //可根据token参数获取相应的加密密钥、AppID等参数
26 | $encodingAESKey = 'D0o8aAIZNfxWJ2EeGmKheadc9Vpe8OZZ8YcbXtLqPUW'; //EncodingAESKey(消息加密密钥)
27 | $appId = 'wx22f0f5085f846137'; //AppID(微信公众号或者小程序ID)
28 |
29 | $this->msgcrypt = new WXBizMsgCrypt($this->token, $encodingAESKey, $appId);
30 | $xml = $GLOBALS["HTTP_RAW_POST_DATA"];
31 | if(empty($xml)) {
32 | $xml = file_get_contents("php://input");
33 | }
34 | if(isset($_GET['encrypt_type']) && strtolower($_GET['encrypt_type']) == 'aes' ){
35 | //有加密信息
36 | $decryptMsg = $this->decryptMsg($xml);
37 | if($decryptMsg === false){
38 | //TODO: 解密失败
39 | }else{
40 | $xml = $decryptMsg;
41 | $this->hasAES = true;
42 | }
43 | }
44 | $this->data = (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
45 | }
46 | }
47 |
48 | /**
49 | * 作用:产生随机字符串,不长于32位
50 | */
51 | public function createNoncestr( $length = 32 ) {
52 | $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
53 | $str ="";
54 | for ( $i = 0; $i < $length; $i++ ) {
55 | $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
56 | }
57 | return $str;
58 | }
59 |
60 | /**
61 | * [encryptMsg 加密消息]
62 | * @param [type] $xml [待加密消息XML格式]
63 | * @param [type] $encodingAesKey [43位的encodingAesKey]
64 | * @param [type] $token [token]
65 | * @param [type] $appId [公众号APPID]
66 | * @return [type] [false标识解密失败,否则为加密后的字符串]
67 | */
68 | private function encryptMsg($xml) {
69 | $timestamp = time();
70 | $nonce = $this->createNoncestr(9);
71 | $encryptMsg = '';
72 | $errCode = $this->msgcrypt->encryptMsg($xml, $timestamp, $nonce, $encryptMsg);
73 | if ($errCode == 0) {
74 | return $encryptMsg;
75 | } else {
76 | return false;
77 | }
78 | }
79 | /**
80 | * [decryptMsg 解密消息体]
81 | * @param [type] $encryptMsg [加密的消息体]
82 | * @return [type] [false =>解密失败,否则为解密后的消息]
83 | */
84 | private function decryptMsg($encryptMsg) {
85 | $xml_tree = new DOMDocument();
86 | $xml_tree->loadXML($encryptMsg);
87 | $array_e = $xml_tree->getElementsByTagName('Encrypt');
88 | //$array_s = $xml_tree->getElementsByTagName('MsgSignature');
89 | $encrypt = $array_e->item(0)->nodeValue;
90 | //$msg_sign = $array_s->item(0)->nodeValue;
91 |
92 | $format = "";
93 | $from_xml = sprintf($format, $encrypt);
94 |
95 | // 第三方收到公众号平台发送的消息
96 | $msg = '';
97 | $errCode = $this->msgcrypt->decryptMsg($this->msg_signature, $this->timestamp, $this->nonce, $from_xml, $msg);
98 | if ($errCode == 0) {
99 | return $msg;
100 | } else {
101 | return false;
102 | }
103 | }
104 |
105 | /**
106 | * 获取微信推送的数据
107 | * @return array 转换为数组后的数据
108 | */
109 | public function request() {
110 | return $this->data;
111 | }
112 | /**
113 | * * 响应微信发送的信息(自动回复)
114 | * @param string $to 接收用户名
115 | * @param string $from 发送者用户名
116 | * @param array $content 回复信息,文本信息为string类型
117 | * @param string $type 消息类型
118 | * @param string $flag 是否新标刚接受到的信息
119 | * @return string XML字符串
120 | */
121 | public function response($content, $type = 'text', $flag = 0) {
122 | $this->data = array('ToUserName' => $this->data['FromUserName'], 'FromUserName' => $this->data['ToUserName'], 'CreateTime' => time(), 'MsgType' => $type);
123 | /* 添加类型数据 */
124 | $this->$type($content);
125 | /* 添加状态 */
126 | $this->data['FuncFlag'] = $flag;
127 | /* 转换数据为XML */
128 | $xml = new SimpleXMLElement('');
129 | $this->data2xml($xml, $this->data);
130 | if($this->hasAES){
131 | exit($this->encryptMsg($xml->asXML()));
132 | }else{
133 | exit($xml->asXML());
134 | }
135 | }
136 | /**
137 | * 回复文本信息
138 | * @param string $content 要回复的信息
139 | */
140 | private function text($content) {
141 | $this->data['Content'] = $content;
142 | }
143 | /**
144 | * 回复音乐信息
145 | * @param string $content 要回复的音乐
146 | */
147 | private function music($music) {
148 | list($music['Title'], $music['Description'], $music['MusicUrl'], $music['HQMusicUrl']) = $music;
149 | $this->data['Music'] = $music;
150 | }
151 | /**
152 | * 回复图文信息
153 | * @param string $news 要回复的图文内容
154 | */
155 | private function news($news) {
156 | $articles = array();
157 | foreach ($news as $key => $value) {
158 | list($articles[$key]['Title'], $articles[$key]['Description'], $articles[$key]['PicUrl'], $articles[$key]['Url']) = $value;
159 | if ($key >= 9) {
160 | break;
161 | }//最多只允许10调新闻
162 | }
163 | $this->data['ArticleCount'] = count($articles);
164 | $this->data['Articles'] = $articles;
165 | }
166 | private function transfer_customer_service($content) {
167 | $this->data['Content'] = '';
168 | }
169 | private function data2xml($xml, $data, $item = 'item') {
170 | foreach ($data as $key => $value) {
171 | /* 指定默认的数字key */
172 | is_numeric($key) && $key = $item;
173 | /* 添加子元素 */
174 | if (is_array($value) || is_object($value)) {
175 | $child = $xml->addChild($key);
176 | $this->data2xml($child, $value, $item);
177 | }else {
178 | if (is_numeric($value)) {
179 | $child = $xml->addChild($key, $value);
180 | }else {
181 | $child = $xml->addChild($key);
182 | $node = dom_import_simplexml($child);
183 | $node->appendChild($node->ownerDocument->createCDATASection($value));
184 | }
185 | }
186 | }
187 | }
188 | private function auth() {
189 | $tmpArr = array($this->token, $this->timestamp, $this->nonce);
190 | sort($tmpArr, SORT_STRING);
191 | $tmpStr = implode($tmpArr);
192 | $tmpStr = sha1($tmpStr);
193 | if (trim($tmpStr) == trim($this->signature)) {
194 | return true;
195 | }else {
196 | return false;
197 | }
198 | }
199 | }
--------------------------------------------------------------------------------
/lib/wxBizMsgCrypt.php:
--------------------------------------------------------------------------------
1 |
5 | * -40001: 签名验证错误
6 | * -40002: xml解析失败
7 | * -40003: sha加密生成签名失败
8 | * -40004: encodingAesKey 非法
9 | * -40005: appid 校验错误
10 | * -40006: aes 加密失败
11 | * -40007: aes 解密失败
12 | * -40008: 解密后得到的buffer非法
13 | * -40009: base64加密失败
14 | * -40010: base64解密失败
15 | * -40011: 生成xml失败
16 | *
17 | */
18 | class ErrorCode
19 | {
20 | public static $OK = 0;
21 | public static $ValidateSignatureError = -40001;
22 | public static $ParseXmlError = -40002;
23 | public static $ComputeSignatureError = -40003;
24 | public static $IllegalAesKey = -40004;
25 | public static $ValidateAppidError = -40005;
26 | public static $EncryptAESError = -40006;
27 | public static $DecryptAESError = -40007;
28 | public static $IllegalBuffer = -40008;
29 | public static $EncodeBase64Error = -40009;
30 | public static $DecodeBase64Error = -40010;
31 | public static $GenReturnXmlError = -40011;
32 | }
33 |
34 | /**
35 | * SHA1 class
36 | *
37 | * 计算公众平台的消息签名接口.
38 | */
39 | class SHA1
40 | {
41 | /**
42 | * 用SHA1算法生成安全签名
43 | * @param string $token 票据
44 | * @param string $timestamp 时间戳
45 | * @param string $nonce 随机字符串
46 | * @param string $encrypt 密文消息
47 | */
48 | public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)
49 | {
50 | //排序
51 | try {
52 | $array = array($encrypt_msg, $token, $timestamp, $nonce);
53 | sort($array, SORT_STRING);
54 | $str = implode($array);
55 | return array(ErrorCode::$OK, sha1($str));
56 | } catch (Exception $e) {
57 | //print $e . "\n";
58 | return array(ErrorCode::$ComputeSignatureError, null);
59 | }
60 | }
61 |
62 | }
63 |
64 | /**
65 | * XMLParse class
66 | *
67 | * 提供提取消息格式中的密文及生成回复消息格式的接口.
68 | */
69 | class XMLParse
70 | {
71 |
72 | /**
73 | * 提取出xml数据包中的加密消息
74 | * @param string $xmltext 待提取的xml字符串
75 | * @return string 提取出的加密消息字符串
76 | */
77 | public function extract($xmltext)
78 | {
79 | try {
80 | $xml = new DOMDocument();
81 | $xml->loadXML($xmltext);
82 | $array_e = $xml->getElementsByTagName('Encrypt');
83 | $array_a = $xml->getElementsByTagName('ToUserName');
84 | $encrypt = $array_e->item(0)->nodeValue;
85 | $tousername = $array_a->item(0)->nodeValue;
86 | return array(0, $encrypt, $tousername);
87 | } catch (Exception $e) {
88 | //print $e . "\n";
89 | return array(ErrorCode::$ParseXmlError, null, null);
90 | }
91 | }
92 |
93 | /**
94 | * 生成xml消息
95 | * @param string $encrypt 加密后的消息密文
96 | * @param string $signature 安全签名
97 | * @param string $timestamp 时间戳
98 | * @param string $nonce 随机字符串
99 | */
100 | public function generate($encrypt, $signature, $timestamp, $nonce)
101 | {
102 | $format = "
103 |
104 |
105 | %s
106 |
107 | ";
108 | return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
109 | }
110 |
111 | }
112 |
113 |
114 | /**
115 | * PKCS7Encoder class
116 | *
117 | * 提供基于PKCS7算法的加解密接口.
118 | */
119 | class PKCS7Encoder
120 | {
121 | public static $block_size = 32;
122 |
123 | /**
124 | * 对需要加密的明文进行填充补位
125 | * @param $text 需要进行填充补位操作的明文
126 | * @return 补齐明文字符串
127 | */
128 | function encode($text)
129 | {
130 | $block_size = PKCS7Encoder::$block_size;
131 | $text_length = strlen($text);
132 | //计算需要填充的位数
133 | $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
134 | if ($amount_to_pad == 0) {
135 | $amount_to_pad = PKCS7Encoder::block_size;
136 | }
137 | //获得补位所用的字符
138 | $pad_chr = chr($amount_to_pad);
139 | $tmp = "";
140 | for ($index = 0; $index < $amount_to_pad; $index++) {
141 | $tmp .= $pad_chr;
142 | }
143 | return $text . $tmp;
144 | }
145 |
146 | /**
147 | * 对解密后的明文进行补位删除
148 | * @param decrypted 解密后的明文
149 | * @return 删除填充补位后的明文
150 | */
151 | function decode($text)
152 | {
153 |
154 | $pad = ord(substr($text, -1));
155 | if ($pad < 1 || $pad > 32) {
156 | $pad = 0;
157 | }
158 | return substr($text, 0, (strlen($text) - $pad));
159 | }
160 |
161 | }
162 |
163 | /**
164 | * Prpcrypt class
165 | *
166 | * 提供接收和推送给公众平台消息的加解密接口.
167 | */
168 | class Prpcrypt
169 | {
170 | public $key;
171 |
172 | function Prpcrypt($k)
173 | {
174 | $this->key = base64_decode($k . "=");
175 | }
176 |
177 | /**
178 | * 对明文进行加密
179 | * @param string $text 需要加密的明文
180 | * @return string 加密后的密文
181 | */
182 | public function encrypt($text, $appid)
183 | {
184 |
185 | try {
186 | //获得16位随机字符串,填充到明文之前
187 | $random = $this->getRandomStr();
188 | $text = $random . pack("N", strlen($text)) . $text . $appid;
189 | // 网络字节序
190 | $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
191 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
192 | $iv = substr($this->key, 0, 16);
193 | //使用自定义的填充方式对明文进行补位填充
194 | $pkc_encoder = new PKCS7Encoder;
195 | $text = $pkc_encoder->encode($text);
196 | mcrypt_generic_init($module, $this->key, $iv);
197 | //加密
198 | $encrypted = mcrypt_generic($module, $text);
199 | mcrypt_generic_deinit($module);
200 | mcrypt_module_close($module);
201 |
202 | //print(base64_encode($encrypted));
203 | //使用BASE64对加密后的字符串进行编码
204 | return array(ErrorCode::$OK, base64_encode($encrypted));
205 | } catch (Exception $e) {
206 | //print $e;
207 | return array(ErrorCode::$EncryptAESError, null);
208 | }
209 | }
210 |
211 | /**
212 | * 对密文进行解密
213 | * @param string $encrypted 需要解密的密文
214 | * @return string 解密得到的明文
215 | */
216 | public function decrypt($encrypted, $appid)
217 | {
218 |
219 | try {
220 | //使用BASE64对需要解密的字符串进行解码
221 | $ciphertext_dec = base64_decode($encrypted);
222 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
223 | $iv = substr($this->key, 0, 16);
224 | mcrypt_generic_init($module, $this->key, $iv);
225 |
226 | //解密
227 | $decrypted = mdecrypt_generic($module, $ciphertext_dec);
228 | mcrypt_generic_deinit($module);
229 | mcrypt_module_close($module);
230 | } catch (Exception $e) {
231 | return array(ErrorCode::$DecryptAESError, null);
232 | }
233 |
234 |
235 | try {
236 | //去除补位字符
237 | $pkc_encoder = new PKCS7Encoder;
238 | $result = $pkc_encoder->decode($decrypted);
239 | //去除16位随机字符串,网络字节序和AppId
240 | if (strlen($result) < 16)
241 | return "";
242 | $content = substr($result, 16, strlen($result));
243 | $len_list = unpack("N", substr($content, 0, 4));
244 | $xml_len = $len_list[1];
245 | $xml_content = substr($content, 4, $xml_len);
246 | $from_appid = substr($content, $xml_len + 4);
247 | } catch (Exception $e) {
248 | //print $e;
249 | return array(ErrorCode::$IllegalBuffer, null);
250 | }
251 | if ($from_appid != $appid)
252 | return array(ErrorCode::$ValidateAppidError, null);
253 | return array(0, $xml_content);
254 |
255 | }
256 |
257 |
258 | /**
259 | * 随机生成16位字符串
260 | * @return string 生成的字符串
261 | */
262 | function getRandomStr()
263 | {
264 |
265 | $str = "";
266 | $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
267 | $max = strlen($str_pol) - 1;
268 | for ($i = 0; $i < 16; $i++) {
269 | $str .= $str_pol[mt_rand(0, $max)];
270 | }
271 | return $str;
272 | }
273 |
274 | }
275 |
276 | /**
277 | * 1.第三方回复加密消息给公众平台;
278 | * 2.第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。
279 | */
280 | class WXBizMsgCrypt
281 | {
282 | private $token;
283 | private $encodingAesKey;
284 | private $appId;
285 |
286 | /**
287 | * 构造函数
288 | * @param $token string 公众平台上,开发者设置的token
289 | * @param $encodingAesKey string 公众平台上,开发者设置的EncodingAESKey
290 | * @param $appId string 公众平台的appId
291 | */
292 | public function WXBizMsgCrypt($token, $encodingAesKey, $appId)
293 | {
294 | $this->token = $token;
295 | $this->encodingAesKey = $encodingAesKey;
296 | $this->appId = $appId;
297 | }
298 |
299 | /**
300 | * 将公众平台回复用户的消息加密打包.
301 | *
302 | * - 对要发送的消息进行AES-CBC加密
303 | * - 生成安全签名
304 | * - 将消息密文和安全签名打包成xml格式
305 | *
306 | *
307 | * @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串
308 | * @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp
309 | * @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce
310 | * @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
311 | * 当return返回0时有效
312 | *
313 | * @return int 成功0,失败返回对应的错误码
314 | */
315 | public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg)
316 | {
317 | $pc = new Prpcrypt($this->encodingAesKey);
318 |
319 | //加密
320 | $array = $pc->encrypt($replyMsg, $this->appId);
321 | $ret = $array[0];
322 | if ($ret != 0) {
323 | return $ret;
324 | }
325 |
326 | if ($timeStamp == null) {
327 | $timeStamp = time();
328 | }
329 | $encrypt = $array[1];
330 |
331 | //生成安全签名
332 | $sha1 = new SHA1;
333 | $array = $sha1->getSHA1($this->token, $timeStamp, $nonce, $encrypt);
334 | $ret = $array[0];
335 | if ($ret != 0) {
336 | return $ret;
337 | }
338 | $signature = $array[1];
339 |
340 | //生成发送的xml
341 | $xmlparse = new XMLParse;
342 | $encryptMsg = $xmlparse->generate($encrypt, $signature, $timeStamp, $nonce);
343 | return ErrorCode::$OK;
344 | }
345 |
346 |
347 | /**
348 | * 检验消息的真实性,并且获取解密后的明文.
349 | *
350 | * - 利用收到的密文生成安全签名,进行签名验证
351 | * - 若验证通过,则提取xml中的加密消息
352 | * - 对消息进行解密
353 | *
354 | *
355 | * @param $msgSignature string 签名串,对应URL参数的msg_signature
356 | * @param $timestamp string 时间戳 对应URL参数的timestamp
357 | * @param $nonce string 随机串,对应URL参数的nonce
358 | * @param $postData string 密文,对应POST请求的数据
359 | * @param &$msg string 解密后的原文,当return返回0时有效
360 | *
361 | * @return int 成功0,失败返回对应的错误码
362 | */
363 | public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg)
364 | {
365 | if (strlen($this->encodingAesKey) != 43) {
366 | return ErrorCode::$IllegalAesKey;
367 | }
368 |
369 | $pc = new Prpcrypt($this->encodingAesKey);
370 |
371 | //提取密文
372 | $xmlparse = new XMLParse;
373 | $array = $xmlparse->extract($postData);
374 | $ret = $array[0];
375 |
376 | if ($ret != 0) {
377 | return $ret;
378 | }
379 |
380 | if ($timestamp == null) {
381 | $timestamp = time();
382 | }
383 |
384 | $encrypt = $array[1];
385 | $touser_name = $array[2];
386 |
387 | //验证安全签名
388 | $sha1 = new SHA1;
389 | $array = $sha1->getSHA1($this->token, $timestamp, $nonce, $encrypt);
390 | $ret = $array[0];
391 |
392 | if ($ret != 0) {
393 | return $ret;
394 | }
395 |
396 | $signature = $array[1];
397 | if ($signature != $msgSignature) {
398 | return ErrorCode::$ValidateSignatureError;
399 | }
400 |
401 | $result = $pc->decrypt($encrypt, $this->appId);
402 | if ($result[0] != 0) {
403 | return $result[0];
404 | }
405 | $msg = $result[1];
406 |
407 | return ErrorCode::$OK;
408 | }
409 |
410 | }
411 |
412 |
--------------------------------------------------------------------------------
/lib/wxapi.php:
--------------------------------------------------------------------------------
1 | token = trim($arg);
38 | }
39 | }
40 | file_put_contents('/tmp/wxmini', date('Y-m-d H:i:s') . print_r($rt, 1).PHP_EOL, FILE_APPEND);
41 | if (!class_exists('SimpleXMLElement')){
42 | file_put_contents('/tmp/wxmini', date('Y-m-d H:i:s') . '-SimpleXMLElement class not exist'.PHP_EOL, FILE_APPEND);
43 | }
44 | if (!function_exists('dom_import_simplexml')){
45 | file_put_contents('/tmp/wxmini', date('Y-m-d H:i:s') . '-dom_import_simplexml function not exist'.PHP_EOL, FILE_APPEND);
46 | }
47 | if(!preg_match("/^[0-9a-zA-Z]{3,42}$/", $this->token)){
48 | file_put_contents('/tmp/wxmini', date('Y-m-d H:i:s') . '-error token, only support characters and number'.PHP_EOL, FILE_APPEND);
49 | }
50 | $this->weixin = new Wechat($this->token);
51 | $this->data = $this->weixin->request();
52 | if ($this->data) {
53 | list($content, $type) = $this->reply($this->data);
54 | if($type){
55 | $this->weixin->response($content, $type);
56 | }
57 | }
58 | }
59 |
60 | private function reply($data){
61 | file_put_contents('/tmp/wechat', date('Y-m-d H:i:s').'-'.json_encode($data).PHP_EOL, FILE_APPEND);
62 | if (isset($data['Event'])) {
63 | $event = strtolower($data['Event']);
64 | switch ($event) {
65 | case 'subscribe': //不带参数/带参数的二维码
66 | break;
67 | case 'unsubscribe'://取消关注
68 | break;
69 | case 'scan'://带参数的二维码
70 | break;
71 | case 'location'://自动获取位置回复
72 | break;
73 | case 'templatesendjobfinish':
74 | break;
75 | case 'masssendjobfinish': //群发
76 | break;
77 | case 'click'://自定义菜单
78 | break;
79 | default:
80 | break;
81 | }
82 | }else if (isset($data['MsgType'])) {
83 | //消息转发到客服
84 | return array('turn on transfer_customer_service', 'transfer_customer_service');
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | var app = getApp()
4 | Page({
5 | data: {
6 | motto: '微信小程序支付',
7 | userInfo: {},
8 | },
9 | onLoad: function () {//生命周期函数--监听页面加载
10 | var that = this
11 | //调用应用实例的方法获取全局数据
12 | app.getUserInfo(function (userInfo) {
13 | //更新数据
14 | that.setData({
15 | userInfo: userInfo
16 | })
17 | });
18 | //登陆获取code
19 | var openid = wx.getStorageSync('openid') || '';
20 | if (!openid) {
21 | wx.login({
22 | success: function (res) {
23 | console.log(res);
24 | that.getOpenId(res.code);
25 | }
26 | });
27 | }
28 | },
29 | onShareAppMessage: function () {
30 | return {
31 | title: '微信小程序支付',
32 | path: 'pages/index/index',
33 | success: function (res) {
34 | // 分享成功
35 | },
36 | fail: function (res) {
37 | // 分享失败
38 | }
39 | }
40 | },
41 | //事件处理函数
42 | bindViewTap: function() {
43 | wx.navigateTo({
44 | url: '../logs/logs'
45 | })
46 | },
47 | showInfo: function (msg) {
48 | wx.showModal({
49 | title: '提示',
50 | showCancel : false,
51 | content: msg
52 | });
53 | },
54 | getOpenId: function (code) {
55 | var that = this;
56 | wx.request({
57 | url: app.globalData.url + 'wxmini.php',
58 | data: {
59 | code: code,
60 | type: 'openid'
61 | },
62 | header: {
63 | 'content-type': 'application/x-www-form-urlencoded'
64 | },
65 | method: 'POST',
66 | success: function (res) {
67 | console.log(res);
68 | if (res.data.resultCode != 0) {
69 | that.showInfo(res.data.errMsg);
70 | return;
71 | }
72 | wx.setStorageSync('openid', res.data.openid);
73 | },
74 | fail: function () {
75 | // fail
76 | },
77 | complete: function (openid) {
78 | // complete
79 | }
80 | });
81 | },
82 | wxpay: function () {
83 | var openid = wx.getStorageSync('openid');
84 | if(!openid){
85 | that.showInfo('获取openid信息失败');
86 | return;
87 | }
88 | this.generateOrder(openid);
89 | },
90 | generateOrder: function (openid) {
91 | var that = this;
92 | wx.request({
93 | url: app.globalData.url + 'wxmini.php',
94 | data: {
95 | type: 'pay',
96 | openid : openid
97 | },
98 | header: {
99 | 'content-type': 'application/x-www-form-urlencoded'
100 | },
101 | method: 'POST',
102 | success: function (res) {
103 | console.log(res);
104 | if (res.data.resultCode != 0) {
105 | that.showInfo(res.data.errMsg);
106 | return;
107 | }
108 | that.pay(res.data.params);
109 | },
110 | fail: function () {
111 | // fail
112 | },
113 | complete: function () {
114 | // complete
115 | }
116 | })
117 | },
118 | pay: function (param) {
119 | var that = this;
120 | wx.requestPayment({
121 | 'timeStamp': param.timestamp,
122 | 'nonceStr': param.nonce_str,
123 | 'package': param.package,
124 | 'signType': param.sign_type,
125 | 'paySign': param.pay_sign,
126 | success: function (res) {
127 | // success
128 | console.log(res);
129 | that.showInfo('支付成功');
130 | },
131 | fail: function (res) {
132 | // fail
133 | console.log(res);
134 | var strMsg = res.errMsg;
135 | if (res.err_desc){
136 | strMsg += ', ' + res.err_desc;
137 | }
138 | that.showInfo(strMsg);
139 | },
140 | complete: function () {
141 | // complete
142 | console.log("pay complete");
143 | }
144 | });
145 | },
146 | send_temp : function(e){
147 | var that = this;
148 | var openid = wx.getStorageSync('openid');
149 | if (!openid) {
150 | that.showInfo('获取openid信息失败');
151 | return;
152 | }
153 | wx.request({
154 | url: app.globalData.url + 'wxmini.php',
155 | data: {
156 | 'form_id': e.detail.formId,
157 | 'openid': openid,
158 | type: 'send',
159 | },
160 | header: {
161 | 'content-type': 'application/x-www-form-urlencoded'
162 | },
163 | method: 'POST',
164 | success: function(res){
165 | // success
166 | console.log(res);
167 | if (res.data.resultCode != 0){
168 | that.showInfo(res.data.errMsg);
169 | return;
170 | }
171 | that.showInfo('发送成功');
172 | // console.log(e.detail.formId);
173 | },
174 | fail: function(err) {
175 | // fail
176 | console.log('失败');
177 | console.log(res);
178 | },
179 | complete: function() {
180 | // complete
181 | }
182 | });
183 | }
184 | })
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{userInfo.nickName}}
6 |
7 |
8 | 价格:¥0.01元
9 |
10 |
11 |
12 | {{motto}}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .userinfo {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | .userinfo-avatar {
9 | width: 128rpx;
10 | height: 128rpx;
11 | margin: 20rpx;
12 | border-radius: 50%;
13 | }
14 |
15 | .demo {
16 | margin-top: 60px;
17 | width: 100px;
18 | }
19 | .demo .price{
20 | border-bottom:1px dashed #C9C9C9;
21 | padding-bottom:10px;
22 | color:#666666;
23 | font-size:12px;
24 | }
25 |
26 | .demo .price strong{
27 | font-weight:normal;
28 | color:#EE6209;
29 | font-size:16px;
30 | font-family:Helvetica;
31 | }
32 |
33 | .userinfo-nickname {
34 | color: #aaa;
35 | }
36 |
37 | .usermotto {
38 | margin-top: 120px;
39 | }
--------------------------------------------------------------------------------
/pages/logs/logs.js:
--------------------------------------------------------------------------------
1 | //logs.js
2 | var util = require('../../utils/util.js')
3 | Page({
4 | data: {
5 | logs: []
6 | },
7 | onLoad: function () {
8 | this.setData({
9 | logs: (wx.getStorageSync('logs') || []).map(function (log) {
10 | return util.formatTime(new Date(log))
11 | })
12 | })
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/pages/logs/logs.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "查看启动日志"
3 | }
--------------------------------------------------------------------------------
/pages/logs/logs.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{index + 1}}. {{log}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pages/logs/logs.wxss:
--------------------------------------------------------------------------------
1 | .log-list {
2 | display: flex;
3 | flex-direction: column;
4 | padding: 40rpx;
5 | }
6 | .log-item {
7 | margin: 10rpx;
8 | }
9 |
--------------------------------------------------------------------------------
/utils/util.js:
--------------------------------------------------------------------------------
1 | function formatTime(date) {
2 | var year = date.getFullYear()
3 | var month = date.getMonth() + 1
4 | var day = date.getDate()
5 |
6 | var hour = date.getHours()
7 | var minute = date.getMinutes()
8 | var second = date.getSeconds()
9 |
10 |
11 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
12 | }
13 |
14 | function formatNumber(n) {
15 | n = n.toString()
16 | return n[1] ? n : '0' + n
17 | }
18 |
19 | module.exports = {
20 | formatTime: formatTime
21 | }
22 |
--------------------------------------------------------------------------------
/wx.php:
--------------------------------------------------------------------------------
1 | index();
--------------------------------------------------------------------------------
/wxmini.php:
--------------------------------------------------------------------------------
1 | 1);
15 | $type = isset($_POST['type']) ? trim($_POST['type']) : '';
16 | switch($type){
17 | case 'openid':
18 | //小程序的appid和appsecret
19 | $appid = 'wx22f0f5085f846137';
20 | $appsecret = '3e5f684e2948de6f74b487386692940a';
21 | $code = isset($_POST['code']) ? trim($_POST['code']) : '';
22 | if(empty($code)){
23 | $ret['errMsg'] = '登录凭证code获取失败';
24 | exit(json_encode($ret));
25 | }
26 | $url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$appsecret&js_code=$code&grant_type=authorization_code";
27 |
28 | $json = json_decode(file_get_contents($url));
29 | if(isset($json->errcode) && $json->errcode){
30 | $ret['errMsg'] = $json->errcode.', '.$json->errmsg;
31 | exit(json_encode($ret));
32 | }
33 | $openid = $json->openid;
34 |
35 | $ret['resultCode'] = 0;
36 | $ret['openid'] = $openid;
37 | exit(json_encode($ret));
38 | break;
39 | case 'send':
40 | //小程序的appid和appsecret
41 | $appid = 'wx22f0f5085f846137';
42 | $appsecret = '3e5f684e2948de6f74b487386692940a';
43 | $access_token = '';
44 |
45 | $openid = isset($_POST['openid']) ? trim($_POST['openid']) : ''; //小程序的openid
46 | if(empty($openid)){
47 | $ret['errMsg'] = '却少参数openid';
48 | exit(json_encode($ret));
49 | }
50 | //表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
51 | $formid = isset($_POST['form_id']) ? trim($_POST['form_id']) : '';
52 | if(empty($formid)){
53 | $ret['errMsg'] = '却少参数form_id';
54 | exit(json_encode($ret));
55 | }
56 |
57 | //消息模板id
58 | $temp_id = 'eogEBS2i4VeS2rZfwda7kdkePLTcbmPm-wW1s7A0ky4';
59 | //获取access_token, 做缓存,expires_in:7200
60 | generate_token($access_token, $appid, $appsecret);
61 |
62 | $data['touser'] = $openid;
63 | $data['template_id'] = $temp_id;
64 | $data['page'] = 'pages/index/index'; //该字段不填则模板无跳转
65 | $data['form_id'] = $formid;
66 | $data['data'] = array(
67 | 'keyword1' => array('value' => '小程序模板消息测试'),
68 | 'keyword2' => array('value' => '100元'),
69 | 'keyword3' => array('value' => '支付成功'),
70 | 'keyword4' => array('value' => time() * 1000),
71 | 'keyword5' => array('value' => '微信支付'),
72 | 'keyword6' => array('value' => date('Y-m-d H:i:s')),
73 | );
74 | $data['emphasis_keyword'] = 'keyword5.DATA'; //模板需要放大的关键词,不填则默认无放大
75 |
76 | $send_url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=' . $access_token;
77 | $str = request($send_url, 'post', $data);
78 | $json = json_decode(request($send_url, 'post', $data));
79 | if(!$json){
80 | $ret['errMsg'] = $str;
81 | exit(json_encode($ret));
82 | }else if(isset($json->errcode) && $json->errcode){
83 | $ret['errMsg'] = $json->errcode.', '.$json->errmsg;
84 | exit(json_encode($ret));
85 | }
86 | $ret['resultCode'] = 0;
87 | exit(json_encode($ret));
88 | break;
89 | case 'pay':
90 | include_once('lib/WxPayPubHelper/WxPayPubHelper.php');
91 |
92 | $openid = isset($_POST['openid']) ? trim($_POST['openid']) : '';
93 | if(empty($openid)){
94 | $ret['errMsg'] = '缺少参数openid';
95 | exit(json_encode($ret));
96 | }
97 |
98 | $order = new UnifiedOrder_pub();
99 | $order->setParameter("openid", $openid);//商品描述
100 | $order->setParameter('out_trade_no', 'phpdemo' . $timestamp);
101 | $order->setParameter('total_fee', 1);
102 | $order->setParameter('trade_type', 'JSAPI');
103 | $order->setParameter('body', 'PHP微信小程序支付测试');
104 | $order->setParameter('notify_url', 'https://www.example.cn/a.php');
105 |
106 | $prepay_id = $order->getPrepayId();
107 | $jsApi = new JsApi_pub();
108 | $jsApi->setPrepayId($prepay_id);
109 | $jsApiParams = json_decode($jsApi->getParameters());
110 |
111 | $ret['resultCode'] = 0;
112 | $ret['params'] = array(
113 | 'appid' => $jsApiParams->appId,
114 | 'timestamp' => $jsApiParams->timeStamp,
115 | 'nonce_str' => $jsApiParams->nonceStr,
116 | 'sign_type' => $jsApiParams->signType,
117 | 'package' => $jsApiParams->package,
118 | 'pay_sign' => $jsApiParams->paySign,
119 | );
120 | exit(json_encode($ret));
121 | break;
122 | default :
123 | $ret['errMsg'] = 'No this type : ' . $type;
124 | exit(json_encode($ret));
125 | break;
126 | }
127 |
128 | function generate_token(&$access_token, $appid, $appsecret){
129 | $token_file = '/tmp/token';
130 | $general_token = true;
131 | if(file_exists($token_file) && ($info = json_decode(file_get_contents($token_file)))){
132 | if(time() < $info->create_time + $info->expires_in - 200){
133 | $general_token = false;
134 | $access_token = $info->access_token;
135 | }
136 | }
137 | if($general_token){
138 | new_access_token($access_token, $token_file, $appid, $appsecret);
139 | }
140 | }
141 |
142 | function new_access_token(&$access_token, $token_file, $appid, $appsecret){
143 | $token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$appsecret";
144 | $str = file_get_contents($token_url);
145 | $json = json_decode($str);
146 | if(isset($json->access_token)){
147 | $access_token = $json->access_token;
148 | file_put_contents($token_file, json_encode(array('access_token' => $access_token, 'expires_in' => $json->expires_in, 'create_time' => time())));
149 | }else{
150 | file_put_contents('/tmp/error', date('Y-m-d H:i:s').'-Get Access Token Error: '.print_r($json, 1).PHP_EOL, FILE_APPEND);
151 | }
152 | }
153 |
154 | function request($url, $method, array $data, $timeout = 30) {
155 | try {
156 | $ch = curl_init();
157 | /*支持SSL 不验证CA根验证*/
158 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
159 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
160 | /*重定向跟随*/
161 | if (ini_get('open_basedir') == '' && !ini_get('safe_mode')) {
162 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
163 | }
164 | curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
165 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
166 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
167 |
168 | //设置 CURLINFO_HEADER_OUT 选项之后 curl_getinfo 函数返回的数组将包含 cURL
169 | //请求的 header 信息。而要看到回应的 header 信息可以在 curl_setopt 中设置
170 | //CURLOPT_HEADER 选项为 true
171 | curl_setopt($ch, CURLOPT_HEADER, false);
172 | curl_setopt($ch, CURLINFO_HEADER_OUT, false);
173 |
174 | //fail the request if the HTTP code returned is equal to or larger than 400
175 | //curl_setopt($ch, CURLOPT_FAILONERROR, true);
176 | $header = array("Content-Type:application/json;charset=utf-8;", "Connection: keep-alive;");
177 | switch (strtolower($method)) {
178 | case "post":
179 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
180 | curl_setopt($ch, CURLOPT_POST, true);
181 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
182 | curl_setopt($ch, CURLOPT_URL, $url);
183 | break;
184 | case "put":
185 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
186 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
187 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
188 | curl_setopt($ch, CURLOPT_URL, $url);
189 | break;
190 | case "delete":
191 | curl_setopt($ch, CURLOPT_URL, $url.'?'.http_build_query($data));
192 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
193 | break;
194 | case "get":
195 | curl_setopt($ch, CURLOPT_URL, $url.'?'.http_build_query($data));
196 | break;
197 | case "new_get":
198 | curl_setopt($ch, CURLOPT_URL, $url."?para=".urlencode(json_encode($data)));
199 | break;
200 | default:
201 | throw new Exception('不支持的HTTP方式');
202 | break;
203 | }
204 | $result = curl_exec($ch);
205 | if (curl_errno($ch) > 0) {
206 | throw new Exception(curl_error($ch));
207 | }
208 | curl_close($ch);
209 | return $result;
210 | } catch (Exception $e) {
211 | return "CURL EXCEPTION: ".$e->getMessage();
212 | }
213 | }
--------------------------------------------------------------------------------