Completed
Push — master ( ec4bbc...f977bb )
by Wei
06:04
created

WechatPay::getNonceStr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * WechatPay
4
 *
5
 * @license MIT
6
 * @author zhangv
7
 */
8
namespace zhangv\wechat\pay;
9
10
use \Exception;
11
use zhangv\wechat\pay\util\HttpClient;
12
use zhangv\wechat\pay\util\WechatOAuth;
13
use zhangv\wechat\pay\cache\CacheProvider;
14
use zhangv\wechat\pay\cache\JsonFileCacheProvider;
15
16
/**
17
 * Class WechatPay
18
 * @package zhangv\wechat
19
 * @author zhangv
20
 * @license MIT
21
 *
22
 * @method static service\App       App(array $config)
23
 * @method static service\Jsapi     Jsapi(array $config)
24
 * @method static service\Micro     Micro(array $config)
25
 * @method static service\Mweb      Mweb(array $config)
26
 * @method static service\Native    Native(array $config)
27
 * @method static service\Weapp     Weapp(array $config)
28
 * @method static service\Mchpay    Mchpay(array $config)
29
 * @method static service\Redpack   Redpack(array $config)
30
 * @method static service\Coupon    Coupon(array $config)
31
 */
32
class WechatPay {
33
	const TRADETYPE_JSAPI = 'JSAPI',TRADETYPE_NATIVE = 'NATIVE',TRADETYPE_APP = 'APP',TRADETYPE_MWEB = 'MWEB';
34
	const SIGNTYPE_MD5 = 'MD5', SIGNTYPE_HMACSHA256 = 'HMAC-SHA256';
35
	const CHECKNAME_FORCECHECK = 'FORCE_CHECK',CHECKNAME_NOCHECK = 'NO_CHECK';
36
	const ACCOUNTTYPE_BASIC = 'Basic',ACCOUNTTYPE_OPERATION = 'Operation',ACCOUNTTYPE_FEES = 'Fees';
37
	const API_ENDPOINT = 'https://api.mch.weixin.qq.com/';
38
	/** 支付 */
39
	const URL_UNIFIEDORDER = 'pay/unifiedorder';
40
	const URL_ORDERQUERY = 'pay/orderquery';
41
	const URL_CLOSEORDER = 'pay/closeorder';
42
	const URL_REFUND = 'secapi/pay/refund';
43
	const URL_REFUNDQUERY = 'pay/refundquery';
44
	const URL_DOWNLOADBILL = 'pay/downloadbill';
45
	const URL_DOWNLOAD_FUND_FLOW = 'pay/downloadfundflow';
46
	const URL_REPORT = 'payitil/report';
47
	const URL_SHORTURL = 'tools/shorturl';
48
	const URL_MICROPAY = 'pay/micropay';
49
	const URL_BATCHQUERYCOMMENT = 'billcommentsp/batchquerycomment';
50
	const URL_REVERSE = 'secapi/pay/reverse';
51
	const URL_AUTHCODETOOPENID = 'tools/authcodetoopenid';
52
	/** 红包 */
53
	const URL_GETHBINFO = 'mmpaymkttransfers/gethbinfo';
54
	const URL_SENDREDPACK = 'mmpaymkttransfers/sendredpack';
55
	const URL_SENDGROUPREDPACK = 'mmpaymkttransfers/sendgroupredpack';
56
	/** 企业付款 */
57
	const URL_TRANSFER_WALLET = 'mmpaymkttransfers/promotion/transfers';
58
	const URL_QUERY_TRANSFER_WALLET = 'mmpaymkttransfers/gettransferinfo';
59
	const URL_TRANSFER_BANKCARD = 'mmpaysptrans/pay_bank';
60
	const URL_QUERY_TRANSFER_BANKCARD = 'mmpaysptrans/query_bank';
61
	/** 代金券 */
62
	const URL_SEND_COUPON = 'mmpaymkttransfers/send_coupon';
63
	const URL_QUERY_COUPON_STOCK = 'mmpaymkttransfers/query_coupon_stock';
64
	const URL_QUERY_COUPON_INFO = 'mmpaymkttransfers/querycouponsinfo';
65
	/** Sandbox获取测试公钥 */
66
	const URL_GETPUBLICKEY = 'https://fraud.mch.weixin.qq.com/risk/getpublickey';
67
	public static $BANKCODE = [
68
		'工商银行' => '1002', '农业银行' => '1005', '中国银行' => '1026', '建设银行' => '1003', '招商银行' => '1001',
69
		'邮储银行' => '1066', '交通银行' => '1020', '浦发银行' => '1004', '民生银行' => '1006', '兴业银行' => '1009',
70
		'平安银行' => '1010', '中信银行' => '1021', '华夏银行' => '1025', '广发银行' => '1027', '光大银行' => '1022',
71
		'北京银行' => '1032', '宁波银行' => '1056',
72
	];
73
74
	public $getSignKeyUrl = "sandboxnew/pay/getsignkey";
75
	public $sandbox = false;
76
77
	/** @var string */
78
	public $returnCode;
79
	/** @var string */
80
	public $returnMsg;
81
	/** @var string */
82
	public $resultCode;
83
	/** @var string */
84
	public $errCode;
85
	/** @var string */
86
	public $errCodeDes;
87
	/** @var string */
88
	public $requestXML = null;
89
	/** @var string */
90
	public $responseXML = null;
91
	/** @var array */
92
	public $requestArray = null;
93
	/** @var array */
94
	public $responseArray = null;
95
	/** @var array */
96
	protected $config;
97
	/** @var HttpClient */
98
	protected $httpClient = null;
99
	/** @var WechatOAuth */
100
	protected $wechatOAuth = null;
101
	/** @var string */
102
	public $publicKey = null;
103
	/** @var CacheProvider */
104
	public $cacheProvider = null;
105
106
	/**
107
	 * @param $config array 配置
108
	 */
109 54
	public function __construct(array $config) {
110 54
		$this->config = $config;
111 54
		$this->httpClient = new HttpClient(5);
112 54
		$this->cacheProvider = new JsonFileCacheProvider();
113 54
	}
114
115
	/**
116
	 * @param string $name
117
	 * @param string $config
118
	 * @return mixed
119
	 */
120 30
	private static function load($name, $config) {
121 30
		$service = __NAMESPACE__ . "\\service\\{$name}";
122 30
		return new $service($config);
123
	}
124
125
	/**
126
	 * @param string $name
127
	 * @param array  $config
128
	 *
129
	 * @return mixed
130
	 */
131 30
	public static function __callStatic($name, $config) {
132 30
		return self::load($name, ...$config);
0 ignored issues
show
Bug introduced by
$config is expanded, but the parameter $config of zhangv\wechat\pay\WechatPay::load() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
		return self::load($name, /** @scrutinizer ignore-type */ ...$config);
Loading history...
133
	}
134
135 1
	public function setWechatOAuth($wechatOAuth){
136 1
		$this->wechatOAuth = $wechatOAuth;
137 1
	}
138
139 1
	public function getWechatOAuth(){
140 1
		if(!$this->wechatOAuth){
141 1
			$this->wechatOAuth = new WechatOAuth($this->config['app_id'],$this->config['app_secret']);
142
		}
143 1
		return $this->wechatOAuth;
144
	}
145
146 1
	public function setConfig($config){
147 1
		$this->config = $config;
148 1
	}
149
150 3
	public function getConfig(){
151 3
		return $this->config;
152
	}
153
154 39
	public function setHttpClient($httpClient){
155 39
		$this->httpClient = $httpClient;
156 39
	}
157
158 54
	public function setCacheProvider($cacheProvider){
159 54
		$this->cacheProvider = $cacheProvider;
160 54
	}
161
162 1
	public function getCacheProvider(){
163 1
		return $this->cacheProvider;
164
	}
165
166
	/**
167
	 * 统一下单接口
168
	 * @link https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
169
	 * @param array $params
170
	 * @throws Exception
171
	 * @return array
172
	 */
173 8
	public function unifiedOrder($params) {
174 8
		$data = array();
175 8
		$data["appid"] = $this->config["app_id"];
176 8
		$data["device_info"] = (isset($params['device_info'])&&trim($params['device_info'])!='')?$params['device_info']:null;
177 8
		$data["body"] = $params['body'];
178 8
		$data["detail"] = isset($params['detail'])?$params['detail']:null;//optional
179 8
		$data["attach"] = isset($params['attach'])?$params['attach']:null;//optional
180 8
		$data["out_trade_no"] = isset($params['out_trade_no'])?$params['out_trade_no']:null;
181 8
		$data["fee_type"] = isset($params['fee_type'])?$params['fee_type']:'CNY';
182 8
		$data["total_fee"]    = $params['total_fee'];
183 8
		$data["spbill_create_ip"] = $params['spbill_create_ip'];
184 8
		$data["time_start"] = isset($params['time_start'])?$params['time_start']:null;//optional
185 8
		$data["time_expire"] = isset($params['time_expire'])?$params['time_expire']:null;//optional
186 8
		$data["goods_tag"] = isset($params['goods_tag'])?$params['goods_tag']:null;
187 8
		$data["notify_url"] = $this->config["notify_url"];
188 8
		$data["trade_type"] = $params['trade_type'];
189 8
		if($params['trade_type'] == WechatPay::TRADETYPE_NATIVE){
190 1
			if(!isset($params['product_id'])) throw new Exception('product_id is required when trade_type is NATIVE');
191 1
			$data["product_id"] = $params['product_id'];
192
		}
193 8
		if($params['trade_type'] == WechatPay::TRADETYPE_JSAPI){
194 5
			if(!isset($params['openid'])) throw new Exception('openid is required when trade_type is JSAPI');
195 5
			$data["openid"] = $params['openid'];
196
		}
197 8
		$result = $this->post(self::URL_UNIFIEDORDER, $data);
198 5
		return $result;
199
	}
200
201
	/**
202
	 * 查询订单(根据微信订单号)
203
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_2
204
	 * @param $transaction_id string 微信订单号
205
	 * @return array
206
	 */
207 2
	public function queryOrderByTransactionId($transaction_id){
208 2
		$data = array();
209 2
		$data["appid"] = $this->config["app_id"];
210 2
		$data["transaction_id"] = $transaction_id;
211 2
		$result = $this->post(self::URL_ORDERQUERY, $data);
212 1
		return $result;
213
	}
214
215
	/**
216
	 * 查询订单(根据商户订单号)
217
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_2
218
	 * @param $out_trade_no string 商户订单号
219
	 * @return array
220
	 */
221 1
	public function queryOrderByOutTradeNo($out_trade_no){
222 1
		$data = array();
223 1
		$data["appid"] = $this->config["app_id"];
224 1
		$data["out_trade_no"] = $out_trade_no;
225 1
		$result = $this->post(self::URL_ORDERQUERY, $data);
226 1
		return $result;
227
	}
228
229
	/**
230
	 * 查询退款(根据微信订单号)
231
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_5
232
	 * @param $transaction_id string 微信交易号
233
	 * @param $offset int 偏移
234
	 * @return array
235
	 */
236 1
	public function queryRefundByTransactionId($transaction_id,$offset = 0){
237 1
		$data = array();
238 1
		$data["appid"] = $this->config["app_id"];
239 1
		$data["transaction_id"] = $transaction_id;
240 1
		$data["offset"] = $offset;
241 1
		$result = $this->post(self::URL_REFUNDQUERY, $data);
242 1
		return $result;
243
	}
244
245
	/**
246
	 * 查询退款(根据商户订单号)
247
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_5
248
	 * @param $out_trade_no string 商户交易号
249
	 * @param $offset int 偏移
250
	 * @return array
251
	 */
252 1
	public function queryRefundByOutTradeNo($out_trade_no,$offset = 0){
253 1
		$data = array();
254 1
		$data["appid"] = $this->config["app_id"];
255 1
		$data["out_trade_no"] = $out_trade_no;
256 1
		$data["offset"] = $offset;
257 1
		$result = $this->post(self::URL_REFUNDQUERY, $data);
258 1
		return $result;
259
	}
260
261
	/**
262
	 * 查询退款(根据微信退款单号)
263
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_5
264
	 * @param $refund_id string 微信退款单号
265
	 * @param $offset int 偏移
266
	 * @return array
267
	 */
268 1
	public function queryRefundByRefundId($refund_id,$offset = 0){
269 1
		$data = array();
270 1
		$data["appid"] = $this->config["app_id"];
271 1
		$data["refund_id"] = $refund_id;
272 1
		$data["offset"] = $offset;
273 1
		$result = $this->post(self::URL_REFUNDQUERY, $data);
274 1
		return $result;
275
	}
276
277
	/**
278
	 * 查询退款(根据商户退款单号)
279
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_5
280
	 * @param $out_refund_no string 商户退款单号
281
	 * @param $offset int 偏移
282
	 * @return array
283
	 */
284 1
	public function queryRefundByOutRefundNo($out_refund_no,$offset = 0){
285 1
		$data = array();
286 1
		$data["appid"] = $this->config["app_id"];
287 1
		$data["out_refund_no"] = $out_refund_no;
288 1
		$data["offset"] = $offset;
289 1
		$result = $this->post(self::URL_REFUNDQUERY, $data);
290 1
		return $result;
291
	}
292
293
	/**
294
	 * 关闭订单
295
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
296
	 * @param $out_trade_no string 商户订单号
297
	 * @return array
298
	 */
299 1
	public function closeOrder($out_trade_no){
300 1
		$data = array();
301 1
		$data["appid"] = $this->config["app_id"];
302 1
		$data["out_trade_no"] = $out_trade_no;
303 1
		$result = $this->post(self::URL_CLOSEORDER, $data,false);
304 1
		return $result;
305
	}
306
307
	/**
308
	 * 退款 - 使用商户订单号
309
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4
310
	 * @link  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
311
	 * @param $out_trade_no string 商户订单号
312
	 * @param $out_refund_no string 商户退款单号
313
	 * @param $total_fee int 总金额(单位:分)
314
	 * @param $refund_fee int 退款金额(单位:分)
315
	 * @param $ext array 扩展数组
316
	 * @return array
317
	 */
318 1
	public function refundByOutTradeNo($out_trade_no,$out_refund_no,$total_fee,$refund_fee,$ext = array()){
319 1
		$data = ($ext && is_array($ext))?$ext:array();
320 1
		$data["appid"] = $this->config["app_id"];
321 1
		$data["out_trade_no"] = $out_trade_no;
322 1
		$data["out_refund_no"] = $out_refund_no;
323 1
		$data["total_fee"] = $total_fee;
324 1
		$data["refund_fee"] = $refund_fee;
325 1
		$result = $this->post(self::URL_REFUND, $data,true);
326 1
		return $result;
327
	}
328
329
	/**
330
	 * 退款 - 使用微信订单号
331
	 * @param $transaction_id string 微信订单号
332
	 * @param $out_refund_no string 商户退款单号
333
	 * @param $total_fee int 总金额(单位:分)
334
	 * @param $refund_fee int 退款金额(单位:分)
335
	 * @param $ext array 扩展数组
336
	 * @return array
337
	 */
338 1
	public function refundByTransactionId($transaction_id,$out_refund_no,$total_fee,$refund_fee,$ext = array()){
339 1
		$data = ($ext && is_array($ext))?$ext:array();
340 1
		$data["appid"] = $this->config["app_id"];
341 1
		$data["transaction_id"] = $transaction_id;
342 1
		$data["out_refund_no"] = $out_refund_no;
343 1
		$data["total_fee"] = $total_fee;
344 1
		$data["refund_fee"] = $refund_fee;
345 1
		$result = $this->post(self::URL_REFUND, $data,true);
346 1
		return $result;
347
	}
348
349
	/**
350
	 * 下载对账单
351
	 * @link https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_6
352
	 * @param $bill_date string 下载对账单的日期,格式:20140603
353
	 * @param $bill_type string 类型 ALL|SUCCESS
354
	 * @return array
355
	 */
356 1
	public function downloadBill($bill_date,$bill_type = 'ALL'){
357 1
		$data = array();
358 1
		$data["appid"] = $this->config["app_id"];
359 1
		$data["bill_date"] = $bill_date;
360 1
		$data["bill_type"] = $bill_type;
361 1
		$result = $this->post(self::URL_DOWNLOADBILL, $data);
362 1
		return $result;
363
	}
364
365
	/**
366
	 * 下载资金账单
367
	 * @link https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_18&index=7
368
	 * @param $bill_date string 资金账单日期,格式:20140603
369
	 * @param $account_type string 资金账户类型 Basic|Operation|Fees
370
	 * @param $tar_type string 压缩账单
371
	 * @return array
372
	 */
373 1
	public function downloadFundFlow($bill_date,$account_type = self::ACCOUNTTYPE_BASIC,$tar_type = 'GZIP'){
374 1
		$data = array();
375 1
		$data["appid"] = $this->config["app_id"];
376 1
		$data["bill_date"] = $bill_date;
377 1
		$data["account_type"] = $account_type;
378 1
		$data["tar_type"] = $tar_type;
379 1
		$result = $this->post(self::URL_DOWNLOAD_FUND_FLOW, $data);
380 1
		return $result;
381
	}
382
383
	/**
384
	 * 拉取订单评价数据
385
	 * @link https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11
386
	 * @param string $begin_time 开始时间,格式为yyyyMMddHHmmss
387
	 * @param string $end_time 结束时间,格式为yyyyMMddHHmmss
388
	 * @param int $offset 偏移
389
	 * @param int $limit 条数
390
	 * @return array
391
	 */
392 1
	public function batchQueryComment($begin_time,$end_time,$offset = 0,$limit = 200){
393 1
		$data = array();
394 1
		$data["appid"] = $this->config["app_id"];
395 1
		$data["begin_time"] = $begin_time;
396 1
		$data["end_time"] = $end_time;
397 1
		$data["offset"] = $offset;
398 1
		$data["limit"] = $limit;
399 1
		$data["sign"] = $this->sign($data,WechatPay::SIGNTYPE_HMACSHA256);
400 1
		$result = $this->post(self::URL_BATCHQUERYCOMMENT, $data, true); //cert is required
401 1
		return $result;
402
	}
403
404
	/**
405
	 * 支付结果通知处理
406
	 * @param $notify_data array|string 通知数据
407
	 * @param $callback callable 回调
408
	 * @return null
409
	 * @throws Exception
410
	 */
411 2
	public function onPaidNotify($notify_data,callable $callback = null){
412 2
		if(!is_array($notify_data)) $notify_data = $this->xml2array($notify_data);
413 2
		if(!$this->validateSign($notify_data)) throw new Exception('Invalid paid notify data');
414 1
		if($callback && is_callable($callback)){
415 1
			return call_user_func_array( $callback , [$notify_data] );
416
		}
417
	}
418
419
	/**
420
	 * 退款结果通知处理
421
	 * @param string|array $notify_data 通知数据(XML/array)
422
	 * @param callable $callback 回调
423
	 * @return mixed
424
	 * @throws Exception
425
	 */
426 2
	public function onRefundedNotify($notify_data,callable $callback = null){
427 2
		if(!is_array($notify_data)) $notify_data = $this->xml2array($notify_data);
428 2
		if(!$this->validateSign($notify_data)) throw new Exception('Invalid refund notify data');
429 1
		if($callback && is_callable($callback)){
430 1
			return call_user_func_array( $callback ,[$notify_data] );
431
		}
432
	}
433
434
	/**
435
	 * 验证数据签名
436
	 * @param $data array 数据数组
437
	 * @return boolean 数据校验结果
438
	 */
439 5
	public function validateSign($data) {
440 5
		if (!isset($data["sign"])) {
441 1
			return false;
442
		}
443 4
		$sign = $data["sign"];
444 4
		unset($data["sign"]);
445 4
		return $this->sign($data) == $sign;
446
	}
447
448
	/**
449
	 * 响应微信支付后台通知
450
	 * @param array $data
451
	 * @param string $return_code 返回状态码 SUCCESS/FAIL
452
	 * @param string $return_msg  返回信息
453
	 * @param bool $print
454
	 * @return string
455
	 */
456 1
	public function responseNotify($print = true,$data = [],$return_code="SUCCESS", $return_msg= 'OK') {
457 1
		$data["return_code"] = $return_code;
458 1
		if ($return_msg) {
459 1
			$data["return_msg"] = $return_msg;
460
		}
461 1
		$xml = $this->array2xml($data);
462 1
		if($print === true) print $xml;
463 1
		else return $xml;
464
	}
465
466
	/**
467
	 * 交易保障
468
	 * @link https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_8&index=8
469
	 * @param string $interface_url
470
	 * @param string $execution_time
471
	 * @param string $return_code
472
	 * @param string $result_code
473
	 * @param string $user_ip
474
	 * @param string $out_trade_no
475
	 * @param string $time
476
	 * @param string $device_info
477
	 * @param string $return_msg
478
	 * @param string $err_code
479
	 * @param string $err_code_des
480
	 * @return array
481
	 */
482 1
	public function report($interface_url,$execution_time,$return_code,$result_code,$user_ip,$out_trade_no = null,$time = null,$device_info = null,
483
	                       $return_msg = null,$err_code = null,$err_code_des = null){
484 1
		$data = array();
485 1
		$data["appid"] = $this->config["app_id"];
486 1
		$data["interface_url"] = $interface_url;
487 1
		$data["execution_time"] = $execution_time;
488 1
		$data["return_code"] = $return_code;
489 1
		$data["result_code"] = $result_code;
490 1
		$data["user_ip"] = $user_ip;
491 1
		if($out_trade_no) $data["out_trade_no"] = $out_trade_no;
492 1
		if($time) $data["time"] = $time;
493 1
		if($device_info) $data["device_info"] = $device_info;
494 1
		if($return_msg) $data["return_msg"] = $return_msg;
495 1
		if($err_code) $data["err_code"] = $err_code;
496 1
		if($err_code_des) $data["err_code_des"] = $err_code_des;
497 1
		return $this->post(self::URL_REPORT, $data, false);
498
	}
499
500
	/**
501
	 * 转换短链接
502
	 * @link https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_9&index=8
503
	 * @param $longurl
504
	 * @return string
505
	 */
506 1
	public function shortUrl($longurl){
507 1
		$data = array();
508 1
		$data["appid"] = $this->config["app_id"];
509 1
		$data["long_url"] = $longurl;
510 1
		$result = $this->post(self::URL_SHORTURL,$data,false);
511 1
		return $result['short_url'];
512
	}
513
514
	/**
515
	 * sandbox环境获取验签秘钥
516
	 * @link https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=23_1
517
	 * @return array
518
	 */
519 1
	public function getSignKey(){
520 1
		$data = array();
521 1
		$data["mch_id"] = $this->config["mch_id"];
522 1
		$result = $this->post($this->getSignKeyUrl,$data,false);
523 1
		return $result['sandbox_signkey'];
524
	}
525
526
	/**
527
	 * 获取JSAPI所需要的页面参数
528
	 * @param string $url
529
	 * @param string $ticket
530
	 * @return array
531
	 */
532 1
	public function getSignPackage($url, $ticket = null){
533 1
		if(!$ticket) $ticket = $this->getTicket();
534 1
		$timestamp = time();
535 1
		$nonceStr = $this->getNonceStr();
536 1
		$rawString = "jsapi_ticket=$ticket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
537 1
		$signature = sha1($rawString);
538
539
		$signPackage = array(
540 1
			"appId" => $this->config['app_id'],
541 1
			"nonceStr" => $nonceStr,
542 1
			"timestamp" => $timestamp,
543 1
			"url" => $url,
544 1
			"signature" => $signature,
545 1
			"rawString" => $rawString
546
		);
547 1
		return $signPackage;
548
	}
549
550
	/**
551
	 * 获取JSAPI Ticket
552
	 * @param boolean $cache
553
	 * @return string
554
	 */
555 1
	public function getTicket($cache = true){
556 1
		$ticket = null;
557 1
		$cacheKey = 'jsapi_ticket';
558 1
		if($cache === true){
559 1
			$data = $this->cacheProvider->get($cacheKey);
560 1
			if ($data && $data->expires_at > time()) {
561 1
				$ticket = $data->ticket;
562
			}
563
		}
564 1
		if(!$ticket){
565 1
			$data = $this->getWechatOAuth()->getTicket();
566 1
			if($cache === true){
567 1
				$this->cacheProvider->set($cacheKey,$data,time() + $data->expires_in);
568
			}
569 1
			$ticket = $data->ticket;
570
		}
571 1
		return $ticket;
572
	}
573
574 39
	protected function post($url, $data,$cert = true) {
575 39
		if(!isset($data['mch_id']) && !isset($data['mchid'])) $data["mch_id"] = $this->config["mch_id"];
576 39
		if(!isset($data['nonce_str'])) $data["nonce_str"] = $this->getNonceStr();
577 39
		if(!isset($data['sign'])) $data['sign'] = $this->sign($data);
578 39
		$this->requestXML = $this->responseXML = null;
579 39
		$this->requestArray = $this->responseArray = null;
580
581 39
		$this->requestArray = $data;
582 39
		$this->requestXML = $this->array2xml($data);
583
		$opts = [
584 39
			CURLOPT_SSL_VERIFYPEER => false,
585 39
			CURLOPT_SSL_VERIFYHOST => false,
586 39
			CURLOPT_RETURNTRANSFER => true,
587 39
			CURLOPT_TIMEOUT => 10
588
		];
589 39
		if($cert == true){
590 32
			$opts[CURLOPT_SSLCERTTYPE] = 'PEM';
591 32
			$opts[CURLOPT_SSLCERT] = $this->config['ssl_cert_path'];
592 32
			$opts[CURLOPT_SSLKEYTYPE] = 'PEM';
593 32
			$opts[CURLOPT_SSLKEY] = $this->config['ssl_key_path'];
594
		}
595 39
		$processResponse = true;
596 39
		if(in_array($url,[self::URL_DOWNLOADBILL,self::URL_DOWNLOAD_FUND_FLOW,self::URL_BATCHQUERYCOMMENT])){
597 3
			$processResponse = false;
598
		}
599 39
		if($this->sandbox === true) $url = "sandboxnew/{$url}";
600
601 39
		$content = $this->httpClient->post(self::API_ENDPOINT . $url,$this->requestXML,[],$opts);
602 39
		if(!$content) throw new Exception("Empty response with {$this->requestXML}");
603
604 39
		$this->responseXML = $content;
605 39
		if($processResponse)
606 36
			return $this->processResponseXML($this->responseXML);
607 3
		else return $this->responseXML;
608
	}
609
610
	/**
611
	 * @param $responseXML
612
	 * @return array
613
	 * @throws Exception
614
	 */
615 36
	private function processResponseXML($responseXML){
616 36
		$result = $this->xml2array($responseXML);
617 36
		$this->responseArray = $result;
618 36
		if(empty($result['return_code'])){
619 1
			throw new Exception("No return code presents in {$this->responseXML}");
620
		}
621 35
		$this->returnCode = $result["return_code"];
622 35
		$this->returnMsg = isset($result['return_msg'])?$result['return_msg']:'';
623
624 35
		if ($this->returnCode == "SUCCESS") {
625 29
			if(isset($result['result_code']) && $result['result_code'] == "FAIL"){
626 3
				$this->resultCode = $result['result_code'];
627 3
				$this->errCode = $result['err_code'];
628 3
				$this->errCodeDes = $result['err_code_des'];
629 3
				throw new Exception("[$this->errCode]$this->errCodeDes");
630
			}else{
631 26
				return $result;
632
			}
633 6
		} else if($this->returnCode == 'FAIL'){
634 6
			throw new Exception($this->returnMsg);
635
		}
636
	}
637
638 48
	public function sign($data,$sign_type = WechatPay::SIGNTYPE_MD5) {
639 48
		ksort($data);
640 48
		$string1 = "";
641 48
		foreach ($data as $k => $v) {
642 48
			if ($v && trim($v)!='') {
643 48
				$string1 .= "$k=$v&";
644
			}
645
		}
646 48
		$stringSignTemp = $string1 . "key=" . $this->config["api_key"];
647 48
		if($sign_type == WechatPay::SIGNTYPE_MD5){
648 46
			$sign = strtoupper(md5($stringSignTemp));
649 3
		}elseif($sign_type == WechatPay::SIGNTYPE_HMACSHA256){
650 2
			$sign = strtoupper(hash_hmac('sha256',$stringSignTemp,$this->config["api_key"]));
651 1
		}else throw new Exception("Not supported sign type - $sign_type");
652 47
		return $sign;
653
	}
654
655 40
	private function array2xml($array) {
656 40
		$xml = "<xml>" . PHP_EOL;
657 40
		foreach ($array as $k => $v) {
658 40
			if($v && trim($v)!='')
659 40
				$xml .= "<$k><![CDATA[$v]]></$k>" . PHP_EOL;
660
		}
661 40
		$xml .= "</xml>";
662 40
		return $xml;
663
	}
664
665 38
	private function xml2array($xml) {
666 38
		$array = [];
667 38
		$tmp = (array) simplexml_load_string($xml);
668 38
		foreach ( $tmp as $k => $v) {
669 38
			$array[$k] = (string) $v;
670
		}
671 38
		return $array;
672
	}
673
674 43
	protected function getNonceStr() {
675 43
		return substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"),0,32);
676
	}
677
678
}