Passed
Push — master ( 5074d3...58a63a )
by Carlos
03:04 queued 21s
created

API::getSandboxSignKey()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.5726

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 4
nop 0
dl 0
loc 29
ccs 7
cts 13
cp 0.5385
crap 5.5726
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
/**
13
 * API.php.
14
 *
15
 * @author    overtrue <[email protected]>
16
 * @copyright 2015 overtrue <[email protected]>
17
 *
18
 * @see      https://github.com/overtrue
19
 * @see      http://overtrue.me
20
 */
21
22
namespace EasyWeChat\Payment;
23
24
use Doctrine\Common\Cache\Cache;
25
use Doctrine\Common\Cache\FilesystemCache;
26
use EasyWeChat\Core\AbstractAPI;
27
use EasyWeChat\Core\Exception;
28
use EasyWeChat\Support\Collection;
29
use EasyWeChat\Support\XML;
30
use Psr\Http\Message\ResponseInterface;
31
32
/**
33
 * Class API.
34
 */
35
class API extends AbstractAPI
36
{
37
    /**
38
     * Merchant instance.
39
     *
40
     * @var Merchant
41
     */
42
    protected $merchant;
43
44
    /**
45
     * Sandbox box mode.
46
     *
47
     * @var bool
48
     */
49
    protected $sandboxEnabled = false;
50
51
    /**
52
     * Sandbox sign key.
53
     *
54
     * @var string
55
     */
56
    protected $sandboxSignKey;
57
58
    /**
59
     * Cache.
60
     *
61
     * @var Cache
62
     */
63
    protected $cache;
64
65
    const API_HOST = 'https://api.mch.weixin.qq.com';
66
67
    // api
68
    const API_PAY_ORDER = '/pay/micropay';
69
    const API_PREPARE_ORDER = '/pay/unifiedorder';
70
    const API_QUERY = '/pay/orderquery';
71
    const API_CLOSE = '/pay/closeorder';
72
    const API_REVERSE = '/secapi/pay/reverse';
73
    const API_REFUND = '/secapi/pay/refund';
74
    const API_QUERY_REFUND = '/pay/refundquery';
75
    const API_DOWNLOAD_BILL = '/pay/downloadbill';
76
    const API_REPORT = '/payitil/report';
77
78
    const API_URL_SHORTEN = 'https://api.mch.weixin.qq.com/tools/shorturl';
79
    const API_AUTH_CODE_TO_OPENID = 'https://api.mch.weixin.qq.com/tools/authcodetoopenid';
80
    const API_SANDBOX_SIGN_KEY = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
81
82
    // order id types.
83
    const TRANSACTION_ID = 'transaction_id';
84
    const OUT_TRADE_NO = 'out_trade_no';
85
    const OUT_REFUND_NO = 'out_refund_no';
86
    const REFUND_ID = 'refund_id';
87
88
    // bill types.
89
    const BILL_TYPE_ALL = 'ALL';
90
    const BILL_TYPE_SUCCESS = 'SUCCESS';
91
    const BILL_TYPE_REFUND = 'REFUND';
92
    const BILL_TYPE_REVOKED = 'REVOKED';
93
94
    /**
95
     * API constructor.
96
     *
97
     * @param \EasyWeChat\Payment\Merchant   $merchant
98
     * @param \EasyWeChat\Payment\Cache|null $cache
99
     */
100 12
    public function __construct(Merchant $merchant, Cache $cache = null)
101
    {
102 12
        $this->merchant = $merchant;
103 12
        $this->cache = $cache;
104 12
    }
105
106
    /**
107
     * Pay the order.
108
     *
109
     * @param Order $order
110
     *
111
     * @return \EasyWeChat\Support\Collection
112
     */
113 1
    public function pay(Order $order)
114
    {
115 1
        return $this->request($this->wrapApi(self::API_PAY_ORDER), $order->all());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...ORDER), $order->all()); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 115 which is incompatible with the return type documented by EasyWeChat\Payment\API::pay of type EasyWeChat\Support\Collection.
Loading history...
116
    }
117
118
    /**
119
     * Prepare order to pay.
120
     *
121
     * @param Order $order
122
     *
123
     * @return \EasyWeChat\Support\Collection
124
     */
125 1
    public function prepare(Order $order)
126
    {
127 1
        $order->notify_url = $order->get('notify_url', $this->merchant->notify_url);
128 1
        if (is_null($order->spbill_create_ip)) {
129 1
            $order->spbill_create_ip = ($order->trade_type === Order::NATIVE) ? get_server_ip() : get_client_ip();
130 1
        }
131
132 1
        return $this->request($this->wrapApi(self::API_PREPARE_ORDER), $order->all());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...ORDER), $order->all()); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 132 which is incompatible with the return type documented by EasyWeChat\Payment\API::prepare of type EasyWeChat\Support\Collection.
Loading history...
133
    }
134
135
    /**
136
     * Query order.
137
     *
138
     * @param string $orderNo
139
     * @param string $type
140
     *
141
     * @return \EasyWeChat\Support\Collection
142
     */
143 1 View Code Duplication
    public function query($orderNo, $type = self::OUT_TRADE_NO)
144
    {
145
        $params = [
146 1
            $type => $orderNo,
147 1
        ];
148
149 1
        return $this->request($this->wrapApi(self::API_QUERY), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...::API_QUERY), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 149 which is incompatible with the return type documented by EasyWeChat\Payment\API::query of type EasyWeChat\Support\Collection.
Loading history...
150
    }
151
152
    /**
153
     * Query order by transaction_id.
154
     *
155
     * @param string $transactionId
156
     *
157
     * @return \EasyWeChat\Support\Collection
158
     */
159 1
    public function queryByTransactionId($transactionId)
160
    {
161 1
        return $this->query($transactionId, self::TRANSACTION_ID);
162
    }
163
164
    /**
165
     * Close order by out_trade_no.
166
     *
167
     * @param $tradeNo
168
     *
169
     * @return \EasyWeChat\Support\Collection
170
     */
171 1
    public function close($tradeNo)
172
    {
173
        $params = [
174 1
            'out_trade_no' => $tradeNo,
175 1
        ];
176
177 1
        return $this->request($this->wrapApi(self::API_CLOSE), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...::API_CLOSE), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 177 which is incompatible with the return type documented by EasyWeChat\Payment\API::close of type EasyWeChat\Support\Collection.
Loading history...
178
    }
179
180
    /**
181
     * Reverse order.
182
     *
183
     * @param string $orderNo
184
     * @param string $type
185
     *
186
     * @return \EasyWeChat\Support\Collection
187
     */
188 1
    public function reverse($orderNo, $type = self::OUT_TRADE_NO)
189
    {
190
        $params = [
191 1
            $type => $orderNo,
192 1
        ];
193
194 1
        return $this->safeRequest($this->wrapApi(self::API_REVERSE), $params);
195
    }
196
197
    /**
198
     * Reverse order by transaction_id.
199
     *
200
     * @param int $transactionId
201
     *
202
     * @return \EasyWeChat\Support\Collection
203
     */
204
    public function reverseByTransactionId($transactionId)
205
    {
206
        return $this->reverse($transactionId, self::TRANSACTION_ID);
207
    }
208
209
    /**
210
     * Make a refund request.
211
     *
212
     * @param string $orderNo
213
     * @param string $refundNo
214
     * @param float  $totalFee
215
     * @param float  $refundFee
216
     * @param string $opUserId
217
     * @param string $type
218
     * @param string $refundAccount
219
     * @param string $refundReason
220
     *
221
     * @return Collection
222
     */
223 1
    public function refund(
224
        $orderNo,
225
        $refundNo,
226
        $totalFee,
227
        $refundFee = null,
228
        $opUserId = null,
229
        $type = self::OUT_TRADE_NO,
230
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS',
231
        $refundReason = ''
232
        ) {
233
        $params = [
234 1
            $type => $orderNo,
235 1
            'out_refund_no' => $refundNo,
236 1
            'total_fee' => $totalFee,
237 1
            'refund_fee' => $refundFee ?: $totalFee,
238 1
            'refund_fee_type' => $this->merchant->fee_type,
239 1
            'refund_account' => $refundAccount,
240 1
            'refund_desc' => $refundReason,
241 1
            'op_user_id' => $opUserId ?: $this->merchant->merchant_id,
242 1
        ];
243
244 1
        return $this->safeRequest($this->wrapApi(self::API_REFUND), $params);
245
    }
246
247
    /**
248
     * Refund by transaction id.
249
     *
250
     * @param string $orderNo
251
     * @param string $refundNo
252
     * @param float  $totalFee
253
     * @param float  $refundFee
254
     * @param string $opUserId
255
     * @param string $refundAccount
256
     * @param string $refundReason
257
     *
258
     * @return Collection
259
     */
260
    public function refundByTransactionId(
261
        $orderNo,
262
        $refundNo,
263
        $totalFee,
264
        $refundFee = null,
265
        $opUserId = null,
266
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS',
267
        $refundReason = ''
268
        ) {
269
        return $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, self::TRANSACTION_ID, $refundAccount, $refundReason);
270
    }
271
272
    /**
273
     * Query refund status.
274
     *
275
     * @param string $orderNo
276
     * @param string $type
277
     *
278
     * @return \EasyWeChat\Support\Collection
279
     */
280 1 View Code Duplication
    public function queryRefund($orderNo, $type = self::OUT_TRADE_NO)
281
    {
282
        $params = [
283 1
            $type => $orderNo,
284 1
        ];
285
286 1
        return $this->request($this->wrapApi(self::API_QUERY_REFUND), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...UERY_REFUND), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 286 which is incompatible with the return type documented by EasyWeChat\Payment\API::queryRefund of type EasyWeChat\Support\Collection.
Loading history...
287
    }
288
289
    /**
290
     * Query refund status by out_refund_no.
291
     *
292
     * @param string $refundNo
293
     *
294
     * @return \EasyWeChat\Support\Collection
295
     */
296
    public function queryRefundByRefundNo($refundNo)
297
    {
298
        return $this->queryRefund($refundNo, self::OUT_REFUND_NO);
299
    }
300
301
    /**
302
     * Query refund status by transaction_id.
303
     *
304
     * @param string $transactionId
305
     *
306
     * @return \EasyWeChat\Support\Collection
307
     */
308
    public function queryRefundByTransactionId($transactionId)
309
    {
310
        return $this->queryRefund($transactionId, self::TRANSACTION_ID);
311
    }
312
313
    /**
314
     * Query refund status by refund_id.
315
     *
316
     * @param string $refundId
317
     *
318
     * @return \EasyWeChat\Support\Collection
319
     */
320
    public function queryRefundByRefundId($refundId)
321
    {
322
        return $this->queryRefund($refundId, self::REFUND_ID);
323
    }
324
325
    /**
326
     * Download bill history as a table file.
327
     *
328
     * @param string $date
329
     * @param string $type
330
     *
331
     * @return \Psr\Http\Message\ResponseInterface
332
     */
333 1
    public function downloadBill($date, $type = self::BILL_TYPE_ALL)
334
    {
335
        $params = [
336 1
            'bill_date' => $date,
337 1
            'bill_type' => $type,
338 1
        ];
339
340 1
        return $this->request($this->wrapApi(self::API_DOWNLOAD_BILL), $params, 'post', [\GuzzleHttp\RequestOptions::STREAM => true], true)->getBody();
0 ignored issues
show
Bug introduced by
The method getBody does only exist in Psr\Http\Message\ResponseInterface, but not in EasyWeChat\Support\Collection.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
341
    }
342
343
    /**
344
     * Convert long url to short url.
345
     *
346
     * @param string $url
347
     *
348
     * @return \EasyWeChat\Support\Collection
349
     */
350 1
    public function urlShorten($url)
351
    {
352 1
        return $this->request(self::API_URL_SHORTEN, ['long_url' => $url]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request(self::API...y('long_url' => $url)); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 352 which is incompatible with the return type documented by EasyWeChat\Payment\API::urlShorten of type EasyWeChat\Support\Collection.
Loading history...
353
    }
354
355
    /**
356
     * Report API status to WeChat.
357
     *
358
     * @param string $api
359
     * @param int    $timeConsuming
360
     * @param string $resultCode
361
     * @param string $returnCode
362
     * @param array  $other         ex: err_code,err_code_des,out_trade_no,user_ip...
363
     *
364
     * @return \EasyWeChat\Support\Collection
365
     */
366
    public function report($api, $timeConsuming, $resultCode, $returnCode, array $other = [])
367
    {
368
        $params = array_merge([
369
            'interface_url' => $api,
370
            'execute_time_' => $timeConsuming,
371
            'return_code' => $returnCode,
372
            'return_msg' => null,
373
            'result_code' => $resultCode,
374
            'user_ip' => get_client_ip(),
375
            'time' => time(),
376
        ], $other);
377
378
        return $this->request($this->wrapApi(self::API_REPORT), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...:API_REPORT), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 378 which is incompatible with the return type documented by EasyWeChat\Payment\API::report of type EasyWeChat\Support\Collection.
Loading history...
379
    }
380
381
    /**
382
     * Get openid by auth code.
383
     *
384
     * @param string $authCode
385
     *
386
     * @return \EasyWeChat\Support\Collection
387
     */
388 1
    public function authCodeToOpenId($authCode)
389
    {
390 1
        return $this->request(self::API_AUTH_CODE_TO_OPENID, ['auth_code' => $authCode]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request(self::API...h_code' => $authCode)); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 390 which is incompatible with the return type documented by EasyWeChat\Payment\API::authCodeToOpenId of type EasyWeChat\Support\Collection.
Loading history...
391
    }
392
393
    /**
394
     * Merchant setter.
395
     *
396
     * @param Merchant $merchant
397
     *
398
     * @return $this
399
     */
400 1
    public function setMerchant(Merchant $merchant)
401
    {
402 1
        $this->merchant = $merchant;
403 1
    }
404
405
    /**
406
     * Merchant getter.
407
     *
408
     * @return Merchant
409
     */
410 1
    public function getMerchant()
411
    {
412 1
        return $this->merchant;
413
    }
414
415
    /**
416
     * Set sandbox mode.
417
     *
418
     * @param bool $enabled
419
     *
420
     * @return $this
421
     */
422 10
    public function sandboxMode($enabled = false)
423
    {
424 10
        $this->sandboxEnabled = $enabled;
425
426 10
        return $this;
427
    }
428
429
    /**
430
     * Make a API request.
431
     *
432
     * @param string $api
433
     * @param array  $params
434
     * @param string $method
435
     * @param array  $options
436
     * @param bool   $returnResponse
437
     *
438
     * @return \EasyWeChat\Support\Collection|\Psr\Http\Message\ResponseInterface
439
     */
440 10
    protected function request($api, array $params, $method = 'post', array $options = [], $returnResponse = false)
441
    {
442 10
        $params = array_merge($params, $this->merchant->only(['sub_appid', 'sub_mch_id']));
443
444 10
        $params['appid'] = $this->merchant->app_id;
445 10
        $params['mch_id'] = $this->merchant->merchant_id;
446 10
        $params['device_info'] = $this->merchant->device_info;
447 10
        $params['nonce_str'] = uniqid();
448 10
        $params = array_filter($params);
449
450 10
        $params['sign'] = generate_sign($params, $this->getSignkey($api), 'md5');
451
452 10
        $options = array_merge([
453 10
            'body' => XML::build($params),
454 10
        ], $options);
455
456 10
        $response = $this->getHttp()->request($api, $method, $options);
457
458 10
        return $returnResponse ? $response : $this->parseResponse($response);
459
    }
460
461
    /**
462
     * Return key to sign.
463
     *
464
     * @param string $api
465
     *
466
     * @return string
467
     */
468 10
    protected function getSignkey($api)
469
    {
470 10
        return $this->sandboxEnabled && $api !== self::API_SANDBOX_SIGN_KEY ? $this->getSandboxSignKey() : $this->merchant->key;
471
    }
472
473
    /**
474
     * Request with SSL.
475
     *
476
     * @param string $api
477
     * @param array  $params
478
     * @param string $method
479
     *
480
     * @return \EasyWeChat\Support\Collection
481
     */
482 2
    protected function safeRequest($api, array $params, $method = 'post')
483
    {
484
        $options = [
485 2
            'cert' => $this->merchant->get('cert_path'),
486 2
            'ssl_key' => $this->merchant->get('key_path'),
487 2
        ];
488
489 2
        return $this->request($api, $params, $method, $options);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($api, $params, $method, $options); of type Psr\Http\Message\Respons...Chat\Support\Collection adds the type Psr\Http\Message\ResponseInterface to the return on line 489 which is incompatible with the return type documented by EasyWeChat\Payment\API::safeRequest of type EasyWeChat\Support\Collection.
Loading history...
490
    }
491
492
    /**
493
     * Parse Response XML to array.
494
     *
495
     * @param ResponseInterface $response
496
     *
497
     * @return \EasyWeChat\Support\Collection
498
     */
499 9
    protected function parseResponse($response)
500
    {
501 9
        if ($response instanceof ResponseInterface) {
502
            $response = $response->getBody();
503
        }
504
505 9
        return new Collection((array) XML::parse($response));
506
    }
507
508
    /**
509
     * Wrap API.
510
     *
511
     * @param string $resource
512
     *
513
     * @return string
514
     */
515 8
    protected function wrapApi($resource)
516
    {
517 8
        return self::API_HOST.($this->sandboxEnabled ? '/sandboxnew' : '').$resource;
518
    }
519
520
    /**
521
     * Get sandbox sign key.
522
     *
523
     * @return string
524
     */
525 2
    protected function getSandboxSignKey()
526
    {
527 2
        if ($this->sandboxSignKey) {
528
            return $this->sandboxSignKey;
529
        }
530
531
        // Try to get sandbox_signkey from cache
532 2
        $cacheKey = 'sandbox_signkey.'.$this->merchant->merchant_id.$this->merchant->sub_merchant_id;
533
534
        /** @var \Doctrine\Common\Cache\Cache $cache */
535 2
        $cache = $this->getCache();
536
537 2
        $this->sandboxSignKey = $cache->fetch($cacheKey);
538
539 2
        if (!$this->sandboxSignKey) {
540
            // Try to acquire a new sandbox_signkey from WeChat
541
            $result = $this->request(self::API_SANDBOX_SIGN_KEY, []);
542
543
            if ($result->return_code === 'SUCCESS') {
544
                $cache->save($cacheKey, $result->sandbox_signkey, 24 * 3600);
545
546
                return $this->sandboxSignKey = $result->sandbox_signkey;
547
            }
548
549
            throw new Exception($result->return_msg);
550
        }
551
552 2
        return $this->sandboxSignKey;
553
    }
554
555
    /**
556
     * Return the cache manager.
557
     *
558
     * @return \Doctrine\Common\Cache\Cache
559
     */
560
    public function getCache()
561
    {
562
        return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir());
563
    }
564
}
565