WechatPay   F
last analyzed

Complexity

Total Complexity 103

Size/Duplication

Total Lines 635
Duplicated Lines 0 %

Test Coverage

Coverage 98.58%

Importance

Changes 0
Metric Value
eloc 289
dl 0
loc 635
ccs 277
cts 281
cp 0.9858
rs 2
c 0
b 0
f 0
wmc 103

38 Methods

Rating   Name   Duplication   Size   Complexity  
A setCacheProvider() 0 2 1
A getConfig() 0 2 1
A setConfig() 0 2 1
A load() 0 3 1
A __callStatic() 0 2 1
A __construct() 0 4 1
A getCacheProvider() 0 2 1
A setHttpClient() 0 2 1
A array2xml() 0 8 4
A xml2array() 0 7 2
A getNonceStr() 0 2 1
A queryRefundByRefundId() 0 6 1
A getSignKey() 0 5 1
A queryOrderByTransactionId() 0 6 1
A responseNotify() 0 8 3
A queryOrderByOutTradeNo() 0 5 1
A getSignPackage() 0 16 2
F unifiedOrder() 0 26 14
B report() 0 16 7
A refundByTransactionId() 0 8 3
C post() 0 34 10
B processResponseXML() 0 20 7
A validateSign() 0 7 2
A queryRefundByOutTradeNo() 0 6 1
A downloadBill() 0 6 1
A getTicket() 0 17 6
A shortUrl() 0 6 1
A batchQueryComment() 0 9 1
A refundByOutTradeNo() 0 8 3
A closeOrder() 0 5 1
A queryRefundByTransactionId() 0 6 1
A onRefundedNotify() 0 7 5
A queryRefundByOutRefundNo() 0 6 1
A onPaidNotify() 0 7 5
A sign() 0 15 6
A downloadFundFlow() 0 7 1
A setOAuth() 0 2 1
A getOAuth() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like WechatPay often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WechatPay, and based on these observations, apply Extract Interface, too.

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\OAuth;
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 OAuth */
100
	protected $oauth = 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 setOAuth($oauth){
136 1
		$this->oauth = $oauth;
137 1
	}
138
139 1
	public function getOAuth(){
140 1
		if(!$this->oauth){
141 1
			$this->oauth = new OAuth($this->config['app_id'],$this->config['app_secret']);
142
		}
143 1
		return $this->oauth;
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
	 * @param array $params
169
	 * @throws Exception
170
	 * @return array
171
	 */
172 8
	public function unifiedOrder($params) {
173 8
		$data = array();
174 8
		$data["appid"] = $this->config["app_id"];
175 8
		$data["device_info"] = (isset($params['device_info'])&&trim($params['device_info'])!='')?$params['device_info']:null;
176 8
		$data["body"] = $params['body'];
177 8
		$data["detail"] = isset($params['detail'])?$params['detail']:null;//optional
178 8
		$data["attach"] = isset($params['attach'])?$params['attach']:null;//optional
179 8
		$data["out_trade_no"] = isset($params['out_trade_no'])?$params['out_trade_no']:null;
180 8
		$data["fee_type"] = isset($params['fee_type'])?$params['fee_type']:'CNY';
181 8
		$data["total_fee"]    = $params['total_fee'];
182 8
		$data["spbill_create_ip"] = $params['spbill_create_ip'];
183 8
		$data["time_start"] = isset($params['time_start'])?$params['time_start']:null;//optional
184 8
		$data["time_expire"] = isset($params['time_expire'])?$params['time_expire']:null;//optional
185 8
		$data["goods_tag"] = isset($params['goods_tag'])?$params['goods_tag']:null;
186 8
		$data["notify_url"] = $this->config["notify_url"];
187 8
		$data["trade_type"] = $params['trade_type'];
188 8
		if($params['trade_type'] == WechatPay::TRADETYPE_NATIVE){
189 1
			if(!isset($params['product_id'])) throw new Exception('product_id is required when trade_type is NATIVE');
190 1
			$data["product_id"] = $params['product_id'];
191
		}
192 8
		if($params['trade_type'] == WechatPay::TRADETYPE_JSAPI){
193 5
			if(!isset($params['openid'])) throw new Exception('openid is required when trade_type is JSAPI');
194 5
			$data["openid"] = $params['openid'];
195
		}
196 8
		$result = $this->post(self::URL_UNIFIEDORDER, $data);
197 6
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string|true which is incompatible with the documented return type array.
Loading history...
198
	}
199
200
	/**
201
	 * 查询订单(根据微信订单号)
202
	 * @param $transaction_id string 微信订单号
203
	 * @return array
204
	 * @throws Exception
205
	 */
206 2
	public function queryOrderByTransactionId($transaction_id){
207 2
		$data = array();
208 2
		$data["appid"] = $this->config["app_id"];
209 2
		$data["transaction_id"] = $transaction_id;
210 2
		$result = $this->post(self::URL_ORDERQUERY, $data);
211 2
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string|true which is incompatible with the documented return type array.
Loading history...
212
	}
213
214
	/**
215
	 * 查询订单(根据商户订单号)
216
	 * @param $out_trade_no string 商户订单号
217
	 * @return array
218
	 * @throws Exception
219
	 */
220 1
	public function queryOrderByOutTradeNo($out_trade_no){
221 1
		$data = array();
222 1
		$data["appid"] = $this->config["app_id"];
223 1
		$data["out_trade_no"] = $out_trade_no;
224 1
		return $this->post(self::URL_ORDERQUERY, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_ORDERQUERY, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
225
	}
226
227
	/**
228
	 * 查询退款(根据微信订单号)
229
	 * @param $transaction_id string 微信交易号
230
	 * @param $offset int 偏移
231
	 * @return array
232
	 * @throws Exception
233
	 */
234 1
	public function queryRefundByTransactionId($transaction_id,$offset = 0){
235 1
		$data = array();
236 1
		$data["appid"] = $this->config["app_id"];
237 1
		$data["transaction_id"] = $transaction_id;
238 1
		$data["offset"] = $offset;
239 1
		return $this->post(self::URL_REFUNDQUERY, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUNDQUERY, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
240
	}
241
242
	/**
243
	 * 查询退款(根据商户订单号)
244
	 * @param $out_trade_no string 商户交易号
245
	 * @param $offset int 偏移
246
	 * @return array
247
	 * @throws Exception
248
	 */
249 1
	public function queryRefundByOutTradeNo($out_trade_no,$offset = 0){
250 1
		$data = array();
251 1
		$data["appid"] = $this->config["app_id"];
252 1
		$data["out_trade_no"] = $out_trade_no;
253 1
		$data["offset"] = $offset;
254 1
		return $this->post(self::URL_REFUNDQUERY, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUNDQUERY, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
255
	}
256
257
	/**
258
	 * 查询退款(根据微信退款单号)
259
	 * @param $refund_id string 微信退款单号
260
	 * @param $offset int 偏移
261
	 * @return array
262
	 * @throws Exception
263
	 */
264 1
	public function queryRefundByRefundId($refund_id,$offset = 0){
265 1
		$data = array();
266 1
		$data["appid"] = $this->config["app_id"];
267 1
		$data["refund_id"] = $refund_id;
268 1
		$data["offset"] = $offset;
269 1
		return $this->post(self::URL_REFUNDQUERY, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUNDQUERY, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
270
	}
271
272
	/**
273
	 * 查询退款(根据商户退款单号)
274
	 * @param $out_refund_no string 商户退款单号
275
	 * @param $offset int 偏移
276
	 * @return array
277
	 * @throws Exception
278
	 */
279 1
	public function queryRefundByOutRefundNo($out_refund_no,$offset = 0){
280 1
		$data = array();
281 1
		$data["appid"] = $this->config["app_id"];
282 1
		$data["out_refund_no"] = $out_refund_no;
283 1
		$data["offset"] = $offset;
284 1
		return $this->post(self::URL_REFUNDQUERY, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUNDQUERY, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
285
	}
286
287
	/**
288
	 * 关闭订单
289
	 * @param $out_trade_no string 商户订单号
290
	 * @return array
291
	 * @throws Exception
292
	 */
293 1
	public function closeOrder($out_trade_no){
294 1
		$data = array();
295 1
		$data["appid"] = $this->config["app_id"];
296 1
		$data["out_trade_no"] = $out_trade_no;
297 1
		return $this->post(self::URL_CLOSEORDER, $data,false);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self:...OSEORDER, $data, false) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
298
	}
299
300
	/**
301
	 * 退款 - 使用商户订单号
302
	 * @param $out_trade_no string 商户订单号
303
	 * @param $out_refund_no string 商户退款单号
304
	 * @param $total_fee int 总金额(单位:分)
305
	 * @param $refund_fee int 退款金额(单位:分)
306
	 * @param $ext array 扩展数组
307
	 * @return array
308
	 * @throws Exception
309
	 */
310 1
	public function refundByOutTradeNo($out_trade_no,$out_refund_no,$total_fee,$refund_fee,$ext = array()){
311 1
		$data = ($ext && is_array($ext))?$ext:array();
312 1
		$data["appid"] = $this->config["app_id"];
313 1
		$data["out_trade_no"] = $out_trade_no;
314 1
		$data["out_refund_no"] = $out_refund_no;
315 1
		$data["total_fee"] = $total_fee;
316 1
		$data["refund_fee"] = $refund_fee;
317 1
		return $this->post(self::URL_REFUND, $data,true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUND, $data, true) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
318
	}
319
320
	/**
321
	 * 退款 - 使用微信订单号
322
	 * @param $transaction_id string 微信订单号
323
	 * @param $out_refund_no string 商户退款单号
324
	 * @param $total_fee int 总金额(单位:分)
325
	 * @param $refund_fee int 退款金额(单位:分)
326
	 * @param $ext array 扩展数组
327
	 * @return array
328
	 * @throws Exception
329
	 */
330 1
	public function refundByTransactionId($transaction_id,$out_refund_no,$total_fee,$refund_fee,$ext = array()){
331 1
		$data = ($ext && is_array($ext))?$ext:array();
332 1
		$data["appid"] = $this->config["app_id"];
333 1
		$data["transaction_id"] = $transaction_id;
334 1
		$data["out_refund_no"] = $out_refund_no;
335 1
		$data["total_fee"] = $total_fee;
336 1
		$data["refund_fee"] = $refund_fee;
337 1
		return $this->post(self::URL_REFUND, $data,true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_REFUND, $data, true) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
338
	}
339
340
	/**
341
	 * 下载对账单
342
	 * @param $bill_date string 下载对账单的日期,格式:20140603
343
	 * @param $bill_type string 类型 ALL|SUCCESS
344
	 * @return array
345
	 * @throws Exception
346
	 */
347 1
	public function downloadBill($bill_date,$bill_type = 'ALL'){
348 1
		$data = array();
349 1
		$data["appid"] = $this->config["app_id"];
350 1
		$data["bill_date"] = $bill_date;
351 1
		$data["bill_type"] = $bill_type;
352 1
		return $this->post(self::URL_DOWNLOADBILL, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self::URL_DOWNLOADBILL, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
353
	}
354
355
	/**
356
	 * 下载资金账单
357
	 * @param $bill_date string 资金账单日期,格式:20140603
358
	 * @param $account_type string 资金账户类型 Basic|Operation|Fees
359
	 * @param $tar_type string 压缩账单
360
	 * @return array
361
	 * @throws Exception
362
	 */
363 1
	public function downloadFundFlow($bill_date,$account_type = self::ACCOUNTTYPE_BASIC,$tar_type = 'GZIP'){
364 1
		$data = array();
365 1
		$data["appid"] = $this->config["app_id"];
366 1
		$data["bill_date"] = $bill_date;
367 1
		$data["account_type"] = $account_type;
368 1
		$data["tar_type"] = $tar_type;
369 1
		return $this->post(self::URL_DOWNLOAD_FUND_FLOW, $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self:...NLOAD_FUND_FLOW, $data) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
370
	}
371
372
	/**
373
	 * 拉取订单评价数据
374
	 * @param string $begin_time 开始时间,格式为yyyyMMddHHmmss
375
	 * @param string $end_time 结束时间,格式为yyyyMMddHHmmss
376
	 * @param int $offset 偏移
377
	 * @param int $limit 条数
378
	 * @return array
379
	 * @throws Exception
380
	 */
381 1
	public function batchQueryComment($begin_time,$end_time,$offset = 0,$limit = 200){
382 1
		$data = array();
383 1
		$data["appid"] = $this->config["app_id"];
384 1
		$data["begin_time"] = $begin_time;
385 1
		$data["end_time"] = $end_time;
386 1
		$data["offset"] = $offset;
387 1
		$data["limit"] = $limit;
388 1
		$data["sign"] = $this->sign($data,WechatPay::SIGNTYPE_HMACSHA256);
389 1
		return $this->post(self::URL_BATCHQUERYCOMMENT, $data, true); //cert is required
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self:...RYCOMMENT, $data, true) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
390
	}
391
392
	/**
393
	 * 支付结果通知处理
394
	 * @param $notify_data array|string 通知数据
395
	 * @param $callback callable 回调
396
	 * @return null
397
	 * @throws Exception
398
	 */
399 2
	public function onPaidNotify($notify_data,callable $callback = null){
400 2
		if(!is_array($notify_data)) $notify_data = $this->xml2array($notify_data);
401 2
		if(!$this->validateSign($notify_data)) throw new Exception('Invalid paid notify data');
402 1
		if($callback && is_callable($callback)){
403 1
			return call_user_func_array( $callback , [$notify_data] );
404
		}
405
		return null;
406
	}
407
408
	/**
409
	 * 退款结果通知处理
410
	 * @param string|array $notify_data 通知数据(XML/array)
411
	 * @param callable $callback 回调
412
	 * @return mixed
413
	 * @throws Exception
414
	 */
415 2
	public function onRefundedNotify($notify_data,callable $callback = null){
416 2
		if(!is_array($notify_data)) $notify_data = $this->xml2array($notify_data);
417 2
		if(!$this->validateSign($notify_data)) throw new Exception('Invalid refund notify data');
418 1
		if($callback && is_callable($callback)){
419 1
			return call_user_func_array( $callback ,[$notify_data] );
420
		}
421
		return null;
422
	}
423
424
	/**
425
	 * 验证数据签名
426
	 * @param $data array 数据数组
427
	 * @return boolean 数据校验结果
428
	 * @throws Exception
429
	 */
430 5
	public function validateSign($data) {
431 5
		if (!isset($data["sign"])) {
432 1
			return false;
433
		}
434 4
		$sign = $data["sign"];
435 4
		unset($data["sign"]);
436 4
		return $this->sign($data) == $sign;
437
	}
438
439
	/**
440
	 * 响应微信支付后台通知
441
	 * @param array $data
442
	 * @param string $return_code 返回状态码 SUCCESS/FAIL
443
	 * @param string $return_msg  返回信息
444
	 * @param bool $print
445
	 * @return string
446
	 */
447 1
	public function responseNotify($print = true,$data = [],$return_code="SUCCESS", $return_msg= 'OK') {
448 1
		$data["return_code"] = $return_code;
449 1
		if ($return_msg) {
450 1
			$data["return_msg"] = $return_msg;
451
		}
452 1
		$xml = $this->array2xml($data);
453 1
		if($print === true) print $xml;
454 1
		else return $xml;
455
	}
456
457
	/**
458
	 * 交易保障
459
	 * @param string $interface_url
460
	 * @param string $execution_time
461
	 * @param string $return_code
462
	 * @param string $result_code
463
	 * @param string $user_ip
464
	 * @param string $out_trade_no
465
	 * @param string $time
466
	 * @param string $device_info
467
	 * @param string $return_msg
468
	 * @param string $err_code
469
	 * @param string $err_code_des
470
	 * @return array
471
	 * @throws Exception
472
	 */
473 1
	public function report($interface_url,$execution_time,$return_code,$result_code,$user_ip,$out_trade_no = null,$time = null,$device_info = null,
474
	                       $return_msg = null,$err_code = null,$err_code_des = null){
475 1
		$data = array();
476 1
		$data["appid"] = $this->config["app_id"];
477 1
		$data["interface_url"] = $interface_url;
478 1
		$data["execution_time"] = $execution_time;
479 1
		$data["return_code"] = $return_code;
480 1
		$data["result_code"] = $result_code;
481 1
		$data["user_ip"] = $user_ip;
482 1
		if($out_trade_no) $data["out_trade_no"] = $out_trade_no;
483 1
		if($time) $data["time"] = $time;
484 1
		if($device_info) $data["device_info"] = $device_info;
485 1
		if($return_msg) $data["return_msg"] = $return_msg;
486 1
		if($err_code) $data["err_code"] = $err_code;
487 1
		if($err_code_des) $data["err_code_des"] = $err_code_des;
488 1
		return $this->post(self::URL_REPORT, $data, false);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->post(self:...L_REPORT, $data, false) also could return the type string|true which is incompatible with the documented return type array.
Loading history...
489
	}
490
491
	/**
492
	 * 转换短链接
493
	 * @param $longurl
494
	 * @return string
495
	 * @throws Exception
496
	 */
497 1
	public function shortUrl($longurl){
498 1
		$data = array();
499 1
		$data["appid"] = $this->config["app_id"];
500 1
		$data["long_url"] = $longurl;
501 1
		$result = $this->post(self::URL_SHORTURL,$data,false);
502 1
		return $result['short_url'];
503
	}
504
505
	/**
506
	 * sandbox环境获取验签秘钥
507
	 * @return array
508
	 * @throws Exception
509
	 */
510 1
	public function getSignKey(){
511 1
		$data = array();
512 1
		$data["mch_id"] = $this->config["mch_id"];
513 1
		$result = $this->post($this->getSignKeyUrl,$data,false);
514 1
		return $result['sandbox_signkey'];
515
	}
516
517
	/**
518
	 * 获取JSAPI所需要的页面参数
519
	 * @param string $url
520
	 * @param string $ticket
521
	 * @return array
522
	 */
523 1
	public function getSignPackage($url, $ticket = null){
524 1
		if(!$ticket) $ticket = $this->getTicket();
525 1
		$timestamp = time();
526 1
		$nonceStr = $this->getNonceStr();
527 1
		$rawString = "jsapi_ticket=$ticket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
528 1
		$signature = sha1($rawString);
529
530
		$signPackage = array(
531 1
			"appId" => $this->config['app_id'],
532 1
			"nonceStr" => $nonceStr,
533 1
			"timestamp" => $timestamp,
534 1
			"url" => $url,
535 1
			"signature" => $signature,
536 1
			"rawString" => $rawString
537
		);
538 1
		return $signPackage;
539
	}
540
541
	/**
542
	 * 获取JSAPI Ticket
543
	 * @param boolean $cache
544
	 * @return string
545
	 */
546 1
	public function getTicket($cache = true){
547 1
		$ticket = null;
548 1
		$cacheKey = 'jsapi_ticket';
549 1
		if($cache === true){
550 1
			$data = $this->cacheProvider->get($cacheKey);
551 1
			if ($data && $data->expires_at > time()) {
552 1
				$ticket = $data->ticket;
553
			}
554
		}
555 1
		if(!$ticket){
556 1
			$data = $this->getOAuth()->getTicket();
557 1
			if($cache === true){
558 1
				$this->cacheProvider->set($cacheKey,$data,time() + $data->expires_in);
559
			}
560 1
			$ticket = $data->ticket;
561
		}
562 1
		return $ticket;
563
	}
564
565 39
	protected function post($url, $data,$cert = true) {
566 39
		if(!isset($data['mch_id']) && !isset($data['mchid'])) $data["mch_id"] = $this->config["mch_id"];
567 39
		if(!isset($data['nonce_str'])) $data["nonce_str"] = $this->getNonceStr();
568 39
		if(!isset($data['sign'])) $data['sign'] = $this->sign($data);
569 39
		$this->requestXML = $this->responseXML = null;
570 39
		$this->requestArray = $this->responseArray = null;
571
572 39
		$this->requestArray = $data;
573 39
		$this->requestXML = $this->array2xml($data);
574
		$opts = [
575 39
			CURLOPT_SSL_VERIFYPEER => false,
576 39
			CURLOPT_SSL_VERIFYHOST => false,
577 39
			CURLOPT_RETURNTRANSFER => true,
578 39
			CURLOPT_TIMEOUT => 10
579
		];
580 39
		if($cert == true){
581 32
			$opts[CURLOPT_SSLCERTTYPE] = 'PEM';
582 32
			$opts[CURLOPT_SSLCERT] = $this->config['ssl_cert_path'];
583 32
			$opts[CURLOPT_SSLKEYTYPE] = 'PEM';
584 32
			$opts[CURLOPT_SSLKEY] = $this->config['ssl_key_path'];
585
		}
586 39
		$processResponse = true;
587 39
		if(in_array($url,[self::URL_DOWNLOADBILL,self::URL_DOWNLOAD_FUND_FLOW,self::URL_BATCHQUERYCOMMENT])){
588 3
			$processResponse = false;
589
		}
590 39
		if($this->sandbox === true) $url = "sandboxnew/{$url}";
591
592 39
		$content = $this->httpClient->post(self::API_ENDPOINT . $url,$this->requestXML,[],$opts);
593 39
		if(!$content) throw new Exception("Empty response with {$this->requestXML}");
594
595 39
		$this->responseXML = $content;
0 ignored issues
show
Documentation Bug introduced by
It seems like $content can also be of type true. However, the property $responseXML is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
596 39
		if($processResponse)
597 36
			return $this->processResponseXML($this->responseXML);
598 3
		else return $this->responseXML;
599
	}
600
601
	/**
602
	 * @param $responseXML
603
	 * @return array
604
	 * @throws Exception
605
	 */
606 36
	private function processResponseXML($responseXML){
607 36
		$result = $this->xml2array($responseXML);
608 36
		$this->responseArray = $result;
609 36
		if(empty($result['return_code'])){
610 1
			throw new Exception("No return code presents in {$this->responseXML}");
611
		}
612 35
		$this->returnCode = $result["return_code"];
613 35
		$this->returnMsg = isset($result['return_msg'])?$result['return_msg']:'';
614
615 35
		if ($this->returnCode == "SUCCESS") {
616 29
			if(isset($result['result_code']) && $result['result_code'] == "FAIL") {
617 3
				$this->resultCode = $result['result_code'];
618 3
				$this->errCode = $result['err_code'];
619 3
				$this->errCodeDes = $result['err_code_des'];
620
				//应该在外部进行业务处理,这里可能会返回正在支付中的状态
621
				//throw new Exception("[$this->errCode]$this->errCodeDes, with request XML: {$this->requestXML}");
622
			}
623 29
			return $result;
624 6
		} else if($this->returnCode == 'FAIL'){
625 6
			throw new Exception($this->returnMsg);
626
		}
627
	}
628
629 48
	public function sign($data,$sign_type = WechatPay::SIGNTYPE_MD5) {
630 48
		ksort($data);
631 48
		$string1 = "";
632 48
		foreach ($data as $k => $v) {
633 48
			if ($v && trim($v)!='') {
634 48
				$string1 .= "$k=$v&";
635
			}
636
		}
637 48
		$stringSignTemp = $string1 . "key=" . $this->config["api_key"];
638 48
		if($sign_type == WechatPay::SIGNTYPE_MD5){
639 46
			$sign = strtoupper(md5($stringSignTemp));
640 3
		}elseif($sign_type == WechatPay::SIGNTYPE_HMACSHA256){
641 2
			$sign = strtoupper(hash_hmac('sha256',$stringSignTemp,$this->config["api_key"]));
642 1
		}else throw new Exception("Not supported sign type - $sign_type");
643 47
		return $sign;
644
	}
645
646 40
	private function array2xml($array) {
647 40
		$xml = "<xml>" . PHP_EOL;
648 40
		foreach ($array as $k => $v) {
649 40
			if($v && trim($v)!='')
650 40
				$xml .= "<$k><![CDATA[$v]]></$k>" . PHP_EOL;
651
		}
652 40
		$xml .= "</xml>";
653 40
		return $xml;
654
	}
655
656 38
	private function xml2array($xml) {
657 38
		$array = [];
658 38
		$tmp = (array) simplexml_load_string($xml);
659 38
		foreach ( $tmp as $k => $v) {
660 38
			$array[$k] = (string) $v;
661
		}
662 38
		return $array;
663
	}
664
665 43
	protected function getNonceStr() {
666 43
		return substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"),0,32);
667
	}
668
669
}