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); |
|
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; |
|
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; |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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); |
|
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 |
|
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); |
|
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×tamp=$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
|
|||
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 | } |
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 theid
property of an instance of theAccount
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.