Passed
Push — master ( 1f18bd...f696a0 )
by Carlos
03:08
created

API::getSignkey()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 2
nc 4
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 3
rs 10
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 float  $totalFee
214
     * @param float  $refundFee
215
     * @param string $opUserId
216
     * @param string $type
217
     * @param string $refundAccount
218
     *
219
     * @return \EasyWeChat\Support\Collection
220
     */
221 1
    public function refund(
222
        $orderNo,
223
        $refundNo,
224
        $totalFee,
225
        $refundFee = null,
226
        $opUserId = null,
227
        $type = self::OUT_TRADE_NO,
228
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
229
        ) {
230
        $params = [
231 1
            $type => $orderNo,
232 1
            'out_refund_no' => $refundNo,
233 1
            'total_fee' => $totalFee,
234 1
            'refund_fee' => $refundFee ?: $totalFee,
235 1
            'refund_fee_type' => $this->merchant->fee_type,
236 1
            'refund_account' => $refundAccount,
237 1
            'op_user_id' => $opUserId ?: $this->merchant->merchant_id,
238 1
        ];
239
240 1
        return $this->safeRequest($this->wrapApi(self::API_REFUND), $params);
241
    }
242
243
    /**
244
     * Refund by transaction id.
245
     *
246
     * @param string $orderNo
247
     * @param float  $totalFee
248
     * @param float  $refundFee
249
     * @param string $opUserId
250
     * @param string $refundAccount
251
     *
252
     * @return \EasyWeChat\Support\Collection
253
     */
254
    public function refundByTransactionId(
255
        $orderNo,
256
        $refundNo,
257
        $totalFee,
258
        $refundFee = null,
259
        $opUserId = null,
260
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
261
        ) {
262
        return $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, self::TRANSACTION_ID, $refundAccount);
263
    }
264
265
    /**
266
     * Query refund status.
267
     *
268
     * @param string $orderNo
269
     * @param string $type
270
     *
271
     * @return \EasyWeChat\Support\Collection
272
     */
273 1 View Code Duplication
    public function queryRefund($orderNo, $type = self::OUT_TRADE_NO)
274
    {
275
        $params = [
276 1
            $type => $orderNo,
277 1
        ];
278
279 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 279 which is incompatible with the return type documented by EasyWeChat\Payment\API::queryRefund of type EasyWeChat\Support\Collection.
Loading history...
280
    }
281
282
    /**
283
     * Query refund status by out_refund_no.
284
     *
285
     * @param string $refundNo
286
     *
287
     * @return \EasyWeChat\Support\Collection
288
     */
289
    public function queryRefundByRefundNo($refundNo)
290
    {
291
        return $this->queryRefund($refundNo, self::OUT_REFUND_NO);
292
    }
293
294
    /**
295
     * Query refund status by transaction_id.
296
     *
297
     * @param string $transactionId
298
     *
299
     * @return \EasyWeChat\Support\Collection
300
     */
301
    public function queryRefundByTransactionId($transactionId)
302
    {
303
        return $this->queryRefund($transactionId, self::TRANSACTION_ID);
304
    }
305
306
    /**
307
     * Query refund status by refund_id.
308
     *
309
     * @param string $refundId
310
     *
311
     * @return \EasyWeChat\Support\Collection
312
     */
313
    public function queryRefundByRefundId($refundId)
314
    {
315
        return $this->queryRefund($refundId, self::REFUND_ID);
316
    }
317
318
    /**
319
     * Download bill history as a table file.
320
     *
321
     * @param string $date
322
     * @param string $type
323
     *
324
     * @return \Psr\Http\Message\ResponseInterface
325
     */
326 1
    public function downloadBill($date, $type = self::BILL_TYPE_ALL)
327
    {
328
        $params = [
329 1
            'bill_date' => $date,
330 1
            'bill_type' => $type,
331 1
        ];
332
333 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...
334
    }
335
336
    /**
337
     * Convert long url to short url.
338
     *
339
     * @param string $url
340
     *
341
     * @return \EasyWeChat\Support\Collection
342
     */
343 1
    public function urlShorten($url)
344
    {
345 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 345 which is incompatible with the return type documented by EasyWeChat\Payment\API::urlShorten of type EasyWeChat\Support\Collection.
Loading history...
346
    }
347
348
    /**
349
     * Report API status to WeChat.
350
     *
351
     * @param string $api
352
     * @param int    $timeConsuming
353
     * @param string $resultCode
354
     * @param string $returnCode
355
     * @param array  $other         ex: err_code,err_code_des,out_trade_no,user_ip...
356
     *
357
     * @return \EasyWeChat\Support\Collection
358
     */
359
    public function report($api, $timeConsuming, $resultCode, $returnCode, array $other = [])
360
    {
361
        $params = array_merge([
362
            'interface_url' => $api,
363
            'execute_time_' => $timeConsuming,
364
            'return_code' => $returnCode,
365
            'return_msg' => null,
366
            'result_code' => $resultCode,
367
            'user_ip' => get_client_ip(),
368
            'time' => time(),
369
        ], $other);
370
371
        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 371 which is incompatible with the return type documented by EasyWeChat\Payment\API::report of type EasyWeChat\Support\Collection.
Loading history...
372
    }
373
374
    /**
375
     * Get openid by auth code.
376
     *
377
     * @param string $authCode
378
     *
379
     * @return \EasyWeChat\Support\Collection
380
     */
381 1
    public function authCodeToOpenId($authCode)
382
    {
383 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 383 which is incompatible with the return type documented by EasyWeChat\Payment\API::authCodeToOpenId of type EasyWeChat\Support\Collection.
Loading history...
384
    }
385
386
    /**
387
     * Merchant setter.
388
     *
389
     * @param Merchant $merchant
390
     *
391
     * @return $this
392
     */
393 1
    public function setMerchant(Merchant $merchant)
394
    {
395 1
        $this->merchant = $merchant;
396 1
    }
397
398
    /**
399
     * Merchant getter.
400
     *
401
     * @return Merchant
402
     */
403 1
    public function getMerchant()
404
    {
405 1
        return $this->merchant;
406
    }
407
408
    /**
409
     * Set sandbox mode.
410
     *
411
     * @param bool $enabled
412
     *
413
     * @return $this
414
     */
415 10
    public function sandboxMode($enabled = false)
416
    {
417 10
        $this->sandboxEnabled = $enabled;
418
419 10
        return $this;
420
    }
421
422
    /**
423
     * Make a API request.
424
     *
425
     * @param string $api
426
     * @param array  $params
427
     * @param string $method
428
     * @param array  $options
429
     * @param bool   $returnResponse
430
     *
431
     * @return \EasyWeChat\Support\Collection|\Psr\Http\Message\ResponseInterface
432
     */
433 10
    protected function request($api, array $params, $method = 'post', array $options = [], $returnResponse = false)
434
    {
435 10
        $params = array_merge($params, $this->merchant->only(['sub_appid', 'sub_mch_id']));
436
437 10
        $params['appid'] = $this->merchant->app_id;
438 10
        $params['mch_id'] = $this->merchant->merchant_id;
439 10
        $params['device_info'] = $this->merchant->device_info;
440 10
        $params['nonce_str'] = uniqid();
441 10
        $params = array_filter($params);
442
443 10
        $params['sign'] = generate_sign($params, $this->getSignkey($api), 'md5');
444
445 10
        $options = array_merge([
446 10
            'body' => XML::build($params),
447 10
        ], $options);
448
449 10
        $response = $this->getHttp()->request($api, $method, $options);
450
451 10
        return $returnResponse ? $response : $this->parseResponse($response);
452
    }
453
454
    /**
455
     * Return key to sign.
456
     *
457
     * @param string $api
458
     *
459
     * @return string
460
     */
461 10
    protected function getSignkey($api)
462
    {
463 10
        return $this->sandboxEnabled && $api != self::API_SANDBOX_SIGN_KEY ? $this->getSandboxSignKey() : $this->merchant->key;
464
    }
465
466
    /**
467
     * Request with SSL.
468
     *
469
     * @param string $api
470
     * @param array  $params
471
     * @param string $method
472
     *
473
     * @return \EasyWeChat\Support\Collection
474
     */
475 2
    protected function safeRequest($api, array $params, $method = 'post')
476
    {
477
        $options = [
478 2
            'cert' => $this->merchant->get('cert_path'),
479 2
            'ssl_key' => $this->merchant->get('key_path'),
480 2
        ];
481
482 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 482 which is incompatible with the return type documented by EasyWeChat\Payment\API::safeRequest of type EasyWeChat\Support\Collection.
Loading history...
483
    }
484
485
    /**
486
     * Parse Response XML to array.
487
     *
488
     * @param ResponseInterface $response
489
     *
490
     * @return \EasyWeChat\Support\Collection
491
     */
492 9
    protected function parseResponse($response)
493
    {
494 9
        if ($response instanceof ResponseInterface) {
495
            $response = $response->getBody();
496
        }
497
498 9
        return new Collection((array) XML::parse($response));
499
    }
500
501
    /**
502
     * Wrap API.
503
     *
504
     * @param string $resource
505
     *
506
     * @return string
507
     */
508 8
    protected function wrapApi($resource)
509
    {
510 8
        return self::API_HOST.($this->sandboxEnabled ? '/sandboxnew' : '').$resource;
511
    }
512
513
    /**
514
     * Get sandbox sign key.
515
     *
516
     * @return string
517
     */
518 2
    protected function getSandboxSignKey()
519
    {
520 2
        if ($this->sandboxSignKey) {
521
            return $this->sandboxSignKey;
522
        }
523
524
        // Try to get sandbox_signkey from cache
525 2
        $cacheKey = 'sandbox_signkey.'.$this->merchant->merchant_id.$this->merchant->sub_merchant_id;
526
527
        /** @var \Doctrine\Common\Cache\Cache $cache */
528 2
        $cache = $this->getCache();
529
530 2
        $this->sandboxSignKey = $cache->fetch($cacheKey);
531
532 2
        if (!$this->sandboxSignKey) {
533
            // Try to acquire a new sandbox_signkey from WeChat
534
            $result = $this->request(self::API_SANDBOX_SIGN_KEY, []);
535
536
            if ($result->return_code === 'SUCCESS') {
537
                $cache->save($cacheKey, $result->sandbox_signkey);
538
539
                return $this->sandboxSignKey = $result->sandbox_signkey;
540
            }
541
542
            throw new Exception($result->return_msg);
543
        }
544
545 2
        return $this->sandboxSignKey;
546
    }
547
548
    /**
549
     * Return the cache manager.
550
     *
551
     * @return \Doctrine\Common\Cache\Cache
552
     */
553
    public function getCache()
554
    {
555
        return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir());
556
    }
557
}
558